https://academy.langchain.com/courses/intro-to-langgraphhttps://github.com/shangxiang0907/langchain-academy文章目录Map-Reduce映射-归约Review 复习Goals 目标Problem 问题State 状态Parallelizing joke generation 并行化笑话生成Joke generation (map) 笑话生成mapBest joke selection (reduce) 最佳笑话筛选reduceCompile 编译StudioMap-Reduce映射-归约Review 复习We’re building up to a multi-agent research assistant that ties together all of the modules from this course.我们正在构建一个多功能智能体研究助手该助手将整合本课程所有模块的内容。To build this multi-agent assistant, we’ve been introducing a few LangGraph controllability topics.为构建这一多智能体助手我们已陆续介绍了若干 LangGraph 可控性主题。We just covered parallelization and sub-graphs.我们刚刚讲解了并行化与子图。Goals 目标Now, we’re going to cover map reduce.接下来我们将学习 map reduce。%%capture--no-stderr%pip install-U langchain_openai langgraphimportos,getpassdef_set_env(var:str):ifnotos.environ.get(var):os.environ[var]getpass.getpass(f{var}: )fromdotenvimportfind_dotenv,load_dotenv load_dotenv(find_dotenv(usecwdTrue))_set_env(OPENAI_API_KEY)We’ll use LangSmith for tracing.我们将使用 LangSmith 进行 追踪。_set_env(LANGSMITH_API_KEY)os.environ[LANGSMITH_TRACING]trueos.environ[LANGSMITH_PROJECT]langchain-academyProblem 问题Map-reduce operations are essential for efficient task decomposition and parallel processing.Map-reduce 操作对于高效任务分解与并行处理至关重要。It has two phases:它包含两个阶段(1)Map- Break a task into smaller sub-tasks, processing each sub-task in parallel.1Map—— 将一项任务拆分为若干更小的子任务并对每个子任务进行并行处理。(2)Reduce- Aggregate the results across all of the completed, parallelized sub-tasks.2Reduce—— 汇总所有已完成的并行化子任务的结果。Let’s design a system that will do two things:让我们设计一个具备以下两项功能的系统(1)Map- Create a set of jokes about a topic.1Map—— 为某一主题生成一组笑话。(2)Reduce- Pick the best joke from the list.2Reduce—— 从该列表中选出最佳笑话。We’ll use an LLM to do the job generation and selection.我们将使用 LLM 完成任务生成与筛选工作。importosfromlangchain_openaiimportChatOpenAI# Prompts we will usesubjects_promptGenerate a list of 3 sub-topics that are all related to this overall topic: {topic}.joke_promptGenerate a joke about {subject}best_joke_promptBelow are a bunch of jokes about {topic}. Select the best one! Return the ID of the best one, starting 0 as the ID for the first joke. Jokes: \n\n {jokes}# LLMmodelChatOpenAI(modelos.getenv(OPENAI_MODEL,qwen-plus),base_urlos.getenv(OPENAI_BASE_URL,https://dashscope.aliyuncs.com/compatible-mode/v1),temperature0)State 状态Parallelizing joke generation 并行化笑话生成First, let’s define the entry point of the graph that will:首先定义图的入口点其功能包括Take a user input topic接收用户输入的主题Produce a list of joke topics from it从中生成一组笑话主题Send each joke topic to our above joke generation node将每个笑话主题发送至上述笑话生成节点Our state has ajokeskey, which will accumulate jokes from parallelized joke generation我们的状态中有一个jokes键用于累积来自并行化笑话生成的结果importoperatorfromtypingimportAnnotatedfromtyping_extensionsimportTypedDictfrompydanticimportBaseModelclassSubjects(BaseModel):subjects:list[str]classBestJoke(BaseModel):id:intclassOverallState(TypedDict):topic:strsubjects:listjokes:Annotated[list,operator.add]best_selected_joke:strGenerate subjects for jokes.生成笑话主题。defgenerate_topics(state:OverallState):promptsubjects_prompt.format(topicstate[topic])responsemodel.with_structured_output(Subjects).invoke(prompt)return{subjects:response.subjects}Here is the magic: we use the Send to create a joke for each subject.此处的关键在于我们使用 Send 为每个主题生成一则笑话。This is very useful!这非常实用It can automatically parallelize joke generation for any number of subjects.它能自动为任意数量的主题并行化笑话生成。generate_joke: the name of the node in the graphgenerate_joke图中节点的名称{subject: s}: the state to send{subject: s}待发送的状态Sendallow you to pass any state that you want togenerate_joke!Send允许你向generate_joke传递任意所需状态It does not have to align withOverallState.该状态无需与OverallState对齐。In this case,generate_jokeis using its own internal state, and we can populate this viaSend.本例中generate_joke使用其自身的内部状态而我们可通过Send填充该状态。fromlanggraph.typesimportSenddefcontinue_to_jokes(state:OverallState):return[Send(generate_joke,{subject:s})forsinstate[subjects]]Joke generation (map) 笑话生成mapNow, we just define a node that will create our jokes,generate_joke!现在我们只需定义一个用于生成笑话的节点generate_jokeWe write them back out tojokesinOverallState!我们将生成的笑话写回OverallState中的jokes字段This key has a reducer that will combine lists.该字段配备了一个用于合并列表的归约器reducer。classJokeState(TypedDict):subject:strclassJoke(BaseModel):joke:strdefgenerate_joke(state:JokeState):promptjoke_prompt.format(subjectstate[subject])responsemodel.with_structured_output(Joke).invoke(prompt)return{jokes:[response.joke]}Best joke selection (reduce) 最佳笑话筛选reduceNow, we add logic to pick the best joke.接下来我们添加逻辑以筛选出最佳笑话。defbest_joke(state:OverallState):jokes\n\n.join(state[jokes])promptbest_joke_prompt.format(topicstate[topic],jokesjokes)responsemodel.with_structured_output(BestJoke).invoke(prompt)return{best_selected_joke:state[jokes][response.id]}Compile 编译fromIPython.displayimportImagefromlanggraph.graphimportEND,StateGraph,START# Construct the graph: here we put everything together to construct our graphgraphStateGraph(OverallState)graph.add_node(generate_topics,generate_topics)graph.add_node(generate_joke,generate_joke)graph.add_node(best_joke,best_joke)graph.add_edge(START,generate_topics)graph.add_conditional_edges(generate_topics,continue_to_jokes,[generate_joke])graph.add_edge(generate_joke,best_joke)graph.add_edge(best_joke,END)# Compile the graphappgraph.compile()Image(app.get_graph().draw_mermaid_png())# Call the graph: here we call it to generate a list of jokesforsinapp.stream({topic:animals}):print(s){generate_topics: {subjects: [mammals, reptiles, birds]}} {generate_joke: {jokes: [Why dont mammals ever get lost? Because they always follow their instincts!]}} {generate_joke: {jokes: [Why dont alligators like fast food? Because they cant catch it!]}} {generate_joke: {jokes: [Why do birds fly south for the winter? Because its too far to walk!]}} {best_joke: {best_selected_joke: Why dont alligators like fast food? Because they cant catch it!}}Studio⚠️ Notice⚠️ 注意Since filming these videos, we’ve updated Studio so that it can now be run locally and accessed through your browser.自录制这些视频以来我们已更新 Studio使其支持本地运行并通过浏览器访问。This is the preferred way to run Studio instead of using the Desktop App shown in the video.推荐采用此方式运行 Studio而非视频中演示的桌面应用。It is now calledLangSmith Studioinstead ofLangGraph Studio.它现被称为LangSmith Studio而非LangGraph Studio。Detailed setup instructions are available in the “Getting Setup” guide at the start of the course.详细安装说明请参阅本课程开篇的“环境准备”指南。You can find a description of Studio here, and specific details for local deployment here.您可在此处查阅 Studio 的介绍 链接以及本地部署的具体细节 链接。To start the local development server, run the following command in your terminal in the/studiodirectory in this module:要在本地启动开发服务器请在本模块的/studio目录下于终端中运行以下命令langgraph devYou should see the following output:您应看到如下输出- API: http://127.0.0.1:2024 - Studio UI: https://smith.langchain.com/studio/?baseUrlhttp://127.0.0.1:2024 - API Docs: http://127.0.0.1:2024/docsOpen your browser and navigate to theStudio UIURL shown above.打开浏览器并导航至上方显示的Studio UIURL。Let’s load the above graph in the Studio UI, which usesmodule-4/studio/map_reduce.pyset inmodule-4/studio/langgraph.json.让我们在 Studio UI 中加载上述图其对应文件为module-4/studio/map_reduce.py并在module-4/studio/langgraph.json中指定。