夜雨聆风学习资料网

ARTICLE · 1104853

OCR反漂移:我读源码钉出的3层硬契约

OCR反漂移:我读源码钉出的3层硬契约

OCR 反漂移:我读源码钉出的 3 层硬契约

用 Cursor / Claude Code 审 PR,我踩过同一类翻车,至少 3 种:

1. 范围漂:改动一大,文件被挑着看,漏了还像审完了;

2. 落点漂:评论写得很像那么回事,一点开发现行号对不上;

3. 效果漂:提示词稍改,质量就抖,调试像玄学。

原本以为是「模型还不够强」。没想到翻阿里开源的 Open Code Review(OCR)源码后,结论反过来了:漂,往往不是模型笨,是职责边界错了。

OCR 主张「确定性工程 × Agent 混合驱动」。压缩成工程口号就是——反漂移。 但「稳」很容易被误读成「两次运行评论一字不差」。那不是它在追求的,LLM 系统也不该这么承诺。我读下来钉的是更窄、也更硬的一种稳:

契约稳:审谁、挂哪、怎么记账——不随对话心情漂移; 生成可变:深不深、怎么写、漏不漏——仍由模型决定,允许变。

下面按源码机制走 3 层硬契约,再说规模与接入上的诚实边界。(对照公开仓库,非通稿。)


一、漂移从哪来:把「必须一样」的事交给了概率

代码审查同时要两样能力:完备可定位(工程),以及语义判断与取证(模型)。

通用 Agent + Skill 常把两者塞进同一次自然语言对话。Skill 擅长分发工作流,却钉不住这些「必须可复现」的问题:

必须可复现
Skill 为什么钉不住
谁进了评审分母
没有可密封、可审计的文件集合
评论挂在哪一行
行号是模型生成的,不是匹配出来的
预览与真跑是否一致
两边若各写一套逻辑,必漂

日常我只问一句:

这一步再跑一遍,必须完全一样吗? 必须 → 确定性代码说了算;可以变 → 才打开 Agent 的槽位。

「硬的归 Go」稍滑——Go 里也会发起分组、主审、过滤等模型调用。更准确是:

程序拥有状态机与契约;模型只在契约允许的槽位里推理,结果还要通过确定性闸门才能生效。

混合不是五五开和稀泥,而是 所有权清晰的分层。


二、第一层:选集不漂——「审谁」变成可预览的纯函数

选集逻辑被写成 纯函数:不访问网络、不调模型。按固定门控决定去留——二进制、密钥路径、排除/包含规则、扩展名白名单、默认测试路径、单文件 diff 过大等。更早还有对 node_modules、vendor 一类噪声目录的剔除。

关键约束:命令行预览(--preview)与真实审查 必须得出同一套答案。历史上若两边各算一遍,就会漂——那是工程自欺,不是模型幻觉。

通用 Agent 的「我会审这些文件」是对话里的承诺,跑前难核对、跑后难审计。OCR 把「审谁」提成 可预览的函数值:你预览里看到的待审列表,就是后面覆盖率分母的候选。

从前信任
现在信任
模型会按说明书扫全仓
同一输入下,选集函数给出同一集合

范围若再漂,多半是 git 输入或配置变了——可追查;而不是「今天模型心情不好」。


三、第二层:落点不漂——先救证据,再请模型

通用 Agent 习惯「报一个行号」。行号是生成的,天然漂。

OCR 要求评论带上代码片段(尽量贴近新增行),再用算法去 diff 里匹配落点,顺序刻意是:

1. 先在本文件的变更块上滑动匹配;

2. 不行再做 跨文件确定性搜索:片段若唯一落在别的变更文件,就改挂路径和行号(不用模型);

3. 仍失败才让模型重生片段,再匹配;

4. 再失败 → 宁可未锚定,不假装挂准。

过滤误报时也一样:模型只负责指出「哪几条可能是错的」,真正删除由程序执行。

为什么跨文件重挂必须抢在模型前面?

这里有个很典型的失败模式,源码注释写得很锋利——也是我这次读库最「拍桌子」的一段:

Agent 常会读到 别的文件 的代码,却把评论挂在「当前正在审的文件」上——比如声明在头文件、问题在实现里。若此时直接让模型「重新定位」:喂给它的往往是 错误文件的 diff,提示又逼它交回一段代码,它就会在错误 diff 里找「看起来最像」的片段,覆盖掉原来那份真正指向问题的证据。结果是:看起来定位成功了,实际挂在无关行上。

假锚定,比未锚定更危险。 原本以为「再让模型修一次位置」更稳,其实翻车。

所以程序必须先用原始片段在内存里的变更上做唯一匹配;匹配不到、或匹配到多个文件,都 拒绝猜测——猜错只是换一种错。

宁可不挂,也不用模型在错误上下文里「圆」出一个看起来很准的行号。

行号问题从「生成」变成「检索」:可测、可回归。模型仍要写出好的代码片段;写砸了,系统暴露未锚定,而不是用假行号粉饰。


四、第三层:账本不漂——覆盖是状态机,不是客套话

进入待审集合的文件,会在分发前被 密封:分母冻结,之后不能悄悄变少。结束时,每个文件都必须落到可记录的终态(完成、复用、失败、弃权等)。若总 token 预算触发停派,没轮到的记为预算失败,部分结果仍输出——受控截断,不当成功。

这里最容易说满,必须钉准:

保证
不保证
进范围的文件最终有据可查的终态
组内每个文件都被同等深度审完
「没调度」会被记账
浅审后宣布结束,仍可能记成「完成」
可审计的不完备
「审得很全」的叙事

通用 Agent 的漏审,经常是文件 从未进入任何可观测集合。OCR 先消灭这种账外循环:能失败,不能蒸发。

容易误读
更接近实现的读法
密封 = 模型必须认真审完
密封 = 分母不能撒谎
完成 = 质量达标
完成 = 子任务正常收束
失败 = 系统坏了
失败也可以是诚实的预算截断

可审计的不完备,优于不可审计的「好像很全」。

公开基准里更偏准确率、召回可低一些,和这是同一条线:宁可少报噪声、宁可曝光失败,也不用对话里的「我审过了」换虚假安全感。稳定,首先是工程诚实,其次才是分数好看。


五、大了怎么办:切开,是为了不让「少看」成为续命方式

单次对话塞不下时,通用 Agent 往往靠 选择性遗忘 撑下去——这正是范围漂的温床。

OCR 不在同一轮里硬塞全仓,而是切开再审:

• 先减负:明确不该审的进不了待审集合;

• 再分组:文件多时捆成小组(每组有数量与体积上限;失败就退回「一文件一组」)。分组主要看路径和增删行数,不靠把全文 diff 再喂一遍;

• 分组后:每个小组单独审查,吃该组的 真实 git 统一 diff,默认有限并发;

• 仍超预算:可设总 token 上限,后续小组停派并记失败,而不是装成全审完。

切开和账本是一对:切开降低「被迫少看」的压力;账本保证没派到的不能装成功。

先定义可调度的切片与可审计的终态,再让模型在切片里推理。

(分组阈值等细节随版本调整,以项目文档为准。)


六、降方差:规则表与工具面,而不是更长的说明书

规则: 按文件路径匹配到一套审查原则,再和 diff 一起放进提示。所谓「模板引擎」,实质是规则表加占位符——匹配路径短、可测。它降低的是「选哪套原则」的漂移,不是消灭判断方差。

顺带:多文件多规则时会先按路径排序再拼进提示——避免文件顺序抖动导致提示前缀一直变。管道形状尽量稳,模型输出可以变。

工具: 内置工具刻意收得很窄;计划阶段与主审阶段可见工具不同;收尾轮次甚至只允许写评论和宣布结束。少给铲子,乱挖的方差就小一截。

刻意钉死的
允许浮动的
选集、密封分母、匹配落点、删评权、工具面、截断记账
怎么分组、计划写什么、评论怎么措辞、召回深浅

再次强调:OCR 的「稳」不是「评论可字节级复现」。用错度量,就会失望。


七、若用委托模式:契约可拆走,硬闸默认不搬家

还有一种用法:OCR 自己不调模型,只输出「审哪些文件、用哪套规则」;真正推理交给 Cursor / Claude 等宿主(复用已有订阅额度)。

• 仍在的:选集与规则解析;

• 默认不会完整带走的:密封账本、行号匹配与跨文件重挂、误报过滤那条内嵌硬链。

若只理解成「省一把 API Key」,却以为完整继承了上面 3 层反漂移,会高估稳定性。契约碎片 ≠ 整条闸门。


八、一张总图:每一跳归谁管?

git 变更

  → 选集(纯函数,预览 ≡ 真跑)

  → 密封分母(不能蒸发,只能终态化)

  → 分组(可本地 / 可模型,有硬上限与失败回退)

  → 小组内审查(真实 diff + 规则 + 有限工具)

  → 落点闸门(匹配 → 跨文件重挂 → 可选模型 → 程序删误报)

  → 输出 + 可审计覆盖率

与其问「哪里用了 AI」,不如问:

每一跳的所有权在谁手里?出错时,系统是暴露,还是圆谎?


结语

OCR 值得写的,不是又一个审代码命令行,而是它把「反漂移」拆成可辩论的层:

1. 选集不漂——可预览的纯函数;

2. 落点不漂——先救原始证据,宁可不挂不做假锚定;

3. 账本不漂——分母密封,截断也记账;

4. 规模切开——不让「少看」成为唯一续命策略;

5. 管道收束——规则与工具面降低能省则省的方差。

设计自己的 Agent 系统时,我现在会带走两个问题:

这一步再跑一遍,必须完全一样吗? 这一步失败时,我们是暴露,还是圆谎?

必须一样的,钉子钉在代码里;允许变化的,才留给模型。 失败时优先暴露——假完成,才是漂移最体面的伪装。

少把「稳」寄托在更长的 Skill 上。那往往是漂移最肥沃的土壤。


适用 / 不适用

更适合读这篇的人
暂时用不上的人
在用 Cursor / Claude 等 Agent 审 PR,总觉得「稳不住」
只想要一键安装教程、不关心机制
在设计 Agent 流水线,想分清硬契约与软推理
不碰代码审查、也不做 Agent 工程
愿意接受「可审计的不完备」
要求两次运行评论字节级一致

结论就一句: 反漂移靠的是选集 / 落点 / 账本三层硬契约,不是更长的 Skill,也不是更强的模型。


互动

你用通用 Agent 审 PR,最常踩的是 范围漂 / 落点漂 / 效果漂 哪一种?评论区丢一个场景,我可以按你的场景再拆。

觉得有用就 转发 给同组也在用 Cursor / Claude 审代码的同事。 欢迎关注「码畜生活」,后续还会写大 diff 怎么切开、以及委托模式到底稳了什么。


本文基于对开源项目 alibaba/open-code-review 公开源码与文档的阅读整理,非官方通稿;具体阈值与旗标以当前版本为准。

相关学习资料