G37:从零构造 Planner–Executor 状态机
Planner–executor 状态机把“提出下一步”和“执行受控动作”拆成显式转移,使每次工具调用都有前置条件、结果类型、恢复路径和可重放证据。
内容类型:预习教材(不代表已完成)
日期:2027-03-30
阶段:P3 · AGI Foundations 90
总路线:Day 217 / 360
周次:W6 · Memory、Planning 与 Tool Use
节奏:周二最小机制
状态:教材已备;学习未完成
标签:planner-executor、state-machine、recovery、invariant、tool-use
一句话定义
Planner–executor 状态机把“提出下一步”和“执行受控动作”拆成显式转移,使每次工具调用都有前置条件、结果类型、恢复路径和可重放证据。
学习目标
- 从零定义
PLAN→VALIDATE→EXECUTE→OBSERVE→COMMIT的有限状态机。 - 区分 goal、plan、action、observation、event 与 terminal condition。
- 用 invariant 和 transition table 推导超时、拒绝、冲突与重试的恢复路径。
- 比较显式状态机、自由 Agent loop 与一次性 prompt 的适用边界。
核心知识
- Planner 负责在当前已知状态下提出有限候选,不拥有执行权限;executor 接受通过验证的 typed action,并将 typed result 返回给 reducer。
- 状态机的价值不是保证模型正确,而是限制错误形态:未通过 schema/permission 的动作不会执行,未知结果不会被写成成功,失败可从 event log 重放。
observation是外部工具返回或环境事件;模型对 observation 的解释是另一条派生记录。混合二者会使审计无法区分事实与推断。- Terminal condition 应由可验证状态判断,例如
all_required_fields_resolved,而不是模型自由声称“任务完成”。循环还需有最大步数、时间和成本上限。 - 恢复不是简单重试。根据副作用可分安全重试、查询确认、人工介入和补偿动作;补偿也不是严格回滚,因为外部世界可能已被观察或改变。
机制与推导
定义状态集合:
INIT, PLANNING, VALIDATING, EXECUTING,
OBSERVING, COMMITTING, WAITING_HUMAN,
SUCCEEDED, FAILED, UNKNOWN
转移函数 (\delta(x,event)=x') 应尽量确定。例如:
PLANNING + action_proposed -> VALIDATING
VALIDATING + denied -> WAITING_HUMAN | FAILED
VALIDATING + allowed -> EXECUTING
EXECUTING + timeout(side_effect_possible) -> UNKNOWN
UNKNOWN + status_confirmed(success) -> COMMITTING
UNKNOWN + status_confirmed(absent) -> EXECUTING
COMMITTING + event_saved -> PLANNING | SUCCEEDED
设置三个 invariant:
executed(action) => validated(action, policy_version);committed(success) => observed(typed_success);side_effect_count(logical_action_id) <= 1。
它们不是测试 Gate,而是帮助理解状态所有权的性质。若事件日志为 (E=(e_1,...,e_n)),当前状态可由 reducer 重建:
[ x_n=reduce(reduce(\cdots reduce(x_0,e_1),e_2),\ldots,e_n) ]
这要求 event 不被事后静默修改,并记录 schema、tool 与 policy 版本。自由循环通常把状态藏在自然语言中,难以证明这些 invariant 是否成立。
Planner 可输出最多 (K) 个候选,每次只执行一个;停止条件为:
[ stop=goal_verified\lor steps\ge B_s\lor cost\ge B_c\lor risk>r_{max} ]
预算耗尽应形成 incomplete 或 needs_review,不能冒充成功。
最小练习或观察步骤
- 用字典或 enum 写出上述状态与 transition table,不需要任何 Agent SDK。
- 定义一个目标:“读取三条合成记录并生成差异草稿”;工具只有
read_record(id)与save_draft(text,key)。 - Planner 只返回
{action, args, rationale_summary};validator 检查 allowlist、参数和剩余预算,executor 只接受验证 token。 - 手工注入五条轨迹:正常成功、参数错误、权限拒绝、只读超时、写入超时未知。逐条检查下一状态。
- 保存事件后从空状态 replay,比较重建状态与原状态;不要记录或要求隐藏思维链。
- 把自由 while-loop 版本与状态机版本画成两张图,比较可观察点和错误恢复,而不是只比代码行数。
常见误区与边界
- Planner 输出了函数名就直接调用,绕过 schema、policy 与预算检查。
- 把所有工具异常归为同一
error,丢失“明确未执行”和“结果未知”的关键差别。 - 模型说“完成”就进入 SUCCESS,没有独立 terminal predicate。
- event log 保存完整敏感 payload 或隐藏推理文本,制造新的隐私与安全面。
- 认为状态机能消除语义错误;它只能让动作边界和恢复更可控。
- toy 状态机不等于生产级 Agent runtime,本日不连接外网和真实业务写接口。
研究/系统场景连接
在客户投诉摘要场景,Planner 可提出“读取已授权工单→对照政策→保存建议草稿”,executor 只能操作合成数据和草稿区。任何“关闭投诉”“退款”“修改客户资料”都不在 allowlist。研究 Agent 同样可以读取本地论文清单、生成实验配置草稿,却不能自行下载任意代码或覆盖原始结果。显式 WAITING_HUMAN 不是能力不足的尴尬,而是自主程度与风险相匹配的控制状态。
自检问题
- action proposal 与 action authorization 为什么必须是不同事件?
- 写入超时应进入 FAILED 还是 UNKNOWN,为什么?
- 哪三个 invariant 最能阻止错误状态被写成成功?
- event replay 依赖哪些版本和不可变性条件?
- 状态机相对自由循环仍然不能解决什么?
专业课程对齐
- 阅读 ReAct 原始论文,将其 action/observation 序列映射到 typed event,同时保留论文任务与本地 runtime 的差异。
- 阅读 Toolformer 原始论文,观察工具选择学习目标,再列出 schema validation、权限和恢复为何仍属于系统层。
- 阅读 Generative Agents 原始论文,关注 observation、reflection 与 planning 的组织方式,并审视哪些状态属于仿真而非外部事实。
深入学习提示
可进一步比较 hierarchical state machine、behavior tree、Petri net 与 event sourcing。不要为了“工程完整”引入消息队列和数据库;先用一页 transition table 找出所有未知状态。深入研究点是 failure semantics:在非幂等世界里,恢复为何需要查询、人工或补偿,而不是盲重试。
学后填写区
- 实际画出的状态与事件:
- 手推的失败轨迹:
- 一个保持或破坏的 invariant:
- replay 需要的版本信息:
- 明确禁止的动作:
- 尚未理解的问题: