news 2026/9/29 10:18:20

Langchain实战8-LangGraph进阶

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Langchain实战8-LangGraph进阶

本节进入 LangGraph 进阶:用 State、Node、Edge、Reducer 与条件路由构建可循环、可暂停、可恢复的 AI 工作流,并以“文章生成—审核—修改”为例串起核心能力。

1. LangGraph 的核心抽象

抽象作用
State工作流当前快照,保存节点共享的数据
Node读取 State、执行任务、返回部分更新
Edge指定固定的下一节点
Conditional Edge根据 State 动态选择下一节点
Reducer定义新旧状态如何合并
Checkpointer保存各步骤状态,用于恢复、记忆与人工介入

可以把 LangGraph 理解为:节点负责做事,边负责决定接下来做什么,状态负责携带全过程数据。

2. 定义 State

fromoperatorimportaddfromtypingimportAnnotatedfromtyping_extensionsimportTypedDictclassArticleState(TypedDict,total=False):topic:stroutline:strdraft:strfeedback:strapproved:boolrevision_count:inthistory:Annotated[list[str],add]

没有声明 Reducer 的字段默认采用覆盖策略。例如新节点返回{"draft": "新内容"},旧草稿会被替换。

history使用operator.add作为 Reducer,每个节点返回的新列表会追加到旧列表:

return{"history":["已完成初稿"]}

节点只需要返回发生变化的字段,不必复制完整 State。

3. 编写节点

defbuild_outline(state:ArticleState)->dict:response=model.invoke(f"为主题《{state['topic']}》生成一份五段式技术文章大纲。")return{"outline":response.content,"revision_count":0,"history":["outline_created"],}defwrite_draft(state:ArticleState)->dict:response=model.invoke("请根据以下大纲撰写技术文章,包含解释、代码与小结:\n"f"{state['outline']}")return{"draft":response.content,"history":["draft_created"],}defreview_draft(state:ArticleState)->dict:response=model.invoke("请审核下面的文章。若可发布,第一行输出 PASS;""否则第一行输出 REVISE,并列出最多三条具体修改意见。\n\n"f"{state['draft']}")text=response.content approved=text.lstrip().startswith("PASS")return{"approved":approved,"feedback":text,"history":["review_passed"ifapprovedelse"review_failed"],}defrevise_draft(state:ArticleState)->dict:response=model.invoke("根据审核意见修改文章。只输出修改后的完整正文。\n\n"f"原文:\n{state['draft']}\n\n"f"审核意见:\n{state['feedback']}")return{"draft":response.content,"revision_count":state.get("revision_count",0)+1,"history":["draft_revised"],}

每个节点保持单一职责,便于单元测试、重试和替换模型。

4. 条件路由与循环

fromtypingimportLiteraldefroute_after_review(state:ArticleState,)->Literal["revise","finish"]:ifstate.get("approved",False):return"finish"ifstate.get("revision_count",0)>=2:return"finish"return"revise"

revision_count是循环保险。任何循环图都应设置业务上限或recursion_limit,否则模型可能在“审核—修改”之间无限运行。

5. 组装与编译 Graph

fromlanggraph.graphimportStateGraph,START,ENDfromlanggraph.checkpoint.memoryimportInMemorySaver builder=StateGraph(ArticleState)builder.add_node("outline",build_outline)builder.add_node("draft",write_draft)builder.add_node("review",review_draft)builder.add_node("revise",revise_draft)builder.add_edge(START,"outline")builder.add_edge("outline","draft")builder.add_edge("draft","review")builder.add_conditional_edges("review",route_after_review,{"revise":"revise","finish":END,},)builder.add_edge("revise","review")graph=builder.compile(checkpointer=InMemorySaver(),)

Graph 必须先compile()才能执行。编译阶段会检查部分结构问题,并注入 Checkpointer 等运行能力。

6. 执行工作流

config={"configurable":{"thread_id":"article-2026-001"},"recursion_limit":20,}result=graph.invoke({"topic":"LangGraph 状态图入门","history":[],},config=config,)print(result["draft"])print(result["history"])

同一thread_id下的状态可以被 Checkpointer 恢复。生产中应使用数据库型 Checkpointer,而不是只存在进程内存中的实现。

7. 流式观察节点执行

forupdateingraph.stream({"topic":"LangGraph 状态图入门","history":[]},config=config,stream_mode="updates",):print(update)
  • updates:每步只返回节点产生的字段更新;
  • values:每步返回累计后的完整 State;
  • 消息流模式:适合逐 Token 展示模型输出。

调试复杂图时,updates往往最容易看出是哪个节点修改了哪个字段。

8. 使用Command同时更新状态和路由

当节点本身已经知道下一步去哪,可以返回Command:

fromtypingimportLiteralfromlanggraph.typesimportCommanddefclassify(state:ArticleState,)->Command[Literal["draft","finish"]]:ifnotstate.get("outline"):returnCommand(update={"history":["missing_outline"]},goto="finish",)returnCommand(update={"history":["outline_ready"]},goto="draft",)

Command适合节点在一次判断中同时完成“更新状态”和“选择目的地”。条件边适合把路由逻辑独立成函数,两种方式按可读性选择。

9. 人工介入:暂停与恢复

fromlanggraph.typesimportinterrupt,Commanddefhuman_review(state:ArticleState)->dict:decision=interrupt({"message":"请审核文章","draft":state["draft"],"requested_fields":["approved","feedback"],})return{"approved":bool(decision["approved"]),"feedback":decision.get("feedback",""),"history":["human_reviewed"],}

图运行到interrupt()时会暂停并把状态保存到 Checkpointer。人工完成审核后,使用相同的thread_id恢复:

resumed=graph.invoke(Command(resume={"approved":False,"feedback":"代码示例需要补充异常处理。",}),config=config,)

恢复时节点会从头重新执行到interrupt(),因此中断之前的副作用必须幂等,或移动到中断之后。

10. 状态设计原则

  1. 保存原始数据,而不是把所有内容提前格式化成 Prompt;
  2. 每个字段含义明确,避免多个节点随意复用同一个字符串;
  3. 列表累计必须声明 Reducer;
  4. 大文件与数据库连接不要直接写入 State,只保存引用;
  5. 会跨中断恢复的数据必须可序列化;
  6. 对包含副作用的节点增加幂等键。

11. LangChain Agent 与 LangGraph 的关系

create_agent()适合标准“模型—工具—模型”循环;当流程包含多个角色、确定性步骤、复杂路由、并行分支或人工节点时,再使用 LangGraph 显式编排。

两者不是二选一:一个完整的 Agent 可以作为 LangGraph 的节点或子图,Agent 中的 Middleware 仍然继续生效。

12. 常见问题

12.1 状态列表被覆盖

为需要累计的字段声明 Reducer;否则每次返回的新值会覆盖旧值。

12.2 图无法结束

检查条件路由是否存在到END的分支,并为循环增加最大次数与recursion_limit。

12.3 恢复后重复发信或写库

节点可能因恢复或重试再次执行。副作用操作应使用幂等键,并记录执行状态。

12.4 中断后找不到状态

确认编译时提供了 Checkpointer,恢复时使用原来的thread_id,并且持久化后端仍可访问。

13. 本节小结

LangGraph 的价值在于把隐式的 Agent 推理过程变成显式、可观察、可暂停、可恢复的状态机。复杂工作流的稳定性主要取决于清晰的 State、单一职责节点、确定的终止条件和幂等副作用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 10:14:48

直流电机驱动实战:PWM、H桥与单片机协同设计指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 10:14:11

Altium Designer浮动许可智能释放方案

1. 许可瓶颈不是卡在软件上,而是卡在人和流程里Altium Designer许可不够用——这句话我听客户说了不下二十遍,每次都是同一个场景:设计团队五个人,公司只买了三套浮动许可,上午十点一到,总有人弹出“Licens…

作者头像 李华
网站建设 2026/9/29 10:08:36

WordPress targetSms插件RCE漏洞解析与加固

做安全应急这几年,我处理过不少“看起来人畜无害的插件突然变成突破口”的案例。最近在漏洞情报里看到WordPress的targetSms插件暴露出一个远程命令执行漏洞(CVE-2025-3776),第一反应就是:又来了。短信通知、验证码、营…

作者头像 李华
网站建设 2026/9/29 10:08:32

成本限流实战:跨节点配额与分区降级配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 10:08:02

肠道菌群三大常见误区:很多人的肠道养护,一直在做无用功

肠道菌群三大常见误区:很多人的肠道养护,一直在做无用功 在肠道健康科普普及的当下,越来越多人开始重视肠道菌群,但市面上碎片化的养生认知,也让大众陷入了大量误区。很多人看似常年在养护肠道、调节菌群,实…

作者头像 李华
网站建设 2026/9/29 10:08:01

牛顿-拉夫逊法潮流计算:自编通用程序替代runpf全解析

1. 项目背景:为什么要写一个替代runpf的程序先说点实在的,Matpower 里的 runpf 确实是电力系统潮流计算的一把好手,调用方便、数据格式统一,几乎成了默认工具。但我这几年的实际使用中,越来越觉得它像个“黑盒”——你…

作者头像 李华