mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
add map reduce docs
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "95a87145-34d0-4f97-b45f-5c9fd8532c8a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Map Reduce\n",
|
||||
"\n",
|
||||
"A common pattern in agents is to generate a list of objects, do some work on each of those objects, and then combine the results. This is very similar to the common [map-reduce](https://en.wikipedia.org/wiki/MapReduce) operation. This can be tricky for a few reasons. First, it can be tought to define in structured graph ahead of time because the length of the list of objects may be unknown. Second, in order to do this map-reduce you need multiple versions of the state to exist... but the graph shares a common shared state, so how can this be?\n",
|
||||
"\n",
|
||||
"LangGraph supports this via the `Send` api. This can be used to allow a conditional edge to `Send` multiple different states to multiple nodes. The state it sends can be different from the state of the core graph.\n",
|
||||
"\n",
|
||||
"Let's see what this looks like in action! We'll put together a toy example of generating a list of words, and then writing a joke about each word."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "0f0f78e4-423d-4e2d-aa1a-01efaec4715f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'generate_topics': {'subjects': ['cat', 'dog', 'elephant', 'lion', 'tiger']}}\n",
|
||||
"{'generate_joke': {'jokes': ['Why did the tiger lose at poker? Because he was playing with a cheetah!']}}\n",
|
||||
"{'generate_joke': {'jokes': [\"Why don't elephants use computers? Because they're afraid of the mouse!\"]}}\n",
|
||||
"{'generate_joke': {'jokes': ['Why did the lion eat the tightrope walker? He wanted a well-balanced meal!']}}\n",
|
||||
"{'generate_joke': {'jokes': ['Why was the cat sitting on the computer? Because it wanted to keep an eye on the mouse!']}}\n",
|
||||
"{'generate_joke': {'jokes': [\"Why do dogs run in circles before lying down? Because they're trying to make a 'ruff' impression!\"]}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langgraph.constants import Send\n",
|
||||
"from langgraph.graph import END, Graph, StateGraph\n",
|
||||
"import operator\n",
|
||||
"from typing import TypedDict, Annotated\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel\n",
|
||||
"\n",
|
||||
"# Model and prompts\n",
|
||||
"# Define model and prompts we will use\n",
|
||||
"subjects_prompt = \"\"\"Generate a comma separated list of between 2 and 5 {topic}.\"\"\"\n",
|
||||
"joke_prompt = \"\"\"Generate a joke about {subject}\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Subjects(BaseModel):\n",
|
||||
" subjects: list[str]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Joke(BaseModel):\n",
|
||||
" joke: str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"model = ChatOpenAI()\n",
|
||||
"\n",
|
||||
"# Graph components: define the components that will make up the graph\n",
|
||||
"\n",
|
||||
"# This will be the overall state of the main graph.\n",
|
||||
"# It will contain a topic (which we expect the user to provide)\n",
|
||||
"# and then will generate a list of subjects, and then a joke for\n",
|
||||
"# each subject\n",
|
||||
"class OverallState(TypedDict):\n",
|
||||
" topic: str\n",
|
||||
" subjects: list\n",
|
||||
" # Notice here we use the operator.add\n",
|
||||
" # This is because we want combine all the jokes we generate\n",
|
||||
" # from individual nodes back into one list - this is essentially\n",
|
||||
" # the \"reduce\" part\n",
|
||||
" jokes: Annotated[list, operator.add]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# This will be the state of the node that we will \"map\" all\n",
|
||||
"# subjects to in order to generate a joke\n",
|
||||
"class JokeState(TypedDict):\n",
|
||||
" subject: str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# This is the function we will use to generate the subjects of the jokes\n",
|
||||
"def generate_topics(state: OverallState):\n",
|
||||
" prompt = subjects_prompt.format(topic=state['topic'])\n",
|
||||
" response = model.with_structured_output(Subjects).invoke(prompt)\n",
|
||||
" return {\"subjects\": response.subjects}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Here we generate a joke, given a subject\n",
|
||||
"def generate_joke(state: JokeState):\n",
|
||||
" prompt = joke_prompt.format(subject=state['subject'])\n",
|
||||
" response = model.with_structured_output(Joke).invoke(prompt)\n",
|
||||
" return {\"jokes\": [response.joke]}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Here we define the logic to map out over the generated subjects\n",
|
||||
"# We will use this an edge in the graph\n",
|
||||
"def continue_to_jokes(state: OverallState):\n",
|
||||
" # We will return a list of `Send` objects\n",
|
||||
" # Each `Send` object consists of the name of a node in the graph\n",
|
||||
" # as well as the state to send to that node\n",
|
||||
" return [Send(\"generate_joke\", {\"subject\": s}) for s in state['subjects']]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Construct the graph: here we put everything together to construct our graph\n",
|
||||
"graph = StateGraph(OverallState)\n",
|
||||
"graph.add_node(\"generate_topics\", generate_topics)\n",
|
||||
"graph.add_node(\"generate_joke\", generate_joke)\n",
|
||||
"graph.set_entry_point(\"generate_topics\")\n",
|
||||
"graph.add_conditional_edges(\"generate_topics\", continue_to_jokes)\n",
|
||||
"graph.add_edge(\"generate_joke\", END)\n",
|
||||
"app = graph.compile()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Call the graph: here we call it to generate a list of jokes\n",
|
||||
"for s in app.stream({\"topic\": \"animals\"}):\n",
|
||||
" print(s)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "37ed1f71-63db-416f-b715-4617b33d4b7f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -497,7 +497,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
"version": "3.11.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
Reference in New Issue
Block a user