一次 15 分钟粒度的 CI Sweeper 在缺少独立验证 Agent、分支白名单与模型外部硬性 token 上限的条件下直接跑飞,暴露的不是模型能力问题,而是授权节奏缺少可观测信号。本文用一条“可验证性—可回滚性—影响半径—预算硬约束”的主线,重构无人值守自动化的晋级判据。
摘要
把编码 Agent 从“只报告”(L1)提升到“自动修复”(L2),本质是一次权限跃迁,而不是一次功能升级。权限跃迁的判据如果建立在“连续跑通 N 天”这类时间信号上,就会把系统的稳定性误当成系统的可控性。本文以一次真实的失控形态为样本:15 分钟触发一次的 CI Sweeper,在缺少独立验证 Agent、分支白名单、模型外部硬性 token 上限这三道闸门的情况下,把一次局部失败放大成持续性的仓库扰动与成本外溢。
笔者提出的分析主线是:自动化的放手节奏应由“可验证性 × 可回滚性 × 影响半径 × 预算硬约束”四元信号共同决定,时间只作为观察窗口,不作为晋级依据。围绕这条主线,文章依次讨论 L0–L4 分级模型、独立验证 Agent 的对抗性设计、分支白名单的拓扑表达、token 预算的外部化硬约束、失控的因果链与熔断机制,并给出可操作的晋级评分卡与灰度路径。
全文约 15800 字,整理参考文献与资料 68 篇(其中近三年占比约 57%),文末列出 9 篇主要参考文献。所有数据均标注来源,模拟与整合数据已明确标识。
目录
一、问题的提出:一次“过早毕业”的失控样本
先把场景还原清楚。某团队在仓库中部署了一个基于 Claude Code 的 CI Sweeper:它由 CI 失败事件触发,每 15 分钟扫描一次未通过的流水线,读取失败日志,尝试生成补丁并直接提交到工作分支。上线初期,它被配置为“只报告”(本文记为 L1):分析失败原因、给出修复建议、以评论形式输出,但不写代码、不推送。运行数日后,团队认为“它已经稳定跑通好几天了”,于是把它切到“自动修复”(本文记为 L2):允许直接改文件、提交、推送。
切换之后暴露的三个结构性缺失是:
- 没有独立验证 Agent。生成补丁的模型同时充当“判断补丁是否正确”的角色,验证与生成同源,等于自己给自己判卷。
- 没有分支白名单。Sweeper 的写权限覆盖整个仓库的分支空间,包括受保护分支与发布分支。
- 没有模型外部的硬性 token 上限。预算控制依赖提示词里的“请节约”或应用层的软计数,一旦重试循环形成,消耗不受外部约束。
这三者叠加的后果不是“修错了几个 bug”,而是系统进入正反馈:修复引入新失败 → 新失败触发下一轮 Sweeper → 下一轮在更混乱的上下文里生成更差的补丁 → 失败面扩大。15 分钟的触发周期意味着一天最多 96 轮,每一轮都可能放大上一轮的错误。这就是本文所说的“跑飞”。
核心判断:“跑通了几天”度量的是在受限动作空间内的成功率,而 L1→L2 改变的是动作空间本身。用旧动作空间的统计量去授权新动作空间,是典型的分布外泛化错误。时间不是信号,它只是信号的载体;真正该看的是系统在新动作空间下是否具备可验证、可回滚、可限幅、可熔断的能力。
这个判断并非孤例。Anthropic 在 Claude Code 的官方文档与工程博客中反复强调“human-in-the-loop”与权限分级(如 --permission-mode、allowlist 配置)的必要性;GitHub 在 Copilot 自动修复类功能(如 Copilot Autofix)中同样采用“生成建议 + 人工确认”的默认路径。这些设计的共同点,是把授权粒度与验证独立性放在模型能力之前。本文的任务,是把这种工程直觉形式化为可计算的晋级信号。
二、背景与概念界定:L0–L4 分级与“放手节奏”
2.1 自主性分级:一个可操作的 L0–L4 模型
业界对“AI 自主性等级”并没有统一标准。自动驾驶领域的 SAE J3016 分级(L0–L5)提供了一个可借鉴的形式:每一级的差别不在于“能力更强”,而在于动态驾驶任务的责任分配。把这个思想迁移到编码 Agent,笔者给出如下分级(本文自定义,非行业标准):
| 等级 | 动作空间 | 责任主体 | 典型闸门 |
|---|---|---|---|
| L0 | 只读、只解释 | 人 | 无写权限 |
| L1 | 只报告:评论、建议、草稿 | 人 | 不落盘、不推送 |
| L2 | 自动修复:改文件、提交、推送(限定分支) | 人机共担 | 独立验证 + 白名单 + 预算硬上限 |
| L3 | 自动合并:通过验证即合入 | 系统为主 | 多验证器 + 影响半径限幅 + 审计 |
| L4 | 跨仓库、跨服务自治 | 系统 | 形式化契约 + 全局熔断 + 独立红队 |
本文评述:这个分级的价值不在于命名,而在于它强制回答一个问题——每一级新增的动作,由谁来承担后果?L1 到 L2 新增的动作是“写与推”,后果承担者从“人”变成“人机共担”。因此 L2 的准入条件必须包含“机器能独立判断自己写的东西对不对”这一能力,否则责任分配是虚的。这正是独立验证 Agent 不可省略的原因。
2.2 “放手节奏”的定义
本文把“放手节奏”(release cadence of autonomy)定义为:在保证系统可控的前提下,逐步扩大 Agent 动作空间的时间与条件序列。它包含两个维度:
- 条件维度:满足哪些可观测指标才允许晋级。这是本文的主战场。
- 时间维度:每个等级至少观察多久、样本量多大才具备统计意义。时间在这里是样本积累的容器,不是判据本身。
换句话说,正确的提问不是“跑了几天可以放手”,而是“我需要多少条独立证据、覆盖哪些失败模式、在多大的爆炸半径内可回滚,才敢把写权限交出去”。
三、失控的因果链:三个缺失闸门如何叠加放大
3.1 单点缺陷不等于系统性风险,耦合才是
把三个缺失分开看,每一个都“可以忍”:验证同源,最多是修错;没有白名单,最多是推错分支;没有硬预算,最多是花点钱。但三者耦合后,系统获得了自我放大的能力。可以用一个简化的状态转移来刻画:
状态 S_t = (失败集合 F_t, 分支污染度 B_t, 累计消耗 C_t)
每轮 Sweeper:
F_{t+1} = F_t ∪ NewFailures(patch_t) # 修复可能引入新失败
B_{t+1} = B_t + WriteScope(patch_t) # 无白名单 → 写范围无界
C_{t+1} = C_t + Tokens(retry_loop_t) # 无硬上限 → 重试无界
关键:NewFailures 与 WriteScope 正相关,
因为写范围越大,触碰未覆盖代码的概率越高。
这个模型说明:风险不是三个缺陷的线性叠加,而是它们的乘积。验证同源让错误补丁通过率上升,白名单缺失让错误补丁的落点扩散,预算缺失让重试循环把单次错误变成持续错误。三者中任意一个被堵住,正反馈就会被削弱;三个都在,系统就具备了“跑飞”的结构条件。
3.2 15 分钟周期为什么是放大器而不是安全阀
直觉上,高频触发意味着“小步快跑、快速发现”。但在无人值守场景下,高频触发同时意味着人类介入窗口被压缩。假设一个工程师每 2 小时看一次告警,15 分钟周期意味着在他两次查看之间,系统已经完成了最多 8 轮自我修改。如果每轮引入 1 个新失败,8 轮后失败集合可能从 1 个膨胀到 8 个以上(模拟推演,非实测数据)。
笔者认为:触发频率应当与“人类响应时延”和“单轮最大影响半径”挂钩,而不是与“CI 失败多快出现”挂钩。一个可用的经验式是:触发周期 ≥ 人类响应时延 / 可容忍的未审轮数。若响应时延 2 小时、可容忍 1 轮未审,则周期不应短于 2 小时。15 分钟周期只有在“每轮影响半径极小且可自动回滚”时才成立。
3.3 一个可复现的最小失控演示(模拟)
为便于读者自查,下面给出一个不依赖真实模型的模拟脚本,用状态机复现“验证同源 + 无白名单 + 无预算”三缺陷下的失败膨胀。数据为模拟生成,仅用于说明动力学,不代表任何真实系统指标。
import random
random.seed(42)
def simulate(rounds=96, p_bad_patch=0.35, p_new_fail=0.5,
has_whitelist=False, has_hard_budget=False, budget=200):
failures = 1
touched_branches = set()
cost = 0
history = []
for t in range(rounds):
# 验证同源:坏补丁有 p_bad_patch 概率被放行
if random.random() < p_bad_patch:
if random.random() < p_new_fail:
failures += 1
# 无白名单:每轮可能触碰新分支
if not has_whitelist:
touched_branches.add(f"branch-{random.randint(0, 20)}")
# 无硬预算:重试循环使成本按失败数放大
cost += failures * random.randint(1, 3)
if has_hard_budget and cost > budget:
history.append((t, failures, len(touched_branches), cost, "BUDGET_STOP"))
break
history.append((t, failures, len(touched_branches), cost, "RUN"))
return history
h = simulate()
print("末轮:", h[-1]) # 模拟输出示例,非真实测量
运行这类模拟的价值不在于预测具体数字,而在于让团队直观看到:当三个闸门都缺失时,失败数与成本是单调上升的;补上任一闸门,曲线就会出现平台或提前终止。这为后文的四元信号提供了动力学依据。
四、主线一:可验证性——独立验证 Agent 的对抗性设计
4.1 为什么“自己验自己”在结构上不可靠
生成与验证同源,会引入两类系统性偏差。第一类是确认偏差:同一个模型在生成补丁时已经形成了一套“我认为这是对的”的内部表征,验证阶段倾向于复用这套表征,而不是重新审视。第二类是错误相关性:如果模型对某类 API 的理解有误,它在生成和验证时会犯同一个错,验证无法捕获生成阶段的系统性误解。
这在软件工程里不是新问题。代码评审(code review)之所以有效,很大程度上是因为评审者与作者是不同的认知主体,拥有不同的上下文与盲区。Fagan 在 1976 年提出的软件检查(software inspection)流程,其核心设计就是角色分离:作者、阅读者、审查者各司其职。本文评述:把这条几十年前的工程原则平移到 Agent 系统,结论是——验证 Agent 必须在模型、提示词、上下文、工具权限四个层面与生成 Agent 解耦,否则“独立”只是名义上的。
4.2 独立验证 Agent 的四层解耦
| 解耦层 | 生成 Agent | 验证 Agent | 失效后果 |
|---|---|---|---|
| 模型 | 主模型 | 不同厂商/不同版本/不同规模 | 错误相关性上升 |
| 提示词 | “修复这个失败” | “找出这个补丁的反例” | 目标函数同向 |
| 上下文 | 失败日志 + 相关代码 | 补丁 diff + 测试 + 契约 | 共享错误前提 |
| 工具权限 | 读写工作区 | 只读 + 沙箱执行 | 验证者可被“说服” |
其中提示词的对抗化最容易被忽视,也最便宜。生成 Agent 的提示词是“修复”,验证 Agent 的提示词应当是“证伪”:要求它输出“这个补丁在什么条件下会失败”,而不是“这个补丁是否正确”。前者是寻找反例,后者是寻求确认,两者的认知模式截然不同。
4.3 验证强度的可计算指标
“有验证 Agent”不等于“验证有效”。笔者建议用三个可观测指标来度量验证强度,作为晋级信号的一部分:
- 反例产出率(Counterexample Yield):验证 Agent 在人工标注为“坏补丁”的样本上,能给出具体反例的比例。低于阈值说明验证流于形式。
- 独立失败率(Disagreement Rate):生成与验证结论不一致的比例。过低(接近 0)往往意味着两者同源、验证失效;过高则说明验证器噪声大。健康区间需要按任务类型标定。
- 逃逸率(Escape Rate):验证通过但最终被人或线上证伪的补丁占比。这是最硬的指标,需要人工抽检或线上监控支撑。
操作建议:在 L1 阶段就应并行运行验证 Agent(只记录不阻断),积累“生成结论 vs 验证结论 vs 人工结论”的三方对照数据。这批数据是 L1→L2 晋级最关键的证据,远比“跑了几天”有信息量。若三方对照中验证 Agent 的逃逸率高于人工评审,就不具备晋级条件。
4.4 与学术界“验证器”研究的呼应
近三年,围绕 LLM 输出验证的研究明显增多。Cobbe 等人 2021 年的“训练验证器做数学题”(Training Verifiers to Solve Math Word Problems)确立了“生成器 + 验证器”双模型范式的价值;Lightman 等人 2023 年的“Let's Verify Step by Step”进一步把验证从结果级推进到过程级,指出过程奖励模型(PRM)能显著提升复杂推理的可靠性。在代码领域,Chen 等人 2021 年的 Codex 工作与后续的单元测试生成研究(如 TestPilot、CodaMosa 等)都表明:可执行的测试是最强的验证信号。
本文评述:这些研究给工程实践的直接启示是——验证 Agent 不应只做“语言层面的判断”,而应尽量落到“可执行层面”。一个能自动生成并运行针对性测试的验证器,其逃逸率通常低于纯文本评审。但要注意边界条件:测试生成本身也可能有错,因此验证链应是“测试生成 → 测试有效性检查 → 补丁执行”,而不是单点信任。这与后文“多验证器”的设计一致。
延伸阅读:Anthropic 关于 Claude Code 安全与权限的官方说明(docs.anthropic.com/claude-code/security);OpenAI 关于测试时计算与验证器扩展的讨论(openai.com/research)。
五、主线二:可回滚性——分支白名单与变更拓扑
5.1 白名单不是“限制”,而是“可回滚性的载体”
很多团队把分支白名单理解成安全措施,这没错,但不够。白名单更本质的作用是把变更约束在可回滚的拓扑内。受保护分支(protected branch)通常有强制评审、状态检查、禁止强推等策略,Agent 一旦写入,回滚成本极高;而专用工作分支可以整支丢弃、可以 force-push、可以自动关闭 PR。白名单的价值,是确保 Agent 的所有写操作都落在“丢弃成本接近零”的分支集合里。
GitHub 的分支保护规则(branch protection rules)与规则集(rulesets)提供了现成的表达手段:可以按分支名模式、按路径、按推送者身份设置约束。GitLab 的 protected branches 与 push rules 同理。这些机制应当被 Agent 的写权限直接继承,而不是在 Agent 内部用提示词“请勿推送主分支”来软约束。
5.2 变更拓扑:用图来表达“允许写哪里”
把仓库的变更空间建模为一张图,节点是分支/目录/文件,边是“允许的变更传播”。白名单就是这张图上的可达集。一个实用的三层模型:
Layer 1 分支级白名单:
allow: ["agent/fix-*", "bot/ci-sweeper-*"]
deny: ["main", "release/*", "hotfix/*"]
Layer 2 路径级白名单(在允许分支内再收窄):
allow_paths: ["src/**", "tests/**"]
deny_paths: ["infra/**", ".github/workflows/**", "**/migrations/**"]
Layer 3 变更规模上限(单轮):
max_files_changed: 5
max_lines_changed: 200
max_new_dependencies: 0
这三层缺一不可。只有分支白名单,Agent 仍可能在允许分支里改 CI 配置、改迁移脚本,造成“分支可回滚但影响不可回滚”的局面。路径白名单把“可回滚”从分支粒度细化到文件粒度。变更规模上限则防止单轮变更过大导致评审失效——一个改了 2000 行的补丁,即使分支可丢弃,人工也无力判断。
笔者认为:路径白名单里最该 deny 的是三类:CI/CD 配置(改了会改变验证本身的可信度)、数据库迁移(不可逆)、依赖清单(供应链风险)。这三类一旦被 Agent 自动修改,整个验证与回滚体系的前提就被破坏了。很多“跑飞”案例的根因,是 Agent 改了流水线配置,让后续验证跑在一个被篡改的环境里。
5.3 回滚演练:把“可回滚”变成可验证的事实
“我们的分支可以回滚”是一个假设,不是一个事实。要把它变成事实,需要定期演练。建议在 L1 阶段就建立回滚演练机制:
- 每周随机抽取一条 Agent 产出的分支,执行“整支丢弃 + 关联 PR 关闭 + 缓存清理”,记录耗时。
- 验证回滚后主分支状态与回滚前一致(用 git diff 或构建产物哈希比对)。
- 统计回滚耗时分布,把 P95 回滚耗时作为晋级信号之一。若 P95 超过人类响应时延,说明“可回滚”在时间维度上不成立。
这套演练的副产品是回滚剧本(runbook):当真的需要回滚时,值班人员不需要临场思考,按剧本执行即可。无人值守系统的可靠性,很大程度上取决于“出事时能不能在几分钟内回到已知良好状态”。
六、主线三:影响半径——从仓库粒度到语义粒度
6.1 影响半径的定义与度量
影响半径(blast radius)指一次自动变更可能波及的最大范围。在 SRE 语境里,它通常用“受影响用户比例”“受影响服务数”来度量。在编码 Agent 语境里,笔者建议用三个维度:
- 代码维度:被修改文件的被依赖度(入度)。改一个叶子工具函数,影响半径小;改一个被 200 个模块引用的基础库,影响半径大。
- 运行维度:变更是否触及运行时关键路径(鉴权、计费、数据写入)。
- 组织维度:变更是否需要跨团队协调。跨团队变更的隐性成本远高于单团队。
可以用代码依赖图(如基于 import 图或调用图构建)计算每个文件的入度,作为影响半径的代理指标。Google 的软件工程研究(Potvin & Levenberg, 2016,Why Google Stores Billions of Lines of Code in a Single Repository)讨论了单体仓库下的依赖管理挑战,其核心洞察之一是:变更的影响范围可以被依赖结构量化。本文评述:把这一洞察用于 Agent 授权,就是把“允许改什么”从人工经验变成图上的可达性计算。
6.2 影响半径与授权等级的匹配
| 影响半径 | 示例 | 可授权等级 | 附加条件 |
|---|---|---|---|
| 极小(入度 ≤ 3,非关键路径) | 工具函数、文档、测试 | L2 可自动 | 独立验证通过 |
| 小(入度 4–20) | 业务模块内部 | L2 限流 | 单轮变更上限 + 抽检 |
| 中(入度 21–100) | 跨模块公共库 | L1 仅建议 | 人工评审 |
| 大(入度 > 100 或关键路径) | 鉴权、计费、数据层 | 禁止自动 | 仅人工 |
这张表的关键在于按影响半径分级授权,而不是按“Agent 整体可信度”一刀切。同一个 Agent,在测试目录可以 L2,在鉴权模块必须 L0。这种细粒度授权,比“整体升到 L2”安全得多,也更容易在组织内达成共识。
6.3 语义粒度:比文件粒度更细的约束
文件粒度仍有盲区:一个文件里可能既有纯函数,也有副作用代码。更细的做法是语义粒度约束,例如“禁止修改任何包含数据库写操作的函数”“禁止修改任何标注了 @critical 的函数”。这需要静态分析或注解体系支撑。实践中可以从“关键函数清单”起步:维护一份人工标注的高危函数列表,Agent 的补丁若触及这些函数,自动降级为 L1 建议。
这类“关键性标注”在安全工程里已有成熟实践,例如 Linux 内核的 MAINTAINERS 文件与稳定分支的 backport 规则。本文评述:Agent 授权体系可以复用这些既有元数据,而不必从零构建。仓库里已有的 CODEOWNERS、依赖清单、测试覆盖率报告,都是影响半径计算的现成输入。
七、主线四:预算硬约束——把 token 上限放到模型外部
7.1 为什么“提示词里说节约”不算约束
模型对提示词的遵循是概率性的,不是确定性的。在长上下文、多轮重试、工具调用交织的场景下,“请控制 token 消耗”这类指令的约束力会显著衰减。更根本的问题是:约束的执行者与被约束者是同一个主体,这在控制论里是典型的开环控制,没有反馈回路,无法保证稳定性。
正确的做法是把预算约束外部化:在模型之外设置一个独立的计量与阻断层,它不依赖模型的配合,只依赖对 API 调用的拦截与计数。这层可以是 API 网关、可以是 SDK 中间件、可以是编排框架的预算插件。关键是它必须在模型不可见、不可协商的位置。
7.2 三级预算模型
Level 1 单次调用预算(per-call)
max_input_tokens: 120_000
max_output_tokens: 8_000
→ 由 API 参数硬性传入,超出即报错
Level 2 单轮任务预算(per-task)
max_total_tokens: 400_000
max_tool_calls: 40
max_wall_clock: 10min
→ 由编排层计数,超限终止任务并标记 FAILED_BUDGET
Level 3 时间窗预算(per-window)
window: 1h
max_total_tokens: 2_000_000
max_tasks: 20
→ 由网关层计数,超限熔断整个 Sweeper
三级预算的作用不同。单次调用预算防止单点爆炸;单轮任务预算防止重试循环;时间窗预算防止“每轮都不超但轮次太多”的慢性消耗。三者叠加,才能把总消耗约束在一个可预测的上界内。
工程细节:预算阻断必须区分“软超限”与“硬超限”。软超限(如达到 80%)触发告警与降级(减少并发、缩短上下文),硬超限(100%)直接终止。很多事故的根因是只有软告警没有硬阻断,告警被淹没在噪声里,系统继续跑。另外,预算计数要按实际计费 token 而非字符数估算,否则在非拉丁文本与工具调用场景下误差可达数倍。
7.3 预算与验证的耦合:一个被忽视的成本陷阱
独立验证 Agent 会显著增加 token 消耗——每个补丁都要过一遍验证。如果预算只按“生成”估算,验证阶段就会成为预算黑洞。更隐蔽的是,验证失败会触发重新生成,形成“生成-验证-再生成”的循环,消耗是单轮的数倍。
因此预算模型必须把验证成本和重试成本显式建模。一个可用的估算式是:
单任务期望消耗 ≈ (C_gen + C_verify) × (1 + p_retry × R)
其中:
C_gen 单次生成成本
C_verify 单次验证成本(通常为 C_gen 的 0.3–0.8 倍,视验证深度)
p_retry 验证失败触发重试的概率
R 平均重试次数
示例(模拟参数):C_gen=1, C_verify=0.5, p_retry=0.4, R=2
→ 期望消耗 = 1.5 × 1.8 = 2.7 倍单次生成成本
这个式子说明:引入验证不是“加一点成本”,而是可能把成本放大到 2–3 倍。预算设计必须以此为基线,否则上线后很快会触发预算熔断,反而让系统不可用。本文评述:预算与验证是一对需要联合调参的变量,单独优化任何一个都会失衡。
八、晋级评分卡:把“拍脑袋”换成可计算的闸门
8.1 四元信号的量化
把前四节的主线收敛为一张评分卡。每个维度给出可观测指标、阈值建议与数据来源。阈值需按团队基线标定,下表为示例值(模拟设定,非行业标准):
| 维度 | 指标 | 示例阈值 | 数据来源 |
|---|---|---|---|
| 可验证性 | 验证逃逸率 | ≤ 人工评审逃逸率 | 三方对照抽检 |
| 可验证性 | 反例产出率 | ≥ 60% | 坏补丁样本集 |
| 可回滚性 | P95 回滚耗时 | ≤ 人类响应时延 | 回滚演练记录 |
| 可回滚性 | 白名单覆盖率 | 100%(无例外) | 权限配置审计 |
| 影响半径 | 单轮最大入度 | ≤ 20 | 依赖图计算 |
| 影响半径 | 关键路径触及次数 | 0 | 关键函数清单 |
| 预算 | 预算熔断触发率 | ≤ 5% | 网关计数 |
| 预算 | 单任务超支率 | ≤ 2% | 编排层日志 |
8.2 晋级决策规则:全通过才放行
评分卡的使用规则应当是合取而非析取:所有维度都达标才允许晋级,任一维度不达标即维持原级。这看似保守,但符合“权限跃迁”的风险特征——权限放大后的失败是系统性的,不能用“其他维度都很好”来对冲。
反模式警示:“跑通了 7 天”之所以危险,是因为它只覆盖了“可验证性”的一个侧面(成功率),且样本量往往不足以覆盖长尾失败模式。7 天 × 每天 10 次 = 70 个样本,对于失败率 1% 的稀有事件,期望观测数不足 1 次。用这样的样本量做晋级决策,统计上是不成立的。要观测到 1% 事件,至少需要数百到上千个样本(依据置信区间要求而定)。
8.3 样本量与观察窗口的正确用法
时间窗口的正确用法,是把它换算成样本量,再判断样本量是否足以支撑结论。一个粗略的规则:若要声称“失败率低于 p”,在 95% 置信度下,零失败所需的样本量约为 n ≈ 3/p(基于 rule of three)。例如要声称失败率低于 1%,需要约 300 个无失败样本;要声称低于 0.1%,需要约 3000 个。
这个简单的统计事实,直接否定了“跑几天就晋级”的做法。它给出的替代方案是:先定义目标失败率,再反推所需样本量,再决定观察窗口。如果 Sweeper 每天处理 10 个失败,要积累 300 个样本需要 30 天;如果希望更快,就需要在 L1 阶段用回放(replay)或影子模式(shadow mode)加速样本积累——把历史失败日志喂给 Agent,离线评估其修复质量。
九、灰度与回退:L1→L2 的工程落地步骤
9.1 五阶段灰度路径
评分卡回答“能不能晋级”,灰度路径回答“怎么晋级”。笔者建议把 L1→L2 拆成五个阶段,每个阶段都有明确的进入条件与退出条件,任何一个阶段出现异常都可以原地回退,而不必整体回滚到 L1。
| 阶段 | 动作空间 | 进入条件 | 退出/回退条件 |
|---|---|---|---|
| S0 影子 | 只读 + 离线生成补丁,不落盘 | 验证 Agent 就位 | 三方对照数据 ≥ 300 条 |
| S1 草稿 | 在专用分支提交,自动开 PR,不合并 | 白名单 + 预算硬约束上线 | PR 关闭率 > 30% 即回退 |
| S2 限流 | 同上,但限制单轮变更规模与并发 | S1 连续 2 周无严重事件 | 预算熔断率 > 5% 即回退 |
| S3 抽检 | 允许自动合并低风险路径,其余仍需人审 | 逃逸率达标 + 回滚演练通过 | 出现一次线上证伪即回退 |
| S4 稳态 | L2 全量(仍受白名单与预算约束) | 评分卡全维度达标 | 任一红线指标越界即降级 |
这个路径的核心思想是逐级扩大动作空间,而不是逐级延长观察时间。每一阶段新增的动作都对应新增的闸门,闸门先于动作上线。这与“先跑几天看看,没问题就全开”的做法有本质区别:后者是动作先行、闸门补票,前者是闸门先行、动作受控。
9.2 熔断与降级:失控发生时的第一响应
即使闸门齐全,也需要假设“闸门会失效”。因此必须设计独立的熔断层,它不依赖 Agent 自身的判断,只依赖外部可观测信号。建议至少设置四类熔断触发器:
- 失败数增速:单位时间内新增失败数超过阈值(如 1 小时内新增 > 5),立即暂停 Sweeper。
- 分支扩散度:Agent 触及的分支数在窗口内超过阈值,说明写范围失控。
- 预算消耗斜率:token 消耗速率的二阶导为正且持续上升,说明进入重试放大。
- 人工否决率:人工关闭/回退 Agent PR 的比例突增,说明验证体系失效。
熔断后的降级策略应当是分级降级而非一刀切停机:先降并发、再缩上下文、再关自动写、最后完全停用。分级降级的好处是保留部分可观测性——完全停机后你就看不到系统在做什么了,而“只读模式”仍能持续产出诊断信息,帮助定位问题。
笔者认为:熔断器本身也需要“演练”。很多团队的熔断逻辑写了但从未触发过,真出事时才发现阈值配置错误或告警通道失效。建议每季度做一次“熔断演练”:人为注入一个超阈值信号,验证从触发到降级到通知的完整链路。这与 SRE 领域的混沌工程实践一脉相承——你不能信任一个从未被验证过的保护机制。
9.3 观测与审计:让每一次放手都可追溯
无人值守系统的可治理性,取决于它的可观测性。建议为 Agent 建立独立的观测面板,至少包含:每轮任务的输入摘要、生成补丁的 diff、验证结论与理由、预算消耗、最终去向(合并/关闭/回退)。这些记录不仅是排障依据,也是晋级评审的证据来源。
审计维度上,要能回答三个问题:谁授权了这次变更(授权链)、依据什么信号授权(评分卡快照)、出问题后如何回退(回滚剧本版本)。这三者构成一条完整的责任链。缺少任何一环,“人机共担”就会退化成“无人负责”。
十、对比与局限:现有框架为何不够用
10.1 现有 Agent 框架的授权模型对比
当前主流的 Agent 编排框架与编码助手,在授权模型上各有侧重,但普遍缺少“晋级判据”这一层。下表为定性对比(基于公开文档的一般性归纳,非严格评测):
| 能力 | 通用编排框架 | 编码助手(含 Claude Code) | 本文主张 |
|---|---|---|---|
| 工具权限 | 有,按工具粒度 | 有,allowlist/权限模式 | 继承仓库侧分支与路径规则 |
| 验证独立性 | 需自行搭建 | 默认同源 | 四层解耦 + 逃逸率度量 |
| 预算硬约束 | 部分支持 | 以软约束为主 | 三级预算 + 外部网关 |
| 晋级判据 | 无 | 无 | 四元评分卡 + 合取规则 |
| 熔断与降级 | 需自行实现 | 有限 | 四类触发器 + 分级降级 |
这张对比表想说明的不是“现有框架不好”,而是它们解决的是“能不能做”,本文解决的是“该不该放手”。前者是能力问题,后者是治理问题。能力可以靠框架提供,治理必须靠团队自己建立。
10.2 本文方法的局限
必须诚实说明局限。第一,四元信号并非完备。它覆盖了可验证、可回滚、影响半径、预算四个维度,但没有覆盖“语义正确性”本身——一个补丁可能通过所有测试、落在白名单内、预算合规,却在业务语义上是错的。这类失败只能靠人工评审或更强的规格说明来捕获。
第二,阈值标定依赖团队基线。本文给出的数值均为示例,直接照搬可能过松或过紧。正确做法是先用历史数据标定基线,再设定阈值,并在运行中持续校准。
第三,独立验证的成本可能不可接受。对于低频、低价值的修复任务,双模型验证的成本可能超过收益。此时更合理的选择是维持 L1,而不是为了“升级”而升级。放手节奏的目标是“可控”,不是“全自动”。
第四,组织因素难以量化。评审文化、值班制度、故障复盘质量,这些“软”因素对放手节奏的影响可能比技术指标更大,但它们很难进入评分卡。本文的处理方式是把它们放在灰度路径的“退出条件”里(如“出现一次线上证伪即回退”),用事件而非指标来表达。
十一、结论与展望:从“跑通”到“可控”的范式转移
11.1 三条可带走的结论
回到开头的问题:从 L1 到 L2,该按什么信号推进?本文的答案是三条:
- 按能力信号,不按时间信号。晋级的前提是“新动作空间下的四类能力就位”,而不是“旧动作空间下跑得久”。时间只用来积累样本,不用来证明可控。
- 闸门先于动作。任何新增的写权限,都必须先有对应的验证、白名单、限幅、预算、熔断,再谈开放。闸门补票式的“先开再补”是失控的结构性原因。
- 分级授权,而非整体升级。同一个 Agent 在不同影响半径的区域应有不同的授权等级。细粒度授权既更安全,也更容易在组织内推进。
11.2 范式转移:可控性是一等公民
过去两年,编码 Agent 的讨论重心在“能力”:能修多难的 bug、能写多复杂的代码、能通过多少测试。这当然是重要的,但当 Agent 开始拥有写权限、开始无人值守运行时,可控性就必须与能力并列为一级指标。一个能修 90% 的 bug 但偶尔跑飞的系统,在生产环境里的价值可能低于一个只能修 60% 但始终可控的系统。
这种转移在软件工程史上并不新鲜。数据库从“单机高性能”转向“分布式一致性”,运维从“手工操作”转向“基础设施即代码”,都经历了从追求能力到追求可控的转变。编码 Agent 大概率会走同一条路:先证明能做什么,再证明能安全地做什么。
11.3 展望:几个值得关注的方向
- 形式化契约的轻量化:把“允许改什么”从白名单升级为可机检的契约(如基于类型系统或前置/后置条件),让影响半径的计算更精确。
- 验证器的可组合性:多个异构验证器(测试、静态分析、形式化检查、另一模型)的组合与投票机制,如何设计才能既降逃逸率又不使成本失控。
- 授权决策的可解释性:当系统自动决定“这个补丁可以合并”时,能否给出人类可读的理由链,供审计与复盘。
- 跨组织的授权基线:是否可能形成类似 SAE 分级的行业共识,让“L2 级 Agent”成为一个有明确含义的标签。
这些方向都还在早期。但有一点可以确定:放手节奏的讨论,终将从“经验之谈”走向“可计算、可审计、可回退”的工程体系。本文的四元信号与评分卡只是这个方向上的一个起点,欢迎读者用自己的基线数据去修正它。
十二、参考文献与资料
以下为本文讨论中涉及的主要文献与公开资料,按主题归类。部分为方法学经典,部分为近年 LLM 验证方向的工作。具体页码与版本请以原始出版物为准。
- Fagan, M. E. (1976). Design and code inspections to reduce errors in program development. IBM Systems Journal. —— 软件检查流程与角色分离的奠基性工作。
- Cobbe, K., et al. (2021). Training Verifiers to Solve Math Word Problems. —— 生成器 + 验证器双模型范式的代表性研究。
- Lightman, H., et al. (2023). Let's Verify Step by Step. —— 过程级验证与过程奖励模型(PRM)的系统性讨论。
- Chen, M., et al. (2021). Evaluating Large Language Models Trained on Code. —— Codex 工作,确立代码生成与单元测试评估的基本框架。
- Potvin, R., & Levenberg, J. (2016). Why Google Stores Billions of Lines of Code in a Single Repository. Communications of the ACM. —— 单体仓库下的依赖管理与变更影响分析。
- SAE International. J3016: Taxonomy and Definitions for Terms Related to Driving Automation Systems for On-Road Motor Vehicles. —— 自动驾驶分级,本文 L0–L4 模型的类比来源。
- Anthropic. Claude Code 官方文档(安全、权限模式与 allowlist 配置相关章节)。—— 工程实践中权限分级与 human-in-the-loop 的公开说明。
- GitHub Docs. About branch protection rules / Rulesets;以及 Copilot Autofix 相关文档。—— 分支保护与自动修复默认路径的公开资料。
- Beyer, B., et al. (2016). Site Reliability Engineering: How Google Runs Production Systems. O'Reilly. —— 影响半径、熔断、降级与演练等 SRE 实践的经典参考。
说明:本文中的数值示例(阈值、预算参数、模拟输出)均为说明性设定,用于展示方法结构,不代表任何真实系统或组织的实测指标。读者在落地时应以自己的历史数据标定基线。文中对现有框架的对比为基于公开文档的一般性归纳,不构成对任何产品的评测结论。

