AI 评审意见定位不准是怎么回事:位置漂移的成因与应对
数据截至 2026-07,各项目能力以官方文档当前版本为准。
评审意见挂错行,绝大多数时候不是模型”看不懂代码”,而是让模型去做了一件它天生不擅长的事——数行号。意见内容和意见位置是两个应该分开处理的问题:内容交给模型判断,位置交给确定性的代码计算,漂移就能压下去一大截。
不少人第一次遇到这个问题的反应是”换个更强的模型试试”。换完往往会发现,意见质量确实变好了,但挂错行的毛病还在,只是错得更自信了。这是因为定位不准的根源在流程设计,不在模型能力档位——只要还是让模型在输出里自己写一个行号数字,它就一定会有一定比例地写偏。
先分清”定位不准”其实有三种
把它们混在一起说,排查就无从下手。实际会遇到的是这三种:
第一种是行号偏移。意见说的问题是真的,代码里也确实存在,但评论挂到了上面或下面几行,有时候挂到了一个空行或者右花括号上。看的人得自己往上下扫两眼才能对上,多来几次就懒得看了。
第二种是指错文件。多文件变更时,模型把 A 文件里的问题写成了 B 文件的路径。这在一个 PR 里同时改了几个结构相似的文件(比如三个 controller、四个测试文件)时特别容易发生。
第三种最麻烦:位置对了,但说的事是错的。评论挂在第 42 行说”这里没做空判断”,可空判断就在第 40 行,模型看到的上下文被截断在第 41 行之后了。这种严格说不算定位问题,但表现出来跟前两种混在一起,用的人只会得出一个结论——“这工具不准”。
排查时先归类,再往下走。前两种是工程问题,第三种是上下文供给问题,解法完全不同。
行号为什么这么容易漂:diff 里有三套坐标
这是最被低估的一点。同一处代码,在一次 PR 评审的链路里至少有三种不同的”行号”:
- 原文件的行号:改动前那个版本里,这行排第几。
- 新文件的行号:改动后那个版本里,这行排第几。
- diff 内部的偏移量:在 unified diff 那段文本里,这行是从 hunk 头往下数的第几行(包含上下文行、
+行、-行,甚至包含 hunk 头本身)。
模型拿到的输入通常是第三种形态的文本,而代码托管平台的评审评论接口要的往往是前两种之一,具体要哪个、字段怎么叫,以各平台官方文档当前版本为准。中间这层换算如果指望模型顺手做掉,它就得在生成文字的同时做一次心算——同时数上下文行、跳过被删除的行、再加上 hunk 头里的起始行号。这类计数任务恰恰是语言模型的弱项,一个 hunk 里有几十行时,偏个一两行是常态。
更隐蔽的一个坑:一个文件里有多个 hunk 时,如果只把几段 hunk 拼在一起丢给模型,它很容易把第二个 hunk 的行当成第一个 hunk 的延续,于是整段意见的行号系统性地偏掉几十行。这种错看起来像”随机乱指”,其实规律性很强。
上下文截断怎么把漂移变成漏审
第三种表现的根子在这里。变更文件一多,把全部内容塞进一次请求就不现实,于是要做切分。切分方式决定了两件事:
切在哪里。按固定行数切,很容易把一个函数从中间劈开。模型只看到后半截,自然会认为参数没校验、资源没释放——因为校验和 try 都在前半截。这类误报的比例,往往比模型”能力不够”造成的误报高得多。
切完谁负责合。同一个问题被切进了两个片段,模型在两边各提一次,最终输出里就是两条内容雷同、行号不同的意见。用的人看到重复评论,对整个工具的信任度掉得很快。
实际能用的做法:切分尽量按语法边界(函数、类、代码块)而不是行数;每个片段带上足够的前后文(比如所在函数的完整签名和入口几行);输出后做一次同文件近距离意见的合并去重。这几步都是确定性代码能做的,不需要再问一次模型。
一种正在被采用的解法:确定性流水线 + 模型判断
把”哪些文件要看、片段怎么切、评论最终落在哪一行”交给写死的代码,把”这段代码有没有问题、问题是什么”交给模型,是目前比较常见的一种拆法。
阿里开源的 open-code-review(CLI 命令是 ocr)就是按这个思路做的:它分析 git diff,把变更文件送给 LLM agent,产出带行级定位的结构化评审意见,架构上是确定性流水线(文件筛选、规则匹配)与 LLM Agent 动态决策两段结合。项目仓库称这种混合架构可以消除通用 agent 常见的”位置漂移”与”覆盖不全”,并且有独立的评论定位模块;仓库还称其内置规则集经过微调,覆盖 NPE、线程安全、XSS、SQL 注入这几类问题。这些描述来自项目仓库自述,本文不替它下”实际效果如何”的结论。
想上手试的话,仓库给出的命令是这样的:
npm install -g @alibaba-group/open-code-review
ocr config provider # 选 LLM 供应商
ocr config model # 选模型
cd your-project
ocr review # 评审已暂存/未暂存的改动
交互式配置会引导选供应商、填 API key,并自动做连通性测试。除了默认的评审模式,还有几种别的用法:
ocr review --from main --to feature-branch # 分支对比
ocr scan --path internal/agent # 整文件审计
ocr delegate preview # 由 AI agent 执行评审
模型供应商方面,仓库自述兼容 OpenAI 与 Anthropic 格式;CI 集成方面列出的是 GitHub Actions、GitLab CI、GitFlic CI、Gerrit 这四个。协议是 Apache-2.0(Copyright 2026 Alibaba)。截至 2026-07-28,GitHub 上 star 约 15,000、fork 约 1,000。
关于规模和开销,仓库自述称在阿里内部经”数万开发者”使用、发现过”数百万个代码缺陷”,并称在更高精度的同时 token 消耗约为 Claude Code 的 1/9。这几条都是项目仓库自己的口径,没有第三方复现的公开基准,看的时候当作”厂商这么讲”来读就好,不宜据此推出谁比谁强的结论——各家 AI 评审工具目前都缺可交叉验证的公开基准,能比的只有机制差异。
自己搭评审流程时可以直接抄的几条做法
不管你用现成工具还是自己写脚本,下面这些都能落到实处:
让模型输出代码片段而不是行号。要求它在意见里原样复述出问题的那一行(或那几行)的代码文本,然后由你的代码在文件里做字符串匹配,反查出真实行号。匹配不上就说明这条意见指向的代码根本不存在——顺手还能过滤掉一部分幻觉出来的意见。
匹配时限定搜索范围。同一行代码在文件里出现多次很常见(比如 return nil)。先在模型声称的位置附近一个小窗口内找,找不到再扩大范围,找到多处就取最近的一处,并把这条意见标记为低置信度。
把不在 diff 范围内的意见降级处理。平台的评审评论接口通常只允许把评论挂到本次 diff 涉及的行上,超出范围的挂不上去。与其让它报错丢失,不如统一收集起来,作为一条总结性评论发在 PR 正文里。
结构化输出而不是让模型写 markdown。让它返回文件路径、代码片段、问题类型、严重程度这些字段,剩下的排版由你的代码拼。模型一旦开始写自由格式的评审报告,行号就会混进散文里,解析和校验都变难。
同一文件的意见合并去重。距离很近、描述高度相似的两条合成一条,保留更具体的那条。
怎么验证定位到底准不准
自我感觉不算数,得有个能重复跑的口径。可行的最小做法:
- 从自己项目的历史 PR 里挑二三十个,覆盖单文件小改、多文件重构、纯删除、纯新增文件这几类形态——不同形态的漂移率差别很大,只测一类会得出过于乐观的结论。
- 人工标一遍每个 PR 里真实存在的问题及其行号,作为基准答案。
- 跑工具,把输出的每条意见按”位置准确、位置偏移、指错文件、内容错误”四类归档。
- 分开统计两个数:位置准确率(挂对行的意见占全部意见的比例)和覆盖率(基准答案里被发现的比例)。这两个数往往此消彼长,只看一个会被误导。
有了这套基准,换模型、改切分策略、调提示词,效果变好还是变坏就有据可依了,不用靠感觉吵架。
说说局限
有几件事得讲清楚:
自建回归集的样本量通常只有几十个 PR,统计意义有限,换个技术栈、换个代码风格,数字可能就变了。它的价值在于横向比较自己的几套配置,不适合拿去当公开结论。
跨文件的问题基本没法用行级评论表达好。比如”这个接口改了签名,但另外三个调用方没跟着改”,硬要挂到某一行上反而别扭,这类更适合走总结性评论。
还有一类问题工具是发现不了的:业务逻辑对不对。代码写得规范、没有空指针、SQL 也拼得安全,但这个折扣该不该给这个用户,只有懂业务的人能判断。AI 评审在这上面帮不了忙,它更像是把明显的低级问题先筛掉,让人把注意力留给真正需要判断的地方。
最后,工具本身也在变。上面提到的命令、集成清单、协议这些以项目仓库当前文档为准,隔几个月回去看一眼比照着旧文章照抄稳妥。
小结
评审意见定位不准,先分清是行号偏移、指错文件,还是上下文被截断导致的内容错误,三者解法不同。行号会漂的根本原因是 diff 里存在原文件、新文件、diff 内部三套坐标,让模型在生成文字的同时做换算,本来就不可靠。可靠的做法是让模型输出代码片段、由确定性代码反查行号,并对匹配不上、不在 diff 范围内的意见做降级处理。想知道改动有没有效果,得自己攒一套覆盖多种 PR 形态的回归集,分开统计位置准确率和覆盖率。至于各家工具谁更准,目前没有可交叉验证的公开基准,厂商自述的数字看看就好,别当结论用。