<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>LangGraph on Morgoth</title><link>https://canuxcheng.com/tags/langgraph/</link><description>Recent content in LangGraph on Morgoth</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Wed, 22 Jul 2026 10:00:00 +0800</lastBuildDate><atom:link href="https://canuxcheng.com/tags/langgraph/index.xml" rel="self" type="application/rss+xml"/><item><title>LangGraph</title><link>https://canuxcheng.com/post/ai_langgraph/</link><pubDate>Wed, 22 Jul 2026 10:00:00 +0800</pubDate><guid>https://canuxcheng.com/post/ai_langgraph/</guid><description>&lt;p&gt;LangGraph 是一个用于构建有状态 Agent 的底层编排框架。它把 Agent 的执行过程描述成一张图：状态在节点之间流动，边决定下一步执行哪个节点，直到任务完成。&lt;/p&gt;
&lt;p&gt;普通的聊天程序通常只有一次模型调用，而 Agent 会不断重复“思考、调用工具、读取结果、继续思考”的循环。随着流程变长，我们还会遇到分支、重试、持久化、人工审批和故障恢复等问题。LangGraph 的价值就是让这些控制逻辑变得显式、可追踪和可恢复。&lt;/p&gt;
&lt;h2 id="langgraph-与-langchain"&gt;LangGraph 与 LangChain&lt;/h2&gt;
&lt;p&gt;LangChain 和 LangGraph 解决的问题并不完全相同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LangChain 提供模型、Prompt、Tool、Retriever 和预构建 Agent 等高层抽象；&lt;/li&gt;
&lt;li&gt;LangGraph 提供状态图、持久化、流式执行和 Human-in-the-loop 等编排能力；&lt;/li&gt;
&lt;li&gt;LangSmith 用于追踪、评估和观察 Agent 的执行过程。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果只是开发标准的 ReAct Agent，LangChain 的 &lt;code&gt;create_agent&lt;/code&gt; 通常更简单。如果需要自定义执行路径、审批节点、并行任务或长时间运行的工作流，再直接使用 LangGraph。&lt;/p&gt;
&lt;h2 id="核心概念"&gt;核心概念&lt;/h2&gt;
&lt;p&gt;一张 LangGraph 主要由三个部分组成。&lt;/p&gt;
&lt;h3 id="state"&gt;State&lt;/h3&gt;
&lt;p&gt;State 是整个工作流共享的数据。它可以保存消息、用户信息、中间结果、重试次数或审批状态。&lt;/p&gt;
&lt;p&gt;节点不应该随意修改 State，而是返回需要更新的部分。LangGraph 会根据 State Schema 和 Reducer 合并更新。&lt;/p&gt;
&lt;h3 id="node"&gt;Node&lt;/h3&gt;
&lt;p&gt;Node 是执行实际工作的函数，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;调用 LLM；&lt;/li&gt;
&lt;li&gt;执行 Tool；&lt;/li&gt;
&lt;li&gt;查询数据库；&lt;/li&gt;
&lt;li&gt;检查权限；&lt;/li&gt;
&lt;li&gt;等待人工审批。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;节点读取当前 State，并返回一个局部 State 更新。&lt;/p&gt;
&lt;h3 id="edge"&gt;Edge&lt;/h3&gt;
&lt;p&gt;Edge 决定节点之间的执行顺序：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通 Edge 固定跳转到下一个节点；&lt;/li&gt;
&lt;li&gt;Conditional Edge 根据 State 动态选择路径；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;START&lt;/code&gt; 和 &lt;code&gt;END&lt;/code&gt; 分别表示图的入口和结束。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个 Tool-calling Agent 的图通常如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;START
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Agent Node ─── 无工具调用 ───▶ END
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; │ 有工具调用
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Tool Node
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; └──────────────────────────▶ Agent Node
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;模型可以连续调用多个工具。只有当模型返回最终答案、不再产生 Tool Call 时，图才会进入 &lt;code&gt;END&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="开发一个-agent"&gt;开发一个 Agent&lt;/h2&gt;
&lt;p&gt;下面开发一个简单的天气 Agent。模型负责理解用户意图，Tool 负责返回天气信息，LangGraph 负责控制模型和 Tool 之间的循环。&lt;/p&gt;
&lt;h3 id="安装依赖"&gt;安装依赖&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -m venv .venv
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;source .venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install -U langgraph langchain langchain-openai
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过环境变量提供模型凭据：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export OPENAI_API_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不要把 API Key 写入代码或提交到版本控制。&lt;/p&gt;
&lt;h3 id="定义-tool"&gt;定义 Tool&lt;/h3&gt;
&lt;p&gt;Tool 是 Agent 与外部世界交互的接口。真实项目中的 Tool 可以调用 API、数据库或内部服务；这里使用静态数据，使示例可以专注于 Agent 的执行逻辑。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; langchain.tools &lt;span style="color:#f92672"&gt;import&lt;/span&gt; tool
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_weather&lt;/span&gt;(city: str) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;查询城市当前的天气。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; weather &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;北京&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;晴，26°C&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;上海&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;多云，29°C&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;深圳&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;阵雨，31°C&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; weather&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(city, &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;暂时没有 &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;city&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 的天气数据&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;函数的类型标注和 Docstring 会成为 Tool Schema 的一部分。描述应该清晰说明工具能做什么，但不要让模型看到内部凭据或不需要的参数。&lt;/p&gt;
&lt;h3 id="定义模型节点"&gt;定义模型节点&lt;/h3&gt;
&lt;p&gt;将 Tool 绑定到模型后，模型可以选择返回普通消息，也可以返回结构化 Tool Call。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; langchain.chat_models &lt;span style="color:#f92672"&gt;import&lt;/span&gt; init_chat_model
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; langgraph.graph &lt;span style="color:#f92672"&gt;import&lt;/span&gt; MessagesState
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tools &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [get_weather]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;model &lt;span style="color:#f92672"&gt;=&lt;/span&gt; init_chat_model(&lt;span style="color:#e6db74"&gt;&amp;#34;openai:gpt-4.1-mini&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;bind_tools(tools)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;call_model&lt;/span&gt;(state: MessagesState):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; system_message &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;你是一个天气助手。需要实时数据时调用工具，不要编造天气。&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; model&lt;span style="color:#f92672"&gt;.&lt;/span&gt;invoke([system_message, &lt;span style="color:#f92672"&gt;*&lt;/span&gt;state[&lt;span style="color:#e6db74"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;]])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;: [response]}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;MessagesState&lt;/code&gt; 已经定义了消息列表及对应的 Reducer。节点只返回新消息，Reducer 会把它追加到历史记录，而不是覆盖整个列表。&lt;/p&gt;
&lt;h3 id="构建执行图"&gt;构建执行图&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ToolNode&lt;/code&gt; 负责执行模型产生的 Tool Call，&lt;code&gt;tools_condition&lt;/code&gt; 则根据模型输出决定进入 Tool Node 还是结束工作流。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; langgraph.checkpoint.memory &lt;span style="color:#f92672"&gt;import&lt;/span&gt; InMemorySaver
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; langgraph.graph &lt;span style="color:#f92672"&gt;import&lt;/span&gt; START, StateGraph
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; langgraph.prebuilt &lt;span style="color:#f92672"&gt;import&lt;/span&gt; ToolNode, tools_condition
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder &lt;span style="color:#f92672"&gt;=&lt;/span&gt; StateGraph(MessagesState)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_node(&lt;span style="color:#e6db74"&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;, call_model)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_node(&lt;span style="color:#e6db74"&gt;&amp;#34;tools&amp;#34;&lt;/span&gt;, ToolNode(tools))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_edge(START, &lt;span style="color:#e6db74"&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_conditional_edges(&lt;span style="color:#e6db74"&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;, tools_condition)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_edge(&lt;span style="color:#e6db74"&gt;&amp;#34;tools&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;checkpointer &lt;span style="color:#f92672"&gt;=&lt;/span&gt; InMemorySaver()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;graph &lt;span style="color:#f92672"&gt;=&lt;/span&gt; builder&lt;span style="color:#f92672"&gt;.&lt;/span&gt;compile(checkpointer&lt;span style="color:#f92672"&gt;=&lt;/span&gt;checkpointer)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;StateGraph&lt;/code&gt; 只是构建器，调用 &lt;code&gt;compile()&lt;/code&gt; 后才会得到可以执行的 Graph。这里加入了内存 Checkpointer，用于在同一个 Thread 中保存每一步的 State。&lt;/p&gt;
&lt;h3 id="运行-agent"&gt;运行 Agent&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;config &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;configurable&amp;#34;&lt;/span&gt;: {&lt;span style="color:#e6db74"&gt;&amp;#34;thread_id&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;demo-thread&amp;#34;&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;inputs &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;上海和深圳哪个城市更热？&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; state &lt;span style="color:#f92672"&gt;in&lt;/span&gt; graph&lt;span style="color:#f92672"&gt;.&lt;/span&gt;stream(inputs, config, stream_mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;values&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; state[&lt;span style="color:#e6db74"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;][&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pretty_print()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行过程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用户消息进入 &lt;code&gt;agent&lt;/code&gt; 节点；&lt;/li&gt;
&lt;li&gt;模型生成两个天气 Tool Call；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tools_condition&lt;/code&gt; 把执行路径路由到 &lt;code&gt;tools&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ToolNode&lt;/code&gt; 执行工具并把结果写入消息列表；&lt;/li&gt;
&lt;li&gt;图重新进入 &lt;code&gt;agent&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;模型比较工具结果并生成最终回答；&lt;/li&gt;
&lt;li&gt;没有新的 Tool Call，图进入 &lt;code&gt;END&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="多轮对话与持久化"&gt;多轮对话与持久化&lt;/h2&gt;
&lt;p&gt;Checkpointer 会按照 &lt;code&gt;thread_id&lt;/code&gt; 保存 Graph State。下一次调用使用相同的 Thread ID，Agent 就能继续之前的对话：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;follow_up &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;那北京呢？&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; graph&lt;span style="color:#f92672"&gt;.&lt;/span&gt;invoke(follow_up, config)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(result[&lt;span style="color:#e6db74"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;][&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;content)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;InMemorySaver&lt;/code&gt; 适合本地开发和测试，进程退出后数据就会消失。生产环境应该使用持久化 Checkpointer，例如 PostgreSQL，或者使用自动管理持久化的 Agent Server。&lt;/p&gt;
&lt;p&gt;Checkpoint 不只是聊天记忆，它还支持：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从失败前的成功步骤继续执行；&lt;/li&gt;
&lt;li&gt;查看每个节点执行后的 State；&lt;/li&gt;
&lt;li&gt;回到历史 Checkpoint 进行调试；&lt;/li&gt;
&lt;li&gt;暂停工作流并等待人工输入。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="human-in-the-loop"&gt;Human-in-the-loop&lt;/h2&gt;
&lt;p&gt;当 Agent 准备执行发送消息、修改数据或产生费用等高风险操作时，可以使用 &lt;code&gt;interrupt()&lt;/code&gt; 暂停 Graph：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; langgraph.types &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Command, interrupt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;review_action&lt;/span&gt;(state):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; decision &lt;span style="color:#f92672"&gt;=&lt;/span&gt; interrupt(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;question&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;是否允许执行该操作？&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;action&amp;#34;&lt;/span&gt;: state[&lt;span style="color:#e6db74"&gt;&amp;#34;pending_action&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;decision&amp;#34;&lt;/span&gt;: decision}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Graph 会保存当前 State，并把审批请求返回给调用方。审批完成后，使用相同的 &lt;code&gt;thread_id&lt;/code&gt; 恢复执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;graph&lt;span style="color:#f92672"&gt;.&lt;/span&gt;invoke(Command(resume&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;approve&amp;#34;&lt;/span&gt;), config)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;恢复时，包含 &lt;code&gt;interrupt()&lt;/code&gt; 的节点会从头重新执行。因此，应把不可重复的副作用放在审批之后，并保证可能重试的节点具有幂等性。&lt;/p&gt;
&lt;h2 id="生产实践"&gt;生产实践&lt;/h2&gt;
&lt;h3 id="state-保持精简"&gt;State 保持精简&lt;/h3&gt;
&lt;p&gt;State 应保存控制流程需要的数据，而不是无限增长的原始内容。长对话需要裁剪、摘要或外部存储，否则模型上下文和 Checkpoint 都会不断膨胀。&lt;/p&gt;
&lt;h3 id="tool-保持单一职责"&gt;Tool 保持单一职责&lt;/h3&gt;
&lt;p&gt;每个 Tool 应完成一个清晰动作，并使用严格的参数类型。读操作和写操作最好分开，写操作可以单独接入审批节点。&lt;/p&gt;
&lt;h3 id="处理错误与重试"&gt;处理错误与重试&lt;/h3&gt;
&lt;p&gt;外部 API 可能超时或限流。根据异常类型配置重试策略，不要让模型在未知错误上无限循环。工具错误应该返回足够的信息供模型调整，但不能泄露凭据和内部实现。&lt;/p&gt;
&lt;h3 id="控制循环"&gt;控制循环&lt;/h3&gt;
&lt;p&gt;Agent 可能因为错误的 Tool Call 不断循环。生产环境需要限制执行步数、调用次数和总超时时间，并为无法完成的任务提供明确的退出路径。&lt;/p&gt;
&lt;h3 id="持久化前考虑幂等性"&gt;持久化前考虑幂等性&lt;/h3&gt;
&lt;p&gt;恢复和重试可能再次执行节点。支付、发送消息或写数据库等操作应使用幂等键，避免产生重复副作用。&lt;/p&gt;
&lt;h3 id="增加可观测性"&gt;增加可观测性&lt;/h3&gt;
&lt;p&gt;Agent 的问题通常不是单个函数报错，而是选择了错误路径或错误工具。应记录节点输入输出、Tool Call、延迟、Token 使用量和最终执行路径，并使用 LangSmith 等工具进行追踪和评估。&lt;/p&gt;
&lt;h2 id="什么时候使用-langgraph"&gt;什么时候使用 LangGraph&lt;/h2&gt;
&lt;p&gt;适合直接使用 LangGraph 的场景包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Agent 有明确的多步骤流程和条件分支；&lt;/li&gt;
&lt;li&gt;需要暂停、恢复或长时间运行；&lt;/li&gt;
&lt;li&gt;需要保存状态或跨会话记忆；&lt;/li&gt;
&lt;li&gt;高风险 Tool 必须经过人工审批；&lt;/li&gt;
&lt;li&gt;需要多个 Agent 或子图协作；&lt;/li&gt;
&lt;li&gt;需要精确控制失败重试和执行路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果需求只是“模型调用几个工具并回答问题”，优先从高层 Agent API 开始。只有当默认循环无法表达业务流程时，再下沉到 LangGraph。这样既能保留开发效率，也不会过早引入复杂度。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.langchain.com/oss/python/langgraph/overview" target="_blank" rel="noopener"
&gt;LangGraph overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.langchain.com/oss/python/langgraph/graph-api" target="_blank" rel="noopener"
&gt;Graph API overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.langchain.com/oss/python/langchain/tools" target="_blank" rel="noopener"
&gt;Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.langchain.com/oss/python/langgraph/persistence" target="_blank" rel="noopener"
&gt;Persistence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.langchain.com/oss/python/langchain/human-in-the-loop" target="_blank" rel="noopener"
&gt;Human-in-the-loop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>