阶段图:模型要先理解任务,还要再理解一张描述任务的图
用可验证的 DAG 表达节点依赖、执行权限、完成屏障、失败传播与恢复边界。
本页目录 · 18 节
一份 Checklist 能告诉 Agent「先看代码、再修改、最后测试」,却不能可靠表达并行、等待、重入、完成屏障和副作用权限。
当任务需要这些性质时,它已经不是一份提示词清单,而是一张执行图。Marshall 的 stage-graph.yaml 就是这张图的唯一权威。
Stage Graph 究竟表达什么
每个 Stage 包含六类信息:
purpose: 当前阶段要交付的结果
predecessor: 允许的前序阶段
input_policy: 输入角色、前序结果与 freshness 规则
nodes: 节点、依赖、执行者、输出和检查
completion_barrier: 哪些节点共同决定阶段完成
transitions: 完成后允许去哪里
每个 Node 则至少绑定:
phase:perception、explore、draft、verify、critic、synthesis、finalize 等认知职责;executor:parent、deterministic、worker、provider、isolated-executor 或 host;depends_on:必须先完成的 Node;required_output_roles:必须发布的逻辑产物;required_checks:必须由 Runtime 验证的条件;- 可选
worker_lane:该节点是否允许风险驱动的并行 Worker。
顺序来自依赖边,而不是 YAML 中出现的行号。列在前面不代表先执行;只有 Graph 才拥有执行语义。
一张真实的 Design DAG
成熟版 design 的核心拓扑是:
flowchart TD
P[Perception Bootstrap] --> C[Context Selection]
C --> E[Explore]
E --> S[Pre-draft Specialists]
E --> Q[Question Gate]
S --> Q
Q --> D[Design Draft]
D --> V[Deterministic Verifier]
D --> R[Post-draft Critic]
V --> X[Repair Decision]
R --> X
X --> F[Final Design]
这里有三个清单难以表达的性质:
question-gate同时依赖主探索和预草稿 Specialist,避免还没完成可发现事实的探索就把问题抛给用户;deterministic-verifier与post-draft-critic都依赖草稿,但互不依赖,可以并行;repair-decision是 Join,只有机器校验和语义评审都完成,才能决定修复或最终化。
final-design 是 Completion Barrier。即使前面所有 Node 都标成 completed,只要最终产物、决策来源或 Jira Issue Spec 的绑定不完整,Stage 仍然不能完成。
Impl Graph 本质上是一条权限链
实现阶段更接近严格 Chain:
flowchart LR
B[Branch Gate] --> T[Task Trace]
T --> M[Modification Plan]
M --> G[Write Grant]
G --> E[Isolated Executor]
E --> V[Patch Verification]
V --> P[Patch Promotion]
P --> R[Post-promotion Readback]
R --> S[Spec Sync]
这里不能把 write-grant 和 isolated-executor 对调,也不能在 Patch Verification 前执行 Promotion。顺序一旦改变,后续 Executor 就可能使用尚未签发的能力,或把未校验候选修改写进目标工作区。
所以 Graph 不只是安排工作,它还定义了 Authority Flow:哪个结果产生下一步权限。
Completion Barrier 为什么不能等同于「最后一个节点」
线性流程的最后一个节点结束,通常意味着阶段结束。DAG 不一定。
成熟版 Impl 的 Completion Barrier 同时包含 Post-promotion Verification 与 Spec Sync。原因是代码验证和稳定契约同步可以形成两个不同结果:
- 代码已经正确提升并通过测试;
- 受影响的稳定 Spec 要么已在同一 Patch 更新,要么明确证明无需修改。
如果只把 spec-sync 当最后一个节点,Runtime 可能看不到前一条验证是否仍然有效;如果只看 Promotion,则会把「文件写入成功」误当成「实现阶段完成」。Barrier 表达的是一组必须同时成立的后置条件。
Graph 必须同时绑定 Data Plane
一张只写 Node 名称的图仍然容易被绕过。Marshall 还要求每条边绑定当前 Result:
NodeResultV1
= node identity
+ dependency fingerprints
+ input fingerprint
+ output path/hash bindings
+ verification
+ decision/action/outcome events
下游 Node 启动时,不只是检查 dependency.status == completed,还会验证 Dependency Fingerprint 与不可变 Result。依赖的输入或 Artifact 变化后,旧完成状态失效。
Stage 完成时,StageResultV1 再聚合必需 Node Result、Output Binding、Open Risk、Verification 和 Transition。Graph 负责结构,Result 负责证明这次运行真的满足结构。
Freshness 是 DAG 的时间维度
静态 DAG 只描述依赖关系,不能说明一个旧结果今天是否还可用。成熟版为 Stage 配置 Input Policy:
freshness: none:前序结果一旦完成即可使用;freshness: tracked:当前代码、Provider 或前序输入变化会使结果失效;stale_route: block:不能靠模型重新解释旧摘要继续推进。
例如 Design 基于 Commit A 完成,但 Impl 开始前 HEAD 已经变为 B。图结构没变,Data Dependency 已经变了。此时正确行为不是把 Design Node 留在 completed,而是把相关结果标记 Stale,回到能重新建立证据的节点。
Graph Loader 必须严格,而不是「尽量理解」
stage_graph.py 对控制面采用 Fail Closed:未知 Stage、未知 Node、空 Completion Barrier、Barrier 引用不存在节点、Dependency 环或非法 Transition 都会直接失败。
这是机器配置与自然语言的关键区别。自然语言可以在歧义下继续交流;控制图如果「猜一个最像的节点」,可能绕过等待、审批或写入限制。
Graph 的机械验证至少应覆盖:
- 所有 Node ID 唯一;
depends_on只引用当前 Stage 已知节点;- 图无环,或显式 Loop 有最大轮数和停止条件;
- Barrier 非空且可达;
- Output Role 能在 Artifact Map 中解析;
- Executor Type 已注册;
- Transition 目标存在,且 Terminal 明确;
- 并行分支的 Join 覆盖所有必需结果;
- Mode 与 Worker 上限不互相矛盾。
何时用代码编排,何时交给 LLM
OpenAI Agents SDK 把编排分成两类:让 LLM 根据上下文决定下一步,或用代码固定顺序。前者适合开放问题,后者在速度、成本和行为上更可预测。Marshall 把两者组合:
- Graph 决定不可交换的依赖、权限和完成条件;
- Parent Agent 决定 Node 内如何探索和综合;
- Deterministic Executor 负责 Hash、Schema 与状态检查;
- Worker 只处理可以独立并行的窄问题。
这比「全部写死」或「全部交给模型」更实用。关键是找出哪些顺序一旦错误会产生真实后果。
错误怎样沿不同拓扑传播
成熟版还为 Stage 标注 Topology 和 Error Path:
| 拓扑 | 典型错误传播 |
|---|---|
| Chain | 前一步错误级联到后续所有步骤 |
| Route | 错误分发使正确能力根本没有进入流程 |
| Parallel | 多分支产生冲突、重复或不一致证据 |
| Orchestrate | 拆分错误让 Worker 各自解决了错误子问题 |
| Loop | 错误反馈在每轮被放大,形成无止境返工 |
| Hierarchy | 上层错误被多个下层实例同时放大 |
因此 Graph 设计不只要考虑 Happy Path,还要为每种拓扑提供不同的控制:Chain 做中间验证,Parallel 做 Barrier 与 Dedupe,Loop 明确最大轮数,Hierarchy 限制深度和所有权。
常见失败模式
Graph 与文档漂移
Command 说一个顺序,Graph 执行另一个顺序。解决方式是 Graph 成为唯一执行权威,文档只解释,不复制完整 Node List;Validator 检查引用而不是维护两份事实。
Barrier 太弱
只等待「最后一个节点」,漏掉并行分支失败。Barrier 应描述业务后置条件,而不是 UI 上看起来最后的方框。
Barrier 太强
把所有可选 Worker 都放进 Barrier,低风险任务也必须等待无关报告。可选分支只能在 Selection 中实际选中后进入 Barrier。
Hidden Side Effect
一个 parent Node 在 Prompt 中悄悄执行 Provider Write。Executor Type 必须与工具权限绑定;语义名称不能代替技术限制。
Retry without Idempotency
节点失败后从头重跑,导致外部动作重复。对 Provider 与 Host Node,要在 Graph 外配合 Operation ID、Readback 和 Reconciliation。
Workflow for Workflow's Sake
模型本来能在一次会话内完成的任务,也被拆成过多 Node,每个都生成 Result。图的维护成本超过它控制的风险。
一张图什么时候值得存在
显式 Stage Graph 适合以下情况:
- 任务跨进程、跨会话或需要长时间等待;
- 存在不可安全重复的外部副作用;
- 多个执行者可能并行、失败或晚到;
- 恢复必须从最近有效节点继续;
- 完成需要多个独立条件同时成立;
- 审计要求重建实际执行顺序。
它不适合仅仅因为「任务有很多步骤」。如果每一步都需要新的模型判断,顺序会随发现变化,而且失败可在当前 Worktree 内轻易恢复,LLM 原生计划通常更经济。
AWS Step Functions 的 Task、Choice、Parallel、Wait、Succeed 与 Fail 状态展示了机器化 Workflow 的价值:这些控制适用于真正需要持久执行历史和严格转移的流程。它们不意味着每一次代码修改都应该先变成 State Machine。
参考资料
- OpenAI Agents SDK: Agent orchestration:LLM 编排与代码编排的权衡,以及并行只适合独立任务。
- AWS Step Functions workflow states:Task、Choice、Parallel、Map、Wait 与终止状态。
- AWS Step Functions error handling:Retry、Catch 和 Parallel/Map 的失败传播。
- Temporal Documentation:持久化执行、故障恢复和历史重放;本文仅作工作流设计类比,并不表示 Marshall 实现了 Temporal 的运行保证。
相关文章
从 Workflow Harness 到轻量 Prompt:Codex 5.6 之后我删掉了什么
当模型的原生流程已经够强,Harness 会从效率放大器变成上下文负担。这次重写保留了安全边界,同时大幅精简 Prompt 和运行时。
目标路由:「帮我改一下」和「帮我发个 PR」不该走同一条路
路由不是给请求贴标签,而是先确定这次授权了哪些副作用、什么证据才算完成。