实践来了|我们使用 AI Agent重构单体应用学到了什么
例如,在 Cursor,我们看到许多客户使用规划模式,并借助更大、更慢的模型(例如 GPT 5.4 或 Opus)生成具体的 plan.md 文件,根据需要编辑该文件,然后使用更小、更快、更擅长编码的模型(例如 Composer)进行实际构建。在1Password,我们将代理工具应用于一个数百万行的Go单体应用,在本篇博客中,我们将分享哪些方面有效,哪些方面失败,以及这些经验对在生产系统中采用AI
AI Agent 越来越多地被用于重构大型代码库,但许多团队并不清楚它们在哪些方面成功,在哪些方面失败。在1Password,我们将代理工具应用于一个数百万行的Go单体应用,在本篇博客中,我们将分享哪些方面有效,哪些方面失败,以及这些经验对在生产系统中采用AI的团队有何启示。
情况是这样的:1Password 运行着一个名为 B5 的大型 Go 单体应用。多年来,它一直是我们产品的基础,并且在生产环境中,无论在可靠性还是可扩展性方面,都持续表现良好。
如今,旨在以高请求率和低延迟支持人工和代理驱动的工作流程。随着我们不断添加和增强其功能,我们需要更清晰的服务边界和更独立的扩展特性。这意味着随着时间的推移,我们需要对系统的各个部分进行演进,同时确保我们已建立的隐私性、性能、可靠性和安全性。
制定切实可行的方案来解决这个问题,听起来像是 Agent 擅长的任务。
就我们而言,这意味着应用智能重构:使用 AI 智能体来分析、规划和执行整个代码库的更改,从依赖关系映射到系统分解。
这个故事还有另一种版本,其中智能工具分析大型代码库,生成清晰的提取计划,然后服务分解从此遵循可预测的路径。
故事的部分情节确实如预期般发展。我们构建了一个智能体工具链,它分析了数百万行代码,并为我们提供了清晰且可靠的提取顺序。这项工作极大地改进了我们对系统分解的思考方式。
然而,最终更有价值的是我们把这些工具应用到实际生产环境中的实际变更后所学到的东西。这部分往往被忽略,但它才是真正决定这种Agent重构方法是否有效的关键所在。
构建分析层
我们首先要解决的问题是排序。在一个大规模处理敏感数据的系统中,提取顺序是保证系统正确性的一个关键因素。如果顺序错误,可能会引入难以察觉且后期更难纠正的隐蔽故障。
为了解决这个问题,我们构建了一个 Agent 工具链,该工具链结合了几个不同的真理来源。
我们使用Agent来理解代码结构,使用 SQL 解析来识别数据依赖关系,并使用 DataDog MCP 集成来引入运行时耦合数据。这些方法结合起来,为我们提供了域所有权图、耦合图和优先级提取顺序。
输出结果基本符合经验丰富的工程师对系统的预期。它建议首先从 Vault 入手,因为它拥有独立的 API、数据集和安全边界;其次是 Billing,然后是 AuthN 和 AuthZ,而 Identity 则作为核心。
一种特别有效的模式是利用智能体构建确定性工具,而不是依赖它们进行持续的解释。在这种情况下,智能体帮助编写了SSA分析器的部分代码,然后分析器生成了一个可复现的领域图。这种区别至关重要,因为一旦工具存在,你就可以基于一个稳定的产物进行推理,而不是争论模型认为系统是什么样子。
这项工作的一个意外好处是,我们为支持分析而添加的工具也提高了我们在 DataDog 中的端到端交易可见性,这在本项目之外也很有用。
![]()
寻找人类与智能体的比例
在进行提取分析的同时,我们对代码库中一项长期存在的清理任务应用了相同的方法。
我们的 Go 服务器过去MustBegin会启动数据库事务,并在失败时引发 panic。这种行为在早期开发阶段是合理的,因为它可以快速发现数据库问题。但在生产环境中,当连接超时或请求上下文被取消时,这种行为就不合适了。在这种情况下,返回一个干净的错误信息才是正确的做法。
此次迁移需要更新生产代码和测试代码中的 3000 多个调用点,因此它一直被搁置在待办事项列表中。
我们采用的方法结构非常严谨。我们使用 SSA 生成了每个调用站点的确定性清单,将这些站点分类为少量模式,并为每种模式定义了明确的模板。在此基础上,我们编写了一个详细的 playbook,其中描述了Agent应如何执行迁移,包括常见故障模式列表以及何时停止并升级问题的明确说明,而不是靠猜测。为了扩展执行规模,我们使用 git worktree 并行运行多个代理,从而确保变更的隔离性。
执行本身只用了几个小时。大部分时间都花在了构建工具和编写规范上。
![]()
这个比例至关重要。当工作内容完全明确且有界时,代理既快速又准确。当它们遇到超出规范范围的情况时,系统会将其揭示出来,而不是试图隐式地解决它。
代理需要更强的约束。
然后我们转向了一个更复杂的任务,那就是从单体架构中提取服务。
即使对于规模相对较小的服务,这类工作也需要在模式演化、读写路径、部署顺序和共享数据契约等方面进行协调变更。这些都是相互依存的决策,必须按正确的顺序执行。我们发现这项任务的主要问题在于排序和不变式。
例如,Agent会在更新负责插入新的代码之前,尝试回填 UUID 列。即使底层系统设计良好,这种操作顺序也会导致数据静默丢失。在其他情况下,它会将共享表视为新服务独立拥有的表,这会在部署时造成冲突。即使我们提供了关于顺序和约束的明确指令,这些模式仍然存在。
我们还观察到一种反复出现的行为,我们内部将其称为“推测”。当代理缺乏足够的上下文信息时,它会用一些看似合理但未经验证的假设来填补空白。例如,在某个案例中,它推断某个特定的标识符格式是 ULID,并通过一系列更改来传播这一假设,最终导致整个会话不得不回滚。
行之有效的模式是使用代理生成确定性工件,然后强制执行这些约束条件。例如,在 Cursor,我们看到许多客户使用规划模式,并借助更大、更慢的模型(例如 GPT 5.4 或 Opus)生成具体的 plan.md 文件,根据需要编辑该文件,然后使用更小、更快、更擅长编码的模型(例如 Composer)进行实际构建。”——Tido Carriero,Cursor 工程副总裁
对于这类工作而言,生产力提升确实存在,但幅度较为有限。实际上,我们看到的提升幅度大约在20%到30%之间。Agent发挥了作用,但他们并不能取代细致的协调和审核。
这表明我们在 1Password 正在经历一场更广泛的转变。人工智能Agent正在成为系统中一种新型的角色,它引入了传统模型无法处理的不确定性、持久性和规模性。这不仅对工程工作流程产生影响,而且对跨系统的访问和信任管理方式也具有重要意义。
为使用代码中的人工智能Agent团队提供的经验教训
其他团队可以从 1Password 的经验中汲取许多教训,而且这些经验的应用范围远不止于此。
第一课:智能重构的瓶颈不在于代码生成
Agent在读取代码、分析结构和制定变更方面非常高效。难点在于管理具有顺序约束或难以逆向的决策序列。这包括模式变更、部署顺序和共享状态边界。如果这些问题处理不当,无论生成的代码多么简洁,系统都会失败。
第二课:非决定论需要谨慎控制。
语言模型具有非确定性,这正是它们的优势所在。然而,在生产环境迁移中,这种可变性却成为风险来源。我们行之有效的模式是使用代理构建确定性工具,例如分析器和清单,然后将后续工作限制在这些输出之上。即使代理本身并非完全可预测,这种方法也能构建一个稳定的基础。
第三课:不完整的规范会导致隐式规范。
当智能体缺乏足够的上下文信息时,它会自行填补空白,但这种填补方式往往在局部上合理,却在全局上是错误的。解决此问题的唯一可靠方法是明确定义规则,包括不变式、顺序约束以及针对任何超出既定模式的情况的清晰升级路径。
另一个重要的转变在于如何看待服务覆盖范围。目标并非让Agent处理所有可能的情况,而是让它能够自信地处理已知的模式,并在遇到模糊不清的情况时迅速升级处理。这就需要明确自动化停止的界限,以及何时需要人工判断介入。
第四课:并行处理只有在隔离问题已经解决的情况下才有效。
同时运行多个代理可能非常有效,但前提是变更彼此独立且冲突已从结构上消除。否则,最终只会增加不一致的可能性,而不是缩短执行时间。
这将如何影响 1Password 内部使用Agent的方法
我们正在整个工程组织内推广智能体工具,并清楚地了解它在哪些方面能够发挥作用。
我们知道,当问题被明确定义时,智能体才能发挥最大效用,而确定性工具则提供了实现这一点所需的约束条件。工程师仍然负责定义系统边界、建模依赖关系以及确保顺序正确。
这些见解将帮助我们转变分配给工程师的工作性质,让我们明白,最具杠杆作用的活动不是编写代码或提示模型,而是以安全可预测的方式定义系统。
我们正在研究的问题,包括在实时流量下分解生产系统和构建多智能体执行架构,目前还没有成熟的方案。我们正在实时构建这些方案,而大部分有趣的工程工作也正是在这个过程中进行的。
更多推荐




所有评论(0)