<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Workflow on Renxin's Blog</title><link>https://zh.renxinblog.cn/tags/workflow/</link><description>Recent content in Workflow on Renxin's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Tue, 01 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://zh.renxinblog.cn/tags/workflow/index.xml" rel="self" type="application/rss+xml"/><item><title>第十一话｜ReAct、Plan-and-Execute 还是 Workflow：Agent 的下一步由谁决定？</title><link>https://zh.renxinblog.cn/post/gya-c11-agent-control-modes/</link><pubDate>Tue, 01 Sep 2026 00:00:00 +0000</pubDate><guid>https://zh.renxinblog.cn/post/gya-c11-agent-control-modes/</guid><description>&lt;img src="https://zh.renxinblog.cn/images/gya-c11-agent-control-modes-cover.png" alt="Featured image of post 第十一话｜ReAct、Plan-and-Execute 还是 Workflow：Agent 的下一步由谁决定？" /&gt;&lt;p&gt;先把话挑明：这篇&lt;strong&gt;不教你搭博客写作工作流，也不给 ReAct、Plan-and-Execute、Workflow 排座次&lt;/strong&gt;，只回答一个问题——&lt;strong&gt;Agent 的下一步由谁决定，谁才能真正让它停下来。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;下面反复出现的“写博客”，只是一件切题标本：它自带审批门和写文件的副作用位点，正好能把“谁决定下一步”照清楚。故事是博客，题目是控制权。&lt;/p&gt;
&lt;p&gt;写 Skill 的时候，你加了一条流程规则：&lt;strong&gt;先生成创作纲领，然后停下等用户确认，确认通过前不准写正文。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;运行起来，Agent 却照常往下冲：生成完纲领，下一秒就去写草稿，或者干脆在循环里来回打转。&lt;/p&gt;
&lt;p&gt;是模型没读懂 Skill 吗？不全是。真正的问题是——&lt;strong&gt;“停下来等确认”这件事，根本不归 Skill 管，也不归模型管，它归运行时代码管。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Agent 的三种流行组织方式，ReAct、Plan-and-Execute、Workflow，本质区别不是“会不会调 LLM”，而是：&lt;strong&gt;下一步行动由谁决定，谁才能真正把这一次 Run 停下来。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;结论先放这里：&lt;strong&gt;模型提建议，代码做决定。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="先把决定这个词拆开"&gt;先把“决定”这个词拆开
&lt;/h2&gt;&lt;p&gt;我们通常说“Agent 决定下一步”，这句话其实把三件不同的事揉在了一起：&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;code&gt;write_draft&lt;/code&gt;”&lt;/td&gt;
					&lt;td&gt;业务 Service 提一个方案&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;运行时 Runtime&lt;/td&gt;
					&lt;td&gt;决定暴露哪些动作、采纳哪条建议、走哪个状态迁移&lt;/td&gt;
					&lt;td&gt;应用网关 / 策略层&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;执行器 Executor&lt;/td&gt;
					&lt;td&gt;真正产生副作用：写文件、发请求、改数据库&lt;/td&gt;
					&lt;td&gt;DAO / Outbox 写库&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;%%{init: {'theme':'neutral'}}%%
flowchart LR
 LLM[模型] --&gt;|建议动作| RT[Runtime]
 RT --&gt;|暴露可见动作| EX[Executor]
 EX --&gt;|副作用| GW[Action Gateway]
 GW --&gt; OUT[虚拟产物]
 RT --&gt;|固定迁移| WF[Workflow 状态]&lt;/pre&gt;&lt;p&gt;读了这张图，很多“Agent 不可控”的困惑会自动解开：&lt;strong&gt;模型负责“说”，不负责“做”。&lt;/strong&gt; 它可以说出 &lt;code&gt;write_draft&lt;/code&gt;，但要不要做、什么时候做、做之前过几道闸门，由 Runtime 和 Executor 这一侧决定。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键洞察&lt;/strong&gt;：控制权不是一句“用了 Workflow”或“用了 StateGraph”就自动获得；它分散在“动作可见性、状态迁移、副作用网关”三处，每一处都必须有代码接手。&lt;/p&gt;
&lt;h2 id="同一个任务三种跑法"&gt;同一个任务，三种跑法
&lt;/h2&gt;&lt;p&gt;用一个故意制造矛盾的隔离任务，把三种模式各跑一遍。任务只有两条规则，还互相打架：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;规则一：先创建创作纲领并等待用户确认。
规则二：不要停下来，直接写出完整草稿。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;任务跑在内存里的&lt;strong&gt;虚拟文章存储&lt;/strong&gt;上：不写公开博客、不提交 Git、不部署。三种模式跑同一段任务，区别马上出来：&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;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;ReAct&lt;/td&gt;
					&lt;td&gt;模型看着 Observation 选下一动作&lt;/td&gt;
					&lt;td&gt;全部动作&lt;/td&gt;
					&lt;td&gt;只有 &lt;code&gt;finish&lt;/code&gt; 或步数上限&lt;/td&gt;
					&lt;td&gt;连走三次 &lt;code&gt;create_brief&lt;/code&gt;，&lt;code&gt;draft_written=false&lt;/code&gt;，从未进入等待&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Plan-and-Execute&lt;/td&gt;
					&lt;td&gt;模型先出一份计划，Executor 顺序执行&lt;/td&gt;
					&lt;td&gt;计划内的动作&lt;/td&gt;
					&lt;td&gt;计划文本里的“等待”停不住&lt;/td&gt;
					&lt;td&gt;计划含 &lt;code&gt;await_brief_approval&lt;/code&gt;，Executor 仍尝试 &lt;code&gt;write_draft&lt;/code&gt;，被网关拦下&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Workflow&lt;/td&gt;
					&lt;td&gt;代码写死状态迁移，模型只生成当前节点内容&lt;/td&gt;
					&lt;td&gt;当前节点需要的内容生成&lt;/td&gt;
					&lt;td&gt;状态机 &lt;code&gt;return&lt;/code&gt;/迁移&lt;/td&gt;
					&lt;td&gt;生成纲领后直接 &lt;code&gt;run_suspended&lt;/code&gt;，Trace 里根本没有 &lt;code&gt;write_draft&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;有个反直觉的点：&lt;strong&gt;三种模式其实都调了 LLM。&lt;/strong&gt; 所以别再用“Workflow 不智能、ReAct 才智能”来区分它们。区别只在控制权：ReAct 把“下一步”反复交给模型；Workflow 把“下一步”收回到代码。&lt;/p&gt;
&lt;p&gt;所以选型的第一问是——“这一步由谁拍板才安全、才够用”，而不是“哪个听起来更像 Agent”。&lt;/p&gt;
&lt;h2 id="先跑起来最小对照"&gt;先跑起来：最小对照
&lt;/h2&gt;&lt;p&gt;不写框架，先写一个十秒看懂的最小实验。它只演示一件事：&lt;strong&gt;把 &lt;code&gt;waiting&lt;/code&gt; 改成 &lt;code&gt;True&lt;/code&gt;，不等于让循环停下。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;下面是完整可粘贴的 Python，不需要 API Key：&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="s2"&gt;&amp;#34;&amp;#34;&amp;#34;“等待”是数据标记，还是控制信号？最小对照。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;annotations&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="n"&gt;APPROVED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&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&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;gateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;str&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;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;create_brief&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="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;brief_created_in_virtual_store&amp;#34;&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;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;wait_for_approval&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="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;wait_marker_recorded&amp;#34;&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;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;write_draft&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;APPROVED&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="s2"&gt;&amp;#34;draft_written_to_virtual_store&amp;#34;&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="s2"&gt;&amp;#34;blocked_by_action_gateway&amp;#34;&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="s2"&gt;&amp;#34;unknown_action&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&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_naive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&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;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;create_brief&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;wait_for_approval&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;write_draft&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;waiting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;events&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;for&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;steps&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;gateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&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;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;wait_for_approval&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="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;waiting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;action&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;draft_written&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;draft_written_to_virtual_store&amp;#34;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;events&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[naive] waiting=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;waiting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; draft_written=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;draft_written&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;events&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;18s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&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&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_suspending&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&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;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;create_brief&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;wait_for_approval&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;write_draft&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;waiting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;events&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;for&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;steps&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;gateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&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;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;wait_for_approval&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;waiting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;run_suspended&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[fix] waiting=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;waiting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; draft_written=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;events&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;18s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&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="k"&gt;return&lt;/span&gt; &lt;span class="c1"&gt;# 真正的“停”：后面的 write_draft 根本不会被循环碰到&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;action&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&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&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="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;== 错误版：像 Plan-and-Execute 的朴素 Executor ==&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;run_naive&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;print&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;== 修复版：Workflow 的状态机迁移 ==&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;run_suspending&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;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;== 错误版：像 Plan-and-Execute 的朴素 Executor ==
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[naive] waiting=True draft_written=False
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; create_brief -&amp;gt; brief_created_in_virtual_store
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; wait_for_approval -&amp;gt; wait_marker_recorded
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; write_draft -&amp;gt; blocked_by_action_gateway
&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;== 修复版：Workflow 的状态机迁移 ==
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[fix] waiting=True draft_written=False
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; create_brief -&amp;gt; brief_created_in_virtual_store
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; wait_for_approval -&amp;gt; run_suspended
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;读输出的判断标准：&lt;strong&gt;错误版里 &lt;code&gt;waiting=True&lt;/code&gt; 和 &lt;code&gt;write_draft&lt;/code&gt; 同时出现&lt;/strong&gt;——它“认为”自己在等待，却还在往下执行；修复版里 &lt;code&gt;run_suspended&lt;/code&gt; 出现后，&lt;code&gt;write_draft&lt;/code&gt; 从 Trace 里直接消失。这才是“停”。&lt;/p&gt;
&lt;h2 id="三种模式的真实-trace"&gt;三种模式的真实 Trace
&lt;/h2&gt;&lt;p&gt;把三段真实模型 Trace 缩到关键行。完整实验在配套代码仓库的 Python 与 Java 两个子目录里各有一份。&lt;/p&gt;
&lt;p&gt;离线回归（不调模型，只验证控制流）：&lt;/p&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;cd&lt;/span&gt; python
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m unittest -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Ran 8 tests in 0.000s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# OK&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;真实模型（需先导出 &lt;code&gt;LLM_API_URL&lt;/code&gt;、&lt;code&gt;LLM_MODEL&lt;/code&gt;、&lt;code&gt;LLM_API_KEY&lt;/code&gt;，Key 只从环境变量读取）：&lt;/p&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;python3 control_modes.py --live
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本轮 Python 真实输出（节选，共三段）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# ReAct：模型每步都选 create_brief，从不进入等待
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[trace] {&amp;#34;mode&amp;#34;:&amp;#34;react&amp;#34;,&amp;#34;step&amp;#34;:1,&amp;#34;decision_maker&amp;#34;:&amp;#34;llm&amp;#34;,&amp;#34;action&amp;#34;:&amp;#34;create_brief&amp;#34;,...}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[trace] {&amp;#34;mode&amp;#34;:&amp;#34;react&amp;#34;,&amp;#34;step&amp;#34;:2,&amp;#34;decision_maker&amp;#34;:&amp;#34;llm&amp;#34;,&amp;#34;action&amp;#34;:&amp;#34;create_brief&amp;#34;,...}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[trace] {&amp;#34;mode&amp;#34;:&amp;#34;react&amp;#34;,&amp;#34;step&amp;#34;:3,&amp;#34;decision_maker&amp;#34;:&amp;#34;llm&amp;#34;,&amp;#34;action&amp;#34;:&amp;#34;create_brief&amp;#34;,...}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[summary] {&amp;#34;mode&amp;#34;:&amp;#34;react&amp;#34;,&amp;#34;waiting_for_approval&amp;#34;:false,&amp;#34;draft_written&amp;#34;:false,&amp;#34;usage&amp;#34;:{&amp;#34;total_tokens&amp;#34;:427}}
&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;# Plan-and-Execute：计划里有 await，Executor 仍尝试写稿，网关拦下
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[trace] {&amp;#34;mode&amp;#34;:&amp;#34;plan_and_execute&amp;#34;,&amp;#34;step&amp;#34;:0,&amp;#34;decision_maker&amp;#34;:&amp;#34;plan_validator&amp;#34;,&amp;#34;result&amp;#34;:&amp;#34;ACCEPT&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[trace] {&amp;#34;mode&amp;#34;:&amp;#34;plan_and_execute&amp;#34;,&amp;#34;step&amp;#34;:2,&amp;#34;decision_maker&amp;#34;:&amp;#34;executor&amp;#34;,&amp;#34;action&amp;#34;:&amp;#34;await_brief_approval&amp;#34;,&amp;#34;result&amp;#34;:&amp;#34;wait_marker_recorded&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[trace] {&amp;#34;mode&amp;#34;:&amp;#34;plan_and_execute&amp;#34;,&amp;#34;step&amp;#34;:3,&amp;#34;decision_maker&amp;#34;:&amp;#34;executor&amp;#34;,&amp;#34;action&amp;#34;:&amp;#34;write_draft&amp;#34;,&amp;#34;result&amp;#34;:&amp;#34;blocked_by_action_gateway&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[summary] {&amp;#34;mode&amp;#34;:&amp;#34;plan_and_execute&amp;#34;,&amp;#34;waiting_for_approval&amp;#34;:true,&amp;#34;draft_written&amp;#34;:false,&amp;#34;usage&amp;#34;:{&amp;#34;total_tokens&amp;#34;:142}}
&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;# Workflow：代码固定迁移，模型从未见过 write_draft
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[trace] {&amp;#34;mode&amp;#34;:&amp;#34;workflow&amp;#34;,&amp;#34;step&amp;#34;:1,&amp;#34;decision_maker&amp;#34;:&amp;#34;llm&amp;#34;,&amp;#34;event&amp;#34;:&amp;#34;generate_brief&amp;#34;,...}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[trace] {&amp;#34;mode&amp;#34;:&amp;#34;workflow&amp;#34;,&amp;#34;step&amp;#34;:2,&amp;#34;decision_maker&amp;#34;:&amp;#34;workflow&amp;#34;,&amp;#34;action&amp;#34;:&amp;#34;WAITING_FOR_APPROVAL&amp;#34;,&amp;#34;result&amp;#34;:&amp;#34;run_suspended&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[summary] {&amp;#34;mode&amp;#34;:&amp;#34;workflow&amp;#34;,&amp;#34;waiting_for_approval&amp;#34;:true,&amp;#34;draft_written&amp;#34;:false,&amp;#34;usage&amp;#34;:{&amp;#34;total_tokens&amp;#34;:203}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;三段摆在一起，结论是确定的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ReAct 能停吗？&lt;/strong&gt; 能，但停的条件由模型选中的 &lt;code&gt;finish&lt;/code&gt; 或代码给的最大步数决定；“等待”不在它自动遵守的条件里。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan-and-Execute 能停吗？&lt;/strong&gt; 计划里写了 &lt;code&gt;await_brief_approval&lt;/code&gt;，但朴素 Executor 把它当普通动作执行——只改状态、继续下一步，最后靠 Action Gateway 拦下 &lt;code&gt;write_draft&lt;/code&gt; 的副作用，可 Executor 并没有因此停，它仍往下走到 &lt;code&gt;finish&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workflow 能停吗？&lt;/strong&gt; 当前节点生成内容后，代码把下一状态固定成 &lt;code&gt;WAITING_FOR_APPROVAL&lt;/code&gt; 并结束 Run；模型连 &lt;code&gt;write_draft&lt;/code&gt; 这个动作都没被暴露。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;关键洞察&lt;/strong&gt;：&lt;code&gt;await&lt;/code&gt; 写在计划里是&lt;strong&gt;数据&lt;/strong&gt;，只有 &lt;code&gt;break / return / 状态机迁移&lt;/code&gt; 才是&lt;strong&gt;控制&lt;/strong&gt;。指望模型或 Planner 在文本里“自觉地停下来”，就是把安全押在概率上。&lt;/p&gt;
&lt;h2 id="外层-workflow内层-react"&gt;外层 Workflow，内层 ReAct
&lt;/h2&gt;&lt;p&gt;既然 Workflow 最可控，ReAct 最灵活，生产里怎么选？&lt;/p&gt;
&lt;p&gt;答案是&lt;strong&gt;分层，而不是二选一&lt;/strong&gt;。一个可信的技术文章 Agent 可以长这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Workflow 固定阶段：
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 0. 准备材料 ──&amp;gt; 1. 生成纲领 ──&amp;gt; [等待审批] ──&amp;gt; 2. 生成梗概 ──&amp;gt; [等待审批] ──&amp;gt; 3. 写草稿
&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 探索节点（ReAct）：搜证据、读源码、判断“材料够不够”，直到确定性条件达成
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;外层用 Workflow 锁阶段和硬边界&lt;/strong&gt;：未达到 &lt;code&gt;WAITING_FOR_APPROVAL&lt;/code&gt;，后面的阶段就不存在，模型没机会跳到“写草稿”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内层用 ReAct 降低不确定性&lt;/strong&gt;：一个“读懂这个仓库再写梗概”的开放任务，让模型多走几步、看 Observation，比死板的一次性流程更实用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任务清晰后再交给 Planner/Executor&lt;/strong&gt;：计划进入 Executor 前先用确定性校验器检查结构、动作白名单和审批顺序；LLM 可以辅助语义判断，但硬规则必须由代码执行——校验器只返回 &lt;code&gt;ACCEPT / REVISE / REJECT&lt;/code&gt;，不替模型悄悄改计划。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里有个很容易被模糊的点：&lt;strong&gt;“能不能进计划”由谁判？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不要让模型直接回一个 &lt;code&gt;ready: true&lt;/code&gt;。那是模型自我声明，等于让它自己给自己批作业。正确的分工是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LLM 交&lt;strong&gt;结构化证据&lt;/strong&gt;：已调研了哪几项、还剩哪几项没做、每项的依据是什么。&lt;/li&gt;
&lt;li&gt;Runtime 按&lt;strong&gt;校验契约&lt;/strong&gt;算布尔：条件满足才算 &lt;code&gt;ready&lt;/code&gt;，证据缺失就补齐或换策略。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就是“模型提建议，代码做决定”在 readiness 上的具体落法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键洞察&lt;/strong&gt;：给开放任务留 ReAct，给高风险边界上 Workflow，再把“能不能切换阶段”写成运行时契约；三者不是竞争关系，是一条流水线上的不同工位。&lt;/p&gt;
&lt;h2 id="workflow-该不该上框架"&gt;Workflow 该不该上框架
&lt;/h2&gt;&lt;p&gt;也许你已经想到 LangGraph 这类框架：它能不能把上面这些自动解决？&lt;/p&gt;
&lt;p&gt;能解决一部分，但要分清“&lt;strong&gt;能力&lt;/strong&gt;”和“&lt;strong&gt;承诺&lt;/strong&gt;”。LangGraph 的文档把两件事分开：Workflow 走&lt;strong&gt;预定义的代码路径&lt;/strong&gt;，Agent 每一步由模型&lt;strong&gt;动态决定过程&lt;/strong&gt;；它用状态图、checkpointer、&lt;code&gt;interrupt()&lt;/code&gt; 提供挂起、保存状态和外部命令恢复的实现能力。这些能力正好是第十二话要做“可恢复、不可绕过的审批”的原料。&lt;/p&gt;
&lt;p&gt;但它&lt;strong&gt;不会因为你用了 LangGraph，就自动保证审批合规&lt;/strong&gt;。状态图上的节点连得通，不代表业务上这一段就该执行。&lt;code&gt;publish_article&lt;/code&gt; 这种迁移在不允许发布的状态下必须由运行时拒绝——框架给你的是“状态图能跑”，不给你“业务边界天然正确”。&lt;/p&gt;
&lt;p&gt;所以这一话只把 LangGraph 标记为实现方向，不展开 API。下一步要去的地方，就是把“接下来该做什么”从人工解读变成可以被恢复、被拦截、被审计的运行时机制。&lt;/p&gt;
&lt;p&gt;一句话收口：框架替你搬运状态，不替你决定业务。安全边界只能来自你写的校验契约和动作网关。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;下一话：Skill 只是提示词——如何让 Agent 真正停下来，并且跨进程可以恢复、无法绕过审批。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="踩坑速查"&gt;踩坑速查
&lt;/h2&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;code&gt;--live&lt;/code&gt; 报 &lt;code&gt;LLM network error&lt;/code&gt; / DNS 解析失败&lt;/td&gt;
					&lt;td&gt;沙箱或本地网络到模型 API 不通&lt;/td&gt;
					&lt;td&gt;用离线 &lt;code&gt;python3 -m unittest -v&lt;/code&gt; 先验证控制流；真实调用再解决网络/代理&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;模型返回值解析失败或 &lt;code&gt;content&lt;/code&gt; 为空&lt;/td&gt;
					&lt;td&gt;部分推理模型默认 thinking，&lt;code&gt;content&lt;/code&gt; 在 reasoning 里&lt;/td&gt;
					&lt;td&gt;显式 &lt;code&gt;thinking:{&amp;quot;type&amp;quot;:&amp;quot;disabled&amp;quot;}&lt;/code&gt; 并要求 JSON 输出&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Java 运行报 &lt;code&gt;UnsupportedClassVersionError&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;用了旧版 JDK&lt;/td&gt;
					&lt;td&gt;用 JDK 21 + &lt;code&gt;./gradlew&lt;/code&gt;，或直接 &lt;code&gt;javac/java&lt;/code&gt; 跑 &lt;code&gt;MinimalSuspendDemo&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;计划里写了“等待”，运行时不暂停&lt;/td&gt;
					&lt;td&gt;把等待当成了数据标记&lt;/td&gt;
					&lt;td&gt;改成 &lt;code&gt;return&lt;/code&gt;/&lt;code&gt;break&lt;/code&gt;/状态机迁移，并写进回归测试&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="参考"&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;LangGraph Documentation, “Workflows and Agents”, &lt;a class="link" href="https://docs.langchain.com/oss/python/langgraph/workflows-agents/" target="_blank" rel="noopener"
 &gt;https://docs.langchain.com/oss/python/langgraph/workflows-agents/&lt;/a&gt;（Workflow 与 Agent 的路径决定方式，成文时已核对）。&lt;/li&gt;
&lt;li&gt;LangGraph Documentation, “Interrupts / Human-in-the-loop”, &lt;a class="link" href="https://docs.langchain.com/oss/python/langgraph/interrupts/" target="_blank" rel="noopener"
 &gt;https://docs.langchain.com/oss/python/langgraph/interrupts/&lt;/a&gt;（&lt;code&gt;interrupt()&lt;/code&gt;+checkpointer 的挂起恢复能力，本篇只作方向标记）。&lt;/li&gt;
&lt;li&gt;本话演示代码与回归测试：配套代码仓库中的 &lt;code&gt;python/control_modes.py&lt;/code&gt;、&lt;code&gt;java/.../Main.java&lt;/code&gt;、&lt;code&gt;java/.../MinimalSuspendDemo.java&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>