第一时间捕获有价值的信号
本文译自 Minions: Stripe’s one-shot, end-to-end coding agents。1300 PRs/周听着吓人,但 Stripe 的 codebase 可能是全行业最适合 AI 写的——文档齐、测试覆盖高、模块边界清楚。大部分公司搬不走这套,瓶颈不是 agent 不行,是代码库本身不够 AI-friendly。未来在大公司里面纯 AI 写代码也应该不远了,有严格的流程来管理对应的过程,有人类工程师最后 review 把关。
在整个行业中,智能体编码已经从新奇刺激变成了标配,随着基础模型持续改进,无人值守的编码代理已经从可能变成了现实。
Minions 是 Stripe 自研的编码代理。它们完全无人值守,专为一次性完成任务而构建。Stripe 每周合并的 PR 中有超过一千个完全由 Minions 生成,虽然它们经过人类审核,但其中没有任何人类编写的代码。
我们的开发者仍然可以与 Claude 和 Cursor 等代理进行规划和协作,但在我们最受限的资源之一是开发者注意力的世界里,无人值守代理允许任务并行化。
典型的 Minions 运行从一条 Slack 消息开始,以一个通过 CI 并准备好供人类审核的 PR 结束,中间没有任何交互。我们经常看到工程师同时启动多个 Minions,使他们能够并行完成许多不同的任务。这在值班轮班期间特别有用,可以有效解决可能出现的许多小问题。
在本博客系列的第一部分中,我们将向你展示我们的工程师如何使用 Minions 以及它们能做什么。在第二部分中,我们将深入探讨底层实现以及我们是如何构建它们的。
我们为什么自己构建?
从零开始原型编程与向 Stripe 代码库贡献代码有着根本的不同。
Stripe 的代码库在几个大型仓库中包含数亿行代码。我们的大部分后端使用 Ruby(不是 Rails)编写,带有 Sorbet 类型,这是一个相对不常见的技术栈。自始至终,我们的代码使用了大量 Stripe 独有的自研库,因此 LLM 原生并不熟悉。
风险很高:这段代码在生产环境中处理超过 1 万亿美元的年支付交易量。同时,Stripe 对金融机构有许多复杂的现实世界依赖,以及我们的代码必须遵守的监管和合规义务。
当系统的约束相对较少时,LLM 代理非常擅长从零开始构建软件。然而,在 Stripe 这样规模、复杂性和成熟度的代码库上进行迭代,本质上要困难得多。人类必须建立复杂的心智模型才能在我们的仓库中进行有效的更改,而让代理在其上下文窗口的限制内发展正确的直觉并使用正确的工具是具有挑战性的。
多年来,Stripe 在开发者生产力基础上进行了投资,在开发生命周期的所有阶段支持我们独特的约束——源代码控制、环境、代码生成、CI 等等——因此我们定制的 Minions 工具与这些工具紧密集成。Minions 使用与 Stripe 人类工程师相同的开发者工具,这些工具同样使他们能够在我们的规模上有效运作:如果对人类有好处,对 LLM 也有好处。
使用 Minions 是什么感觉?
Minions 有几个不同的入口点,设计为尽可能符合人体工程学地与 Stripes 所在的位置集成。虽然我们提供 CLI 和 Web 界面来启动 Minions,但工程师最常从 Slack 启动一个。通过标记我们的 Slack 应用,工程师可以直接从讨论变更的线程中启动 Minion,并且它能够访问整个线程和包含的任何链接作为上下文。
如果你是一名从事内部工具的工程师,你可能会用这样的消息启动一个 Minion:

一条调用 Minion 运行的 Slack 消息
Minions 也可以从 Stripe 内部的其他应用程序中调用。我们的内部文档平台、功能标志平台和内部工单 UI 都与 Minions 集成。例如,当我们的 CI 系统检测到不稳定的测试时,我们会创建自动化工单,提示用户使用 Minion 修复问题。

一个带有启动可以修复它的 Minion 按钮的不稳定测试工单
当 Minion 工作时,或者之后,工程师可以在 Web UI 中看到 Minion 做出的决策和采取的行动。

管理 Minion 运行的 Web 界面示例
一旦它完成任务,Minion 会创建一个分支,将其推送到 CI,并按照 Stripe 的 PR 模板准备一个拉取请求。如果代码看起来不错,工程师打开 PR 并请求另一位 Stripe 工程师审核。如果不是,他们可以给 Minion 进一步的指令,完成后它会将更新的代码推送到分支。
工程师也可以在完成的 Minion 运行后手动迭代。虽然我们的北极星是一个没有任何人类代码的 PR,但一个不完全正确的 Minion 运行通常仍然是工程师专注工作的极好起点。
Minions 如何工作?
Minion 有很多阶段,在本系列的第二部分中,我们将有更多关于 Minions 如何工作的细节。许多细节是 Stripe 特定的,但我们确实认为有一些可推广的经验教训。为了勾起你的兴趣,这里有一个简短的时间顺序浏览。
Minion 运行在一个隔离的开发者环境中开始——或者叫”devbox”——这与 Stripe 工程师编写代码的机器类型相同。Devbox 是预热的,因此可以在 10 秒内启动一个,并预加载了 Stripe 代码和服务。它们与生产资源和互联网隔离,因此我们可以在 devbox 上运行 Minions 而无需人工权限检查。这也提供了并行化,而没有像 git worktrees 那样的开销——这在 Stripe 无法扩展。
核心代理循环运行在 Block 编码代理 goose 的一个分支上,这是最早广泛使用的编码代理之一,我们很早就 fork 了它。我们以一种固执己见的方式定制了编排流程,将代理循环和确定性代码交错在一起——用于 git 操作、linters、测试等等——以便 Minion 运行混合了代理的创造力和它们始终会完成 Stripe 必需步骤(如 linters)的保证。
一般来说,Minions 读取与 Cursor 和 Claude Code 等人工操作工具相同的编码代理规则文件,消耗几种不同的代理规则文件格式。然而,Stripe 有许多无条件规则是不切实际的,因此 Stripe 几乎所有代理规则都是基于子目录有条件应用的。
Minions 连接到 MCP,它为可联网的 LLM 函数调用提供了通用语言。这就是它们如何收集上下文,如内部文档、工单详情、构建状态、通过 Sourcegraph 搜索的代码智能等等。实际上,我们甚至在 Minion 运行开始之前就对看起来可能的链接确定性地运行相关的 MCP 工具,以更好地注入上下文。
由于 MCP 是 Stripe 所有代理(不仅仅是 Minions)的通用语言,我们构建了一个名为 Toolshed 的中央内部 MCP 服务器,它托管了 400 多个 MCP 工具,涵盖内部系统和我们在 Stripe 使用的 SaaS 平台。Minions 和其他代理可以连接到全部工具中经过配置但精心策划的子集。
Minions 的构建目标是一次性完成任务,但如果它们没有做到,那么给代理反馈是关键。我们通过几层自动化测试来做到这一点,Minions 可以针对这些测试进行迭代。第一道防线是一个自动化的本地可执行文件,它使用启发式方法在每次 git push 时选择并自动运行选定的 lints。这花费不到五秒钟。
在考虑开发者生产力时,我们寻求”左移反馈”。这意味着,如果 CI 中会失败的任何 lint 步骤在 IDE 或 git push 时强制执行,并立即呈现给工程师,这对人类和代理都是最好的。
如果本地测试没有发现任何问题,CI 会在推送时有选择地运行 Stripe 测试电池中的测试——有超过三百万个。我们的许多测试都有失败的自动修复,我们会自动应用。如果测试失败没有自动修复,我们将其发送回 Minion 尝试修复。
由于 CI 运行消耗 Token、计算和时间,我们最多只有两轮 CI。如果初始推送后测试失败,我们提示 Minion 修复失败的测试并第二次推送,但然后就结束了。在速度和完整性之间存在平衡,LLM 运行多轮完整 CI 循环的边际收益递减。我们认为”通常一次,最多两次 CI 运行——并且只有在我们本地修复了所有可以修复的内容之后”的指导达到了良好的平衡。
简而言之,Minions 配备了我们给人类工程师的相同工具,以及遵循 Stripe 最佳实践编写代码所需的上下文。并且工程师可以并且确实将它们作为其正常工作职责的一部分,以符合人体工程学的方式调用。
第二部分:深入技术实现
Devboxes:预热就绪
为了获得最大效果,大规模无人值守智能体编码需要一个可并行化、可预测且隔离的云开发者环境。人类应该能够给许多代理逻辑上独立的工作。代理应该有干净的环境和工作目录:如果代理相互干扰彼此的更改,就会不必要地在解决问题上浪费 Token。完全自主还要求代理系统地隔离,不能在特权或敏感机器上采取破坏性行动,特别是使用人类的个人凭证时。
要让代理在开发者的笔记本电脑上运行并具备所有这些属性是具有挑战性的。容器化或 git worktrees 可以提供帮助,但它们很难结合,并且从根本上很难构建具有开发者 shell 所有能力但受到适当约束的本地代理。
然而,通过运行在 Stripe 工程师使用的相同标准开发者环境——devbox 上,Stripe 的 Minions 默认获得了这些属性。
Stripe devbox 是一个 AWS EC2 实例,包含我们的源代码并运行正在开发的服务。大多数人类编写的 Stripe 代码已经在通过 SSH 远程连接到 devbox 的 IDE 中生成。在 DevOps 术语中,devboxes 是”牛,不是宠物”:它们是标准化且易于替换的,而不是定制且长期存在的。
许多工程师每个任务使用一个 devbox——一个 Stripe 工程师可能同时有半打在运行。

工程师活动 devbox 列表的片段,包含 Minion 运行
我们希望启动一个新 devbox 的感觉毫不费力,因此我们的目标是让它在 10 秒内准备就绪。为了实现这个”预热就绪”标准,我们主动配置并预热一个 devbox 池,以便在开发者需要时它们已经准备好。这包括克隆庞大的 git 仓库、预热 Bazel 和类型检查缓存、启动在 devboxes 上持续运行的代码生成服务等等。10 秒后,devbox 所有者会有一个签出到 Stripe 所有主要仓库最近 master 副本的盒子,可以立即打开 REPL、运行测试、进行代码更改并类型检查,或启动 Web 服务。
早在 LLM 编码代理存在之前,我们就为人类工程师的需求构建了 devboxes。事实证明,并行化、可预测性和隔离性也是 Stripe 工程师能够最有效工作的非常理想的属性。对人类有好处的对代理也有好处,构建在这个基础设施原语上作为 LLM 代理的天然家园获得了回报。
代理
与已经为人类开发提供动力的 devboxes 相比,我们的代理工具是为 Minions 用例定制构建的。
在 2024 年末,随着编码代理在整个行业兴起,我们内部 fork 了 Block 的 goose——最早广泛使用的编码代理之一——并定制它以在 Stripe 的 LLM 基础设施中工作。随着时间的推移,我们将 goose 的功能开发重点放在 Minions 的需求上,而不是人类监督工具的需求上:那个用例已经被 Cursor 和 Claude Code 等第三方工具很好地填补了,这些工具已经提供给我们的工程师。
实际上,Minions 最独特的方面是没有监督人类。现成的本地编码代理通常优化为作为工程师的伴侣来完成代码更改,通常可以说是”有人在背后看着”。然而,Minions 是完全无人值守的,因此我们的代理工具不能使用面向人类的功能,如可中断性或人类触发的命令来启动或引导代理运行。
另一方面,隔离的 devbox 环境意味着代理不需要确认提示;代理可能犯的任何错误都被限制在一个 devbox 的有限爆炸半径内,因此我们可以安全地以完全权限运行代理并跳过确认提示。
我们还可以精确调整专门针对 Stripe 开发流程的优化。我们根据 Stripe 系统的特殊性进行了许多小优化。一个更大的优化——事实证明对我们的 Minions 实现更基础——是蓝图(blueprint)的概念。
蓝图
编排 LLM 流程最常见的原语是 工作流和代理。工作流是一个通过固定步骤图运行的 LLM 系统,其中图中的每个节点负责总体目标的一个狭窄范围部分,预定义的边控制这些离散节点之间的执行流程。
另一方面,代理通常是一个更简单的”带工具的循环”编排模式,其中 LLM 依靠自己的判断重复调用其可用的工具,并基于这些工具调用的结果——决定下一步做什么。
Minions 用我们称为”蓝图”的原语编排。蓝图是在代码中定义的指导 Minion 运行的工作流。蓝图将工作流的确定性与代理处理未知情况的灵活性结合起来:给定节点可以运行确定性代码或专注于任务的代理循环。本质上,蓝图就像一组代理技能与确定性代码交织在一起,以便特定子任务可以得到最适当的处理。
例如,在驱动 Minions 的蓝图中,有带有”实现任务”或”修复 CI 失败”等标签的类代理节点。这些代理节点有广泛的自由根据输入做出自己的决定。然而,蓝图也有带有”运行配置的 linters”或”推送更改”等标签的节点,这些是完全确定性的:那些特定节点根本不调用 LLM——它们只是运行代码。
因此,蓝图是一种在智能体运行中保证某些子任务确定性完成的方式。Minion 蓝图最终看起来像一个混合确定性代码节点和自由流动代理节点的状态机。

示例蓝图。确定性节点用矩形表示,智能体子任务用云形状表示。
根据我们的经验,编写代码来确定性地完成我们可以预料到的小决策——例如”总是在运行结束时 lint 更改”——在大规模上节省了 Token(和 CI 成本),并给代理少一点把事情搞错的机会。总的来说,我们发现”将 LLM 放入包含的盒子中”复合成了系统级可靠性的优势。蓝图机制使这些子代理的上下文工程变得容易,无论是这包括约束工具、修改系统提示,还是根据手头子任务的需要简化对话上下文。
各个团队还可以设置针对其专业需求优化的蓝图。例如,我们有团队构建自定义蓝图来编码运行棘手的 LLM 辅助迁移,这些迁移无法通过简单的完全确定性代码修改来完成。
上下文收集:规则文件
在像 Stripe 这样的大型代码库中,即使有好的 linters,一个在没有任何指导的情况下被放开的代理可能会在遵循最佳实践或使用正确库方面遇到困难。为了帮助解决这个问题,各种代理规则格式——想想 CLAUDE.md 或 AGENTS.md——允许代理在遍历目录结构时自动”学习”代码库。
由于我们仓库的规模,我们非常谨慎地使用无条件全局规则,否则代理的整个上下文窗口会在代理甚至开始之前就被规则填满。相反,我们几乎完全从限定于特定子目录或文件模式的文件给 Minions 上下文,这些文件在代理遍历文件系统时自动附加。
从我们的角度来看,最好避免规则文件的重复,而是让我们的代理读取人类指导的代理使用的相同上下文。鉴于此,我们标准化为一个支持这些功能的流行规则格式——Cursor 的——并修改我们的工具以允许 Minions 除了以前的自研格式外还读取这些规则。
我们现在还将 Cursor 规则同步为 Claude Code 也可以读取的格式,以便我们三个最流行的编码代理(Minions、Cursor 和 Claude Code)都可以从 Stripe 工程师在我们代码库中搭建的规则文件中的指导中受益。
上下文收集:MCP
从文件系统读取对于静态上下文收集效果很好,但代理经常需要使用联网工具调用动态获取信息。特别是,为了完全注入用户请求,Minions 需要检索内部文档、工单详情、构建状态、代码智能等信息。发布后,模型上下文协议(MCP)迅速成为联网工具调用的全行业标准,我们转向将 Minions 与其集成。
Stripe 已经构建或集成了许多在不同框架上运行的代理:无代码内部代理构建器、在专用服务上运行的自定义代理、第三方现成代理、命令行智能体工具和其他编码代理,以及智能体 Slack 机器人。所有这些代理,不仅仅是 Minions,都需要 MCP 能力,通常包括重叠的通用工具集。
为了支持所有这些,我们构建了一个名为 Toolshed 的集中式内部 MCP 服务器,它使 Stripe 工程师可以轻松编写新工具并使它们自动可被我们的智能体系统发现。我们所有的智能体系统都能够使用 Toolshed 作为共享能力层;向 Toolshed 添加一个工具会立即为我们整个数百个不同代理的舰队赋予能力。
Toolshed 目前包含近 500 个用于内部系统和我们在 Stripe 使用的 SaaS 平台的 MCP 工具。代理在给定一个”更小的盒子”和一组精心策划的工具时表现最佳,因此我们配置不同的代理仅请求与其任务相关的 Toolshed 工具的一个子集。Minions 也不例外,默认情况下只提供故意选择的一小部分工具,尽管每个用户的可定制性允许工程师配置额外的按主题分组的工具集供他们自己的 Minions 使用。
由于 Minions 自主运行并完全自由调用其 MCP 工具,我们还有一个内部安全控制框架,确保它们不能使用其工具执行破坏性操作。然而,作为第一道防线,我们的 devboxes 已经在我们的 QA 环境中运行,因此,Minions 无法访问真实用户数据、Stripe 的生产服务或任意网络出口。这不是偶然的:我们特意构建了隔离的 devboxes,以便人类有一个可以在其中安全实验的环境。但是,就像许多其他事情一样,对人类安全的开发环境对 Minions 同样有用。
……并迭代
虽然我们构建 Minions 的目标是一次性完成它们的任务,但给代理它们可以迭代的自动化反馈以取得进展是关键。Stripe 巨大的预先存在的测试电池——超过三百万个——可以提供这种反馈。然而,虽然推送的分支会在 CI 中运行所有相关测试,但我们不想过度依赖 CI 来获得所有代码反馈。
在考虑开发者生产力时,我们尝试在”左移反馈”原则下运作。这个短语意味着,如果我们知道自动化检查会使 CI 失败,那么最好它也在 IDE 中强制执行并立即呈现给工程师,因为这是向用户提供反馈的最快方式。
例如,我们有预推送钩子来修复最常见的 lint 问题。一个后台守护进程预先计算应用于更改的 lint 规则启发式并缓存运行这些 lints 的结果,因此开发者通常可以在推送时在不到一秒内获得 lint 修复。
Minions 自然也与这个框架集成,因此它们不必通过迭代自动格式化器或类似工具来浪费 Token 或 CI 分钟。我们在代理开发循环蓝图中作为确定性节点运行一部分 linters,并在推送代理分支之前在本地循环运行那个 lint 节点,以便分支有机会第一次就通过 CI。
在本地运行所有测试是不可行的,因此我们还在标准 Minion 蓝图中包含一轮针对完整 CI 套件的迭代。在 Minion 推送更改后,我们运行 CI 并自动应用失败测试的任何自动修复。如果有没有自动修复的失败,我们将失败发送回蓝图代理节点,并给 Minion 再一次机会在本地修复失败的测试。在第二次推送和 CI 运行后,我们将分支发送回其人类操作员进行手动审查。
为什么只有一到两轮 CI?这里在速度和完整性之间存在平衡;CI 运行消耗 Token、计算和时间,并且我们认为如果 LLM 针对无限多轮完整 CI 循环运行,边际收益会递减。我们认为我们的策略在这里的竞争考虑因素之间达到了良好的平衡。
总结
Minions 只是 Stripe 使用 AI 来加速我们工程师的一种方式,但我们认为它们是一个很好的例子,展示了我们如何能够将行业标准概念——如代理工具和 MCP——与我们自己的内部工具和基础设施混合,我们的工程师多年来不断调整这些工具和基础设施以最大化开发者生产力。
无论是通过改进文档、开发者环境还是迭代循环,我们一次又一次地发现,我们在人类开发者生产力方面的投资随着时间的推移在代理世界中获得了回报。
Minions 已经改变了 Stripe 软件工程的格局。随着我们利用行业中最新和最伟大的技术构建我们的代理体验,并适应在 Stripe 规模上工作,我们将继续使它们变得更好。结合我们在人类开发者体验的艰苦战斗中学到的品味和专业知识,我们将使它们成为最好的。