logo

LangGraph:状态机与循环

如果你做过稍微复杂一点的 Agent,很快就会遇到一个现实问题:流程根本不是线性的。

它通常更像这样:

  • 读资料
  • 调工具
  • 发现信息不够
  • 再查一次
  • 结果不对
  • 回退重试

这也是为什么很多人用纯链式方式搭 Agent,最后会越来越难维护。LangGraph 之所以有吸引力,不是因为它“更高级”,而是因为它把循环、分支和状态显式地拉到了台面上。

我自己会把 LangGraph 看成一个很典型的“第二阶段工具”。第一阶段你只是想把 Agent 跑起来,很多东西还可以靠 prompt 和几个函数硬接;第二阶段你开始反复遇到重试、分支、回退、断点,这时候图式编排才会真的变顺手。

[PROMPT_LAB_BANNER]


1. 核心概念:StateGraph

LangGraph 的核心思路可以概括成一句话:

把 Agent 当成一个有状态的流程图,而不是一串 prompt。

它把三个东西讲得很清楚:

  • 当前状态是什么
  • 下一步该跑哪个节点
  • 什么情况下继续、回退或者结束
stateDiagram-v2
    [*] --> LLM : 输入
    LLM --> Tool : 调用工具
    Tool --> LLM : 返回结果
    
    state LLM {
        [*] --> Thinking
        Thinking --> Deciding
    }
    
    state Tool {
        API_Call --> Parse_Result
    }
    
    LLM --> End : 完成任务
    Tool --> Error : 异常
    Error --> LLM : 重试 (Loop)

State(状态)

状态就是整个流程共享的那份“任务现场”。所有节点都围绕它工作。

一个好的状态对象,通常不会什么都塞,而是只放当前流程真正需要反复读写的部分,比如消息、错误信息、阶段结果、迭代次数。

我通常不建议把整个世界都塞进 state。state 一旦变成“什么都记一点”的大字典,后面就很容易出现另外一种问题:流程看起来有状态,实际没人知道哪几个字段才是关键字段。

class AgentState(TypedDict):
    messages: list[BaseMessage]
    current_code: str
    error_logs: str
    iterations: int

Nodes(节点)

节点负责做事。它可以是:

  • 调一次 LLM
  • 调一个工具
  • 做格式校验
  • 跑测试
  • 决定是否需要人工审批

重点不是节点名字,而是节点职责要尽量单一。不然一旦出了问题,你很难知道是推理错了、工具错了,还是状态污染了。

这也是很多人第一次把 Agent 做复杂以后才意识到的问题。流程一长,如果一个节点同时做“推理 + 调工具 + 处理错误 + 更新状态”,最后 trace 看起来会非常难读。你知道它错了,但不容易知道它到底错在哪一步。

Edges(边)

边定义流程怎么走。

  • 普通边:执行完 A 直接去 B
  • 条件边:根据状态决定去 B、C,还是回到 A

很多 Agent 的“智能感”,其实就体现在条件边上。它让系统不只是往前冲,而是能根据结果改路线。

如果换成更贴近真实项目的说法,条件边其实回答的是几个很朴素的问题:

  • 现在该继续还是该停
  • 这次失败值不值得重试
  • 是回上一层,还是换一条路径

很多“看起来像智能体”的行为,底层其实就是这些判断被写清楚了。


2. 一个最典型的例子:写代码,检查,不行就重来

假设你要做一个简单的 coding agent,流程可能就是:

  • 先写代码
  • 再检查
  • 如果失败,回去重写
  • 如果通过,结束

这正是 LangGraph 适合处理的场景。

如果让我用一句更直接的话来概括:只要你已经确定“失败以后不是直接报错退出,而是还要继续判断下一步”,LangGraph 这类思路就很值得看。

graph LR
    Start((Start)) --> Write[编写代码]
    Write --> Check[运行检查]
    
    Check -->|有错误| Write
    Check -->|通过| End((End))
from langgraph.graph import StateGraph, END

# 1. 定义状态
class State(TypedDict):
    code: str
    error: str

# 2. 定义节点函数
def write_code(state):
    # 调用 LLM 生成代码
    return {"code": "print('hello')"}

def check_code(state):
    # 模拟运行检查
    if "error" in state["code"]:
        return {"error": "Syntax Error"}
    return {"error": ""}

# 3. 定义条件逻辑
def router(state):
    if state["error"]:
        return "write_code" # 有错,循环回去重写
    return "end" # 没错,结束

# 4. 构建图
workflow = StateGraph(State)
workflow.add_node("write_code", write_code)
workflow.add_node("check_code", check_code)

workflow.set_entry_point("write_code")
workflow.add_edge("write_code", "check_code")
workflow.add_conditional_edges(
    "check_code",
    router,
    {"write_code": "write_code", "end": END}
)

app = workflow.compile()

这个例子虽然小,但它已经比“单次 prompt 生成代码”更接近真实工程。因为真实 coding agent 很少一次就成,通常都是:

  • 先写
  • 再跑
  • 报错
  • 回看日志
  • 再改

一旦流程里天然带循环,图模型就会比线性链更顺手。


3. Human-in-the-loop:生产环境里这一步非常关键

Agent 并不值得无条件信任。越接近真实资源,越需要人为介入。

比如下面这些动作,很多团队都会要求显式审批:

  • 发邮件给客户
  • 改生产配置
  • 提交数据库写操作
  • 部署代码

LangGraph 的一个现实价值,就是它比较适合在关键节点停下来,等人检查状态,再决定继续。

这种断点能力很重要,因为真正上线以后,系统不是“能自动跑就行”,而是要“自动跑的时候还能被接管”。

这一点对 Google 索引本身没有直接帮助,但对内容质量有帮助。因为很多真正有经验的工程文章,都会强调“哪里必须交回给人”,而不是把自动化写成万能答案。这样的内容通常也更像真人经验总结,而不是概念拼装。

这也是我自己很看重的一点。凡是把 Agent 写成“全自动就能解决一切”的内容,短期也许吸引眼球,但长期质量通常不高,读者也很难真正拿去落地。


4. 为什么很多复杂 Agent 会选 LangGraph

特性传统链式编排LangGraph
控制流偏线性明确支持分支和循环
状态管理容易散落在各处状态集中定义
失败恢复处理起来别扭更容易设计重试和回退
长流程维护规模一大就容易乱更适合复杂流程

如果你在搜“LangGraph 适合什么场景”或者“LangGraph 和 LangChain 区别”,其实想找的通常也就是这类判断:什么时候图比链更值得上。


小结

LangGraph 真正解决的,不是“怎么让 Agent 看起来更聪明”,而是“怎么让它在复杂流程里别失控”。

如果你的 Agent 只是一次问答,没必要上这么重的框架。

我会再说得更直白一点:如果你现在还没遇到循环、回退、状态持久化这些问题,那大概率还没到非用 LangGraph 不可的时候。

但只要你开始遇到下面这些需求:

  • 需要循环重试
  • 需要持久化状态
  • 需要中途人工接管
  • 需要明确的分支控制

那 LangGraph 这类状态流思路就会变得很有价值。

如果你的流程还停留在“一问一答 + 最多调一个工具”,那真的不用急着上。LangGraph 更像是你在 Agent 做到第二阶段以后,自然会碰到的答案,而不是所有项目的起点。

所以它更像一把工程化工具,不是一个入门仪式。什么时候开始频繁觉得链式流程很别扭,什么时候再认真上它,通常最合适。

AI Agent 开发实战手册
AI Engineer

AI Agent 开发实战手册

从 0 到 1 掌握 AI Agent 开发:涵盖自主计划、工具调用、MCP 协议与多智能体编排实战。

AI Agent 开发实战手册LangGraph:状态机与循环

LangGraph:状态机与循环

如果你做过稍微复杂一点的 Agent,很快就会遇到一个现实问题:流程根本不是线性的。

它通常更像这样:

  • 读资料
  • 调工具
  • 发现信息不够
  • 再查一次
  • 结果不对
  • 回退重试

这也是为什么很多人用纯链式方式搭 Agent,最后会越来越难维护。LangGraph 之所以有吸引力,不是因为它“更高级”,而是因为它把循环、分支和状态显式地拉到了台面上。

我自己会把 LangGraph 看成一个很典型的“第二阶段工具”。第一阶段你只是想把 Agent 跑起来,很多东西还可以靠 prompt 和几个函数硬接;第二阶段你开始反复遇到重试、分支、回退、断点,这时候图式编排才会真的变顺手。

Prompt Lab

把这章的知识,直接变成实战能力

进入交互式实验室,用真实任务练 Prompt,10 分钟快速上手。

进入 Prompt Lab →

#1. 核心概念:StateGraph

LangGraph 的核心思路可以概括成一句话:

把 Agent 当成一个有状态的流程图,而不是一串 prompt。

它把三个东西讲得很清楚:

  • 当前状态是什么
  • 下一步该跑哪个节点
  • 什么情况下继续、回退或者结束
stateDiagram-v2 [*] --> LLM : 输入 LLM --> Tool : 调用工具 Tool --> LLM : 返回结果 state LLM { [*] --> Thinking Thinking --> Deciding } state Tool { API_Call --> Parse_Result } LLM --> End : 完成任务 Tool --> Error : 异常 Error --> LLM : 重试 (Loop)

#State(状态)

状态就是整个流程共享的那份“任务现场”。所有节点都围绕它工作。

一个好的状态对象,通常不会什么都塞,而是只放当前流程真正需要反复读写的部分,比如消息、错误信息、阶段结果、迭代次数。

我通常不建议把整个世界都塞进 state。state 一旦变成“什么都记一点”的大字典,后面就很容易出现另外一种问题:流程看起来有状态,实际没人知道哪几个字段才是关键字段。

python
class AgentState(TypedDict): messages: list[BaseMessage] current_code: str error_logs: str iterations: int

#Nodes(节点)

节点负责做事。它可以是:

  • 调一次 LLM
  • 调一个工具
  • 做格式校验
  • 跑测试
  • 决定是否需要人工审批

重点不是节点名字,而是节点职责要尽量单一。不然一旦出了问题,你很难知道是推理错了、工具错了,还是状态污染了。

这也是很多人第一次把 Agent 做复杂以后才意识到的问题。流程一长,如果一个节点同时做“推理 + 调工具 + 处理错误 + 更新状态”,最后 trace 看起来会非常难读。你知道它错了,但不容易知道它到底错在哪一步。

#Edges(边)

边定义流程怎么走。

  • 普通边:执行完 A 直接去 B
  • 条件边:根据状态决定去 B、C,还是回到 A

很多 Agent 的“智能感”,其实就体现在条件边上。它让系统不只是往前冲,而是能根据结果改路线。

如果换成更贴近真实项目的说法,条件边其实回答的是几个很朴素的问题:

  • 现在该继续还是该停
  • 这次失败值不值得重试
  • 是回上一层,还是换一条路径

很多“看起来像智能体”的行为,底层其实就是这些判断被写清楚了。


#2. 一个最典型的例子:写代码,检查,不行就重来

假设你要做一个简单的 coding agent,流程可能就是:

  • 先写代码
  • 再检查
  • 如果失败,回去重写
  • 如果通过,结束

这正是 LangGraph 适合处理的场景。

如果让我用一句更直接的话来概括:只要你已经确定“失败以后不是直接报错退出,而是还要继续判断下一步”,LangGraph 这类思路就很值得看。

graph LR Start((Start)) --> Write[编写代码] Write --> Check[运行检查] Check -->|有错误| Write Check -->|通过| End((End))
python
from langgraph.graph import StateGraph, END # 1. 定义状态 class State(TypedDict): code: str error: str # 2. 定义节点函数 def write_code(state): # 调用 LLM 生成代码 return {"code": "print('hello')"} def check_code(state): # 模拟运行检查 if "error" in state["code"]: return {"error": "Syntax Error"} return {"error": ""} # 3. 定义条件逻辑 def router(state): if state["error"]: return "write_code" # 有错,循环回去重写 return "end" # 没错,结束 # 4. 构建图 workflow = StateGraph(State) workflow.add_node("write_code", write_code) workflow.add_node("check_code", check_code) workflow.set_entry_point("write_code") workflow.add_edge("write_code", "check_code") workflow.add_conditional_edges( "check_code", router, {"write_code": "write_code", "end": END} ) app = workflow.compile()

这个例子虽然小,但它已经比“单次 prompt 生成代码”更接近真实工程。因为真实 coding agent 很少一次就成,通常都是:

  • 先写
  • 再跑
  • 报错
  • 回看日志
  • 再改

一旦流程里天然带循环,图模型就会比线性链更顺手。


#3. Human-in-the-loop:生产环境里这一步非常关键

Agent 并不值得无条件信任。越接近真实资源,越需要人为介入。

比如下面这些动作,很多团队都会要求显式审批:

  • 发邮件给客户
  • 改生产配置
  • 提交数据库写操作
  • 部署代码

LangGraph 的一个现实价值,就是它比较适合在关键节点停下来,等人检查状态,再决定继续。

这种断点能力很重要,因为真正上线以后,系统不是“能自动跑就行”,而是要“自动跑的时候还能被接管”。

这一点对 Google 索引本身没有直接帮助,但对内容质量有帮助。因为很多真正有经验的工程文章,都会强调“哪里必须交回给人”,而不是把自动化写成万能答案。这样的内容通常也更像真人经验总结,而不是概念拼装。

这也是我自己很看重的一点。凡是把 Agent 写成“全自动就能解决一切”的内容,短期也许吸引眼球,但长期质量通常不高,读者也很难真正拿去落地。


#4. 为什么很多复杂 Agent 会选 LangGraph

特性传统链式编排LangGraph
控制流偏线性明确支持分支和循环
状态管理容易散落在各处状态集中定义
失败恢复处理起来别扭更容易设计重试和回退
长流程维护规模一大就容易乱更适合复杂流程

如果你在搜“LangGraph 适合什么场景”或者“LangGraph 和 LangChain 区别”,其实想找的通常也就是这类判断:什么时候图比链更值得上。


#小结

LangGraph 真正解决的,不是“怎么让 Agent 看起来更聪明”,而是“怎么让它在复杂流程里别失控”。

如果你的 Agent 只是一次问答,没必要上这么重的框架。

我会再说得更直白一点:如果你现在还没遇到循环、回退、状态持久化这些问题,那大概率还没到非用 LangGraph 不可的时候。

但只要你开始遇到下面这些需求:

  • 需要循环重试
  • 需要持久化状态
  • 需要中途人工接管
  • 需要明确的分支控制

那 LangGraph 这类状态流思路就会变得很有价值。

如果你的流程还停留在“一问一答 + 最多调一个工具”,那真的不用急着上。LangGraph 更像是你在 Agent 做到第二阶段以后,自然会碰到的答案,而不是所有项目的起点。

所以它更像一把工程化工具,不是一个入门仪式。什么时候开始频繁觉得链式流程很别扭,什么时候再认真上它,通常最合适。

常见问题

开发 AI Agent 需要掌握哪些编程语言?
首选 Python 或 TypeScript。Python 是 AI 生态的基石,而 TypeScript 在开发 MCP Server 和网页端交互时效率极高。借助 Cursor 等 AI 原生编辑器,编程门槛已大幅降低。
MCP 协议目前支持哪些模型?
MCP 是开放协议,目前对 Claude 3.5 系列支持最完美。通过 MCP Proxy,GPT-4o 和 Gemini 也可以间接访问 MCP Server 数据源。
AI Agent 会导致程序员失业吗?
不会,但会改变程序员的工作内容。未来的开发者将从“写代码”转向“管理 Agent 团队”,重点在于系统架构设计、复杂逻辑校验和 Agent 的提示词优化。