尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

用 LangGraph 搭了一个能自主决策、能暂停确认、还能记住对话的 Agent,我搞明白了状态图

用 LangGraph 搭了一个能自主决策、能暂停确认、还能记住对话的 Agent,我搞明白了状态图 第 9 周用 LangChain 把 RAG 全链路跑通了但有一个问题一直困扰我Chain 是线性的Agent 虽然能自主选工具但流程还是一条路走到底。如果中间结果不同需要走不同的处理路径呢第 10 周学 LangGraph就是来解决这个问题的。一、StateGraph给 Agent 画一张地图1.1 Chain 的局限第 9 周学的 LCEL 管道符很好用prompt | llm | parser数据从左流到右清晰简洁。但真实业务很少有只走一条路的情况。用户问3号设备温度超标了你要走温度处理流程。用户问设备振动异常你要走振动处理流程。用户问帮我查一下5号设备你得先查数据再判断要不要告警。这些分支逻辑管道符搞不定。LangGraph 的 StateGraph 就是为这个设计的。它有四个核心概念概念是什么类比State共享数据容器所有节点读写同一份数据白板Node处理函数读 State 做处理返回更新工位上的工人Edge节点间的固定连接“A 之后走 B”传送带Conditional Edge根据 State 动态选择下一个节点岔路口的道岔前三个和 Chain 差不多。关键是第四个——Conditional Edge 让流程可以根据中间结果走不同的分支。温度故障振动故障其他 START 分类问题 条件边问题类型 温度处理 振动处理 通用处理 生成回答 END这张图说的是用户问题进来后先分类然后根据分类结果走不同的处理分支最后汇合到生成回答。条件边是 LangGraph 比 Chain 强的地方——同一个图不同输入走不同路径。1.2 代码长什么样fromlanggraph.graphimportStateGraph,START,END graphStateGraph(AgentState)graph.add_node(classify,classify_question)graph.add_node(handle_temp,handle_temperature)graph.add_node(handle_vib,handle_vibration)graph.add_node(generate,generate_answer)graph.add_edge(START,classify)# 条件边根据分类结果走不同分支graph.add_conditional_edges(classify,lambdastate:state[category],{温度故障:handle_temp,振动故障:handle_vib,其他:handle_generic})graph.add_edge(handle_temp,generate)graph.add_edge(handle_vib,generate)graph.add_edge(handle_generic,generate)graph.add_edge(generate,END)appgraph.compile()add_conditional_edges那三行是核心。读 State 里的category字段根据值决定走哪个节点。二、ReAct 循环让 LLM 自己决定调什么工具2.1 把 Tool Calling 搬进状态图第 9 周 Day 60 学过 ReAct Agent——LLM 在循环中交替执行推理和行动自己决定调哪个工具。但那时候用的是 LangChain 的create_react_agent内部结构是个黑箱。第 10 周把这个黑箱拆开了。用 LangGraph 手动搭一个 ReAct Agent其实就两个节点加一个条件边有 tool_calls没有 tool_calls START LLM 节点思考 决策 should_continue需要调工具 Tool 节点执行工具 END输出回答LLM 节点调用大模型。如果 LLM 认为需要调工具消息里会附带tool_calls工具名 参数。条件边should_continue检查有没有tool_calls有就走 Tool 节点执行工具执行完回到 LLM 节点继续思考没有就走 END 输出最终回答。这个循环会一直转直到 LLM 觉得信息够了不再请求工具调用。defshould_continue(state):last_messagestate[messages][-1]ifhasattr(last_message,tool_calls)andlast_message.tool_calls:returntools# 走 Tool 节点returnend# 走 END这个函数就是循环的方向盘。三行代码决定了 Agent 是继续干活还是停下来回答。2.2 和 Chain 的本质区别对比ChainLCELLangGraph Agent流程固定写死在代码里动态LLM 自主决定工具调用程序员 if-else 控制LLM 根据意图自动选择循环不支持ReAct 循环可以多次调工具状态无共享状态State 在节点间传递Chain 像流水线工人按固定步骤操作。LangGraph Agent 像有经验的师傅根据情况自己判断下一步做什么。三、高级特性让 Agent 从能跑到能用3.1 流式输出app.invoke()等整个 Agent 跑完才返回。如果 Agent 调了三个工具、循环了三轮用户要等十几秒。LangGraph 有两种流式模式。stream_modevalues是节点级——每个节点执行完后输出 State 更新适合调试。stream_modemessages是 token 级——LLM 每生成一个字就推送和 ChatGPT 体验一样适合给用户看。forchunkinapp.stream({messages:[HumanMessage(content3号设备温度怎么样)]},stream_modemessages):msg,metadatachunkifmsg.content:print(msg.content,end,flushTrue)3.2 子图大流程拆小流程一个大的 StateGraph 里的某个节点本身也可以是另一个完整的 StateGraph。就像做菜这个步骤在总流程图里是一个框但展开后里面有备料 → 烹饪 → 摆盘三个子步骤。# 子图故障处理内部流程sub_graphStateGraph(ProcessState)sub_graph.add_node(retrieve,retrieve_knowledge)sub_graph.add_node(check_alert,check_if_alert_needed)sub_graph.add_node(send_alert,send_alert_node)# ... 连接边# 主图子图作为一个节点main_graphStateGraph(MainState)main_graph.add_node(classify,classify_question)main_graph.add_node(process,sub_graph.compile())# 子图作为节点main_graph.add_node(generate,generate_answer)子图的好处是模块化。故障处理流程可以独立开发、测试、调试然后作为一个整体嵌入主图。改子图不影响主流程。3.3 Human-in-the-Loop关键步骤暂停确认有些操作不能全自动。发告警、创建工单、修改设备配置万一 Agent 判断错了呢interrupt_before[send_alert]让 Agent 在执行告警节点前暂停。等人确认后传入Command(resumeapproved)继续执行。appgraph.compile(interrupt_before[send_alert],checkpointerMemorySaver()# 暂停后需要保存状态)必须有 Checkpointer 才能暂停。暂停后 State 被保存人确认后从断点继续。3.4 Checkpointer状态持久化Checkpointer 解决两件事。一是 Agent 跑到一半挂了从断点继续而不是从头开始。二是多轮对话——同一个thread_id的多次调用共享 StateAgent 能记住上一轮说了什么。fromlanggraph.checkpoint.memoryimportMemorySaver appgraph.compile(checkpointerMemorySaver())# 第一轮resultapp.invoke({messages:[HumanMessage(content3号设备温度偏高)]},config{configurable:{thread_id:user_001}})# 第二轮Agent 记得上一轮的内容resultapp.invoke({messages:[HumanMessage(content那帮我查一下具体数值)]},config{configurable:{thread_id:user_001}})# Agent 知道那指的是3号设备学习用 MemorySaver内存重启丢失生产用 SqliteSaver 或 PostgresSaver持久化到磁盘。四、记忆管理记住该记的忘掉该忘的4.1 为什么不能全记Checkpointer 能保存完整对话历史但全传给 LLM 有三个问题上下文窗口有限对话太长会超限token 按量计费越长越贵噪声太多影响回答质量。4.2 三种策略策略保留什么token 消耗信息丢失Buffer完整对话历史线性增长最终超限不丢Window最近 K 轮固定上限早期对话全丢Summary历史摘要 最近几轮原文固定上限摘要可能丢细节是否不需要需要对话有多长 10 轮Buffer全记需要记住早期信息Window只记最近 K 轮Summary摘要 最近几轮生产环境推荐 Window Summary 组合最近 5 轮保留原文细节完整更早的压缩成 LLM 生成的摘要保留关键事实token 消耗固定。State 里仍然保存完整历史Checkpointer 不丢数据但传给 LLM 的消息列表经过记忆管理处理只放最有价值的信息。五、整合多步骤 Agent 工作流5.1 把第 10 周学的全部串起来Day 68 做了一个完整的多步骤 Agent 工作流把这一周学的东西全用上了是✅否 用户提问 意图分类 数据检索 AgentReAct 调工具查数据 知识检索 AgentReAct 调工具查手册 分析判断是否超警戒值 需要告警⏸️ 暂停等确认HITL 告警 工单 Agent 记录日志 生成回答 Checkpointer保存状态这张图里用到了第 10 周所有知识点知识点在哪里用StateGraph整体架构6 个节点 条件边ReAct Agent数据检索、知识检索、告警处理各自是独立的 ReAct Agent子图每个专员 Agent 可以拆成子图独立开发流式输出最终回答用 messages 模式逐字推送HITL告警前interrupt_before暂停等人确认Checkpointer多轮对话 状态持久化记忆管理Window Summary 控制 token每个阶段是一个独立的节点有自己的工具集和 Prompt。阶段之间通过 State 传递数据。改一个阶段不影响其他阶段。六、这周的整体感受第 9 周学 LangChain 的时候最大的感受是组件化做得好。第 10 周学 LangGraph最大的感受是控制力终于够了。Chain 是线性的遇到分支就没办法。Agent 能自主选工具了但流程还是一条路走到底。LangGraph 把这两个问题一起解决了——StateGraph 给你画地图的能力条件边让你在岔路口走不同的路ReAct 循环让每个节点内部也能自主决策。另一个收获是对记忆的理解深了。以前觉得 Agent 记住对话不就是把消息列表存起来吗。学完才知道全记不行token 爆全丢不行忘了关键信息得在中间找平衡。Window Summary 的组合是目前见过最实用的方案。十周下来技术栈的拼图越来越完整了周学了什么能力1-3Spring Boot Redis 多线程 AOP能写后端业务代码4Docker K8s CI/CD能部署上线5-6微服务 消息队列能拆微服务、做异步通信7ES 分布式基础能做全文搜索、理解分布式8Spring AI能用 Java 做 AI 对话 RAG9LangChain RAG 全链路能用 Python 搭生产级 RAG10LangGraph Agent 工作流能搭多步骤、带分支、能记忆的 Agent 系统下周学大模型微调PyTorch 基础 LoRA/QLoRA HuggingFace从用模型到调模型。
返回列表