← 返回 关于

智能体编程正让 CI 承压:Anthropic 如何扩展测试影响分析服务

2026-09-16 · 原文链接

AI 正在推动 CI 演进

Anthropic 工程师如今平均每季度交付的代码量,是 2021—2025 年期间的 8 倍。其中 80% 的代码由 Claude 编写,而在 PR 的审查与批准中,Claude 同样发挥着重要作用。

写代码不再是瓶颈;一旦 PR 审查也提速,CI 就开始承压。

与此同时,我们代码库中的测试数量增长到了原来的 10 倍,工程师人数也略有增加。这些变化使 CI 作业量在六个月内达到了原来的 25 倍(如果你正试着核算这些数字,先提醒一句:并非每个 PR 都会运行所有测试,后面会解释)。

这样的增长数次险些让我们的测试影响分析服务不堪重负。为了不成为下一个瓶颈,我们推倒重来,重新设计了服务架构。但走到这一步并不顺利:我们先后做了三次应急修补,分别撑了 70 天、29 天,以及不到一天。

随着智能体不断加速代码生成与审查,更多工程团队很可能很快就会遇到 CI 扩容的挑战。我预计,可水平扩展的测试选择架构会成为行业标准,因为使用智能体的团队既会产生更多 PR,也会编写更多测试。

本文将介绍我们如何在 Anthropic 扩展测试影响分析服务,以及我付出代价才学到的一课:始终为指数级增长做准备。购置更强的机器、让处理过程并行化,或者重启服务(没错,这招依然出奇地有效),这些扩容手段都很常见,并不是本文想传达的核心心得。

关键在于,每一种手段能争取到的时间,都只剩下一年前的一小部分。另一方面,既然写代码不再是瓶颈,彻底改造、重新设计一个服务所需的时间也大幅缩短了,而且这种做法如今更具可持续性。

越能提前预见这些压力,并规划架构如何随之演进,就越能少在治标不治本的措施上浪费时间。

测试影响分析的架构

我认识的许多同行所在的组织,仍然会为每次变更运行所有测试。这在一定规模内行得通,却无法持续扩展:CI 检查关卡会越来越耗时、昂贵,也越来越不可信。

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

在 Anthropic,我们构建了一个确定性的测试影响分析服务,也叫测试选择服务。它根据测试的历史表现及软件包相关性,决定每次变更该运行哪些测试。这种做法并不少见,市场上也有一类供应商专门提供这方面的产品。

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

这套机制很有效,但当每秒都有多个 CI 作业运行时,监听器就开始越来越跟不上 PR 队列的速度。在 AI 原生软件开发生命周期(SDLC)中,短暂的滞后也可能产生很大影响。例如,监听器落后 20 分钟,就可能意味着数万条测试更新尚未同步给选择器。

这些逻辑全都运行在一个进程中,因为要持续维护每个测试的历史记录,就需要由单一写入者来更新结果。这一初版(v0)设计让我们无法进行水平分片。

通往架构重设计的曲折之路

到去年 10 月,服务就已经出现了承压迹象,我们连续两天收到了值班告警。

补丁一:换更强的机器

第一次修补很简单:我们把运行服务的 CPU 核数翻了一倍。我们也知道,这只能撑一阵子。

根据真实事件重现的对话。

即便增长趋势已很清楚,归谁负责却仍不明确。没人想再接手一项基础设施,CI 团队也有更紧迫的问题要处理。

补丁二:分片

这时,服务监听器不断累积的处理延迟,已经让我们频繁收到值班告警。为了推动一些长期修复,我在内部版 Claude Tag 中开启了一个长期会话,专门监控这项服务。只要监听器积压超过 50,000 个作业,Claude 就会通知我,接着讨论下一步该怎么做。

这个会话持续了几个月。无需反复向它交代之前的尝试和上下文,确实很有帮助。Claude 经常主张彻底改造,但我们最后通常还是选择再打一个补丁。

内部版 Claude Tag 中的原始对话,部分内容已隐去。

到了 2 月,CI 作业量的指数级增长再次让服务承压。这一次,我们决定改成并行处理。

要保证测试结果的正确顺序,监听器并不需要一个全局的单一写入者,而是每个软件包各有一个写入者,分别保证代码库各部分的测试结果顺序即可。Claude 为我们生成了代码,将每个软件包的状态拆分为独立分片,每个分片由自己的 worker 处理。

我们同样知道,这次修补也只能撑一阵子,但没想到它只为我们争取到了 29 天。

补丁三:每天重启

到了 3 月,大多数工作日到了下午过半,进程就会触及内存上限。我们再次寻找应急办法,但:

我们还发现,每天重启会让服务逐渐落后得更严重。有几次,服务落后超过一个小时,导致大量作业结果未被监听器记录。

需要说清楚的是,这不代表那些 PR 没有运行 CI,也不代表未经测试的代码被推到了生产环境。实际情况是,监听器漏掉了一些结果,导致测试选择组件使用过时的数据,判断 PR 应该运行和跳过哪些测试。其主要后果是,我们运行了那些本就极不稳定,或已经普遍失败的测试。

重新设计

是时候重新设计服务了——其实早就该这么做。我们采纳了 Claude 的建议:给测试选择服务加一个数据库,准确地说,是一个内存数据存储。这样,我们就把原先由单实例承担的大量内存处理工作转移了出去。

现在,任何监听器 worker 都能处理任何结果,将其写入内存存储中的追加日志(journal),然后继续处理下一项,而不必在自身内存里保留状态。它们因此变成无状态的,也就可以水平扩展。另有一个轻量的独立消费进程,每隔几秒就把日志汇总成每个测试的历史记录,让选择器能够快速查询相关结果历史。

这套分布式架构的运行成本更高,但比起摇摇欲坠的单实例,扩容和内存剖析都容易得多。一名工程师用了三周就完成了这个项目。如果放在一年前,所需时间会接近一个季度。

排队等待处理的作业结果事件数,取每小时最大值。改造前:大多数日子都会积压,且逐周增长。切换并调优后:曲线趋于平稳。

后续还有一些微调,例如日志容量和 worker 数量,大部分由 Claude 自主完成。此后,我们的服务一直保持稳定。

如果重来,我会怎么做

如果带着现在的认识回到 2025 年 10 月,我处理这个项目以及其他项目的方式都会有所不同。

首先,我会把 AI 带来的指数级增长考虑进去。随着每位工程师平均使用的智能体数量上升,以及加速 PR 审批的机制日益成熟,CI 作业量会呈指数级增长。

这也逐渐改变了 Anthropic 的 PR 形态,因为 Claude 更偏好小规模、细粒度的 PR(这也是不该为每个 PR 运行所有测试的另一个充分理由)。结果就是,每天的 CI 作业量变得更多。此外,智能体会在夜间和周末推送代码,因此活动量的基线也被抬高了。但负载依然会突增,因为相当多的 PR 仍由人类工程师推动和批准。

我给工程团队的建议是:无论自建还是采购,都要假设两个季度内,架构承受的负载会达到现在的 25 倍。“过度设计”这个概念开始有些淡化,至少,判定它的门槛正在大幅提高。只要预算允许,现在就可以在初版设计中,按预期规模的 10—20 倍来做规划。

为服务配置监测能力,让它们成为 Claude 的眼睛和耳朵。这样,Claude 就能通过逐步试探、持续优化来解决问题,比我们手动处理更好、更快。尤其要确保,进入和处理完成的 CI 作业数量相等。

从一开始,就把状态放在进程之外。我也会避免把任何关键服务部署成单实例,除非能度量它的运行情况,以及金丝雀发布变更的效果。CI 演进得太快,已经容不得我们继续沿用其他做法。

更多 CI 资源

我还写过一篇文章,介绍我们如何使用 Claude Tag(beta)加速 CI 值班响应。