删代码比写代码难。Uber工程团队在官方博客里给出的判断是:feature flag全量上线后,残留的条件分支、参数、配置和测试会变成技术债,不仅拖累工程师,也让AI Agent读到更多死代码,会话更长、成本更高。
他们为此搭了一套叫AutoClean的系统,用AI Agent加确定性代码变换来清理已全量上线的过期flag。累计清理了1.6万多个flag、超过100万行代码。核心思路被拆成四句话:确定性的事交给工具,需要判断的事交给Agent,正确性交给CI加多层验证,最后的业务决策留给人。
清理看起来是"搜索加删除",实际是一条传播链。把flag内联成true只是第一步,接下来要判断函数参数能不能删(得查所有调用方)、旧分支是否已经不可达、配置字段还有没有别处依赖、测试该删还是该改写。Uber团队的结论是:静态工具擅长确定性改写,AI的价值在这些传播决策上。
三阶段架构
第一阶段是改写。Agent具备文件搜索和编辑能力,布尔代数和条件化简则通过MCP调用Uber自己的确定性引擎PolyglotPiranha来完成——条件化简一旦出错,就会悄悄改变线上走哪个分支。Agent循环执行,直到没有可清理的为止。
第二阶段是修CI。以monorepo的全量CI为准,而不是只看本地测试。失败先交给便宜的确定性修复器处理(未使用的默认值、lint),处理不了的再交给Agent修复器,比如判断测试该删还是改写,反复推送直到变绿或者用完重试次数。这里的关键约束是权限收窄:测试修复器只能改测试文件,否则Agent会去改生产代码来让测试通过。
第三阶段是验证。CI绿了也不等于正确,Agent可能删掉本该保留的测试,也可能停得太早没清理干净。所以先跑静态检查,再交给Agent验证层CodeCourt。
CodeCourt:最值得借鉴的设计
这套验证层的做法是,把观察到的Agent失败模式写成明确的正确性属性,每条属性由一个专门的审查Agent并行检查:
- 只删不增
- 行为语义不变
- 不偏离目标,不顺手删其他flag
- 不留Agent痕迹,比如推理注释
全部通过才进入人工审查,任意一条失败就直接放弃这个diff。团队早期试过用单个Agent端到端审查,效果不如拆成多个聚焦的审查者。
背后的原因在于:指令一多,长任务Agent就容易遗漏约束,倾向于只追求"CI通过"这种最直接的信号。所以AutoClean把约束从执行循环中剥离出来,放到验收端去检查。
整套流程里,工具负责确定性改写,Agent负责传播决策与修复,CI和CodeCourt负责拦截错误,人只保留最后的业务判断。Uber团队把这套组合称为用AI删代码的Agent工程实践。
热门跟贴