Anthropic 如何重构测试影响分析服务以应对智能体编码带来的 CI 压力

Claude:Blog(网页)·2026-09-15 03:15·1小时前
AI 导读

Anthropic 工程师每季度交付的代码量是 2021-2025 年均值的 8 倍,其中 80% 由 Claude 编写,六个月内 CI 任务增长 25 倍。

Claude:Blog(网页)
精选
62AI 编辑部评分,满分 100

Anthropic 如何重构测试影响分析服务以应对智能体编码带来的 CI 压力

2026-09-15 03:15· 1小时前
AI 导读

Anthropic 工程师每季度交付的代码量是 2021-2025 年均值的 8 倍,其中 80% 由 Claude 编写,六个月内 CI 任务增长 25 倍。

推荐理由

作者以一手复盘展示了 agent 加速编码后 CI 承压的真实路径,给出的可扩展架构经验可直接迁移到类似团队。

正文 · AI 翻译

AI 正在改变 CI

Anthropic 的工程师平均每个季度提交的代码量是 2021-2025 年间的 8 倍。其中 80% 的代码由 Claude 编写,而且 Claude 在 PR 的审查和批准中也发挥着重要作用

编写代码不再是瓶颈,而一旦 PR 审查被加速,CI 就开始感受到压力。

除此之外,我们代码库中的测试数量增长了 10 倍,而工程师只增加了少量。这一切导致 CI 任务在六个月内增长了 25 倍(如果你想算这笔账:并非每个测试都会在每个 PR 上运行,我稍后会解释)。

这曾多次差点压垮我们的测试影响分析服务。为了避免成为下一个瓶颈,我们把整个系统推倒重来,重新设计了该服务的架构。但走到这一步的道路并不平坦:我们先做了三次快速修补,分别只坚持了 70 天、29 天,然后不到一天。

随着智能体不断加速代码生成和审查,更多工程团队可能很快就会面临 CI 扩展的挑战。我预计,横向扩展的测试选择架构将成为行业标准,因为运行智能体的团队会产生更多的 PR 和更多的测试。

在本文中,我将讨论我们在 Anthropic 如何扩展测试影响分析服务,以及我惨痛学到的教训:永远要为指数增长做好规划。具体的扩展技术——购买更大的机器、并行化进程、或者重启服务(是的,这一招至今依然出奇地管用)——都很常见,并不是本文要传达的洞见。

关键在于,这些技术如今买来的时间收益只是它们一年前的一小部分。另一方面,彻底重构并重新设计一个服务同样只需一小部分时间,而且在编写代码不再是瓶颈的今天,这种方式更具可持续性。

你越能预见这种压力并规划架构如何随之演进,你在权宜之计上浪费的时间就越少。

测试影响分析架构

我的许多同行所在的组织,每次变更时仍然运行所有测试。这在一定程度上行得通,但无法扩展:CI 门禁变得越来越漫长、昂贵且不可信。

此外,人类很擅长判断哪些测试失败与自己无关,而智能体则需要更多的上下文和指引。当它们拿到一组特定的有效测试时,就能更有效地自我验证和迭代。

在 Anthropic,我们构建了一个确定性的测试影响分析(即测试选择)服务,它基于历史表现和包相关性来判断每次变更需要运行哪些测试。这并非罕见做法,已有一类供应商提供该领域的产品。

我们的服务依赖两个确定性组件保持同步:

  • 一个“监听器”记录每次 CI 运行的测试结果。
  • 一个“选择器”读取测试结果历史,并决定在哪些已打开的 PR 上运行哪些测试。

这很有效,但当每秒有多个 CI 任务运行时,监听器会越来越落后于 PR 队列。对于 AI 原生 SDLC 来说,哪怕一点滞后也可能产生重大影响。例如,监听器滞后 20 分钟可能意味着数以万计的测试更新未能应用到选择器上。

  • 如果一次糟糕的变更被合并,某个测试就会开始对其他所有人都失败,导致多次不必要的排查。
  • 如果某个依赖开始出现不稳定,那么不稳定的红色失败就会开始阻塞合并。
  • 如果某个测试被修复或新增了一个测试,在监听器追上之前它都不会运行,从而带来回归风险。

所有这些都运行在单个进程中,因为为每个测试保留运行历史意味着需要由单一写入者来应用结果。这种 v0 设计使我们无法进行水平分片。

重新设计的坎坷之路

去年 10 月,该服务就已显现出吃紧的迹象,我们连续两天收到告警传呼。

补丁 1:换更大的机器

第一个修复很简单:我们把运行该服务的核心数翻倍。我们也清楚这只是权宜之计。

对话为情景重现,基于真实事件。

即便趋势线已经很明显,责任归属仍然模糊不清。没有人愿意再接管另一块基础设施。此外,CI 团队还有更要紧的事要处理。

补丁 2:分片

此时,该服务监听器不断积压的延迟已让我们相当频繁地收到告警传呼。为了推动一些长期修复,我在内部版 Claude Tag 中开启了一个长期会话,专门用于监控该服务。每当监听器积压超过 50,000 个任务时,Claude 就会提醒我,并继续我们关于下一步措施的对话。

这种状况持续了好几个月,而不必不断向它提醒过去的工作或背景信息,这很有帮助。Claude 常常主张进行大重构,但我们通常最终选择再打一个补丁。

与内部版本 Claude Tag 的逐字对话,部分内容已作脱敏处理。

到了二月,CI 任务量的指数级增长再次让服务不堪重负。这一次,我们决定做并行化。

监听器并不需要一个单一写入者来正确排序测试结果,它需要的是每个包各有一个写入者,以便正确排序代码库每个部分的测试结果。Claude 为我们生成了代码,把每个包的状态拆分成一个带有自己 worker 的分片。

我们也知道这个修复只是权宜之计,但没想到它只为我们争取了 29 天。

补丁 3:每日重启

到了三月,进程在大多数工作日的中午过后不久就达到内存上限。我们再次寻找快速修复方案,但是:

  • 我们只找到了四个 bug。
  • 把内存分配器换掉这种快速 hack 毫无效果。我们曾试图优化垃圾回收,但那并不是真正的解决方案。
  • 我们不想冒险对一个已经承受高负载的单例(singleton)做内存分析。
  • 重启带来的效果还不到一天。

我们还发现,每日重启导致服务逐渐落后得越来越多。当落后超过一个小时时——这种情况发生过几次——大量任务结果没有被监听器记录下来。

需要说明的是,这并不意味着这些 PR 上从未运行过 CI,也不意味着未经测试的代码被推到了生产环境。它意味着监听器没有接收到某些结果,也就是说,我们的测试选择组件在使用过时的数据来决定 PR 上要运行哪些测试、不运行哪些测试。大多数情况下,这体现为我们运行了那些本来就已经非常不稳定或全面失败的测试。

重新设计

是时候(早就该)重新设计这个服务了,我们采纳了 Claude 的建议:给测试选择服务配了一个数据库,确切地说是一个内存数据存储。这样做之后,我们实际上把单例以前承担的一大部分内存处理工作卸载了出去。

现在,任何监听器工作进程都可以处理任何结果,将其追加到内存存储中的日志里,然后继续处理,无需在内存中保存任何东西——无状态,因而可以水平扩展。一个单独的小型消费者进程每隔几秒钟把日志汇总成每个测试的历史记录,选择器就可以快速查询相关的结果历史。

这种分布式架构的运行成本更高,但比起一个摇摇欲坠的单例,它的扩展和内存分析要容易得多。这个项目由一名工程师花了三周完成。一年前,这大概需要接近一个季度。

排队中未处理的作业结果事件,每小时最大值。之前:大多数日子都会积压,并逐周增长。切换和调优之后:保持平稳。

期间有一些微调(调整 journal 的大小和 worker 数量),这 largely 由 Claude 自主完成,但我们的服务自那以后一直保持稳定。

我会做出的不同选择

如果我被送回 2025 年 10 月,凭借现在所了解的情况,我会以不同的方式处理这个项目和其他项目。

第一个不同之处是,我会把 AI 的指数级增长考虑在内。随着每位工程师平均使用的智能体数量上升,以及加速 PR 审批变得更加成熟,CI 作业会呈指数级增长。

这在 Anthropic 已经随时间改变了 PR 的形态,因为 Claude 更喜欢更小、更细粒度的 PR(这也是不应对每个 PR 运行所有测试的又一个好理由)。这转化为每天更多的 CI 作业。此外,由于智能体会在夜间和周末推送代码,活跃度的下限被抬高了,但由于人类工程师仍然驱动并审批大量 PR,负载仍保持突发性的特点。

我给工程团队的建议是:无论你是自建还是采购,都要假设你的架构会在两个季度内达到 25 倍的负载。“过度设计”这一概念正开始略微淡出,或者说至少门槛已经大幅提高了。只要预算允许,你现在在 v0 设计中就可以为 10-20 倍于预期规模的负载做好准备。

为你的服务配置可观测性工具,让它们成为 Claude 的眼睛和耳朵。这让 Claude 能够爬山式地增量修复问题,比我们手动操作要快得多、好得多。特别是,要确保进入的 CI 任务数量与完成的数量一致。

从一开始就要把状态隔离在进程之外。我还会避免以单实例方式运行任何关键服务,除非你能够对其进行度量以及任何金丝雀变更。CI 的演进速度太快了,不这样做寸步难行。

更多 CI 相关资源

我还写过一篇关于我们如何加速 使用 Claude Tag 的 CI on call(测试版)的文章。

视频 · 前往原文观看

来源:Claude:Blog(网页)· claude.com