LangGraph 深度解析:如何构建具备“循环思维”的生产级智能体

关键词:LangGraph, LLM Agent, 循环思维, 状态机, 多Agent, 生产级应用, LangChain生态

摘要:在大语言模型(LLM)从“单次调用工具”到“自主循环思考”的跃迁中,LangChain官方推出的LangGraph彻底打破了传统LangChain“链式调用的线性局限性。本文将以“小学生搭积木机器人下棋”为核心故事线,一步一步带你从什么是LangGraph、它的核心概念(节点、边、状态、循环控制、工具集成、多Agent协作”,再到如何从零搭建一个具备思考-验证-纠错-循环的单Agent工具调用系统**,接着扩展到多Agent生产级架构,最终覆盖数学模型、算法原理、最佳实践、行业趋势等全维度内容。无论你是刚接触LLM Agent小白,还是正在寻找生产级Agent框架的工程师,都能通过这篇文章像玩游戏一样轻松掌握LangGraph的精髓。


背景介绍

为什么需要LangGraph:传统LangChain的“线性死亡陷阱”

让我们从“搭积木机器人下棋”的故事开始

想象一下,你刚买了一个超酷炫的搭积木机器人——传统的LangChain其实就是这个机器人的「预设线性轨道」:你给它铺好一条“第一步选积木→第二步拼底座→第三步拼轮子→第四步拼棋子→第五步拼棋盘→完成!”的轨道,它只会顺着轨道往下走,绝不回头,绝不会半路发现积木颜色不对也不管轮子歪了更不管,哪怕拼好的棋子散架了更不会重新拼,直接到终点喊“完成”——你确定这叫“机器人”?这不就是个“自动传送带嘛!

那我们希望的**“真·搭积木机器人(生产级智能体)** 是什么样的呢?它得会:

  1. “看看现在有什么积木、自己要拼什么(维护当前的「状态」)**;
  2. 选一块积木(执行一个「节点」)**;
  3. 拼上去之后,低头检查一下对不对、有没有问题(验证节点、检查状态、调用工具)**;
  4. 如果对,就继续往下;如果不对,就回到前几步重新选重新拼(走「循环边」「条件边」回到之前的节点,不是回到起点!)**;
  5. 甚至如果自己搞不定拼轮子,就喊旁边的“轮子专家机器人”来帮忙拼(多Agent协作,通过状态共享)**;
  6. 最后拼好整个棋盘和棋子后,还要跟你下一局,输了还会复盘总结经验到自己的“记忆”里(维护长期状态)**;
  7. 下次再拼会避开上次的错误,还会用上次总结的经验选更好的积木(循环利用长期状态和短期状态)**!

好的,那这个“真·搭积木机器人”的功能,传统的LangChain线性链能不能实现?其实也不是完全不能——比如用SequentialChain加各种RouterChain加各种Callback加各种Memory,但写出来的代码会像一团**用碎布片缝的口袋一样:逻辑混乱、难以维护、难以调试、难以扩展、很难保证生产级的稳定性!

传统LangChain线性链的三个核心痛点(也就是“线性死亡陷阱”的三个标志):
  1. **状态难以统一管理:每个Chain的输入输出都是“黑盒”——你不知道下一个Chain能不能拿到上一个Chain的所有信息?要把每个Chain的输出塞进Memory里,塞进去还要取出来还要处理冲突,烦都烦死了;
  2. **完全没有显式的循环控制:传统Chain都是线性的,没有办法显式地指定“如果拼错了回到第二步选积木的地方”——只能靠`while循环套Chain,调试的时候根本不知道机器人现在在轨道的哪一环绕圈;
  3. **条件分支、循环、工具调用、多Agent协作都是“补丁式”叠加”:写出来的代码别人看不懂,自己过两天自己也看不懂,稍微改一点需求就要重构整个代码库!

那这个时候,谁来拯救我们的“真·搭积木机器人”呢?没错!就是我们今天的主角——**LangGraph!

LangChain官方团队看到了大家的“线性死亡陷阱”,于是推出了LangGraph——一个专门为**具备“循环思维”“自主思考”“多Agent协作”“状态统一管理”设计的生产级Agent框架!

本文的目的和范围

目的:

本文的目的是像给小学生讲故事一样,一步一步带你从0到1,再从1到N,掌握LangGraph的核心概念、核心算法、核心代码实现、生产级架构设计、最佳实践、行业趋势,让你能够用LangGraph搭建出真正有用的、稳定的、可扩展的、具备“循环思维”的生产级智能体!

范围:

本文的范围包括但不限于:

  1. **LangGraph的核心概念(节点、边、状态、状态机、工具集成、短期状态、长期状态、条件边、循环边、终止边、起始节点、终止节点);
  2. **LangGraph的核心架构与核心算法(LangGraph State Machine Algorithm、状态压缩算法);
  3. **LangGraph的核心数学模型(马尔可夫决策过程(MDP)扩展、状态空间模型);
  4. **从零到1用LangGraph搭建一个具备“思考-验证-纠错-循环”的单Agent“拼写检查+润色工具调用系统;
  5. **从1到N用LangGraph搭建一个具备“产品经理→UI设计师→前端开发→测试工程师”的四角色多Agent协作内容创作系统;
  6. **LangGraph的生产级最佳实践(状态管理最佳实践、循环控制最佳实践、工具调用最佳实践、多Agent协作最佳实践、调试最佳实践、部署最佳实践);
  7. **LangGraph的行业发展与未来趋势(LLM Agent框架的演变历史、LangGraph与其他Agent框架的对比(AutoGen、CrewAI、AutoGPT、BabyAGI)、LangGraph的未来发展方向);
  8. **LangGraph的常见问题与解答、扩展阅读与参考资料。
不在本文范围的内容:
  1. **LangChain的基础入门知识(本文默认你已经有了基本的LangChain入门知识,比如知道什么是LLM、什么是ChatOpenAI、什么是Tool、什么是Memory);
  2. **LLM的基础原理(本文默认你已经有了基本的LLM基础原理知识,比如知道什么是Transformer、什么是Token);
  3. **其他LLM Agent框架的深入解析(本文只会简单对比其他Agent框架,不会深入解析);
  4. **LangGraph的高级功能(本文只会讲LangGraph的核心生产级功能,不会讲LangGraph的实验性功能)。

本文的预期读者

本文的预期读者包括但不限于:

  1. **刚接触LLM Agent的小白(通过“搭积木机器人下棋”的故事线,轻松理解LangGraph的核心概念);
  2. **正在使用传统LangChain开发Agent,遇到“线性死亡陷阱”的工程师(找到解决痛点的方法);
  3. **正在寻找生产级Agent框架的架构师(找到生产级Agent架构的最佳实践);
  4. **对LLM Agent感兴趣的产品经理(了解生产级Agent的功能和架构);
  5. **对LLM Agent领域的研究者(了解LangGraph的核心算法和数学模型)。

本文的文档结构概述

本文的文档结构将按照“**小学生搭积木机器人下棋”的故事线,一步一步展开:

  1. 背景介绍:通过“搭积木机器人下棋”的故事引出传统LangChain的线性死亡陷阱,然后介绍LangGraph的出现,最后介绍本文的目的、范围、预期读者、文档结构概述、术语表;
  2. 核心概念与联系:通过“搭积木机器人下棋”的故事,一步一步解释LangGraph的核心概念,然后解释核心概念之间的关系,最后给出核心概念原理和架构的文本示意图和Mermaid流程图;
  3. 核心算法原理 & 具体操作步骤:详细讲解LangGraph的核心算法原理(LangGraph State Machine Algorithm、状态压缩算法),然后给出具体的操作步骤(创建State、创建Node、创建Edge、编译StateGraph、调用StateGraph);
  4. 数学模型和公式 & 详细讲解 & 举例说明:详细讲解LangGraph的核心数学模型(马尔可夫决策过程(MDP)扩展、状态空间模型,然后给出具体的公式和举例说明;
  5. 项目实战一:从零到1单Agent拼写检查+润色系统:详细讲解如何从零到1用LangGraph搭建一个具备“思考-验证-纠错-循环”的单Agent拼写检查+润色工具调用系统,包括开发环境搭建、源代码详细实现和代码解读、代码解读与分析;
  6. 项目实战二:从1到N四角色多Agent内容创作系统:详细讲解如何从1到N用LangGraph搭建一个具备“产品经理→UI设计师→前端开发→测试工程师”的四角色多Agent协作内容创作系统,包括项目介绍、环境安装、系统功能设计、系统架构设计、系统接口设计、系统核心实现源代码、最佳实践tips;
  7. LangGraph的生产级最佳实践:详细讲解LangGraph的生产级最佳实践(状态管理最佳实践、循环控制最佳实践、工具调用最佳实践、多Agent协作最佳实践、调试最佳实践、部署最佳实践);
  8. 行业发展与未来趋势:详细讲解LLM Agent框架的演变历史(AutoGPT→BabyAGI→LangChain→AutoGen→CrewAI→LangGraph),然后给出LangGraph与其他Agent框架的对比(AutoGen、CrewAI、AutoGPT、BabyAGI)的markdown表格,最后讲解LangGraph的未来发展方向;
  9. 总结:学到了什么?:总结本文的主要内容,再次用“搭积木机器人下棋”的故事强调核心概念和它们之间的关系;
  10. 思考题:动动小脑筋:提出一些思考题,鼓励读者进一步思考和应用所学知识;
  11. 附录:常见问题与解答:解答一些LangGraph的常见问题;
  12. 扩展阅读 & 参考资料:列出一些LangGraph的扩展阅读和参考资料。

本文的术语表

核心术语定义
  1. LangGraph:LangChain官方推出的,专门为具备“循环思维”“自主思考”“多Agent协作”“状态统一管理”设计的生产级Agent框架,本质上是一个**显式的状态机;
  2. LLM Agent:基于大语言模型(LLM)的智能体,能够自主完成特定的任务,具备“感知→思考→决策→行动→验证→循环”的能力;
  3. 状态(State):Agent当前的“处境”,包括但不限于当前的任务、当前的对话历史、当前的工具调用结果、当前的验证结果、当前的长期记忆;
  4. 节点(Node):Agent的“行动单元”,可以是LLM调用、工具调用、状态更新、终止检查;
  5. 边(Edge):Agent的“行动规则”,可以是无条件边、条件边、循环边、终止边;
  6. 状态机(State Machine):一种数学模型,由一组状态、一组输入、一组转换函数、起始状态、终止状态组成;
  7. 工具(Tool):Agent可以调用的外部资源,比如拼写检查API、天气查询API、数据库查询API、代码执行API;
  8. 多Agent协作(Multi-Agent Collaboration):多个Agent通过共享状态或通信完成特定的任务;
  9. 短期状态(Short-Term State):Agent在当前任务执行过程中维护的状态,任务结束后可能会被清除;
  10. 长期状态(Long-Term State):Agent在多个任务执行过程中维护的状态,任务结束后会被保存下来。
相关概念解释
  1. LangChain:一个用于构建LLM应用的开源框架,提供了丰富的组件(LLM、Tool、Memory、Chain、Agent);
  2. Transformer:一种基于自注意力机制的深度学习模型,是当前主流LLM的基础架构;
  3. Token:LLM处理文本的基本单位,可以是一个字、一个词、一个标点符号;
  4. 马尔可夫决策过程(MDP):一种数学模型,用于描述决策过程,由一组状态、一组动作、一组转换概率、一组奖励函数、起始状态、终止状态组成;
  5. Callback:LangChain中用于在Chain或Agent执行过程中触发特定事件的函数。
缩略词列表
  1. LLM:Large Language Model,大语言模型;
  2. Agent:智能体;
  3. API:Application Programming Interface,应用程序编程接口;
  4. MDP:Markov Decision Process,马尔可夫决策过程;
  5. Mermaid:一种用于绘制流程图、状态机图、架构图的开源工具;
  6. JSON:JavaScript Object Notation,一种轻量级的数据交换格式;
  7. Pydantic:一个用于数据验证和设置管理的Python库;
  8. RAG:Retrieval-Augmented Generation,检索增强生成。

核心概念与联系

故事引入:“搭积木机器人下棋的进阶版

在上一章的背景介绍中,我们已经知道了传统的LangChain线性链就像一个“自动传送带”,只能顺着预设的轨道往下走,绝不回头,绝不会纠错,绝不会协作,很难搭建出“真·搭积木机器人”。

那现在,我们把这个“真·搭积木机器人下棋”的故事再细化一下,让它更具体,然后一步一步引出LangGraph的核心概念:


小明的“真·搭积木机器人”叫小朗(Lang),它有以下几个重要的部分:

  1. 小朗的“大脑袋(State Box”——这是小朗的“状态箱”**,里面装着所有小朗需要知道的信息:
    • 短期信息(Short-Term State):当前要拼的是“象棋棋盘+红黑棋子各一套”、已经拼好的部分、刚才拼错的地方、刚才用的什么工具、刚才的验证结果;
    • 长期信息(Long-Term State):上次拼象棋的经验、上次输棋的复盘、小朗喜欢用的积木颜色、小朗的朋友(其他机器人朋友的联系方式);
  2. 小朗的“手手脚脚(Node)”——这是小朗的“行动单元”**,也就是小朗能做的事情:
    • 思考节点(Think Node):小朗看看状态箱里的信息,想想要做什么;
    • 选积木节点(Select Block Node):小朗从积木堆里选一块合适的积木;
    • 拼积木节点(Assemble Block Node):小朗把选好的积木拼上去;
    • 检查积木节点(Check Block Node):小朗低头检查一下拼的对不对、有没有问题;
    • 调用工具节点(Call Tool Node):比如小朗拼轮子拼不好,就调用旁边的“轮子测量工具”来帮忙测量一下轮子的大小;
    • 喊朋友节点(Call Friend Node):比如小朗拼完棋盘后,喊旁边的“象棋大师机器人(Chess Master Friend)来帮忙下一局;
    • 复盘总结节点(Review Node):比如小朗输了棋,就复盘总结经验,把经验放进长期信息里;
    • 终止节点(End Node):小朗完成了所有任务,停下来休息;
  3. 小朗的“指令手册(Edge)”——这是小朗的“行动规则”**,也就是小朗做完一件事情之后,接下来要做什么:
    • 无条件指令(Unconditional Edge):比如小朗做完“选积木节点”之后,必须做“拼积木节点”;
    • 条件指令(Conditional Edge):比如小朗做完“检查积木节点”之后,如果检查结果是“对的”,就做“下一个步骤的节点”;如果检查结果是“错的”,就做“回到选积木节点”;如果检查结果是“需要测量轮子大小”,就做“调用工具节点”;
    • 循环指令(Loop Edge):其实循环指令本质上是一种特殊的条件指令,比如小朗和象棋大师机器人下棋的时候,如果没有分出胜负,就回到“下棋节点”继续下;
    • 终止指令(End Edge):比如小朗做完“复盘总结节点”之后,就做“终止节点”;
  4. 小朗的“启动按钮(Start Node)”——这是小朗的“起始节点”**,按下启动按钮之后,小朗就从这里开始;

好的,现在我们现在我们的“真·搭积木机器人小朗”的故事就讲完了!接下来,我们就根据这个故事,一步一步解释LangGraph的核心概念!

核心概念解释(像给小学生讲故事一样)

在上一节的故事引入中,我们已经通过“真·搭积木机器人小朗”的故事,初步了解了LangGraph的核心概念:状态箱(State)、手手脚脚(Node)、指令手册(Edge)、启动按钮(Start Node)。接下来,我们再详细地、像给小学生讲故事一样解释每一个核心概念!

核心概念一:状态箱(State)——小朗的“大脑袋”

什么是LangGraph的State呢?我们可以用“真·搭积木机器人小朗的“大脑袋”来比喻:

LangGraph的State就像小朗的“大脑袋”,里面装着所有小朗需要知道的信息——不管是当前的短期的、长期的、还是临时的,都装在里面!

那为什么LangGraph的State这么重要呢?因为在传统的LangChain线性链中,每个Chain的输入输出都是“黑盒”,你不知道下一个Chain能不能拿到上一个Chain的所有信息,要把每个Chain的输出塞进Memory里,塞进去还要取出来还要处理冲突,烦都烦死了!而在LangGraph中,State是统一管理的——所有的Node都可以读写同一个State,就像所有的手手脚脚都可以看同一个大脑袋里的信息!

那LangGraph的State具体是什么样子的呢?在LangGraph的State可以是任何Python字典类型,比如Python内置的类型(int、float、str、list、dict),也可以是Pydantic模型——推荐使用Pydantic模型,因为Pydantic模型可以进行数据验证类型提示,让你的代码更稳定、更易读!

我们现在我们用“真·搭积木机器人小朗”的State来举个例子,用Pydantic模型来定义小朗的State:

from typing import TypedDict, Annotated, Sequence
from langgraph.graph.message import add_messages
from langchain_core.messages import BaseMessage

# 这是小朗的短期状态和长期状态的组合
# 注意:这里我们使用了TypedDict,也可以使用Pydantic模型
# 注意:这里我们使用了Annotated和add_messages,这是LangGraph专门为消息列表的特殊注解
# 它的作用是:当我们往messages列表里添加新消息的时候,不会覆盖原来的消息,而是会把新消息追加到原来的消息列表后面
class XiaoLangState(TypedDict):
    # 短期状态
    task: str  # 当前要拼的任务,比如“拼象棋棋盘+红黑棋子各一套”
    assembled_parts: list[str]  # 已经拼好的部分
    selected_block: str | None  # 刚才选的积木
    check_result: str | None  # 刚才的检查结果
    tool_call_result: str | None  # 刚才的工具调用结果
    chess_game_history: list[str]  # 刚才的下棋历史
    # 长期状态
    long_term_memory: list[str]  # 长期记忆,比如上次拼象棋的经验、上次输棋的复盘
    # 对话历史(这个是可选的,如果你要和小朗对话的时候会用到)
    messages: Annotated[Sequence[BaseMessage], add_messages]

哦,对了,刚才的例子里我们用到了一个LangGraph专门为消息列表的特殊注解:Annotated[Sequence[BaseMessage], add_messages]——这个注解的作用是**非常重要!我们可以用“真·搭积木机器人小朗的对话历史”来比喻:

这个注解就像小朗的“对话历史本——当小朗和小明对话的时候,小明说的话、小朗说的话、小朗调用工具得到的结果,都会追加到这个对话历史本的后面,而不是覆盖原来的对话历史!

如果没有这个注解的话,那每次我们往messages列表里添加新消息的时候,就会覆盖原来的消息,那小朗就会忘记之前和小明说的话,那多可怕啊!

好的,核心概念一State就解释完了!接下来我们解释核心概念二Node!

核心概念二:手手脚脚(Node)——小朗的“行动单元”

什么是LangGraph的Node呢?我们可以用“真·搭积木机器人小朗的“手手脚脚”来比喻:

LangGraph的Node就像小朗的“手手脚脚”,是小朗的“行动单元”——小朗每做一件事情,就是一个Node!

那LangGraph的Node具体是什么样子的呢?LangGraph的Node可以是任何Python函数——这个函数的输入是当前的State,输出是要更新的State的部分内容(不需要输出整个State,只需要输出你想更新的部分)!

哦,对了,这个是LangGraph的一个非常重要的特性——增量更新State!我们可以用“真·搭积木机器人小朗的“大脑袋”来比喻:

增量更新State就像小朗的“大脑袋”——小朗不需要把整个大脑袋都拿出来,只需要把大脑袋里的某个或某几个信息拿出来更新一下,然后放回去!这样就不会覆盖大脑袋里的其他信息!

如果没有这个特性的话,那每次我们更新State的时候,都要把整个State都拿出来,然后更新,然后放回去,那很容易覆盖掉大脑袋里的其他信息,那多可怕啊!

那现在我们用“真·搭积木机器人小朗”的Node来举几个例子:

第一个例子:思考节点(Think Node)——小朗看看状态箱里的信息,想想要做什么:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# 初始化LLM
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# 思考节点的提示词模板
think_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是真·搭积木机器人小朗,你的任务是完成用户给你的搭积木任务。现在请你根据当前的状态,思考接下来要做什么。"
                "你可以选择的行动有:选积木、拼积木、检查积木、调用轮子测量工具、喊象棋大师机器人、复盘总结、终止。"
                "请你只需要输出你选择的行动,不要输出其他任何内容!"),
    ("user", "当前状态:\n{state}")
])

# 思考节点的函数
def think_node(state: XiaoLangState) -> dict:
    # 把当前的状态转换成字符串
    state_str = str(state)
    # 调用提示词模板 + LLM + 输出解析器
    chain = think_prompt | llm | StrOutputParser()
    # 调用链,得到思考结果
    think_result = chain.invoke({"state": state_str})
    # 更新状态:把思考结果放进selected_action字段里(注意:我们这里新增了一个selected_action字段,用来存储思考结果)
    return {"selected_action": think_result}

第二个例子:选积木节点(Select Block Node)——小朗从积木堆里选一块合适的积木:

# 选积木节点的提示词模板
select_block_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是真·搭积木机器人小朗,你的任务是完成用户给你的搭积木任务。现在请你根据当前的状态,从积木堆里选一块合适的积木。"
                "积木堆里的积木有:红色正方形积木、红色圆形积木、黑色正方形积木、黑色圆形积木、白色正方形积木、白色圆形积木。"
                "请你只需要输出你选择的积木,不要输出其他任何内容!"),
    ("user", "当前状态:\n{state}")
])

# 选积木节点的函数
def select_block_node(state: XiaoLangState) -> dict:
    # 把当前的状态转换成字符串
    state_str = str(state)
    # 调用链,得到选积木结果
    chain = select_block_prompt | llm | StrOutputParser()
    select_block_result = chain.invoke({"state": state_str})
    # 更新状态:把选积木结果放进selected_block字段里
    return {"selected_block": select_block_result}

第三个例子:检查积木节点(Check Block Node)——小朗低头检查一下拼的对不对、有没有问题:

# 检查积木节点的提示词模板
check_block_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是真·搭积木机器人小朗,你的任务是完成用户给你的搭积木任务。现在请你根据当前的状态,检查一下拼的对不对、有没有问题。"
                "你可以选择的检查结果有:对的、错的、需要测量轮子大小。"
                "请你只需要输出你选择的检查结果,不要输出其他任何内容!"),
    ("user", "当前状态:\n{state}")
])

# 检查积木节点的函数
def check_block_node(state: XiaoLangState) -> dict:
    # 把当前的状态转换成字符串
    state_str = str(state)
    # 调用链,得到检查结果
    chain = check_block_prompt | llm | StrOutputParser()
    check_result = chain.invoke({"state": state_str})
    # 更新状态:把检查结果放进check_result字段里
    return {"check_result": check_result}

哦,对了,还有一种特殊的Node——工具调用节点(Tool Node)!不过在LangGraph中,我们不需要自己写工具调用节点的函数——LangGraph提供了一个**ToolNode**类,我们只需要把我们的Tool传给它,它就会自动帮我们处理工具调用的逻辑!

什么是Tool呢?在LangChain中,Tool是Agent可以调用的外部资源,比如拼写检查API、天气查询API、数据库查询API、代码执行API。我们现在用“真·搭积木机器人小朗的轮子测量工具”来举个例子,定义一个Tool:

from langchain_core.tools import tool

# 轮子测量工具的函数
@tool
def wheel_measurement_tool(wheel_color: str) -> str:
    """
    测量轮子的大小。

    参数:
        wheel_color: 轮子的颜色,可以是红色、黑色、白色。

    返回:
        轮子的大小。
    """
    # 这里我们假装调用了一个外部的轮子测量API
    # 实际开发中,你需要替换成真正的API调用
    if wheel_color == "红色":
        return "红色轮子的大小是直径5厘米"
    elif wheel_color == "黑色":
        return "黑色轮子的大小是直径6厘米"
    elif wheel_color == "白色":
        return "白色轮子的大小是直径7厘米"
    else:
        return "错误:没有这种颜色的轮子"

好的,核心概念二Node就解释完了!接下来我们解释核心概念三Edge!

核心概念三:指令手册(Edge)——小朗的“行动规则”

什么是LangGraph的Edge呢?我们可以用“真·搭积木机器人小朗的“指令手册”来比喻:

LangGraph的Edge就像小朗的“指令手册”,是小朗的“行动规则”——小朗做完一件事情(Node)之后,接下来要做什么,就是由Edge决定的!

那LangGraph的Edge具体有几种类型呢?LangGraph的Edge具体有四种类型

  1. 无条件边(Unconditional Edge):不管当前的State是什么,小朗做完当前的Node之后,必须做下一个指定的Node;
  2. 条件边(Conditional Edge):根据当前的State的某个或某几个字段的值,小朗做完当前的Node之后,做不同的Node;
  3. 循环边(Loop Edge):本质上是一种特殊的条件边,根据当前的State的某个或某几个字段的值,小朗做完当前的Node之后,回到之前的某个或某几个Node;
  4. 终止边(End Edge):小朗做完当前的Node之后,停下来休息,也就是终止整个State Machine的运行。

那现在我们用“真·搭积木机器人小朗”的Edge来举几个例子:

第一个例子:无条件边(Unconditional Edge)——小朗做完“选积木节点”之后,必须做“拼积木节点”:

# 无条件边:从select_block_node到assemble_block_node
# 注意:这里我们用字符串来表示Node的名字

第二个例子:条件边(Conditional Edge)——小朗做完“检查积木节点”之后,根据check_result字段的值,做不同的Node:

首先,我们需要定义一个条件函数(Conditional Function)——这个函数的输入是当前的State,输出是下一个Node的名字

# 条件函数:根据check_result字段的值,返回下一个Node的名字
def check_block_conditional_function(state: XiaoLangState) -> str:
    # 获取当前的check_result字段的值
    check_result = state.get("check_result")
    # 根据check_result字段的值,返回下一个Node的名字
    if check_result == "对的":
        return "next_step_node"
    elif check_result == "错的":
        return "select_block_node"
    elif check_result == "需要测量轮子大小":
        return "wheel_measurement_tool_node"
    else:
        return "end_node"

然后,我们用这个条件函数来定义条件边:

# 条件边:从check_block_node到不同的Node
# 注意:这里我们用check_block_conditional_function来表示条件函数

第三个例子:终止边(End Edge)——小朗做完“复盘总结节点”之后,停下来休息:

# 终止边:从review_node到end_node

哦,对了,在LangGraph中,终止边其实不需要我们自己显式地定义——我们只需要在条件函数中返回一个**END**(这是LangGraph提供的一个特殊的字符串常量),就表示终止整个State Machine的运行!

好的,核心概念三Edge就解释完了!接下来我们解释核心概念四StateGraph!

核心概念四:StateGraph——小朗的“完整机器人”

什么是LangGraph的StateGraph呢?我们可以用“真·搭积木机器人小朗的“完整机器人”来比喻:

LangGraph的StateGraph就像小朗的“完整机器人”——它把State(大脑袋)、Node(手手脚脚)、Edge(指令手册)、Start Node(启动按钮)组装在一起,就是一个完整的机器人!

那LangGraph的StateGraph具体是什么样子的呢?LangGraph的StateGraph是通过**StateGraph**类来创建的——我们需要做的事情很简单:

  1. 创建一个StateGraph对象,传入我们的State类型;
  2. 添加Start Node;
  3. 添加所有的Node;
  4. 添加所有的Edge;
  5. 编译StateGraph对象,得到一个**CompiledGraph**对象;
  6. 调用CompiledGraph对象的**invoke**方法,传入初始的State,得到最终的State!

那现在我们用“真·搭积木机器人小朗”的StateGraph来举个例子(注意:这个例子是不完整的,只是为了展示StateGraph的创建流程):

from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode

# 第一步:创建一个StateGraph对象,传入我们的State类型
graph = StateGraph(XiaoLangState)

# 第二步:添加Start Node
# 注意:在LangGraph中,我们不需要显式地添加Start Node——我们只需要指定第一个Node,就是Start Node之后的第一个Node
# 或者我们可以用graph.set_entry_point("think_node")来指定第一个Node
graph.set_entry_point("think_node")

# 第三步:添加所有的Node
graph.add_node("think_node", think_node)
graph.add_node("select_block_node", select_block_node)
graph.add_node("assemble_block_node", assemble_block_node)
graph.add_node("check_block_node", check_block_node)
# 添加工具调用节点
tools = [wheel_measurement_tool]
tool_node = ToolNode(tools)
graph.add_node("wheel_measurement_tool_node", tool_node)
# 其他节点暂时先不添加,比如next_step_node、call_chess_master_node、review_node、end_node

# 第四步:添加所有的Edge
# 添加无条件边:从select_block_node到assemble_block_node
graph.add_edge("select_block_node", "assemble_block_node")
# 添加无条件边:从assemble_block_node到check_block_node
graph.add_edge("assemble_block_node", "check_block_node")
# 添加条件边:从check_block_node到不同的Node
graph.add_conditional_edges(
    "check_block_node",
    check_block_conditional_function,
    # 这里我们可以传入一个字典,用来映射条件函数的返回值到Node的名字
    # 如果不传入的话,LangGraph会自动使用条件函数的返回值作为Node的名字
)
# 添加条件边:从think_node到不同的Node(这里我们暂时先不写think_node的条件函数
# graph.add_conditional_edges(...)

# 第五步:编译StateGraph对象,得到一个CompiledGraph对象
compiled_graph = graph.compile()

# 第六步:调用CompiledGraph对象的invoke方法,传入初始的State,得到最终的State
initial_state = XiaoLangState(
    task="拼象棋棋盘+红黑棋子各一套",
    assembled_parts=[],
    selected_block=None,
    check_result=None,
    tool_call_result=None,
    chess_game_history=[],
    long_term_memory=[],
    messages=[]
)
final_state = compiled_graph.invoke(initial_state)

# 打印最终的State
print(final_state)

好的,核心概念四StateGraph就解释完了!接下来我们解释核心概念五CompiledGraph的可视化!

核心概念五:CompiledGraph的可视化——小朗的“机器人图纸”

什么是LangGraph的CompiledGraph的可视化呢?我们可以用“真·搭积木机器人小朗的“机器人图纸”来比喻:

LangGraph的CompiledGraph的可视化就像小朗的“机器人图纸”——你可以通过这个图纸,清楚地看到小朗的大脑袋(State)、手手脚脚(Node)、指令手册(Edge)、启动按钮(Start Node)之间的关系!

那LangGraph的CompiledGraph的可视化具体怎么做呢?LangGraph的CompiledGraph提供了一个**draw_mermaid_png**方法(或者draw_mermaid_svg方法),我们只需要调用这个方法,就可以得到一个Mermaid格式的图片!

那现在我们用刚才的不完整的CompiledGraph来举个例子,可视化一下:

# 可视化CompiledGraph
compiled_graph.get_graph().draw_mermaid_png(output_file_path="xiaolang_robot_diagram.png")

哦,对了,调用这个方法之前,你需要安装一个**graphviz库和pygraphviz库(或者mermaid-cli**库)——具体的安装方法我们会在项目实战一的开发环境搭建部分讲!

好的,核心概念五CompiledGraph的可视化就解释完了!接下来我们解释核心概念六多Agent协作!

核心概念六:多Agent协作——小朗的“机器人团队”

什么是LangGraph的多Agent协作呢?我们可以用“真·搭积木机器人小朗的“机器人团队”来比喻:

LangGraph的多Agent协作就像小朗的“机器人团队”——多个机器人通过共享State或者通信完成特定的任务!

那LangGraph的多Agent协作具体怎么做呢?在LangGraph中,多Agent协作有两种方式:

  1. **共享State的方式:多个Agent共享同一个State,每个Agent都是一个Node,通过读写同一个State来完成协作;
  2. **通信的方式:多个Agent通过发送消息来完成协作,每个Agent都是一个独立的CompiledGraph,通过消息队列来完成通信(不过这种方式在LangGraph中还是实验性的,推荐使用共享State的方式)。

那现在我们用“真·搭积木机器人小朗”的“机器人团队”来举个例子——小朗的机器人团队有四个成员:

  1. **产品经理机器人(PM Agent):负责根据用户的需求,写出产品需求文档(PRD);
  2. **UI设计师机器人(UI Agent):根据PRD,设计出UI原型图;
  3. **前端开发机器人(FE Agent):根据UI原型图,写出前端代码;
  4. **测试工程师机器人(QA Agent):根据前端代码,进行测试,写出测试报告;

这四个Agent共享同一个State,每个Agent都是一个Node,通过读写同一个State来完成协作!具体的例子我们会在项目实战二的部分讲!

好的,核心概念六多Agent协作就解释完了!接下来我们解释核心概念之间的关系!

核心概念之间的关系(用小学生能理解的比喻)

在上一节的核心概念解释中,我们已经通过“真·搭积木机器人小朗”的故事,一步一步解释了LangGraph的核心概念:State(大脑袋)、Node(手手脚脚)、Edge(指令手册)、StateGraph(完整机器人)、CompiledGraph的可视化(机器人图纸)、多Agent协作(机器人团队)。接下来,我们再用小学生能理解的比喻,解释核心概念之间的关系!

概念一和概念二的关系:State和Node——大脑袋和手手脚脚

State和Node的关系就像大脑袋和手手脚脚——手手脚脚需要看大脑袋里的信息才能行动,行动之后又会把新的信息放进大脑袋里!

我们可以用“真·搭积木机器人小朗的选积木节点”来举个例子:

  • 小朗的选积木节点(手手脚脚)需要看大脑袋(State)里的任务、已经拼好的部分、长期记忆等信息,才能选一块合适的积木;
  • 小朗的选积木节点(手手脚脚)选好积木之后,又会把选好的积木放进大脑袋(State)里的selected_block字段里!
概念二和概念三的关系:Node和Edge——手手脚脚和指令手册

Node和Edge的关系就像手手脚脚和指令手册——手手脚脚做完一件事情之后,接下来要做什么,就是由指令手册决定的!

我们可以用“真·搭积木机器人小朗的检查积木节点”来举个例子:

  • 小朗的检查积木节点(手手脚脚)做完检查之后,就会看指令手册(Edge)里的条件函数;
  • 指令手册(Edge)里的条件函数会根据大脑袋(State)里的check_result字段的值,告诉小朗接下来要做什么!
概念一、概念二、概念三的关系:State、Node、Edge——大脑袋、手手脚脚、指令手册

State、Node、Edge的关系就像大脑袋、手手脚脚、指令手册——它们三个是一个团队,大脑袋负责存储信息,手手脚脚负责行动,指令手册负责指挥,它们一起合作完成任务!

我们可以用“真·搭积木机器人小朗的拼积木过程”来举个例子:

  1. 小朗的大脑袋(State)里存储着任务、已经拼好的部分、长期记忆等信息;
  2. 小朗的思考节点(手手脚脚)看大脑袋(State)里的信息,思考接下来要做什么;
  3. 小朗的指令手册(Edge)告诉小朗接下来要做选积木节点;
  4. 小朗的选积木节点(手手脚脚)看大脑袋(State)里的信息,选一块合适的积木;
  5. 小朗的指令手册(Edge)告诉小朗接下来要做拼积木节点;
  6. 小朗的拼积木节点(手手脚脚)看大脑袋(State)里的信息,把选好的积木拼上去;
  7. 小朗的拼积木节点(手手脚脚)把已经拼好的部分放进大脑袋(State)里的assembled_parts字段里;
  8. 小朗的指令手册(Edge)告诉小朗接下来要做检查积木节点;
  9. 小朗的检查积木节点(手手脚脚)看大脑袋(State)里的信息,检查一下拼的对不对;
  10. 小朗的检查积木节点(手手脚脚)把检查结果放进大脑袋(State)里的check_result字段里;
  11. 小朗的指令手册(Edge)告诉小朗接下来要做什么;
  12. 重复以上步骤,直到完成任务!
概念一、概念二、概念三、概念四的关系:State、Node、Edge、StateGraph——大脑袋、手手脚脚、指令手册、完整机器人

State、Node、Edge、StateGraph的关系就像大脑袋、手手脚脚、指令手册、完整机器人——把它们三个组装在一起,就是一个完整的机器人!

我们可以用“真·搭积木机器人小朗的完整机器人”来举个例子:

  • 把大脑袋(State)、手手脚脚(Node)、指令手册(Edge)组装在一起,就是一个完整的机器人(StateGraph)!
概念一、概念二、概念三、概念四、概念五的关系:State、Node、Edge、StateGraph、CompiledGraph的可视化——大脑袋、手手脚脚、指令手册、完整机器人、机器人图纸

State、Node、Edge、StateGraph、CompiledGraph的可视化的关系就像大脑袋、手手脚脚、指令手册、完整机器人、机器人图纸——你可以通过机器人图纸,清楚地看到它们之间的关系!

我们可以用“真·搭积木机器人小朗的机器人图纸”来举个例子:

  • 你可以通过机器人图纸(CompiledGraph的可视化),清楚地看到大脑袋(State)、手手脚脚(Node)、指令手册(Edge)、完整机器人(StateGraph)之间的关系!
概念一、概念二、概念三、概念四、概念五、概念六的关系:State、Node、Edge、StateGraph、CompiledGraph的可视化、多Agent协作——大脑袋、手手脚脚、指令手册、完整机器人、机器人图纸、机器人团队

State、Node、Edge、StateGraph、CompiledGraph的可视化、多Agent协作的关系就像大脑袋、手手脚脚、指令手册、完整机器人、机器人图纸、机器人团队——把多个完整机器人组装在一起,共享同一个大脑袋,就是一个机器人团队!

我们可以用“真·搭积木机器人小朗的机器人团队”来举个例子:

  • 把产品经理机器人(PM Agent)、UI设计师机器人(UI Agent)、前端开发机器人(FE Agent)、测试工程师机器人(QA Agent)组装在一起,共享同一个大脑袋(State),就是一个机器人团队(多Agent协作)!

好的,核心概念之间的关系就解释完了!接下来我们给出核心概念原理和架构的文本示意图!

核心概念原理和架构的文本示意图(专业定义)

在上一节的核心概念之间的关系中,我们已经用小学生能理解的比喻,解释了核心概念之间的关系。接下来,我们给出核心概念原理和架构的文本示意图(专业定义)


LangGraph核心概念原理和架构文本示意图

1. 核心层(Core Layer)

1.1 State(状态)

  • 定义:一个结构化的数据结构,用于存储Agent
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐