Open Code Review 项目解析知识图谱 / 分组目录 / 核心架构 / 规则引导分派

规则引导分派

核心架构进阶方法机制G-A · 核心架构

What · 是什么

确定性文件选择/过滤/分束机制:用规则而非语言模型决定审什么、怎么分组
分派阶段完全由确定性代码驱动:文件选择、过滤、分束均由规则与代码完成,不依赖 LLM 决策
文件选择确定要审哪些文件,过滤确定哪些排除并记录原因,分束将文件按规则组织成可并行处理的单元
规则采用四层链:custom(用户自定义)> project(项目级)> global(全局)> system(系统内置),上层规则优先级高于下层,形成可覆盖的规则体系
规则匹配使用模板引擎实现,比纯语言驱动(让模型读规则文本再判断)更稳定——同一规则在不同输入下结果确定可复现
injectDiffMap 机制将被过滤文件的 diff 保留注入,供 file_read_diff 工具在需要时读取,实现『过滤但不封死』

Why · 为什么

原理 · Principle

文件选择与过滤是强约束环节:哪些文件该审、哪些规则适用属于可枚举逻辑,用确定性代码实现可保证行为一致、可单测,而 LLM 在此类任务上表现不稳定

四层规则链让不同范围的规则可叠加覆盖:用户可对单个文件/目录写 custom 规则覆盖全局默认,工程上形成类似 CSS 层叠的规则语义

模板引擎匹配规避了语言驱动的两个问题:prompt 长度膨胀(规则文本全塞进上下文)与判断随机性(同一文件两次判断可能不同)

injectDiffMap 解决『过滤 vs 可读』的矛盾:被过滤文件不进主审查流程,但审查中若确实需要其 diff 上下文,file_read_diff 仍可读取,避免一刀切

How · 怎么做

分派顺序:先按确定性逻辑选择 diff 中的文件集合 → 逐文件执行 whyExcluded 过滤链 → 按规则链匹配结果决定归属与排除原因
规则链匹配:从 custom 开始逐层查找适用规则,命中即生效,custom 层可显式覆盖 project/global/system 层的默认行为
过滤链顺序:binary(二进制)→ 用户 exclude(显式排除)→ 用户 include(显式包含可覆盖 exclude)→ 扩展名白名单(unsupported_ext)→ 默认路径黑名单(default_path)
每条被排除文件记录 ExcludeReason(user_exclude/unsupported_ext/default_path/deleted/binary),输出时可供用户审计为何没审某文件
注入机制:被过滤文件若被后续规则或工具需要,通过 injectDiffMap 写入其 diff 片段,file_read_diff 按需读取,不占主上下文预算

案例 · Cases

代表性案例
项目配置了 global 规则『忽略 vendor/ 与生成代码目录』,但某次审查需要核对 vendor 中一个依赖文件的行为差异——custom 规则 include 该文件,过滤链中 include 覆盖 exclude,该文件进入审查且其余 vendor 文件仍被排除
多语言仓库(Go+JS+Python):system 层扩展名白名单只放行支持语言,二进制与 .min.js 等压缩产物在 binary/unsupported_ext 层被排除,用户可在 project 层加白名单覆盖

反模式 · Anti-patterns

正反实例论证 · Evidence

正例 · Pro
include 覆盖 exclude:把被误伤的文件拉回审查
# 规则配置(custom 层优先级最高)exclude: ["vendor/**", "generated/**"]include: ["vendor/github.com/x/lib/parse.go"] # 覆盖 exclude,该文件进入审查# 其余 vendor 文件仍被排除(ExcludeReason=user_exclude)
四层规则链 custom>project>global>system + include 覆盖 exclude 的明确语义:用户既能批量排除,又能精确放行特例,过滤结果可审计
反例 · Con
include/exclude 优先级颠倒(exclude 覆盖 include)
# 反例:若 exclude 优先级高于 includeexclude: ["vendor/**"]include: ["vendor/github.com/x/lib/parse.go"] # 无效,文件仍被排除// 用户将无法把误伤文件拉回审查,只能改全局规则
优先级颠倒后用户层覆盖能力失效:默认规则误伤的文件无法放行,custom 层形同虚设,规则体系的可定制性被破坏
正例 · Pro
injectDiffMap 保留被过滤文件 diff,供 file_read_diff 按需读取
injectDiffMap["vendor/github.com/x/lib/parse.go"] = diffText // 过滤但不封死// 审查主流程中模型需要该文件上下文时file_read_diff(path="vendor/github.com/x/lib/parse.go") // 从 injectDiffMap 读取,不重跑 git
被过滤文件不进主流程、不占主上下文预算,但需要时可精确读取其 diff,避免一刀切丢弃后反复重跑 git 命令
反例 · Con
过滤后直接丢弃被过滤文件的 diff
// 反例:过滤即删除delete(diffMap, entry.Path) // 后续任何工具都无法再读到该 diff// 模型需要上下文时只能重新执行 git diff → 重复开销
丢弃后审查中一旦需要该文件上下文就必须重跑 git 命令,产生重复 IO 与延迟;且 Review 需要 diff 佐证时无据可依

实践资产 · Assets

规则
内置系统规则(四层规则链最底层,首匹配优先)
internal/config/rules/system_rules.json · 1488 B
源码节选
agent.go 核心节选:Run 流水线 / dispatchSubtasks / executeSubtask / REVIEW_FILTER
internal/agent/agent.go · 3451 B

相关边