第四篇:《LangGraph 实战:用状态机构建可控的 Agent 工作流》
在上一篇文章中我们通过 create_agent 几行代码就构建了一个可运行的 Agent。但 create_agent 是一个“黑盒”——它内部构建了一个 StateGraph但你无法控制流程的细节。当你需要条件分支、循环控制、中断恢复、人工审批时就必须直接使用 LangGraph 的 StateGraph。简单 Agent 的核心是 Model → Action → Observation → Model 的自由循环这种方式灵活但不可预测。进入生产环境后系统关注的问题变为Agent 执行到哪里了哪些步骤不能跳过一个节点最多重复几次服务重启后怎样继续什么时候必须让人确认 本文从 StateGraph 的核心概念讲起深入讲解条件边、中断恢复和 Human-in-the-Loop并通过一个完整的客服 Agent 实战帮你掌握 LangGraph 的编排能力。一、为什么需要 LangGraphLangChain 的 LCEL 适合表达线性数据流但真实 Agent 的流程往往不是线性的核心原则状态机负责确定性的流程控制Agent 负责节点内部的理解、推理与工具调用实现 “确定性的骨架 概率性的智能” 。二、StateGraph 的核心概念LangGraph 的核心抽象只有三个State状态 应用当前快照一个共享的数据结构Node节点 一个函数读取并更新 StateEdge边 定义节点之间的连接和执行顺序最简单的 StateGraphfromlanggraph.graphimportStateGraph,START,ENDfromtypingimportTypedDictclassState(TypedDict):input:stroutput:strdefprocess_node(state:State)-State:return{output:f处理结果:{state[input]}}builderStateGraph(State)builder.add_node(process,process_node)builder.add_edge(START,process)builder.add_edge(process,END)graphbuilder.compile()resultgraph.invoke({input:测试})print(result)# {input: 测试, output: 处理结果: 测试}三、条件边让流程“会拐弯”条件边是 LangGraph 实现循环和分支的核心机制。 它通过一个路由函数根据当前状态动态决定下一个节点。fromlanggraph.graphimportStateGraph,START,ENDdefshould_continue(state:State)-str:路由函数根据状态决定下一步ifstate.get(needs_tool):returncall_toolelifstate.get(is_done):returnendelse:returncontinuebuilder.add_conditional_edges(agent,# 源节点should_continue,# 路由函数{call_tool:tools,# 返回 call_tool → 去 tools 节点continue:agent,# 返回 continue → 回到 agent 节点end:END,# 返回 end → 结束})条件边的典型应用场景Agent 调用工具后判断是否需要继续调用循环质量检查后判断是通过还是重试分支人工审批后判断是批准还是拒绝分流四、Checkpoint让 Agent“记得住”LangGraph 的持久化层通过 Checkpointer 实现。每个线程thread的状态会被自动保存即使服务重启也能恢复。fromlanggraph.checkpoint.memoryimportInMemorySaver checkpointerInMemorySaver()graphbuilder.compile(checkpointercheckpointer)# 每个线程有唯一的 thread_idconfig{configurable:{thread_id:user-123}}# 第一次执行resultgraph.invoke({input:开始任务},configconfig)# 后续执行可以恢复之前的状态result2graph.invoke({input:继续},configconfig)Checkpointer 的价值会话可以暂停和恢复。这对于长时间运行的任务和人工审批流程至关重要。五、中断与恢复Human-in-the-LoopHuman-in-the-LoopHITL 模式让 Agent 在关键节点暂停执行等待人工确认后再继续。5.1 基本用法fromlanggraph.typesimportinterrupt,Commanddefapproval_node(state:State)-State:需要人工审批的节点# interrupt() 会暂停执行返回人工输入的值human_decisioninterrupt({question:是否批准这个操作,operation:state.get(pending_operation),})ifhuman_decisionapproved:return{status:approved}else:return{status:rejected}执行流程图以初始状态启动执行到 interrupt() 时暂停返回一个中断对象调用方检查中断对象获取需要人工决策的 payload调用方通过 Command(resume…) 恢复执行传入人工输入interrupt() 返回人工输入的值节点继续执行5.2 恢复执行# 第一次执行会停在 interruptresultgraph.invoke(initial_state,configconfig)print(result[__interrupt__])# 查看中断信息# 恢复执行传入人工决策finalgraph.invoke(Command(resumeapproved),# 人工批准configconfig)关键注意恢复执行时必须使用发生中断时使用的同一个 thread ID。5.3 HITL 的典型场景六、实战用 LangGraph 构建一个客服 Agent以下是一个完整的客服 Agent包含条件分支、工具调用和人工升级。fromtypingimportTypedDict,Literalfromlanggraph.graphimportStateGraph,START,ENDfromlanggraph.checkpoint.memoryimportInMemorySaverfromlanggraph.typesimportinterrupt,Commandfromlangchain_openaiimportChatOpenAIfromlangchain_core.toolsimporttool# State 定义 classCustomerServiceState(TypedDict):user_query:strcategory:strresponse:strneeds_human:boolhuman_decision:strresolved:bool# 工具定义 tooldefcheck_order_status(order_id:str)-str:查询订单状态。returnf订单{order_id}已发货预计明天到达。tooldefprocess_refund(order_id:str)-str:处理退款请求。returnf订单{order_id}的退款申请已提交。# 节点定义 defclassify_node(state:CustomerServiceState)-CustomerServiceState:对用户问题进行分类querystate[user_query].lower()if退款inqueryor退钱inquery:categoryrefundelif订单inqueryor物流inquery:categoryorderelse:categoryotherreturn{category:category}defroute_by_category(state:CustomerServiceState)-str:根据分类路由到不同处理节点categorystate[category]ifcategoryrefund:returnrefund_handlerelifcategoryorder:returnorder_handlerelse:returnhuman_escalationdeforder_handler(state:CustomerServiceState)-CustomerServiceState:处理订单查询resultcheck_order_status.invoke({order_id:ORD-001})return{response:result,resolved:True}defrefund_handler(state:CustomerServiceState)-CustomerServiceState:处理退款请求需要人工审批ifstate.get(human_decision)approved:resultprocess_refund.invoke({order_id:ORD-001})return{response:result,resolved:True}else:# 触发中断等待人工审批decisioninterrupt({question:是否批准退款,order_id:ORD-001,})ifdecisionapproved:resultprocess_refund.invoke({order_id:ORD-001})return{response:result,human_decision:approved,resolved:True}else:return{response:退款被拒绝,human_decision:rejected,resolved:True}defhuman_escalation(state:CustomerServiceState)-CustomerServiceState:升级到人工客服return{response:已转接人工客服请稍候。,needs_human:True,resolved:False}# 构建图 builderStateGraph(CustomerServiceState)builder.add_node(classify,classify_node)builder.add_node(order_handler,order_handler)builder.add_node(refund_handler,refund_handler)builder.add_node(human_escalation,human_escalation)builder.add_edge(START,classify)builder.add_conditional_edges(classify,route_by_category,{order_handler:order_handler,refund_handler:refund_handler,human_escalation:human_escalation,})builder.add_edge(order_handler,END)builder.add_edge(refund_handler,END)builder.add_edge(human_escalation,END)checkpointerInMemorySaver()graphbuilder.compile(checkpointercheckpointer)# 执行 config{configurable:{thread_id:customer-001}}# 测试订单查询自动处理resultgraph.invoke({user_query:我的订单到哪了},configconfig)print(result[response])# 订单 ORD-001 已发货预计明天到达。# 测试退款需要人工审批config2{configurable:{thread_id:customer-002}}resultgraph.invoke({user_query:我要退款},configconfig2)print(result[__interrupt__])# 触发中断# 人工批准后恢复finalgraph.invoke(Command(resumeapproved),configconfig2)print(final[response])# 订单 ORD-001 的退款申请已提交。七、LangGraph 与 create_agent 的关系官方推荐从 create_agent 开始快速原型当需要更精细的控制时下降到 StateGraph。两者可以混合使用——外层用 StateGraph 控制业务边界节点内部用 create_agent 处理不确定问题。八、小结StateGraph 三要素State共享状态、Node执行单元、Edge流转控制。条件边通过路由函数根据状态动态决定下一个节点实现分支和循环。Checkpointer持久化会话状态支持中断恢复。Human-in-the-Loop通过 interrupt() 暂停通过 Command(resume…) 恢复实现人工审批。核心原则状态机负责确定性流程控制Agent 负责节点内部的智能决策。create_agent vs StateGraph前者快速原型后者精细控制按需选择。

相关新闻

MATLAB频谱分析与功率谱绘制实战指南

MATLAB频谱分析与功率谱绘制实战指南

1. 频谱分析与功率谱绘制的工程价值在信号处理领域,频谱分析就像给信号做X光检查,能揭示时域波形背后隐藏的频率成分。我十年前第一次用MATLAB做电机振动分析时,发现时域信号看起来只是杂乱无章的波动,但转换到频域后,…

2026/10/15 15:02:45 阅读更多 →
Cursor Token用量查询与模型选型指南:用TaoToken统一Key看清每一笔消耗

Cursor Token用量查询与模型选型指南:用TaoToken统一Key看清每一笔消耗

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

2026/10/15 15:02:45 阅读更多 →
大厂4-5年经验工程师转大模型:优势、路径与避坑指南

大厂4-5年经验工程师转大模型:优势、路径与避坑指南

最近被问得最多的一个问题:4-5年大厂经验,到底要不要转大模型赛道?问的人多了,我发现这已经不是一个技术问题了,而是一个夹杂着焦虑、好奇和职业规划的复杂决策。今天把我看到的真实情况、拆解的判断逻辑,以…

2026/10/15 15:02:45 阅读更多 →

最新新闻

酒店管理系统PHP源码:从课设到生产部署的完整改造指南

酒店管理系统PHP源码:从课设到生产部署的完整改造指南

简介:一套面向酒店日常运营的信息化管理PHP源码,适合PHP开发者、酒店信息技术人员以及Web应用方向学习者参考。系统基于PHP与Access数据库构建,按接待、餐饮、采购、经理四个核心部门划分业务模块,覆盖客户预订、入住退房、菜单点…

2026/10/15 15:43:46 阅读更多 →
HarmonyOS 7文搜图 + 3DGS:文字描述检索并加载空间资产

HarmonyOS 7文搜图 + 3DGS:文字描述检索并加载空间资产

文搜图 3DGS:文字描述检索并加载空间资产 商品库里有 3000 多个 3DGS 资产,用户在搜索框敲一句"红色运动鞋",期望不是看到一张平面缩略图,而是直接把对应的高斯点模型加载进空间场景里转着看。这件事拆开看是两段能力拼…

2026/10/15 15:43:46 阅读更多 →
Delphi数据库组件源码跨版本编译与实战指南

Delphi数据库组件源码跨版本编译与实战指南

简介:Absolute Database v7.93 源代码包面向 Delphi 开发者,尤其是需要在 Delphi 4 至 11(含 Delphi 11 Alexandria)环境中集成嵌入式关系型数据库的工程师。它提供完整的数据库引擎实现,涵盖事务处理、内存与缓存优化…

2026/10/15 15:43:46 阅读更多 →
HarmonyOS 7重建结果缓存策略:LRU 与按场景分组

HarmonyOS 7重建结果缓存策略:LRU 与按场景分组

重建结果缓存策略:LRU 与按场景分组 商品列表有 50 个 3DGS 场景,用户翻来翻去看。第一次进商品详情要重建(或下载预重建结果),耗时 600-1200 ms。第二次进同一个商品如果还要重建,用户会觉得"我都看过…

2026/10/15 15:43:46 阅读更多 →
老鼠走迷宫课设:用栈与队列吃透DFS和BFS

老鼠走迷宫课设:用栈与队列吃透DFS和BFS

简介:一份面向数据结构课程设计场景的C迷宫求解项目,适合计算机相关专业学生完成课设或练习图、栈、DFS/BFS等算法时参考;项目把迷宫抽象为二维数组表示的图,用0/1标记通路与墙体,以老鼠走迷宫为线索,完整演…

2026/10/15 15:43:46 阅读更多 →
道路检测与整车控制:从解压到实车验证的完整落地链路

道路检测与整车控制:从解压到实车验证的完整落地链路

简介:这是一份面向自动驾驶与车辆工程方向学习者的C综合项目压缩包,围绕道路检测与整车控制两大主题,涵盖传感器数据处理、车道线/斑马线识别、控制策略等实现思路,适用于正在研究ADAS、车辆动力学或Visual Studio工程实践的开发者…

2026/10/15 15:42:41 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/15 9:49:14 阅读更多 →