跳到正文
美团技术团队·· 3 小时前精选AI 评分67

美团分布式存储团队分享 AI Coding 实践:AI 评审能做什么、会在哪里出错

如何在“不能出错”的系统里使用 AI Coding?分布式存储实践与边界

AI 导读

美团基础技术部结合分布式存储系统实践,说明 AI Coding 已用于功能开发、测试生成、脚本编写和问题排查,但一个版本改动量增加会推高缺陷绝对数量,因此需要配套 AI 评审、AI 测试验证、AI Debug 和受控自动化。

推荐理由

美团存储团队给出 AI 评审在真实系统中的收益与误判边界,并说明如何用测试和日志把结论变成可验证证据。

正文

AI 能让代码写得更快,但在「不能出错」的分布式存储系统里,如何确保提效不变成风险?本文结合美团基础技术部在 AI 评审、问题排查和自动化开发中的真实实践,分享 AI 能帮上什么忙、容易在哪些地方犯错,以及哪些地方仍需要人来把关。比「多写代码」更重要的,是让代码和结论经得起验证。希望这些经验能为复杂系统中的 AI 应用提供参考,在享受效率提升的同时,守住质量与稳定性的底线。

1. 背景:为什么分布式存储需要更审慎地使用 AI Coding

今年年初以来,随着Agent的普及,AI Coding 在功能开发、测试生成、脚本编写和执行、问题排查这些场景里都已经用得上了:读代码更快,实现和测试可以先让它写一版,研发之外的一部分杂活也能交给它去跑。

效率上去了,质量上的账也要重新算。同样的人力,一个版本里能交付的功能更多,改动的代码也更多,就算 AI 写的代码千行缺陷率和人写的持平,一个版本累计引入的缺陷绝对数量照样会涨。所以 AI Coding 的目标不能只是更快地写代码,提速的同时,代码质量和验证充分性还得比常规研发要求得更高。

分布式存储把这个问题放大了。代码规模大,模块之间关系复杂,一致性、并发、持久化、故障恢复和性能每一项都碰得到。一处看上去很局部的改动,平时可能一切正常,等到节点重启、磁盘异常、网络抖动或者高并发的时候才暴露出来。出了问题,受影响的也不会只是某一个功能,而是上层业务的数据可用性和系统稳定性,严重时还会波及数据正确性。存储服务支撑着大量上层业务,故障影响面大,修复也困难,对缺陷的容忍度极低。

要满足这样的质量要求,光让 AI 生成代码不够,AI 评审(Code Review)、AI 测试与验证、AI Debug 和受控自动化也得一起建起来,AI 生成的代码才能放心地进入研发流程。本文结合存储团队在 AI 评审、AI Debug 和 AI 自动化上的实践,讨论在分布式存储这类复杂度高、容错低的系统里,怎样一边用 AI 提速,一边靠更严格的评审、测试、调试和自动化验证守住稳定性与数据正确性,希望对其他类似系统落地 AI Coding 也有参考价值。

2. AI 评审(Code Review):有益补充,但不能替代人对设计背景的判断

2.1 AI 评审擅长的地方

1. 能注意到跨文件一致性

例:ENABLE_UNITTEST 在三处调用点控制测试 mock 变体的编译分支,但 CMake 迁移把这个名字换成了 WITH_UNITTEST,旧名字再也没被赋过值。单看任何一个文件都挑不出毛病,这个 bug 只存在于文件和文件之间的落差里。

2. 能构造可执行证据

例:AI 指出“这里没做边界检查”之后没有停下,接着构造了一个声明 1 GiB 数值、实际包体只有 5 字节的畸形包,打到真实的解码入口,把崩溃现场抓了出来。

3. 会承认自己证明不了的部分

例:一处疑似的并发竞态,AI 只能靠代码走读找到可疑路径。它明确说,要证实这段交错时序需要 ThreadSanitizer,而当前的工具链跑不了,于是把这条标成“未验证”,没有直接定性为缺陷。

4. AI 对一些常见竞态问题识别率高

例:一处“先读校验、再写推进”、中间没有互斥的更新,多次评审都稳定地识别了出来,给出的修复方向也一致(合并成单个原子操作)。这类常见的并发模式识别得很稳,可以固化成固定检查项。

5. AI 不会漏掉 PR 中被删掉的代码

例:一次重构把某个接口合并成单次 RPC,顺手删掉了成功后清理本地缓存的旧逻辑。AI 指出这会留下一个短暂的时间窗口,期间可能读到过期的旧数据。而人对被删掉的行,本来就最不敏感。

6. AI 会结合存量代码评审增量改动

例:评审之前,AI 会先分析存量代码。改动较大的 PR 里,新代码可能顺手修掉了一个原有问题,人工评审容易把这种顺带修复当成新问题反馈;AI 则会把“原本就有、这次已顺手修掉”的问题单独列出来,并声明不作为问题上报。

7. AI 评审不会局限于代码层面的变更

例:某个 PR 里,三个仓库的构建文件互相用 feature 分支名引用对方,没有固定版本,合入顺序不对或者分支被清理,主干构建就断了。这属于“合并编排”层面的问题,只看 PR 页面的人恰好最容易漏掉,而 AI 把构建文件也当成了评审对象。

2.2 AI 评审容易出错的地方

AI 的误判多数是因为看不到代码之外的东西,比如运维流程、设计决策和团队约定。我们的实践中也看到了AI 评审在修复建议、信噪比、定级和证据取舍上的问题。

1. 代码可达 ≠ 运维可达

例:报告把一处过期 epoch 的代码路径标成了现实风险。人工复核时没有质疑这段代码本身,质疑的是周围的系统到底会不会产生走到这个分支的输入:"EPOCH_NOT_MATCH 只会在迁移分裂+1,分裂之后老的region就不存在了,迁移之后再迁移,src也不存在了,所以 EPOCH_NOT_MATCH 理论上走不到这个分支。"

同样的情况也出现在被报告标成竞态窗口的一天过期时间的问题上:“迁移是人工操作,线上不会存在迁移发起一天还没有成功的,需要人工介入,一天的过期时间就是解决这个问题的”。AI 能建模那个计时器,建模不了包在它外面的人工流程。

2. 把和一般实践不同的有意为之当成了缺陷

例:报告把在生产环境里直接调用 abort() 的一致性检查标成了正确性风险,建议改成记录 Error 日志,不要崩溃。实际我们选择了abort,因为存储系统是正确性高于可用性的,在特定场景下,我们宁可 abort,也不能让这个请求继续而产生非常难处理的脏数据。

3. AI 评审附带的修复建议,本身也需要评审

例:AI 针对一个 32 位溢出给出的修复,是校验声明的 size 和实际收到的字节数是否一致。复核时发现 get_size() 返回的是 int,size 落在 2³¹ 到 2³² 之间会被截断成负数,这条校验本身就会静默失效。个例修好了,同一类问题并没有修好。

4. 廉价的“请确认”条目挤占了真正的信号

例:AI 评审把线程池默认值、重命名的配置项、被移除的二级缓存刷新开关都标成了“待确认”。每一条最后得到的回复,依据的都是团队自己压测得出的历史数据,和代码缺陷无关:"根据压测调整的,后台线程数调低是为了降低对前台的影响。"

5. AI 评审的问题定级失真

AI 缺少一道界限明确的质量门槛:同一类理论上可能的问题,这次被标成最高级,下次又可能被标成不重要。拿到一份 AI 报告,定级没法完全信,为了保险只能把所有问题挨个审一遍,反而很难聚焦到真正高等级的问题上。

6. 把注释、头文件断言和设计文档当作证据

例:两份 AI 评审用的是同一个“场景 × 约束”框架,却给出了相反的结论。一份直接复述头文件里的断言判定实现是对的;另一份去核对了具体的实现,判定是错的。另一个例子是简单依赖设计文档,即迁移设计文档中要求的版本连续性校验,在代码里却没有任何引用。

两份评审看的代码没有区别,区别在于有没有去验证这些“说明”本身。

2.3 让评审结论可执行:构建测试、集成测试与运行时日志

静态评审只能说明“代码里存在这条路径”。这条路径是否真的会出错、已有的保护能不能拦住、线上有没有发生过,还需要另外三类证据来回答。

2.3.1 构建与单测:把推理变成红绿

实验:AI对一个项目全仓评审的 117 个疑似问题,逐条写 gtest 验证,测试在 master 上失败即视为确认。结果:36 条红测试确认,3 条被证伪(误报),41 条只能靠源码确认,37 条未验证。

  • 判决确定。变红的测试集中在三类:输入触发 abort、错误被吞掉后仍然报成功、解码越界。严重度按失败现象来定就行,不用再看 AI 的措辞。
  • 能证伪。有 3 条发现被测试推翻了,这是静态评审很难给出的结论。
  • 能暴露 CI 自身的问题。比如测试报告里硬编码了 failures="0",失败用例也不进汇总,所以要让 AI 自己构建、看原始输出,不能只信 CI 的绿灯。
  • 边界。竞态和 UAF 需要 sanitizer,时序问题需要调度控制,故障注入点、受限主机、时钟偏移这些条件也还有缺口,“未验证”那份清单其实就是测试基础设施要补的课。变红的测试也只能证明代码会出错,证明不了线上会产生那个输入:有一个数据迁移场景,测试确认会有数据过期问题,人工复核后发现,在人工操作的流程里,这个过期恰恰是有意设计的。

2.3.2 贴近运行时的集成测试:主要价值是降误报

比如我们有一个项目的类实现做了特殊考虑,使得我们可以在单进程里就拉起真实的 Raft 多节点集群,全仓验证所有需要“生产集群”的场景。

  • 让已有保护参与验证。一个 AI 发现的问题称 leader 切换会导致删除丢数据,而已有的一条集成测试恰好在删除窗口切主,结果证明是安全的。
  • 裁决分歧。比如“快照能否捕获 split 中间态”,两次 AI 评审的意见相反,走真实的 change_peer 入口、加同步点注入写一个测试,就能直接判定。

fake/mock 的边界就是验证的边界。fake 要走真实入口、用生产配置,并写明替换了什么。

例:分布式 KV 的 hang 告警 Bug 没被测出来,是因为测试用的设置和实际生产发生的不同;网络阶段的丢弃保护在单测里被手工喂了时间戳,线上路径却从来不设置。

2.3.3 运行时日志:从“可能发生”到“已经发生”

实验:把一台存储节点 11 天的 WARN 日志(67.8 万行、34 个调用点)交给 AI 映射回源码,找到 5 个缺陷,对应的同学人工确认全部属实,没有误报。

  • 发现静态审查不会怀疑的问题。例:“fail times 1”11 天只出现一次,说明计数器卡住了,hang 告警永远不会触发。
  • 证伪错误假设。例:一个长达 19 分钟的响应,各阶段耗时之和与总耗时的比值恒为 1.0,由此排除了时钟错误,进而定位到发送阶段缺少丢弃保护。
  • 让定级有依据。例:实测阶段耗时已经达到 1,156 秒,离存储这个耗时的 int32 溢出已经不到 2 倍。

其中两个缺陷并不是日志直接触发的,而是 AI 为了解释日志去读相邻代码时发现的,同样属实,日志提供的真实配置和数量级,可能也让相邻代码的推理更准。这只是一个样本,不能说明总能零误报,但足以支持一个做法:能拿到运行时日志时,评审和 Debug 都应附上。日志的局限是只覆盖执行过的路径,行号也可能与部署版本不符(需按消息文本定位)。

2.3.4 小结

证据 | 能回答 | 回答不了 |

来源:美团技术团队 · tech.meituan.com