阶段图:模型要先理解任务,还要再理解一张描述任务的图

用可验证的 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]

这里有三个清单难以表达的性质:

  1. question-gate 同时依赖主探索和预草稿 Specialist,避免还没完成可发现事实的探索就把问题抛给用户;
  2. deterministic-verifierpost-draft-critic 都依赖草稿,但互不依赖,可以并行;
  3. 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-grantisolated-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。

参考资料

相关文章

在做类似的事情?

很乐意就分布式系统、交付流程和应用 AI 交换意见。

[email protected]