Appearance
模型计划如何变成真实执行:ToolExecutor、Planner 与 GraphCompiler
前置:Context、Retrieval 和 Tool Catalog 怎样接起来
核心问题:Planner 给出计划后,为什么还需要 GraphCompiler,ToolExecutor 又怎样保证只执行已经验证和授权的动作?
模型返回 shell call,Executor 先 sandbox policy,再运行并保存 exit/log artifact。Planner 生成 nodes/deps,若 cycle validation fail则重新规划,而不是边执行边猜。
Tool Executor
最终 Harness 把 ToolExecutor 作为唯一副作用入口,并注入 Registry、PolicyEngine、ExecutionRecordStore 与具体 Runner。Planner/Model 只能产生 ToolCall intent,不能持有 Runner。
🔍 源码对照:内部 Tool Executor 不应依赖 MCP request 类型
完整源码与通用分析见 06-04 Tool Search / Execution Pipeline。这里不重复代码;这里关注内部 contract:Planner / GraphCompiler 只产生内部 ToolCall,ToolExecutor 不应依赖 MCP request 类型。
源码说明
案例仓库展示了 Tool adapter、workflow、subagent runtime,但不一定按我们的 Planner → GraphCompiler → Scheduler 命名拆分。这反而是很好的验证:Shadow Harness 的 GraphCompiler 不是为了模仿某个框架,而是为了把概率性的 StructuredPlan 转成 deterministic graph,并在执行前做 dependency/cycle/capability validation。
工程边界
不能因为 Grok Bot 里找不到同名 GraphCompiler 就认为这个抽象多余;也不能把它的 workflow 文件直接翻译成我们的 DAG model。
Shadow Harness 对照
- Planner 只产生 semantic intent/plan;GraphCompiler 产生可验证 ExecutableGraph。
- ToolExecutor 不接受 Planner 任意函数对象,只接受已解析的 ToolBinding。
验证
让 Planner 输出循环依赖、未知 Tool、无权限 Tool 三类计划;必须在 graph compile/validation 阶段失败,不能调度后才发现。
Planner
Planner 通过 PlannerPort 接收 task + current observation snapshot,只返回版本化 StructuredPlan。测试环境可以替换成 ScriptedPlanner,而不改 Runtime。
Planner 不写 NodeState、不 claim worker、不执行 Tool;这些属于 Compiler/Scheduler/Executor。
Graph Compiler
GraphCompiler 是模型输出与 Runtime 之间的 deterministic seam:把 StructuredPlan 转成内部 ExecutableGraph,分配 stable node identity,并验证 dependency/cycle/capability binding。
compile 失败返回 plan/config error;未经 compile 的结构不能进入 Scheduler。
三个组件怎样接力,而不是互相越权
Planner 收到当前 Context 后提出语义计划:
text
search auth → inspect token logic → patch → testGraphCompiler 把它变成有稳定 node id 和 dependency 的 Graph,并在执行前做 validation。Scheduler 只消费这个已验证 Graph。运行到 patch 时,ToolExecutor 再负责 schema validation、permission、timeout、真实调用和 ToolResult normalization。
text
Planner probabilistic intent
↓
GraphCompiler deterministic structure
↓
Scheduler deterministic readiness/admission
↓
ToolExecutor controlled side effect如果 Planner 直接调用 Python function,权限和 trace 会被绕过;如果 ToolExecutor 决定“下一步应该测试”,它又侵入了 Planner 的语义职责。Shadow Harness 的价值就在于把这些边界做成可以单独测试和替换的组件。
Failure Modes
Planner 输出与 Executor Contract 没有编译边界
症状: Planner 直接生成可执行对象,GraphCompiler 只是透传。
为什么危险: 模型输出变化会直接改变执行语义,无法独立验证 DAG/invariant。
处理: Planner 只产 StructuredPlan;Compiler 生成内部 ExecutableGraph;Executor 只接受已验证 graph/node。
如何验证
| Case | 断言 |
|---|---|
| Planner 输出与 Executor Contract 没有编译边界 | 让 ScriptedPlanner 输出悬空依赖或未知 Tool,断言 GraphCompiler 拒绝且 ToolExecutor 调用数为 0。 |
关联源码
自测
- Planner 输出“修改文件后跑测试”,为什么必须经过 GraphCompiler 才能让 Scheduler 执行?
- ToolExecutor 应负责真实调用,Planner 应负责意图;如果 Planner 直接调用 Tool,会把哪些边界揉在一起?