目标路由:「帮我改一下」和「帮我发个 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 有多层表达,但每层职责不同:

  1. routes/README.md 描述人能理解的入口与阶段关系;
  2. commands/marshall/*.md 定义每个 Goal 接受的输入、输出和拒绝边界;
  3. stage-graph.yaml 才是节点、Barrier 与 Transition 的执行权威;
  4. 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

reviewlocal_reviewpr-reviewpr_commit 等别名在不同文件映射不同,导致同一个请求走两条路径。别名必须先归一化,再进入唯一 Goal Map,并由测试检查所有 Target 可解析。

3. Capability-driven Routing

因为 Agent 能用 Git、Bitbucket 或 AWS,就把工具能力当成授权。这是典型 Confused Deputy:系统拥有能力,但当前用户意图没有授予它。

4. Completion Leakage

impl 的测试通过,被下游解释成 pushdeploy 已完成。每个 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 就是权限模型的一部分。

它的成本是会在高后果边界增加停顿,也要求产品把含糊的「完成」拆成多个可观察状态。收益是每次授权都对应一个清楚的撤销成本和证据层级,模型可以在边界内更自主,而不用在每个微小动作前征求同意。

参考资料

相关文章

在做类似的事情?

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

[email protected]