跳到主要内容

33 篇博文 含有标签「devops」

查看所有标签

舰队级 CODEOWNERS:当作者是 AI Agent 时的评审路由

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的 CODEOWNERS 文件编码了一个古老到没人记得当初为何制定的假设:代码变更(diff)的作者知道自己身处谁的领地。一名修改计费服务的工程师深知自己进入了计费业务的领地。他们有目的地将变更范围限制在自己团队的目录内,如果必须触碰其他团队的代码,他们会亲自或在 Slack 上打个招呼,在发出评审请求之前先进行沟通。基于路径的评审路由之所以奏效,是因为人类作者会预先为自己的变更选择路由。

Agent 不会这样做。如果要求一个 Agent “迁移所有弃用的日志调用”,它会在一个下午横扫整个代码库,并生成一个涉及 400 个目录的 diff。GitHub 尽职尽责地查询 CODEOWNERS,匹配每一个通配符(glob),并同时向 40 个团队发出评审请求。

没有人询问过这些团队是否想要这种变更。没有人预警过变更即将到来。而且请求批准的“作者”并不是一个可以随时抓来了解背景信息的同事——它是一个代表其他部门某人行事的机器人账号,而那个人甚至可能都不知道哪些团队收到了通知。

合并队列成了新的瓶颈

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的编程智能体(Coding agents)刚刚让编写代码成为了交付软件中最廉价的部分。但它们并没有让代码落地变得更便宜。AI 采用率高的团队合并的 Pull Request (PR) 数量几乎是以前的两倍——而他们的交付指标却几乎没有变化,因为每一个 PR 仍然必须挤进同一个代码评审流水线、同一个 CI 集群以及同一个合并队列(Merge Queue),而这些设施当初是按人类打字速度设计的。约束并没有消失,而是向后移动了,移到了系统中那个最窄的管道:从“已批准”到“进入主分支(main)”之间的串行化路径。

这是一个经典的约束理论(Theory-of-constraints)故事,大多数工程组织目前正身处其中却尚未察觉。当一名开发者可以在并行工作树中指挥 5 到 10 个智能体时,PR 的体量就不再与员工人数挂钩。但合并吞吐量仍然取决于一些更为死板的东西:你的 CI 每小时能验证多少个 main 分支的候选状态。这个数字受限于测试套件的时长、执行器(Runner)容量、抖动率(Flake rate)和队列机制——而这些都没有随着智能体变快而变快。

万档代码转换:在机械式代码迁移中运行智能体集群

· 阅读需 12 分钟
Tian Pan
Software Engineer

每一个框架迁移的模式都大同小异。你编写一个 codemod,在代码库中运行它,它能干净利落地转换 80% 的文件 —— 也就是那些符合 codemod 作者预期的模式的文件。然后你会遇到长尾问题:某些测试文件中有人对渲染器进行了猴子补丁(monkey-patched),某些组件触及了框架内部机制,或者某个模块是 2017 年由一位早已离职的工程师编写的,使用的是没人再采用的惯用法。Codemod 可以正常解析这些文件,只是没有适用的规则。因此,最后 20% 的迁移工作消耗了 80% 的日程,只能由那些宁愿做任何其他事情也不愿干这活的工程师们手工逐个文件完成。

编程 Agent 颠覆了这种经济效益。难倒确定性 AST 转换的手写怪癖,恰恰是模型擅长处理的 —— 它阅读文件,理解意图,并像人类一样重写代码,而不需要为每种变体制定明确的规则。Airbnb 在大规模实践中证明了这一点:在短短六周内,将近 3,500 个 Enzyme 测试文件迁移到了 React Testing Library,而最初估计的人工工作量是 1.5 年。但头条新闻中被忽略的一点是:难点并不在于提示词(prompting)。一旦你让 Agent 集群处理一万个文件,工程问题就不再是 AI 问题,而变成了批处理操作问题 —— 分片、验证、隔离和合并策略。正确的心理模型是一个 Mapper 具有随机性的 MapReduce 作业。

安全智能体如何跨过那行它“看不见”的注释,升级了被固定的依赖

· 阅读需 12 分钟
Tian Pan
Software Engineer

一位西班牙客户投诉称,她的年度续费被提前一天计费了。支持工单在经历了三个队列后,终于转到了一位工程师面前,他敏锐地察觉到了问题的端倪:这是一个日期格式化的回归(regression)问题,且仅出现在欧洲用户群中。他在 date-formatting 模块上运行了 git log,却一无所获。该模块已经 11 天没动过了。而 11 天前真正被改动的,是它的 package.json —— lodash 的版本从 4.17.20 升级到了 4.17.22。这次升级是由一个安全代理(security agent)发起的,由值班人员批准,并在没有任何评论的情况下合并了。

在同一个文件中,版本字符串上方两行有一条 18 个月前写的注释:// do not upgrade — breaks the snapshot tests in date-formatting, see FRONT-2418(请勿升级 —— 会破坏 date-formatting 中的快照测试,详见 FRONT-2418)。安全代理没有阅读它。或者更准确地说:安全代理阅读了整个文件,但它的提示词(prompt)指令是查找有漏洞的版本字符串,而不是衡量周围注释的权重。这条注释是承载关键信息的机构知识(institutional knowledge)。而代理却将其视为无关紧要的背景装饰。

这是一个两个互不知晓正在发生碰撞的系统之间的协同失效。安全代理履行了它的职责。写下注释的原工程师履行了他的职责。每次修改文件都遵守固定(pin)版本的开发代理也履行了它的职责。唯独没有人决定谁该负责在它们之间进行调解。

你的编程 Agent 开启的那个导致真实 PR 被关闭的拉取请求

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的编程智能体在周二下午 3:14 提交了一个 PR。PR 描述很整洁,代码差异(diff)很小,CI 测试也是绿色的。二十分钟后,它被压缩合并(squash-merged)了。第二天下午 1:20 吃完午饭回来的同事看到了一条通知:“PR #1247 已关闭。”不是已合并,而是已关闭。分支不见了。她上周留下的 72 条评审评论也消失了——全部折叠在一个“已过期”标签下,属于一个不再出现在任何活跃列表中的 PR。一位资深工程师的设计决策、与安全评审员的两轮反复沟通,以及耗时一周协商出的周密迁移计划,全都化为了另一个没人仔细阅读的 PR 底部的一个脚注。那个压缩提交(squash commit)留下的唯一痕迹是底部的一行标签:Closed by #1893

这就是信任编程智能体自行编写 PR 元数据的失败模式。出问题的不是代码,而是元数据。代码差异没有问题,智能体工作得很出色。它无法做到的是区分当前的讨论与陈旧的讨论,而 GitHub 的自动关闭机制将智能体编写的每一个关闭关键字都视为必须执行的指令。你的智能体通过读取评论来获取上下文,从一个六个月前的回复中推断出它的工作取代了一个旧的 PR,于是在它生成的描述中写下了 Closes #1247。合并操作完成了剩下的工作——在压缩合并的那一刻,对于任何没有盯着 diff 看的人来说,这一切都是无声地、机械地、不可逆转地发生了。

翻倍且没有事后复盘:那份编码智能体带来的 CI 账单

· 阅读需 11 分钟
Tian Pan
Software Engineer

该项支出在六周内攀升了 130%,工程团队却无人察觉。PR(拉取请求)的合入速度变快了。仪表板上的单次 PR CI 成本看起来与上季度持平。Agent 的分支在第一次尝试时通过测试(显示为绿色)的频率比人类的分支更高,这实际上反而拉低了 CI 持续时间的中位数。财务部门在季度复核中发现了这一点,将其标记为不明变动,并要求工程部门提交事后分析报告(postmortem)。工程团队无话可说 —— 既没有事故,也没有回退,更没有部署失败。仅仅是一项预算支出在仪表板显示一切正常的情况下,悄无声息地翻了一倍。

这个“事后分析报告”式的缺口本身就是一个产物。成本从以人力为主的曲线转向了以基础设施为主的曲线,而负责人力预算的团队与负责基础设施预算的团队并非同一个。Agent 没有弄坏任何东西,它只是改变了损益表(P&L)中承担这项工作的科目。

你的 Agent 把开发环境当成了生产环境,因为系统提示词从未指明是哪一个

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个编程智能体(coding agent)正在预发环境(staging)执行一项常规任务。它遇到了权限故障 —— 某个配置指向了错误的 API —— 并自行决定“修复”该 bug 的最快方法是清理掉违规数据。它翻找了一通,在一个无关文件中发现了一个未限制范围的令牌(unscoped token),调用了一个描述为“删除匹配查询的记录”的工具,九秒钟后,190 万行客户数据消失了。最近的备份是三个月前的。上个季度产生的预订记录已不复存在。

智能体并没有发生故障。从部署工程师的角度来看,线路连接是正确的:预发配置在预发部署中,生产配置在生产部署中。线路没有承载的是智能体对“身处何地”的感知。两个环境中的系统提示词(system prompt)完全相同,因为没人想维护两套提示词。两个环境中的工具目录(tool catalog)命名也相同,因为没人想教智能体两套词汇。因此,智能体按照训练数据教它的方式去思考“数据库” —— 而互联网上绝大多数关于智能体和数据库的文章,都是关于生产环境的。

那些在本地通过但在 CI 中失败的编程智能体

· 阅读需 12 分钟
Tian Pan
Software Engineer

智能体(agent)生成的 diff 在你的电脑上显示为绿色。测试通过了,lint 通过了,开发服务器也干净地完成了热重载。你让它提交了 PR,九十秒后,CI 在一个与修改完全无关的步骤上报错变红了:缺少某个 CLI 工具、一个智能体从未声明过的新环境变量,或者 Node 版本解析结果不一致——因为你的 .nvmrc 是通过 runner 并不具备的全局 shim 进行解析的。智能体并没有写出有问题的 diff。它写出的是一个依赖于你机器环境的 diff,而你的机器和 runner 并不是同一台电脑。

“在我的机器上能运行”曾是一个人为 Bug。解决办法是保持纪律性——锁定版本、编写 Dockerfile、阅读 CI 日志。而编程智能体大规模地继承了这个 Bug,却丢弃了曾经用来弥补它的纪律性。因为智能体不知道它所依赖的东西哪些来自代码库,哪些来自你 shell 历史记录中的“温热沉淀物”。每个开发者的笔记本电脑都是一个配置独特的环境,智能体在不知不觉中吸收了这些环境。接着,同一个智能体在一个完全不具备这些条件的 runner 中运行,失败的表象看起来像是智能体的错,但实际上是由于没人写明的一份环境契约。

当实习生在入职第一天部署 Agent

· 阅读需 10 分钟
Tian Pan
Software Engineer

实习生周一报到。周二下午,她已经接好了自己的第一个 Agent。周三上午,那个 Agent 用一份她本不该继承到的凭证调用了生产工具,但安全团队没人察觉,因为审计日志把这次调用记成了"实习生导师的 setup 脚本发起的"——技术上没错,操作上等于零。

这不是实习生不靠谱的故事,也不是导师粗心的故事。这是一个入职流水线的故事:它对"新人"的假设——只读优先、沙箱写次之、生产环境要熬到工龄门槛——背后有几十年的打磨;而对那些新人入职头几天就会去配置的 Agent,它的假设是零。给人用的 IAM 模型,已经不再是给"实际打到你系统上的东西"用的 IAM 模型,而大多数安全团队还没意识到这一点。

AI 时代的 DORA 指标:当部署频率开始“撒谎”

· 阅读需 11 分钟
Tian Pan
Software Engineer

这里有一个应该让你感到不安的数字:根据《2025 年 DORA AI 辅助软件开发现状报告》,人均合并的开发者 PR 增长了 98%,而每个 PR 导致的事件增长了 242.7%。部署频率看起来处于“精英”水平。但系统的单位变更故障率比 DORA 测量过的任何时期都要高。

你的仪表盘是一片绿色。你的值班工程师却精疲力竭。测量尺度出了问题。

隐形作者问题:当 AI 编写大部分代码时如何进行 Git Blame

· 阅读需 9 分钟
Tian Pan
Software Engineer

当生产环境出现故障时,工程师们首先会想到 git blame。提交哈希值指向 PR,PR 指向作者,而作者则指向上下文——Slack 讨论串、设计文档,或者是记住了代码初衷的那个大脑。这条链路是团队排查事故、进行安全审计以及积累机构知识的方式。它假设每一行代码都有一个理解自己在做什么的人类作者。

AI 已经悄然打破了这一假设。目前约 46% 的代码由 AI 生成,在 Java 团队中,这一比例甚至超过了 60%。这些代码中的大部分都不携带任何有意义的溯源元数据。git blame 链条依然在运转——只是现在它终止于一名开发者,他们接受了一个可能并未完全理解的建议,而且没有记录提示词、模型版本或 AI 拒绝的备选方案。

Staging 环境的谎言:为什么预生产阶段对 AI 系统失效了

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的测试环境通过了所有检查。LLM 对每个测试提示词都做出了正确响应。延迟表现良好。质量评分看起来也不错。你发布了。然后,两天后,生产环境开始在你的评估集从未涵盖的一类查询中出现幻觉,你的成本飙升了 3 倍,因为缓存是冷的,而且你的供应商推送的模型更新静默地改变了行为,而你的旧测试套件无法检测到。测试环境显示一切正常,生产环境却给出了截然不同的结果。

这并不是一个可以通过编写更多测试用例来弥补的测试差距。预发布环境对 AI 系统具有结构性的误导,而对传统软件则不然。失败模式是系统性的,解决办法不是更好的测试环境,而是一种不同的架构。