← 返回 Blog

Agent 加速写代码之后,CI 成了新的产品瓶颈

Anthropic 的 CI 复盘显示,代码生成加速后,测试选择服务才是新的交付约束;产品团队要把测试影响分析做成可观测、无状态、能横向扩展的基础设施。

Agent 让代码和测试快速增加后,CI 通过测试影响分析和无状态架构保持可扩展
文章目录

Anthropic 的工程师现在平均每季度交付的代码量,是 2021—2025 年平均水平的 8 倍;其中 80% 的代码由 Claude 编写,Claude 也参与大量 PR 的审查与批准。与此同时,代码库里的测试数量增长了 10 倍,CI 作业在六个月内增长了 25 倍。

真正先被压垮的不是写代码,而是决定“这次改动应该跑哪些测试”的服务。Anthropic 的测试影响分析系统经历了加机器、分片和每日重启三个补丁,分别只买来约 70 天、29 天和不到一天。最后他们改成了带内存日志的无状态、可横向扩展架构。

这次复盘给 AI 产品团队的信号很直接:当 Agent 把代码生成变快,CI 不再是研发工具的后台细节,而是决定产品能否安全迭代的核心产品能力。

先把“测试选择”看成一个有状态产品#

Anthropic 的服务由两个确定性组件组成:

  • Listener:记录每次 CI 运行的测试结果;
  • Selector:读取历史结果,根据代码包相关性决定一个 PR 要跑哪些测试。

这套机制比每次改动都跑完整测试集更快,也更适合 Agent 生成的小粒度 PR。但它有一个隐藏的状态同步问题:Listener 必须持续追上 PR 队列,Selector 才能用最新结果做判断。

当 Listener 落后 20 分钟,可能意味着有数万条测试更新还没有进入 Selector。结果不是“CI 完全不跑”,而是选择器拿着旧数据做决策:已经修好的测试可能还不运行,新加的测试迟迟不会加入,广泛失败或本来就 flaky 的测试反而继续阻塞合并。

这类服务不能只把“测试通过率”当作产品指标。还要观测输入的 CI 作业数、Listener 积压、Selector 使用的数据新鲜度,以及测试结果从产生到被消费的时间。

测试影响分析把 CI 作业流拆成记录、聚合和选择三个可观测环节
Listener 记录结果,Journal 暂存事件,Consumer 聚合历史,Selector 为每个 PR 选择相关测试。

三次补丁为什么都只能短暂止痛#

补丁一:换更大的机器#

第一次修复很直接:把服务运行的 CPU 核数翻倍。它解决了眼前的容量问题,但没有改变单进程、单写入者的结构。增长曲线不变,机器只是更晚达到极限。

补丁二:按代码包分片#

第二次尝试把每个代码包的状态拆成独立 shard,让每个 worker 负责对应分区的测试结果顺序。这个方向比垂直扩容更接近问题本身,但只支撑了 29 天。

补丁三:每天重启#

第三次是每天重启进程。它能暂时缓解内存上涨,却让系统在重启期间继续落后;当 Listener 落后超过一小时,很多结果没有被记录,Selector 只能使用越来越陈旧的历史。

三个补丁并非完全错误。它们的问题在于都在处理“现在为什么慢”,没有重新定义“状态应该放在哪里、谁可以并行写入、积压如何被发现”。对于被 Agent 推高的负载,这个差别会越来越明显。

真正的重构:把 Listener 变成无状态 worker#

重构后的关键变化不是换一种更快的语言,而是把状态从进程内存中移出去:

  1. 任意 Listener worker 都可以处理任意测试结果;
  2. worker 把事件追加到内存数据存储中的 journal,然后立即释放本地状态;
  3. 一个小型 Consumer 每隔几秒把 journal 汇总成每个测试的历史;
  4. Selector 读取聚合后的历史,快速判断当前 PR 需要执行的测试。

这样,Listener 不需要由单个进程维护全部测试历史,也就不再被单写入者限制。服务变成无状态、可横向扩展的结构,积压可以用 worker 数量和 journal 容量直接处理。

这套分布式架构的运行成本更高,但更容易扩容和做内存画像。Anthropic 记录的结果是:一个工程师用三周完成项目;切换和调优后,队列里的未处理测试结果不再持续增长,服务保持稳定。

Agent 时代,CI 的“正确性”也需要产品定义#

每次 PR 都跑完整测试集并不等于更安全。测试选择器如果给 Agent 一组与改动相关、有效且可解释的测试,Agent 才能更快自证、迭代和修复。相反,如果选择器用过时的历史误选测试,Agent 会把时间花在不相关的失败上,研发团队也会逐渐不再相信 CI 的红灯。

因此,测试影响分析至少要同时回答四个问题:

产品问题 应观测的信号 失败时的动作
选择器是否跟得上? CI 输入量、Listener 积压、事件处理延迟 扩 worker 或降级到更保守的测试集合
选择结果是否新鲜? 结果产生时间与 Selector 读取时间的差值 标记 stale,禁止用旧历史做激进裁剪
选择结果是否覆盖风险? 相关包命中率、漏跑后的回归样本 把漏测样本加入回归集,扩大相关性规则
CI 是否仍值得信任? flaky 比例、重复失败、合并阻塞时长 隔离 flaky 测试,保留人工升级和全量重跑入口

测试选择不是一次性优化,而是一个需要持续验证的产品策略。只要代码结构、Agent 行为和测试分布变化,相关性历史也会变化。

给 AI 原生研发团队的四个设计规则#

1. 设计时就按两个季度后的负载估算#

Anthropic 的建议是:默认两个季度后会遇到 25 倍负载,v0 架构要在预算允许时按感知规模的 10—20 倍设计。这里的数字来自单个团队的工程经验,不是所有组织的容量基准;可复用的是提前把增长假设写进架构评审,而不是等报警之后再加机器。

2. 把关键状态移出单进程#

如果一个服务既接收事件、排序事件、保存历史,又决定下一步动作,它就很难横向扩展。先把事件日志、聚合状态和决策查询拆开,再决定需要几层缓存或数据库。

3. 给 Agent 可读的运行信号#

服务要像 Agent 的眼睛和耳朵:输入与输出的作业数应该可比较,积压、滞后和错误要有明确阈值,事件要能被回放。这样 Agent 才能持续爬坡修复,而不是只在故障后猜发生了什么。

4. 让降级策略成为产品功能#

当 Listener 落后或历史过期时,系统应该知道什么时候扩大测试集合、暂停自动合并、转人工审批,而不是默默给出一个看似精确但基于旧数据的选择。可解释的保守降级,比错误的“高效”更值得信任。

来源与边界#

  • 原始来源:Anthropic,Agentic coding is straining CI. Here’s how we scaled test impact analysis at Anthropic,发布于 2026 年 9 月 14 日,作者为 Sachin Malhotra。
  • 研究对象:Anthropic 的测试影响分析服务,以及 Agent 加速代码生成和 PR 审查后形成的 CI 运行环境。
  • 公开事实:工程师平均每季度交付代码量约为 2021—2025 年平均水平的 8 倍,Claude 编写约 80% 的代码;测试数量增长 10 倍,CI 作业在六个月内增长 25 倍;服务曾经历约 70 天、29 天和不到一天的三次短期修复窗口。
  • 方法与系统设置:Listener 记录测试结果,Selector 根据历史和包相关性选择测试;原先是单进程单写入者,后改为 Listener worker 将事件写入内存 journal,由独立 Consumer 每隔几秒聚合测试历史,Selector 再读取聚合结果。
  • 关键结果:重构项目由一名工程师用三周完成;切换和调优后,未处理测试结果的队列保持平稳。文章没有提供统一的绝对延迟、成本或漏测率基准。
  • 原文能说明什么:Agent 带来的代码与 PR 增长会把 CI 和测试选择服务推成新的系统瓶颈;把状态移出进程、让处理无状态化,并持续观测输入输出积压,有助于提高可扩展性。
  • 原文不能证明什么:Anthropic 的 25 倍负载、8 倍代码量和三周重构周期不能直接作为其他团队的容量承诺或 ROI;测试影响分析也不能替代高风险变更的人工判断和全量验证。
  • 本文产品化解读:把 CI 当作 Agent 研发产品的一部分来设计,用新鲜度、积压、漏测回归和降级策略定义“可用”,是基于该工程复盘提出的产品判断,不是 Anthropic 的通用标准。