<?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 Renxin's Blog</title><link>https://zh.renxinblog.cn/tags/langgraph/</link><description>Recent content in LangGraph on Renxin's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Tue, 12 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://zh.renxinblog.cn/tags/langgraph/index.xml" rel="self" type="application/rss+xml"/><item><title>第六话｜状态管理：从 dict 到 StateGraph</title><link>https://zh.renxinblog.cn/post/c06-state-management/</link><pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate><guid>https://zh.renxinblog.cn/post/c06-state-management/</guid><description>&lt;img src="https://zh.renxinblog.cn/images/c06-state-management-cover.png" alt="Featured image of post 第六话｜状态管理：从 dict 到 StateGraph" /&gt;&lt;!--
第六话演示代码: https://github.com/renxin2024/GYA/tree/main/c06-state-management（state_machine.py + langgraph_demo.py）
演进主线: 阶段2 Agent 循环第 2 篇 —— 循环有了，状态怎么管
上一篇: 第五话 手写 ReAct Agent（循环的诞生）
下一篇: 第七话 记忆：上下文、短期、长期
--&gt;
&lt;p&gt;第五话我们写出了一个会多步决策的 Agent。回头看它的执行轨迹，你注意到一个细节吗——&lt;strong&gt;每一步之前，模型都要&amp;quot;想&amp;quot;一次&lt;/strong&gt;：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;=== Step 1 ===
[Thought] 用户想要三件事：查天气、算数、总结...
[Action] 调用 get_weather(...) → [Observation] 北京: 多云，25℃
[Action] 调用 calculator(...) → [Observation] 56088

=== Step 2 ===
[Thought] 我有了两个结果，现在总结...
[Final Answer] 北京今天多云... 123×456=56088
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;也就是说，这个循环里**&amp;ldquo;下一步做什么&amp;quot;是由模型推理决定的**：模型说&amp;quot;我要调工具&amp;rdquo;，代码就去调；模型说&amp;quot;我该总结了&amp;quot;，循环就结束。这很自由——但代价是，&lt;strong&gt;每一步的路由决策都花掉一次模型推理&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;现在问一个问题：如果这个流程是固定的——先理解意图、再调工具、最后总结——每次让模型重新&amp;quot;想&amp;quot;一遍下一步去哪，是不是有点浪费？更麻烦的是，如果任务是&amp;quot;查完笔记，有冲突就提醒，没冲突就生成报告&amp;quot;，&amp;ldquo;去哪&amp;quot;的判断写在模型脑子里，它偶尔会想歪，而且每一步都在消耗 token 做路由而不是做任务。&lt;/p&gt;
&lt;p&gt;我先说结论：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;复杂任务的 Agent，需要一个显式的状态管理：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;State&lt;/strong&gt;：共享的状态对象（所有节点读写同一份）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node&lt;/strong&gt;：一个执行单元（函数：读 State → 做事 → 返回更新）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Edge&lt;/strong&gt;：节点间的路由（确定性代码，不走 LLM）
这三个东西组在一起，就是 &lt;strong&gt;StateGraph&lt;/strong&gt;——&lt;strong&gt;图管流程（确定性的传送带），LLM 管内容（只在需要语义的节点被调用）&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这一篇，我们把&amp;quot;流程控制&amp;quot;从模型手里收回来，交给确定性的代码——先写一个&lt;strong&gt;手写状态机&lt;/strong&gt;（零依赖），再用 &lt;strong&gt;LangGraph 的 StateGraph&lt;/strong&gt; 跑同一个任务。你会发现：&lt;strong&gt;框架不是黑盒，它只是把你手写的东西声明式化了。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="一旧世界循环里的状态为什么撑不住"&gt;一、旧世界：循环里的状态为什么撑不住
&lt;/h2&gt;&lt;p&gt;第五话的 while 循环，本质上是个&amp;quot;自由形式&amp;quot;的流程：每次迭代，模型决定下一步做什么，代码照做。它的问题在第四话笔记里已经埋下伏笔——&lt;strong&gt;ReAct 让 LLM 包办一切&lt;/strong&gt;：推理、路由、调用、总结，全在循环里。&lt;/p&gt;
&lt;p&gt;规模化后，四个问题暴露：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;问题&lt;/th&gt;
					&lt;th&gt;表现&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;条件分支不可靠&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&amp;ldquo;查完笔记该去哪&amp;quot;写在模型推理文本里，模型偶尔跑偏&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;只能串行&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;需要&amp;quot;同时查 3 个工具再聚合&amp;quot;时，ReAct 只能一个个来&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;无法优雅暂停&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;想&amp;quot;等用户确认再继续&amp;rdquo;，循环很难挂起&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;路由成本高&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;每一步都让 LLM 想&amp;quot;下一步去哪&amp;rdquo;，token 费在路由上而不是任务上&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 A[用户问题] --&gt; B[LLM 思考&lt;br&gt;推理+路由混合]
 B --&gt; C[执行工具]
 C --&gt; D{模型决定&lt;br&gt;下一步?}
 D --&gt;|再想一次| B
 D --&gt;|结束| E[最终答案]&lt;/pre&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;关键洞察&lt;/strong&gt;：ReAct 循环的问题不是&amp;quot;循环&amp;quot;本身，而是&lt;strong&gt;路由决策也交给了 LLM&lt;/strong&gt;。让模型每次都想&amp;quot;下一步去哪&amp;quot;，既贵又不稳——路由这种事，代码做得又快又准。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="二转折显式状态机state--node--edge-三件套"&gt;二、转折：显式状态机——State / Node / Edge 三件套
&lt;/h2&gt;&lt;p&gt;解决思路来自一个古老而成熟的概念：&lt;strong&gt;状态机（State Machine）&lt;/strong&gt;。把它用在 Agent 上，就是三个核心概念：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# State：共享状态&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="c1"&gt;# 对话历史&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="c1"&gt;# 用户意图（要哪些工具）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="c1"&gt;# 工具结果&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;next_step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="c1"&gt;# 路由指针&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Node：执行单元&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;intent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# 只更新需要的字段&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Edge：路由表（确定性代码）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;next_step&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;parse_intent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parse_intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;next_step&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;execute_tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;execute_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对照你熟悉的 Java 概念：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Java 状态机&lt;/th&gt;
					&lt;th&gt;StateGraph&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;State 枚举&lt;/td&gt;
					&lt;td&gt;Node（可执行代码）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;transition(event)&lt;/td&gt;
					&lt;td&gt;Edge（自动流转）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Context 上下文&lt;/td&gt;
					&lt;td&gt;State（共享字典）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Guard 条件&lt;/td&gt;
					&lt;td&gt;条件边的路由函数（确定性 Python）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键设计原则：图管流程，LLM 管内容。&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;图的节点 = 一个单元操作
├── 纯代码节点 → 调工具、做计算（零 token）
├── 代码+LLM 节点 → 需要语义推理时调 LLM
└── 纯 LLM 节点 → 理解意图、生成文本

图的边 = 节点间路由
├── 普通边 → 永远从 A 到 B
└── 条件边 → 根据 State 决定（确定性代码，不走 LLM）
&lt;/code&gt;&lt;/pre&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;关键洞察&lt;/strong&gt;：图是骨架（确定性的传送带），LLM 是肌肉（只坐到需要语义推理的工位上）。&lt;strong&gt;图的职责是控制 LLM 被调用的时机和次数&lt;/strong&gt;——该省的路由 token 一分不花。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="三原理讲透为什么状态要显式"&gt;三、原理讲透：为什么状态要&amp;quot;显式&amp;quot;？
&lt;/h2&gt;&lt;p&gt;你可能觉得：第五话的循环里也有 state 啊，为什么要&amp;quot;显式&amp;quot;？区别在于&lt;strong&gt;谁在管理状态的流转&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;第五话手写循环&lt;/th&gt;
					&lt;th&gt;状态机&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;状态存在哪&lt;/td&gt;
					&lt;td&gt;零散在循环代码里&lt;/td&gt;
					&lt;td&gt;一个显式 State 对象&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;下一步去哪&lt;/td&gt;
					&lt;td&gt;模型推理决定（或 if-else 散落）&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;next_step&lt;/code&gt; 字段 + 路由表&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;节点边界&lt;/td&gt;
					&lt;td&gt;没有，循环体是&amp;quot;一大坨&amp;quot;&lt;/td&gt;
					&lt;td&gt;每个 Node 独立函数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;调试&lt;/td&gt;
					&lt;td&gt;靠打印&lt;/td&gt;
					&lt;td&gt;看 State 快照即知全貌&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;在展开之前，先精确理解 State 的&lt;strong&gt;生命周期&lt;/strong&gt;——这是状态管理和第五话随手传 list 的根本区别：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;1. 初始 State：{&amp;#34;question&amp;#34;: &amp;#34;...&amp;#34;, &amp;#34;messages&amp;#34;: [...], &amp;#34;results&amp;#34;: {}, &amp;#34;intent&amp;#34;: &amp;#34;&amp;#34;, &amp;#34;next_step&amp;#34;: &amp;#34;parse_intent&amp;#34;}
2. parse_intent 执行 → 返回 {&amp;#34;intent&amp;#34;: [...]} ← 只更新 intent 字段
3. 引擎把返回合并进 State → next_step 指向 execute_tools
4. execute_tools 执行 → 返回 {&amp;#34;results&amp;#34;: {...}, &amp;#34;messages&amp;#34;: [...]} ← 只更新自己的字段
5. ...循环...
6. summarize 返回 {&amp;#34;final_answer&amp;#34;: &amp;#34;...&amp;#34;} → next_step=END → 输出最终 State
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;每个 Node 是一个&lt;strong&gt;纯函数&lt;/strong&gt;：输入完整 State，返回&amp;quot;要更新的字段子集&amp;quot;。&lt;strong&gt;Node 不直接改 State&lt;/strong&gt;——它返回更新，由引擎（手写循环或框架）合并。这个设计保证了：任何节点都能拿到完整上下文（读 State），但只负责自己那部分（写自己的字段），不会互相踩踏。这也是&amp;quot;可并行&amp;quot;和&amp;quot;可恢复&amp;quot;能成立的原因——&lt;strong&gt;状态的读写是受控的&lt;/strong&gt;，不是随处可改的全局变量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;显式&amp;quot;的三个直接收益&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;可审计&lt;/strong&gt;：任何时刻，State 里的 &lt;code&gt;intent/results/next_step&lt;/code&gt; 告诉你 Agent 走到哪了、已经干了什么。挂掉时一眼看出问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可恢复&lt;/strong&gt;：State 是数据，可以序列化存盘。长任务挂了，从上次 State 恢复，不用从头跑（&lt;strong&gt;检查点 checkpoint&lt;/strong&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可并行&lt;/strong&gt;：State 明确后，互不依赖的分支可以并行执行再聚合（StateGraph 支持 fan-out/fan-in）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这三件事正是生产 Agent 和 demo Agent 的分水岭——&lt;strong&gt;demo 靠调试，生产靠状态管理&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这里值得多说两句&amp;quot;可并行&amp;quot;和&amp;quot;可恢复&amp;rdquo;，因为它们是把状态管理从&amp;quot;概念正确&amp;quot;推向&amp;quot;工程价值&amp;quot;的关键。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;并行（fan-out / fan-in）&lt;/strong&gt;：假设一个任务要同时查三个数据源（天气、股票、新闻），ReAct 循环只能串行——先查天气、等结果、再查股票、再等结果。显式状态机可以把三个查询节点做成并行分支，全部完成后在聚合节点合并。LangGraph 的 &lt;code&gt;Send&lt;/code&gt; API 就是干这个的。对延迟敏感的生产系统，这可能是 3 倍和 1 倍的差别。&lt;strong&gt;代价是状态必须可合并&lt;/strong&gt;——每个并行分支只更新自己的字段，不能互相覆盖，这正是 State（共享 dict）设计时要小心的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;检查点（checkpoint）&lt;/strong&gt;：State 既然是数据，就可以序列化。LangGraph 有内置的 checkpointer——每步执行完把 State 存下来。长任务（比如批量处理 1000 条数据）跑到第 700 条挂了，恢复时&lt;strong&gt;从第 700 条的 State 继续&lt;/strong&gt;，而不是从头跑。这对 token 成本和工程效率是量级的差别。手写版要做到这点，就是在 &lt;code&gt;run()&lt;/code&gt; 里每步 &lt;code&gt;json.dumps(state)&lt;/code&gt; 存盘——原理一样，框架替你做了。&lt;/p&gt;
&lt;p&gt;还有一点值得注意：&lt;strong&gt;State 的字段设计直接影响系统复杂度&lt;/strong&gt;。原则是&amp;quot;每个节点只更新自己关心的字段&amp;quot;——parse_intent 只写 &lt;code&gt;intent&lt;/code&gt;，execute_tools 只写 &lt;code&gt;results&lt;/code&gt; 和 &lt;code&gt;messages&lt;/code&gt;，互不干扰。如果所有节点都直接改一个&amp;quot;大杂烩&amp;quot; dict，并行和检查点都会变得困难。这跟 Java 里&amp;quot;类职责单一&amp;quot;是同一个道理。&lt;/p&gt;
&lt;h2 id="四动手同一个任务两种实现"&gt;四、动手：同一个任务，两种实现
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;代码&lt;/strong&gt;：&lt;code&gt;https://github.com/renxin2024/GYA/tree/main/c06-state-management&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;state_machine.py&lt;/code&gt;：手写状态机（零依赖）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;langgraph_demo.py&lt;/code&gt;：LangGraph StateGraph（&lt;code&gt;pip install langgraph&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;DEEPSEEK_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-你的key
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 state_machine.py
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv run --with langgraph python3 langgraph_demo.py &lt;span class="c1"&gt;# 或 pip install langgraph&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;任务&lt;/strong&gt;：&amp;ldquo;北京天气怎么样？顺便算一下 123*456，最后把两个答案整理成一句话。&amp;rdquo;&lt;/p&gt;
&lt;h3 id="手写版三节点状态机"&gt;手写版：三节点状态机
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StateMachine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;question&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;results&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="c1"&gt;# 工具结果&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;intent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 语义理解结果&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;next_step&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;parse_intent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 路由指针&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# Node 1：LLM 节点（唯一需要语义的）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;intent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;next_step&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;execute_tools&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# Node 2：代码节点（路由确定性）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;执行&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;next_step&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;summarize&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;summarize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# Node 3：LLM 节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;final_answer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summarize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;next_step&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;END&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# 状态机引擎：while + next_step 路由&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;next_step&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;END&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;next_step&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实测输出（2026-08-20，deepseek-v4-flash）：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;=== Node: parse_intent ===
=== Node: execute_tools ===
=== Node: summarize ===
最终答案: 北京天气多云、25℃、东北风3级；另外，123×456的计算结果是56088。
执行轨迹: intent=[&amp;#39;get_weather&amp;#39;, &amp;#39;calculator&amp;#39;]
 results={&amp;#39;get_weather&amp;#39;: &amp;#39;多云，25℃...&amp;#39;, &amp;#39;calculator&amp;#39;: &amp;#39;56088&amp;#39;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注意三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;路由是 &lt;code&gt;next_step&lt;/code&gt; 字段跳转&lt;/strong&gt;，不是模型推理——&lt;code&gt;parse_intent → execute_tools → summarize&lt;/code&gt;，每一步去哪由代码决定&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM 只被调了 3 次&lt;/strong&gt;：parse_intent（理解意图）、execute_tools 里（决定工具参数）、summarize（总结）——路由本身零 token&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State 一眼可查&lt;/strong&gt;：&lt;code&gt;intent&lt;/code&gt; 和 &lt;code&gt;results&lt;/code&gt; 告诉你&amp;quot;Agent 认为要做什么、已经得到了什么&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="langgraph-版同一三节点声明式表达"&gt;LangGraph 版：同一三节点，声明式表达
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;parse_intent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parse_intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;execute_tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;execute_tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;summarize&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summarize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_entry_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;parse_intent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;parse_intent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;execute_tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_conditional_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;execute_tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;route_after_tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;summarize&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;summarize&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;summarize&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;节点函数和手写版一模一样&lt;/strong&gt;（&lt;code&gt;parse_intent&lt;/code&gt; 的代码是复制的）——区别只在：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;手写版：&lt;code&gt;while&lt;/code&gt; 循环 + &lt;code&gt;getattr(self, next_step)&lt;/code&gt; 路由&lt;/li&gt;
&lt;li&gt;LangGraph：&lt;code&gt;add_node&lt;/code&gt; + &lt;code&gt;add_edge&lt;/code&gt; 声明 + 框架内部替你跑循环&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实测输出与手写版一致（intent 相同、results 相同、最终答案相同）：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[Node: parse_intent] intent=[&amp;#39;get_weather&amp;#39;, &amp;#39;calculator&amp;#39;]
[Node: execute_tools] get_weather → 多云，25℃，东北风 3 级
[Node: execute_tools] calculator → 56088
[Node: summarize] 北京天气多云、25℃；123×456的结果是56088。
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 A[parse_intent&lt;br&gt;LLM 节点] --&gt; B[execute_tools&lt;br&gt;代码节点]
 B --&gt; C{有工具结果?}
 C --&gt;|是| D[summarize&lt;br&gt;LLM 节点]
 C --&gt;|否| D
 D --&gt; E[END]&lt;/pre&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;关键洞察&lt;/strong&gt;：跑同一个任务，手写版和 LangGraph 版输出完全一致——&lt;strong&gt;框架没有魔法，它只是把 while 循环和路由表声明式化了&lt;/strong&gt;。你手写过一遍，用任何框架都是&amp;quot;换个写法&amp;quot;。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="五工程化延伸什么时候该上-stategraph"&gt;五、工程化延伸：什么时候该上 StateGraph？
&lt;/h2&gt;&lt;p&gt;状态机不是万能的。什么时候该用，什么时候 ReAct/手写循环就够？&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;该用 StateGraph&lt;/th&gt;
					&lt;th&gt;ReAct/手写循环就够&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;流程固定、高频重复&lt;/td&gt;
					&lt;td&gt;低频、一次性、探索性任务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;需要并行 + 聚合（fan-out/fan-in）&lt;/td&gt;
					&lt;td&gt;单步骤顺序执行就够&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;需要人机交互中断（等确认）&lt;/td&gt;
					&lt;td&gt;不需要中间暂停&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;需要审计追溯、断点恢复&lt;/td&gt;
					&lt;td&gt;不需要追踪状态&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;规模大、token 成本敏感&lt;/td&gt;
					&lt;td&gt;一天跑一两次&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一个直觉判断法：&lt;strong&gt;如果流程图能画出来（固定节点+固定边），就用 StateGraph；如果流程本身是探索性的（不知道下一步是什么），用 ReAct。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;作为系列的双语言惯例，这里说一句 Java 版的事：&lt;strong&gt;LangGraph 是 Python 生态库&lt;/strong&gt;（基于 asyncio 和 TypedDict 设计），没有官方 Java 等价实现。所以第六话的 Java 版（GYA-Java &lt;code&gt;c06-state-management/&lt;/code&gt;）实现的是&lt;strong&gt;手写状态机等价版&lt;/strong&gt;——State 用 &lt;code&gt;Map&lt;/code&gt;，Node 用 &lt;code&gt;Function&lt;/code&gt;，图声明就是一张注册表：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NODES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LinkedHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;NODES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;parse_intent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parseIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;NODES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;execute_tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;executeTools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;NODES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;summarize&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;summarize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 引擎：while + next_step 路由（与 Python 版同构）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用 Java 跑同一个任务，输出和 Python 版完全一致（intent/results/最终答案相同）。这说明：&lt;strong&gt;状态机的思想是语言无关的&lt;/strong&gt;——不管用 Python 的 LangGraph、Java 的手写注册表，还是你未来可能遇到的任何 Agent 框架，State/Node/Edge 三件套都是同一套骨架。&lt;/p&gt;
&lt;p&gt;另外澄清一个常见误区：&lt;strong&gt;ReAct 不是 StateGraph 的对立面，而是 StateGraph 的一个特例&lt;/strong&gt;——三节点循环图（Think → Act → Observe → 回环）。类比：&lt;code&gt;ArrayList&lt;/code&gt; 是 &lt;code&gt;List&lt;/code&gt; 的实现，ReAct 是 StateGraph 的一种工作流模式。用 StateGraph 也能画出 ReAct 图（第五话的循环就是），只是 StateGraph 能表达的远不止 ReAct。&lt;/p&gt;
&lt;h2 id="下一步"&gt;下一步
&lt;/h2&gt;&lt;p&gt;State 管住了&amp;quot;流程怎么走&amp;quot;。但还有一个大问题没解决：&lt;strong&gt;模型的记忆&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;第五话里我们把所有历史都塞进 messages——对话 20 轮后，prompt 越来越长，直到超出上下文窗口。这时会发生什么？模型&amp;quot;忘记&amp;quot;最早说的话。&lt;strong&gt;上下文窗口 ≠ 记忆&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;下一篇第七话，我们讲三层记忆架构：上下文（窗口内）、短期（会话内）、长期（跨会话）——以及向量检索怎么让 Agent&amp;quot;记住&amp;quot;重要的事。&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;演示代码&lt;/strong&gt;：&lt;code&gt;https://github.com/renxin2024/GYA/tree/main/c06-state-management&lt;/code&gt;（state_machine.py 零依赖 + langgraph_demo.py 需 langgraph；DeepSeek 官方 API，默认模型 &lt;code&gt;deepseek-v4-flash&lt;/code&gt;）。Java 21 手写状态机等价实现见独立仓库 &lt;code&gt;https://github.com/renxin2024/GYA-Java&lt;/code&gt;（&lt;code&gt;c06-state-management/&lt;/code&gt;，Gradle 工程，&lt;code&gt;gradle run&lt;/code&gt;）。
&lt;strong&gt;参考&lt;/strong&gt;：StateGraph 三概念（State/Node/Edge）、图管流程 LLM 管内容、ReAct 是 StateGraph 特例，来自 LangGraph 官方文档与 Agent 工程实践（hello-agents 讲义延伸）；demo 输出为 2026-05-12 本机实测（deepseek-v4-flash，Python 手写版/LangGraph 版/Java 版三跑一致）。&lt;/p&gt;

 &lt;/blockquote&gt;</description></item></channel></rss>