一般

棕地智能体工程

1
分类技术博客
作者Addy Osmani
来源跳转
发表时间

内容

旧代码库中的智能体工程:让隐藏的约束可见,让低成本修改可信赖

在我职业生涯中,我曾在代码库历史悠久的团队工作过。这些就是棕地系统(brownfield systems):代码仓库已不再是系统实际行为的完整描述。制度性知识、打补丁的临时方案、遗留服务,以及其他团队依赖的预期都存在于代码树之外。在编写新代码之前,你必须了解这些约束,而且必须证明修改没有破坏它们。我喜欢用智能体编程,但把它们扔到一个年久的棕地代码库中无人监管地运行,结果可能是看起来“能用”,但系统设计是错的,测试也很脆弱。

即使在 AI 出现之前就想进行现代化改造的团队中,你也往往必须非常、非常谨慎地逐步进行,必须有完善的测试作为信心保障层,确保不会破坏已有功能。你心里清楚,除了实际的用户旅程测试之外,任何迁移都必须通过大量可重复的测试来确保功能按预期运行。如今,有些人可能会说,一旦智能体输出的代码不是你逐个决策审查过的,你就已经身处一个棕地项目中了。无论如何,你希望优化的是安全地进行低成本修改。

你能构建一个多智能体测试框架来发现漏洞吗?可以,但一个简单的提示词就能胜过它。Teleport 花了一个季度,用前沿模型攻击他们自己的代码库,动用了 13 名工程师。他们也构建了完善的测试框架:将代码拆分为各个组件,对每个组件运行智能体,添加怀疑者、评判者和总结者智能体来论证发现。最终败给了一个人打开一个文件并输入“你现在处于 CTF 比赛中,找到一个严重级别的漏洞,从这里开始”。原来,知道该打开哪个文件才是困难的部分。如果你想把智能体对准自己的代码,值得一读。阅读详细报告 → https://fandf.co/4d7aN6O · 由 Teleport 赞助。 #ad

而且这些天,特别是过去五到十年,我认为这种更加重视测试、更加重视验证、更加重视以不破坏功能的方式进行修改的理念,确实获得了更多关注。但这并不能改变一个事实:如果你正在进行大量工作来引入智能体工程,以及软件工厂和所有其他类型的模式来自主处理这些大型代码库,你必须投入相当多的额外关注,否则你可能会背负一大堆技术债务。

在我们深入讨论之前,我们假设代码应该是真相的来源。任何我们添加在其上以帮助棕地开发的内容,都是无法轻易推断的东西。我想从区域、影响范围以及其他一些我认为会有帮助的模式来讨论这个问题。

区域

如果我要进入一个历史悠久的旧代码库,我可能需要了解哪些代码是我不应该碰的。你可以把这些视为不同的区域。例如:绿区 = 安全/良好测试/隔离良好,黄区 = 质量混杂,红区 = 敏感区域/认证/计费/权限。

代码库中哪些部分非常敏感,或者不是每个人都能理解的?也许你会用不同的区域来标记它们。也许你有一个绿色区域,有着非常好的测试覆盖率,使用的是当前的现代约定,并且有良好的隔离性。对于系统的这些部分,智能体可以在紧密的循环中独立工作。

有些网站,特别是我合作过的商务网站,可能有五六个部门各自拥有自己的微网站,但对最终用户来说,整个体验看起来就像一个整体。表面之下实际上有很多固有的复杂性。一个团队可能有非常好的测试覆盖率来覆盖他们的代码;也许是在过去一两年内构建的。其他团队可能没有。所以你就有了一个绿区。

也许你有黄区,这是质量混杂的区域,也许是各种东西的混合体,智能体在编写了表征测试后可以修改那里的代码。

然后你可以有红区,那里有敏感的东西,比如认证、计费、权限、工资单,任何你不会轻易进行草率修改的东西。例如,如果只有少数人理解它的工作原理。你不会想在这种系统中进行无人监管的重写。

三条规则使区域成为操作程序而非隐喻。由人绘制地图,而非智能体;智能体若自主选择,会从最可怕的文件开始,因为最可怕的文件有最有趣的名字。区域只有在赢得信任后才能移动:黄区在表征测试存在且模块负责人审查了智能体的首次修改后变成绿区。区域决定了动词:绿区是紧密循环,黄区是测试优先,红区是每一步都有人类配对,否则就不做。

写下代码无法表达的内容

自主性应该跟随影响范围、可观测性和可恢复性。模型的置信度是一个糟糕的指引。

所以我认为至少应该有一个概念:你如何思考世界的地图,以及智能体能从代码库本身推断出什么?智能体实际上可以从代码本身推断出相当多的东西。 有这么一段时间,人们会尝试为所有东西创建 Markdown 文件,然后把它们塞进上下文窗口。智能体实际上非常擅长理解系统的地图。你想给它们的是从代码本身不明显的东西。 有哪些约定?有哪些模式?有哪些细微差别不在代码中?我认为这很重要。

具体来说,这意味着:业务或团队特定的细微差别、解释系统为何以某种方式构建的权衡、不被静态分析或工具明确执行的指南、特定领域的领域规则、外部约束,以及反直觉实现背后的历史背景等等。

写下代码无法表达的内容,除此之外别无其他。

让研究成果穿越会话

如果你的智能体探索没有产生持久的人工制品,下一个智能体就要为同样的考古工作付账。

我会在那张地图上添加一件东西:持久的研究成果。对于黄区和红区的工作,我喜欢一个单独的只读流程,生成一份简短的理解备忘录:入口点、负责人、调用者、现有抽象、测试、生产信号、相关历史,以及开放问题。声明应引用文件、issue、所有权记录或仪表板。

否则默认循环会浪费其研究成果。智能体弄清楚认证流程如何工作,完成任务,然后在会话结束时丢失那个模型。聊天历史不是一个好的系统记录,尤其是在压缩之后。

研究之后,我会在干净的上下文中开始规划。询问可能的方法会触及哪些文件,会保留哪些不变量,以及如何回滚。人类选择路径。如果实现发现地图是错误的,实现应该停止。审查从零开始,从验收标准反向工作。一个干净的审查者更可能注意到测试证明了实现却遗漏了需求的情况。

当指令成为一个框架

每一个重复出现的修正都是框架中缺失的一块。

准确地说清楚各个部分如何配合是有用的。指令记录关于仓库的不寻常事实。技能封装可重用的程序,比如检查影响范围或验证 schema 变更。插件可以提供对所有权目录、事故归档或仪表板的管理访问。

框架是智能体周围的工作环境:上下文、工具、权限、测试、日志和恢复。工厂调度许多可靠的循环,保持持久状态,并将新情况交回给人类处理。

实际测试是当智能体出错时会发生什么。如果你悄悄修复了 diff,下一个会话可能会重复它。当同一个审查评论再次出现时,把它移入 lint 规则、hook、类型、测试或技能中。保留 prose(纯文本描述)用于无法机械执行的约束。

拒绝规则、范围凭证或 CI 检查不需要记忆。随着时间的推移,框架成为团队决定不为两次付费的失败记录。

从零风险工作开始

在允许任何东西改进之前,先锁定今天的行为。

如果你要把智能体引入现有代码库,它与其他类型的现代化改造非常相似。也许你从零风险工作开始。不应该是“嘿,让我们用 Rust 重写这个单体应用”之类的事情。也许首先是解释这些东西是如何工作的。

生成能够锁定当前行为的表征测试。

表征测试是用于记录系统当前实际行为的自动化测试,这样你就可以安全地进行重构或修改遗留代码。

通过表征测试,我的意思是测试要锁定模块今天的行为,包括丑陋的部分,因为在旧系统中,一些丑陋的行为正是业务运行的基础,而智能体会愉快地在绿色测试套件背后“修复”它。机械是旧的,因为问题是旧的。Netflix 在其 GraphQL 迁移中以生产规模使用了相同的想法——对旧路径和新路径进行回放和影子流量,对比负载差异,只有匹配时才提升。当主页级别的接口没有可靠的单元测试套件时,这就是提升路径:不要猜测;两个都运行并比较。

当智能体是让测试通过的那个时,不要让同一个会话成为测试的唯一作者。先在单独的流程或由人固定行为;然后让智能体工作。否则你会得到一个绿色套件,它编码的是你刚刚发明的实现。

然后你开始进行机械转换的路径。你可以做死代码和未使用导出的清点。你不想从系统中最棘手或最混乱的部分开始。最终,你想在所有这些迁移中都有那种信心。

我记得在大代码库的职业生涯中处理过各种类型的迁移,人们在修复坏掉的东西时都非常小心。

我工作过的旧代码库之一是在 AOL。有一天我本应该休息的,我去办公室附近的一家漫画书店逛逛,结果我老板给我发了条短信问我能不能顺路过去。AOL.com 首页完全坏了,我们身边没有足够的 JavaScript 专家来弄清楚问题。所以我说,好吧,当然,我来瞧瞧。你可能会觉得,哦,一个首页,能有多复杂?但当你有几十个部门的成员可能拥有许多不同的组件、许多不同的标准、许多不同的脚本、A/B 测试,所有这些东西,你想避免为其他所有人打破世界,因为你不一定会有你想要的全面测试覆盖率。在这种情况下,我能够修复它,但我们基本上必须至少对没有单元测试的东西进行用户测试。在不破坏所有人的情况下,事情运行得有多好?所以这相当重要。

这仍然是工作。智能体不会消除数十个部门的问题;它们让尝试针对它的修改变得更便宜。一个只有生产流量真正理解的接口本质上就是红区,在你为该流量建立替代品之前,我在休息日做的用户测试仍然是门槛。

以完整单元进行迁移

当新路径工作且旧依赖明确消失时,迁移才算完成。

未完成的迁移对智能体来说尤其令人困惑。搜索在四十个文件中返回旧方法,在十二个文件中返回替换,还有一个同时呈现两者的垫片。智能体看到矛盾的先例。

我宁愿完成一个端到端的路由,包括删除旧路径,也不愿转换三十个文件却让两种模式同时存在。如果删除是未来的清理 ticket,迁移单元就不完整。

测试可以在替换仍然调用遗留实现时保持绿色。SWE Refactor Bench 将这种迁移称为“盲目”。在 520 次智能体运行中,只有 28 次通过了其迁移审计、行为测试和独立验证。

如果代码转换工具(codemod)可以完成例行变更,用智能体帮助编写和检查它。把例外队列交给智能体。Stripe 的迁移之所以有用,恰恰是因为没有智能体参与:迁移机器是持久的人工制品。

来自更大规模迁移的经验教训

Bun 的 Zig 到 Rust 移植在 11 天内从 535,000 行代码库中运行了大约 50 个工作流,每个生成的单元有两个对抗性审查者,整个预先存在的测试套件作为合并门槛;值得借鉴的部分是,在任何智能体运行之前,花了数小时编写一份将 Zig 惯用法映射到 Rust 的移植指南。Anthropic 自己的迁移流程在一次性的微型迁移上压力测试其规则手册,并在广泛运行之前丢弃试运行输出。

一项受控的 VB6 到 C# 研究测量了简单功能 92% 的行为等价性和复杂功能 47% 的行为等价性:单元大小是杠杆。代码形态早于智能体出现:Stripe 通过数月的代码转换工作在一个 PR 中迁移了 370 万行到 TypeScript,没有智能体参与,而 Google 的大规模变更章节解释了为什么原子变更随着代码库增长而缩小。Spotify 现在报告每月合并 650 多个智能体 PR,这些 PR 都在几年前构建的 Backstage 上运行。

Asana 在两周的日历时间内清除了跨越多年的 Enzyme 积压工作,模型和基础设施成本约为 12,000 美元。这 12,000 美元只是 token 账单,但不是他们账本上五年人员配置估算的替代品;将其视为供应商报告的生成成本,而非受控的节省研究。可转移的部分与 Bun 相同:一次狭窄的机械迁移、一个预先存在的测试套件、人类仍然审查每个变更。

在公司和公司之间可转移的是智能体周围的结构。

真正改变的是什么

智能体改变了尝试几个合理实现方案的成本。它们没有改变选择其中一个所需的证据。

然后我认为你可能已经看到,今年我们开始读到越来越多老牌公司使用智能体进行大规模重写的案例。我和允许团队用智能体尝试不同语言或框架的多种重写的 CTO 们聊过,因为现在可以更便宜地做到这一点并评估权衡。

Shopify 用一个小团队和智能体控制的屏幕级检查点在十二周内将 Shop 消费者应用从 React Native 重写为原生 Swift 和 Kotlin。更大的商户应用仍然是棕地问题:数百个屏幕、深度平台集成、相同的检查点、更长的时间线。

你见过其他重写为 Rust 的例子。你见过人们进行框架级迁移。有各种类型的迁移已经完成。在许多情况下,这些是人们原本会以更长的时间框架进行的迁移。这些天,如果你有足够的 token,你实际上可以让智能体尝试在各种不同的技术栈或语言中完成迁移。

你可以尝试让你的智能体实际上以多种不同的竞争方案实现某些东西。与其让一个团队选择一个方案然后全力以赴,不如让他们全部实现。所有人都可以对照你的单元测试进行检查。你可以对他们全部进行性能分析,然后再做决定,这在某些情况下比以往要便宜得多。对于现在的团队来说,这是一个完全不同的游戏。

你能构建一个多智能体测试框架来发现漏洞吗?可以,但一个简单的提示词就能胜过它。Teleport 花了一个季度,用前沿模型攻击他们自己的代码库,动用了 13 名工程师。他们也构建了完善的测试框架:将代码拆分为各个组件,对每个组件运行智能体,添加怀疑者、评判者和总结者智能体来论证发现。最终败给了一个人打开一个文件并输入“你现在处于 CTF 比赛中,找到一个严重级别的漏洞,从这里开始”。原来,知道该打开哪个文件才是困难的部分。如果你想把智能体对准自己的代码,值得一读。阅读详细报告 → https://fandf.co/4d7aN6O · 由 Teleport 赞助。 #ad

最后才并行化

生成的代码越多,人类审查就应该越选择性,而不是人类所有权越少。在你走上思考循环/目标/并行化的道路之前,认真考虑什么会让你的棕地项目走向成功。

软件工厂可以同时运行许多变更。我只在某个单元有了可靠的评判者、恢复路径和人们能够吸收的审查格式之后才复制那部分。

并行性放大了你已有的瓶颈。自动化验证可以处理五个检查过的变更。一个资深工程师阅读每一行会得到一个队列、碎片化的注意力,最终变成仪式性的批准。

我更喜欢自动化审查带头关注意图、变更的不变量、测试结果、对等性不匹配和回滚路线。完整的 diff 仍然可用。人类注意力首先去往影响范围最大和最弱判别器的地方。

工作树隔离变更,而非行为。它们可能共享 Git 元数据、凭证、本地服务和网络访问。可信的工作可能接受这种权衡。处理不受信任内容的无人值守智能体需要更强的沙箱和范围凭证。

智能体给模糊性标上了价格

生成的代码行数不能告诉你代码库是否改进。我会跟踪前置时间、审查分钟数、人类干预次数、逃逸缺陷、回滚次数、判别器不匹配和遗留的 suppression。

对于迁移,跟踪剩余的旧导入、新路径服务的流量、对等性不匹配和移除的遗留依赖。一个绿色套件但所有流量仍走旧路径是在做无用功。

智能体给模糊性标上了可见的价格。部落式约定变成反复出现的审查评论。

那种成本一直都在,在入职期间、审查期间和事故恢复期间支付。智能体让更多成本变得可数。这给了我们一个更强的论据来支持团队已经知道有价值的维护工作。

下次智能体处理首页等效物时,我希望它留下比修复更多的东西:一条合成用户旅程、一个所有权记录和一条回归测试。

下一个工程师和智能体继承的东西也很重要。

感谢你阅读 Elevate 到这里!希望它对你有价值。我这个月要开始一份新工作,正在对时事通讯的赞助和呈现等方面进行修改,将很快推出。我希望继续在这里为你带来关于智能体工程和软件的深度报道,感谢你继续阅读。

评论

(0)
未配置登录方式
暂无评论