目标路由:「帮我改一下」和「帮我发个 PR」不该走同一条路
路由不是给请求贴标签,而是先确定这次授权了哪些副作用、什么证据才算完成。
本页目录 · 19 节
目标路由最容易被误解成一个分类器:看到「设计」就进入 Design,看到 PR URL 就进入 PR Review。
在真实研发流程里,路由更重要的职责不是识别名词,而是确定三件事:用户要得到什么结果、这次允许产生什么副作用、需要哪些证据才能宣称完成。
如果这三件事没有先确定,Agent 会天然倾向于把相邻动作连起来:修完代码就 Commit,Commit 后就 Push,Push 后就开 PR,Pipeline 绿了就继续部署。每一步在技术上都合理,整条链却超出了最初授权。
路由对象不是 Topic,而是一份 Goal Contract
一个可执行的 Goal 至少包含五个字段:
goal 这次要完成的用户结果
read_scope 可以读取和分析哪些来源
write_surface 允许修改本地、Git、Provider 还是运行环境
stop_boundary 到哪一步必须停止
proof 哪些事实足以支持“完成”
例如「实现一个修复」和「提交这个修复」都可能需要读取代码、运行测试和检查 Diff,但它们的写入面不同:
| Goal | 允许的主要动作 | 明确不包含 | 完成证据 |
|---|---|---|---|
clarify |
读取需求来源,整理已知事实和唯一阻塞问题 | 设计、改代码、外部写入 | 边界清楚或一个可回答的问题 |
design |
探索代码,形成决策完整的方案 | 实现、Commit、Jira 写入 | 关键决策、影响范围、验证与风险 |
impl |
修改已接受范围并做本地验证 | Commit、Push、PR、Deploy | 实际 Diff 与相关检查结果 |
local-review |
只读检查当前 Diff | 修复发现、提交结果 | 有证据的 Findings 或明确无发现 |
commit |
在 Feature Branch 上提交已接受范围 | Push、PR | 本地 HEAD 与 Commit 内容回读 |
push |
非强制推送 Feature Branch | PR、Merge、Deploy | 远端 Ref 指向预期 Commit |
pr-submit |
验证、Feature Commit/Push、准备 PR 预览 | 未确认的 Provider 写入、Merge | 精确预览;确认后再用 PR Readback 证明写入 |
release-verify |
只读检查部署与运行状态 | 发起部署 | Release、Commit 与真实入口回读 |
同一个动词也可能对应不同 Goal。「看一下这个 PR」通常是 Review;「把这个改动发 PR」是 Delivery;「PR 失败了,找原因」是 Diagnose。路由不能只匹配关键词,还要看期望结果和副作用。
为什么撤销成本决定授权粒度
本地编辑、Git 历史、远端协作对象和生产环境的撤销成本不同:
| 动作 | 改变的状态 | 可恢复性 | 影响范围 |
|---|---|---|---|
| 编辑文件 | 当前 Worktree | 通常可通过 Diff 恢复 | 当前操作者 |
| Commit | 本地历史 | 仍可本地调整 | 当前仓库 |
| Push | 远端 Ref | 往往要追加修复;重写历史会影响他人 | 分支消费者 |
| 创建 PR / 评论 | Provider 对象与通知 | 可以关闭或补充,但通知已发生 | 协作团队 |
| Merge | 共享主线 | 可以 Revert,但下游已经可能拉取 | 全体开发者 |
| Deploy / Data Mutation | 运行环境或真实业务状态 | 回滚不一定恢复期间影响 | 用户与业务 |
所以 Goal 不应该按「操作看起来是否连续」合并,而应按「后果是否进入了新的信任域」拆分。Worktree 到本地 Git 是一个边界,本地到 Remote 是一个边界,Remote 到 Provider 协作对象又是一个边界,代码到生产更是独立边界。
Marshall 的路由怎样落到实现
成熟版 Marshall 有多层表达,但每层职责不同:
routes/README.md描述人能理解的入口与阶段关系;commands/marshall/*.md定义每个 Goal 接受的输入、输出和拒绝边界;stage-graph.yaml才是节点、Barrier 与 Transition 的执行权威;- Runtime 只接受图中存在的 Stage 和 Transition,不能由调用者临时扩大。
这里有一个重要原则:路由负责选契约,不负责替模型写方案。
impl 契约可以允许 Agent 自己发现受影响模块、选择测试和调整实现细节;它只约束不能顺手 Commit、不能写范围外文件、不能把未验证行为说成已完成。这样既保留模型的判断力,也固定副作用边界。
路由算法:先判断后果,再判断内容
路由顺序不是「先做意图分类」,而是下面四步:
1. 找到用户明确说出的结果
优先读取字面目标:回答、诊断、计划、实现、评审、提交、推送、开 PR、合并、部署。明确动词高于「通常完整流程会怎样」。
2. 找出请求中的最大副作用
如果请求同时说「修好并 Push」,最大已授权副作用是 Feature Push;如果只说「修好」,最大副作用仍然是本地编辑。不能用工具可用性推导权限。
3. 把必要前置与新 Goal 分开
实现前读取代码和跑测试,是完成实现的内部前置;实现后 Push 不是。PR Submit 前检查分支和 Diff,是交付前置;Merge 不是。判断标准是:缺少这一步,当前 Goal 是否仍可能诚实完成?
4. 为当前 Goal 写清停止条件
停止不是「模型没事可做」,而是已经到达授权边界。例如 PR 的完整 Payload 已准备好,但 Provider Write 需要确认,此时暂停就是正确完成,不是流程未完成。
组合 Goal 也必须保留内部边界
有些团队会把一组常见动作定义为组合目标。Marshall 中的 pr_commit 就是兼容性组合:
Scoped Verification
-> Feature Commit
-> Non-force Feature Push
-> Exact PR Preview
-> Stop for confirmation
-> Provider create/update
-> Live readback
组合并不意味着授权被揉成一个「Yes」。Feature Commit/Push 来自用户调用该组合 Goal;真正创建或更新 Bitbucket PR 仍需要确认当前标题、正文、Reviewer、源分支和目标分支。Payload 改变后,旧确认不能复用。
这类内部停顿能防止一个危险误区:把「我同意这个工作流」解释成「我提前同意工作流未来生成的任何内容」。
Material Ambiguity 才值得提问
路由过于保守会让 Agent 每一步都问,最终产生 Approval Fatigue。有效做法是区分可发现事实和真正的用户选择:
- 当前分支、已有 PR、测试命令和受影响文件,可以通过工具发现;
- 产品行为二选一、是否扩大范围、是否触发外部写入,不能靠猜;
- 小的实现细节可由 Agent 在边界内决定;
- 会改变 Goal 或副作用级别的歧义必须暂停。
成熟版还把 Question Gate 设计成单一活动问题:先做安全的只读探索,只有仍然无法发现的 Material Choice 才进入 Gate;一次只保持一个问题,避免把一串不确定性转嫁给用户。
常见失败模式
1. Intent Escalation
「看一下」被自动升级为修复,「修复」又被升级为交付。解决方式不是再加一句「不要越权」,而是让每个 Goal 明确列出 Stop Boundary。
2. Alias Drift
review、local_review、pr-review、pr_commit 等别名在不同文件映射不同,导致同一个请求走两条路径。别名必须先归一化,再进入唯一 Goal Map,并由测试检查所有 Target 可解析。
3. Capability-driven Routing
因为 Agent 能用 Git、Bitbucket 或 AWS,就把工具能力当成授权。这是典型 Confused Deputy:系统拥有能力,但当前用户意图没有授予它。
4. Completion Leakage
impl 的测试通过,被下游解释成 push 或 deploy 已完成。每个 Goal 必须定义自己的 Proof,不能借用相邻阶段的成功状态。
5. 一切都要求确认
把读文件、编辑已接受范围、运行本地测试也放进审批,会让用户习惯性点击确认,真正高风险 Gate 反而失去注意力。低风险、可恢复、本地且在范围内的动作应尽量自主。
怎样测试一个路由器
路由测试不应该只做关键词样例,还需要覆盖边界:
- 同一句话包含多个动作时,是否识别最大已授权副作用;
- PR URL 在「评审」和「更新」语境下是否进入不同 Goal;
commit是否不会自动包含push;release-verify是否始终只读;- 未知别名是否 Fail Closed,而不是猜一个最近的 Stage;
- 外部 Payload 改变后,是否使旧确认失效;
- 当前 Goal 完成后,是否停止而不是自动选择下一个 Goal。
一个好的 Router 不需要很聪明,但必须可预测。真正开放式的工作留给模型,授权边界留给一个很小、可测试的映射。
适用边界与 Trade-off
当 Agent 只能回答问题时,Goal Routing 只是产品体验;当它能编辑代码、写 Provider、部署和操作数据时,Goal Routing 就是权限模型的一部分。
它的成本是会在高后果边界增加停顿,也要求产品把含糊的「完成」拆成多个可观察状态。收益是每次授权都对应一个清楚的撤销成本和证据层级,模型可以在边界内更自主,而不用在每个微小动作前征求同意。
参考资料
- Running Codex safely:用技术边界区分低风险自主动作与需要明确授权的高风险动作。
- OpenAI Agents SDK: Agent orchestration:LLM 路由与代码路由的不同确定性和适用场景。
- OpenAI Agents SDK: Human-in-the-loop:工具调用暂停、逐次审批、状态持久化与恢复。
- Trustworthy agents in practice:Agent、Harness、Tools 与 Environment 共同决定真实行为和风险。
相关文章
从 Workflow Harness 到轻量 Prompt:Codex 5.6 之后我删掉了什么
当模型的原生流程已经够强,Harness 会从效率放大器变成上下文负担。这次重写保留了安全边界,同时大幅精简 Prompt 和运行时。
阶段图:模型要先理解任务,还要再理解一张描述任务的图
用可验证的 DAG 表达节点依赖、执行权限、完成屏障、失败传播与恢复边界。