LazyLLM Git PR Review 完整流程说明
本文档描述 lazyllm.tools.git.review 模块的完整 PR review 端到端流程。入口为 runner.py 中的 review() 函数,依次经过预分析(架构解析、历史规范提取)、静态 Lint 分析、六轮 LLM 分析(RHunkScan hunk 审查 → RPrDoc PR 设计文档 → RArchReview 架构师评审 → RAgentVerify Agent 验证 → RMod 修改必要性分析 → RDedupMerge 合并去重)、与主链并行的场景推断(RScene)和调用链 bug 分析(RChain)、测试覆盖检查(RCov),最终将 final_comments + rchain_issues + rcov_issues 合并后发布到 GitHub / GitLab / Gitee / GitCode。
源码目录:lazyllm/tools/git/review/(runner.py、pre_analysis.py、rounds.py、coverage_checker.py、constants.py、checkpoint.py、utils.py、poster.py、lint_runner.py)。
1. 整体架构一览
1.1 模块职责
| 模块 | 职责 |
|---|---|
runner.py |
主入口:编排所有子模块;diff 拉取与截断;策略决策(_ReviewStrategy);meta warning 生成;清理 clone 目录;发布评论 |
pre_analysis.py |
仓库 clone;架构文档生成(analyze_repo_architecture);本地模式 arch 分析;历史 review 规范提取(analyze_historical_reviews);PR 摘要;RAgentVerify Agent 工具集构建 |
rounds.py |
Review 轮次核心:RHunkScan hunk 级分析、RPrDoc PR 设计文档、RArchReview 架构师评审、RAgentVerify ReactAgent 验证、RMod 修改必要性分析、RDedupMerge 合并去重 |
coverage_checker.py |
RCov:测试覆盖检查——识别可测符号、按依赖分组、grep 测试文件、评估覆盖缺口(LLM,并行) |
lint_runner.py |
静态 Lint 分析(不调用 LLM),结果直接注入 RDedupMerge |
checkpoint.py |
断点续传:PR 级 checkpoint;阶段枚举 ReviewStage;失效控制(resume_from) |
constants.py |
上下文预算常量;BudgetManager;issue 密度控制;diff 启发式压缩 |
utils.py |
LLM 调用封装(重试/QPS);diff 解析;JSON 解析与修复;进度报告 |
poster.py |
拉取已有 PR 评论;提交平台 review(批量 submit_review + 逐条 fallback) |
1.2 端到端流程图
review(pr_number, ...)
└─ Checkpoint 初始化
└─ 获取 PR 信息 & diff
└─ _compute_diff_stats + _decide_review_strategy
├─ diff 超出 max_diff_chars → 按文件边界截断 → 生成 meta warning issue
└─ _run_pre_analysis → arch_doc + review_spec + clone_dir + agent_instructions
└─ _pre_round_pr_summary → pr_summary
└─ _fetch_existing_pr_comments
└─ _run_lint_analysis → lint_issues
├─ _run_review_pipeline (RHunkScan → RPrDoc → RArchReview → RAgentVerify → RMod → RDedupMerge) → final_comments ┐ 并行
└─ _run_rscene_rchain │
├─ infer_usage_scenarios (RScene) → usage_scenarios │
└─ _rscenario_call_chain (RChain) → rchain_issues ┘
└─ _run_coverage_check (RCov,独立线程,动态超时) → rcov_issues
└─ 合并:final_comments + rchain_issues + rcov_issues → all_comments
└─ 清理 clone 子目录
└─ _post_review_comments → 平台
1.3 阶段顺序总览
| # | 阶段 | 主要产物 |
|---|---|---|
| 0 | Checkpoint 初始化 | pr_dir、checkpoint.json、resume_from 软失效控制 |
| 1 | Diff 拉取与截断 | diff_text(按文件边界截断至 max_diff_chars)、hunks |
| 2 | 策略决策 | _DiffStats、_ReviewStrategy(RAgentVerify 参数自适应) |
| 3 | Meta Warning | 截断时插入 source='meta' 的 issue |
| 4 | 预分析 | arch_doc、review_spec、clone_dir、agent_instructions |
| 5 | PR 摘要 | pr_summary |
| 6 | 已有评论 | existing_comments(供 RDedupMerge 去重) |
| 7 | Lint 分析 | lint_issues(直接注入 RDedupMerge,不过 LLM) |
| 8 | RHunkScan | hunk 级静态审查 issue 列表 |
| 9 | RPrDoc | PR 设计文档(结构化,9 节) |
| 10 | RArchReview | 架构师视角全局设计评审 issue 列表 |
| 11 | RAgentVerify | ReactAgent 验证:验证 RHunkScan+RArchReview issue + 发现新跨文件问题 |
| 12 | RMod | ReactAgent 修改必要性分析:逐文件标记不必要的改动 |
| 13 | RDedupMerge | 确定性去重 + LLM 合并 + Lint 融合 → final_comments |
| 14 | RScene | 场景推断(与 RHunkScan-RMod 并行):推断 2-4 个典型使用场景 → usage_scenarios |
| 15 | RChain | 调用链 bug 分析(RScene 完成后):场景驱动的 bug + 可用性 issue → rchain_issues |
| 16 | RCov | 测试覆盖检查:识别未覆盖符号,评估缺口 → rcov_issues(绕过 RDedupMerge 去重直接追加) |
| 17 | 发布 | 合并 final_comments + rchain_issues + rcov_issues,提交平台 review;更新 checkpoint UPLOAD 阶段 |
| 18 | 清理 | 删除 {pr_dir}/clone/;保留 checkpoint.json |
2. 入口与策略决策(runner.py)
2.1 review() 函数签名
def review(
pr_number: int,
repo: str,
token: str,
llm: Optional[Any] = None,
language: str = 'cn',
post_to_github: bool = True,
clone_target_dir: Optional[str] = None,
arch_cache_path: Optional[str] = None,
resume_from: Optional[str] = None,
clear_checkpoint: bool = False,
backend: str = 'github',
max_diff_chars: int = 120000,
) -> Dict[str, Any]
language:review 评论输出语言,'cn'为简体中文,'en'为英文。post_to_github:False时只返回结果字典,不发布评论,适合调试。arch_cache_path:架构文档缓存目录,跨 PR 复用,避免重复 clone 分析。resume_from:指定从某个ReviewStage重新开始(软失效,不删除 checkpoint)。clear_checkpoint:强制清除 checkpoint,从头开始。
2.2 策略决策(_ReviewStrategy)
根据 diff 规模自动调整 RAgentVerify 的参数,避免大 PR 消耗过多 LLM 调用:
| PR 规模 | 判断条件 | large_file_threshold |
max_files_for_ragent_verify |
max_chunks_per_file |
|---|---|---|---|---|
| 大 PR | >3000 行 或 >50 文件 | 100 字符 | 10 | 2 |
| 中 PR | >1000 行 或 >20 文件 | 150 字符 | 15 | 2 |
| 小 PR | 其余 | 200 字符(默认) | 20(默认) | 3(默认) |
2.3 Diff 截断策略
当 diff 超过 max_diff_chars(默认 120000)时,按文件边界截断(不在 hunk 中间切断),并在结果中插入一条 source='meta' 的 warning issue,告知用户哪些文件被跳过。
3. 预分析(pre_analysis.py)
预分析在所有 review 轮次之前运行,为后续 LLM 提供高质量的仓库上下文。它分为四个子阶段,均支持 checkpoint 缓存。
3.1 仓库 Clone(_fetch_repo_code)
- 使用
git clone --single-branch --depth 1(无 pin_sha 时)或完整 clone(有 pin_sha 时)将仓库拉取到本地。 - 若目标目录已存在完整 clone,优先复用并尝试
git pull更新,避免重复下载。 - 若指定了
pin_sha(PR head commit),通过_pin_clone_to_sha将 clone 切换到精确 commit,确保 review 的代码与 PR 一致。 - clone 目录保存在
arch_cache_path/{owner_repo}/clone/下,跨 PR 复用。 - 本地模式(
review-local):跳过 clone。若arch_doc为空,通过_run_local_arch_analysis直接对本地 repo 路径调用analyze_repo_architecture,无需网络 clone 也能获得架构上下文。
3.2 架构文档生成(analyze_repo_architecture)
架构文档是整个 review 流程的"地图",让 LLM 在不读取全量代码的情况下理解仓库结构。
生成流程(五步):
-
结构化快照(
_collect_structured_snapshot):扫描 clone 目录,收集目录树(2 层)、顶层__init__.py、子包__init__.py、核心模块文件(module.py、flow.py等)、依赖配置文件(pyproject.toml、requirements.txt等)、AGENTS.md等,拼接为结构化快照文本,受_ARCH_SNAPSHOT_BUDGET字符预算约束。 -
DeepWiki 集成(可选):若安装了
mcp包,通过 DeepWiki MCP 服务(_fetch_deepwiki_summary)拉取仓库的预索引架构摘要,注入快照末尾作为背景参考。始终使用base_repo(上游仓库)而非 fork 地址,避免 fork 无数据。DeepWiki 数据可能有 1-3 个月延迟,注入时附带 stale 提示。 -
大纲生成(
_arch_generate_outline):将快照喂给 LLM,生成 N 个章节的大纲(有AGENTS.md时最多_ARCH_OUTLINE_MAX_SECTIONS_WITH_AGENT节,否则_ARCH_OUTLINE_MAX_SECTIONS节)。大纲固定包含两个末尾章节: - 倒数第二节:Concurrency & Multi-User Conventions(线程安全、ContextVar、锁约定)
-
最后一节:Testing & Examples(测试文件位置、示例脚本) 大纲缓存到
arch_outline,避免重复生成。 -
批量填充章节(
_arch_fill_all_sections):对每个章节,先用_arch_collect_snippets_for_section从仓库中 grep 相关代码片段,再将多个章节打包为一个 LLM 调用(_arch_fill_batch_llm),减少 LLM 调用次数。每章节内容上限 3500 字符,缓存到独立 key(arch_section_{title})。 -
Public API Catalog(
_build_public_api_catalog):LLM 识别仓库的公开 API 文件列表,再通过正则提取各文件的公开符号(函数、类、常量),生成 JSON 格式的 API 目录,追加到架构文档末尾。支持 Python、Go、TypeScript、Java、Rust、C++ 等多语言。
最终产物:架构文档 + Public API Catalog,同时生成 arch_index(章节摘要索引)和 arch_symbol_index(符号→章节映射),供 RHunkScan/RArchReview/RAgentVerify 按需检索。所有产物缓存到 arch_cache_path/{owner_repo}/,同一仓库的后续 PR 直接复用。
AGENTS.md 支持:若仓库根目录存在 AGENTS.md,其内容作为 agent_instructions 注入所有 LLM prompt,让项目维护者可以自定义 review 规则(如"忽略 tests/ 目录的命名风格")。
3.3 历史 Review 规范提取(analyze_historical_reviews)
从仓库历史 PR 的 review 评论中提炼出项目特有的代码规范,形成 review_spec。
工作原理:
- 拉取最近 N 条已合并 PR(默认 30 条)的 review 评论。
- 过滤掉机器人评论、过短评论(<20 字符)、纯表情评论。
- 将评论按文件类型分组,喂给 LLM 提炼规范:
- 项目特有的命名约定
- 禁止的模式(如"不允许直接 print,必须用 lazyllm.LOG")
- 必须遵守的架构约束
- 测试要求
- 规范缓存到
arch_cache_path/{owner_repo}/review_spec.json,跨 PR 复用。
这一步使 review 能够"记住"项目历史上反复出现的问题,避免重复提出已知规范。
3.4 PR 摘要生成(_pre_round_pr_summary)
在四轮 review 开始前,先用一次 LLM 调用生成 PR 摘要(pr_summary),包含:
- PR 的核心目的(新功能 / bugfix / 重构)
- 主要改动模块
- 潜在影响范围
PR 摘要作为上下文注入 RArchReview、RAgentVerify 的 prompt,帮助 LLM 从"PR 意图"角度评审,而不是孤立地看每个 hunk。
3.5 RAgentVerify Agent 工具集(_build_scoped_agent_tools_with_cache)
为 RAgentVerify 的 ReactAgent 构建一套文件系统工具,所有工具都限定在 clone 目录内:
| 工具 | 功能 |
|---|---|
read_file_lines |
读取指定文件的指定行范围 |
read_file_skeleton_scoped |
读取文件骨架(类名、函数签名,不含实现) |
grep_symbol |
在仓库中搜索符号定义或引用 |
list_directory |
列出目录内容 |
工具调用有步数上限(默认 15 步),防止 Agent 无限探索消耗预算。
4. 六轮分析(rounds.py)
六轮分析是 review 的核心,每轮有明确的职责分工,形成"广度扫描 → 设计文档 → 架构评审 → 深度验证 → 修改必要性 → 合并去重"的递进结构。与此同时,RScene(场景推断)和 RChain(调用链 bug 分析)与主链并行运行,最终结果在 RDedupMerge 之后合并。
4.1 RHunkScan:Hunk 级静态审查
目标:对每个 diff hunk 进行细粒度的代码质量审查,发现具体的 bug、逻辑错误和安全问题。
工作原理:
- 将 diff 按文件和 hunk 分割,每个 hunk 附带上下文(前后各 N 行)。
- 对每个 hunk 独立调用 LLM,prompt 包含:
- 完整 diff hunk(带行号标注)
- 文件级上下文(函数签名、类定义)
- 架构文档摘要(该文件相关部分)
- PR 摘要
- 历史 review 规范
- 并发处理多个 hunk(
ThreadPoolExecutor),受TOTAL_CALL_BUDGET=60约束。
检查项(_R1_STRICT_RULES + _R1_REVIEW_CHECKLIST):
- Bug 类:空指针/越界访问、条件判断错误、循环边界错误、资源泄漏(文件/连接未关闭)
- 逻辑类:函数行为与名称不符、返回值未检查、异常被静默吞掉
- 安全类:SQL 注入、路径遍历、硬编码密钥、不安全的反序列化
- 并发类:竞态条件、锁使用不当、共享状态未保护
- 配置类:配置项放错位置(应在
configs.py而非tracing/)、可选依赖被加入必选 - 维护性:重构后遗留的孤儿 helper/常量(原来只被删除代码使用的符号)
严格排除项(避免噪音): - 未改动行的已有代码问题 - lint 工具已覆盖的问题(未使用 import、行长度、复杂度指标) - 纯风格问题(除非违反项目规范)
issue 密度控制:每 100 行有效 diff 最多输出 5 个 issue,防止大 PR 产生过多噪音。
输出格式(每个 issue):
{
"path": "lazyllm/tools/git/review/rounds.py",
"line": 42,
"severity": "critical|high|medium|normal",
"bug_category": "bug|security|performance|maintainability|design|style",
"title": "简短标题",
"description": "问题描述",
"suggestion": "修复建议(含代码示例)",
"source": "rhunk_scan"
}
4.2 RPrDoc:PR 设计文档生成
目标:在架构师评审(RArchReview)之前,先生成一份结构化的 PR 设计文档,作为 RArchReview 的输入上下文。
工作原理:
对完整 diff + PR 标题/描述 + 架构文档,用一次 LLM 调用生成包含 9 个章节的设计文档:
| 章节 | 内容 |
|---|---|
| 1. Background & Problem Definition | PR 解决的问题;在现有架构中的位置;新功能/bugfix/重构 |
| 2. Design Goals | 期望达到的效果;设计约束(性能/可扩展性/一致性) |
| 3. Design Approach | 核心思路;为何这样设计;是否有备选方案;是否符合架构分层 |
| 4. Module Impact Analysis | 修改/新增的模块;职责变化;新引入的依赖 |
| 5. API Design | 新增/修改的接口;输入输出;与现有 API 风格一致性 |
| 6. Usage Example | 典型调用示例;对用户使用方式的影响 |
| 7. Compatibility & Impact Scope | 是否影响已有功能;是否为 breaking change |
| 8. Risks & Edge Cases | 潜在问题;未覆盖场景;隐含假设 |
| 9. Extensibility Analysis | 后续类似需求的扩展难度;设计演进空间 |
设计文档保存在 checkpoint 中(pr_design_doc),也作为最终 review 结果的一部分返回。
4.3 RArchReview:架构师设计评审
目标:从全局架构视角评审整个 PR,发现 RHunkScan 无法发现的设计层面问题。
工作原理:
以完整 diff + PR 设计文档(RPrDoc 产物)+ 架构文档 + PR 摘要为输入,用一次 LLM 调用进行全局评审。
11 个评估维度(_RARCH_REVIEW_PROMPT_TMPL):
-
Module Responsibility:新代码是否放在了正确的模块?是否有逻辑被分散到不该放的地方?是否应该放在另一个模块?
-
Layering & Dependencies:是否遵守了现有的层次边界?是否引入了循环依赖?是否跨层直接访问(如 UI 层直接操作数据库)?
-
API Design:新增接口是否简洁、稳定、易用?参数是否过多?是否暴露了不必要的内部细节?
-
Consistency(一致性):
- 同模块的类/函数是否遵循相同的接口模式?
- 若新增了与现有类同类型的类(如新的存储后端、新的模型供应商),是否共享公共基类或 Protocol?
- 相似类是否遵循相同的构造模式(
__init__参数顺序、factory/client 入口)? - 相似类是否实现了相同的关键方法集合(参数名、顺序、返回类型一致)?
-
若项目使用
__call__+forward等分发模式,新类是否遵循同样的模式? -
Abstraction & Reuse(抽象与复用):
- 是否有逻辑在其他地方已经存在?
- 关键检查:若系统此前只有一个概念的实现(如一个存储后端、一个模型供应商),而此 PR 新增了第二个,是否存在公共基类/Protocol/ABC?若不存在,这是设计问题——两个实现应统一在共同抽象下。
-
新抽象的层次是否合适(不过度泛化,也不过度具体)?
-
Complexity:是否引入了不必要的复杂度?是否有可以用单行表达式替代的多步流程?
-
Extensibility:未来类似需求是否容易扩展?是否有硬编码的假设会阻碍扩展?
-
Coupling:模块间耦合是否增加?是否可以通过接口/事件/依赖注入降低耦合?
-
Testability:新代码是否容易测试?是否有全局状态或硬依赖使测试困难?
-
Overall Design Verdict:是否有更简单/更一致的替代设计?最重要的一个架构改进点是什么?
-
Naming & Semantic Clarity(命名与语义清晰度):
- 方法/函数名是否自解释且简洁?
- 方法名是否避免了冗余地包含类名(如应用
ClassA.get_instance()而非ClassA.get_classA_instance())? - 相似方法在兄弟类中的参数名是否一致?
- 若项目提供语法糖(
__or__、__ror__、__getitem__等运算符重载),其语义是否与主流惯例一致(bash 管道|、Python 切片[]等)?若可能误导用户则标记。
4.4 RAgentVerify:ReactAgent 深度验证
目标:RHunkScan 和 RArchReview 只能看到 diff 本身,RAgentVerify 通过 ReactAgent 主动探索仓库,验证已有 issue 的准确性,并发现需要跨文件上下文才能发现的新问题。
工作原理(两阶段):
阶段一:Context Collect(上下文收集)
对每个需要验证的 diff 文件,ReactAgent 使用工具集主动探索仓库:
- 读取文件骨架:先用
read_file_skeleton_scoped了解文件结构,再按需读取具体行。 - 符号追踪:用
grep_symbol找到被修改符号的所有调用方,评估影响范围。 - 基类/接口检查:读取基类定义,确认子类是否满足接口契约。
- 框架机制识别:检查是否使用了 metaclass、
__init_subclass__、registry 等自动注册机制(避免将自动注册的子类误报为"死代码")。 - 兄弟类探索(新增):当 diff 新增一个类时,搜索同基类或同目录下的兄弟类,对比其构造模式、关键方法签名、
__call__/forward分发模式。 - 运算符语义验证(新增):若 diff 修改了
__or__、__getitem__等 dunder 方法,grep 其使用模式,验证语义是否与主流惯例一致。
Context Collect 的输出是结构化 JSON:
{
"explored_symbols": ["sym1", "sym2"],
"related_files": [
{"path": "relative/path.py", "reason": "one-line reason", "lines": [start, end]}
],
"base_classes": [
{"symbol": "BaseClassName", "file": "relative/path.py"}
],
"framework_notes": ["one-line finding about framework mechanism"],
"sibling_classes": [
{"symbol": "ClassName", "file": "relative/path.py", "key_methods": ["method1(args)", "method2(args)"]}
]
}
阶段二:Issue Extract(问题提取)
以 Context Collect 收集的上下文 + RHunkScan/RArchReview 已有 issue 为输入,执行两类任务:
任务 1:验证已有 issue - 确认 issue 是否真实存在(排除误报) - 补充跨文件证据(如"调用方 X 会受到影响") - 若 issue 在仓库其他地方已有相同模式,标记为"项目一贯做法,非问题"
任务 2:发现新问题(需要跨文件上下文才能发现):
- 调用方破坏:修改了函数签名,但调用方未同步更新
- 孤儿符号:重构后遗留的 helper/常量,原来只被删除代码使用
- 基类契约违反:子类未实现基类的抽象方法,或覆盖了不应覆盖的方法
- 基类抽象缺口(新增):若 diff 新增了某概念的第二个实现,但不存在公共基类,报告设计问题
- 兄弟类一致性(新增):若兄弟类(同基类或同角色)的构造签名、关键方法签名、分发模式不一致,报告不一致
- 命名清晰度(新增):若新方法/函数名冗余地包含父类/模块名,或不够自解释,报告命名问题
- 语法糖语义(新增):若 diff 新增或修改了运算符重载(__or__、__getitem__、__lshift__ 等),验证语义是否与主流语言/shell 惯例一致,若可能误导用户则报告
RAgentVerify 的规模控制:
| 参数 | 默认值 | 说明 |
|---|---|---|
R3_MAX_FILES |
20 | 最多处理 20 个文件 |
R3_MAX_CHUNKS_PER_FILE |
3 | 每文件最多 3 个 chunk |
R3_MAX_CHUNKS_HARD |
8 | 每文件硬上限 8 个 chunk |
| Agent 步数上限 | 15 步 | 每次 Context Collect 最多 15 次工具调用 |
大文件(diff 超过 large_file_threshold)会被跳过 RAgentVerify 处理,避免消耗过多预算。
4.5 RMod:修改必要性分析(_rmod_run)
目标:对每个被修改的文件,使用 ReactAgent 判断改动是否有架构层面的合理性——标记不必要的重构、过度设计或违反框架约定的改动。
工作原理:
_rmod_collect_file_diffs提取逐文件 diff;_rmod_new_file_paths识别新建文件(正确处理 unified diff 中--- /dev/null→+++ b/的顺序)。- 对每个文件启动一个
ReactAgent,工具集与 RAgentVerify 相同(read_file_scoped、search_scoped等)。Agent 以 PR 设计文档、架构文档和框架约定为上下文。 - 每个文件有独立超时(
_RMOD_AGENT_TIMEOUT_SECS),文件间并行处理(最多max_workers线程)。 - issue 标记为
source='rmod'、bug_category='design'。 - 结果保存到 checkpoint(
ReviewStage.RMod),支持断点续传。
实现要点:
- 在创建 Agent 前直接在 tools 上设置 execute_in_sandbox = False,不访问私有属性 _tools_manager。
- 使用 concurrent.futures.TimeoutError(别名 FuturesTimeoutError),而非内置 TimeoutError。
4.6 RScene:使用场景推断(infer_usage_scenarios)
目标:理解 PR 修改的公开 API,推断 2-4 个典型的端到端使用场景,为 RChain 提供输入。RScene 与主链(RHunkScan→RArchReview→RAgentVerify→RMod)并行运行,不阻塞主链。
工作原理(两步):
Step 1 — 理解功能模块:ReactAgent 使用工具集主动探索仓库:
- 用 read_file_scoped 读取每个被修改文件的完整骨架(类定义、方法签名、关键常量)
- 用 analyze_symbol 理解核心类的状态机和数据流
- 用 search_in_files 搜索现有测试和示例,了解已有使用模式
Step 2 — 推断典型使用场景:基于理解,输出 2-4 个场景,每个场景包含:
- title:场景名称
- description:场景描述
- api_sequence:API 调用顺序(如 init → configure → run → cleanup)
- call_chain:预期调用链(函数/方法名列表)
- edge_cases:需要检查的边界情况
规模控制:
- 最多处理 max_files_for_ragent_verify 个文件(与 RAgentVerify 共用策略参数)
- 只处理被修改的文件(非新建文件),新建文件无历史行为可推断
- 最多 3 个并行 worker(_RSCENE_MAX_PARALLEL)
- 每个 Agent 超时 _RSCENE_AGENT_TIMEOUT_SECS(180s),最多重试 _RSCENE_AGENT_RETRIES(10)次
Checkpoint:场景结果保存到 ReviewStage.RScene(key: rscene_all),断点续传时直接加载。
4.7 RChain:调用链 Bug 分析(_rscenario_call_chain)
目标:以 RScene 推断的使用场景为驱动,对每个场景的调用链进行深度 bug 分析和 API 可用性评审。RChain 在 RScene 完成后立即启动,与主链并行。
工作原理(两个任务):
Task A — 调用链 Bug 分析:
1. 用 read_file_scoped / analyze_symbol 追踪调用链中每一步
2. 验证调用方与被调用方之间的输入/输出契约是否一致
3. 检查异常传播路径:异常是否被正确捕获或向上传递
4. 检查资源生命周期:文件/连接/锁是否在所有路径上正确释放
5. 检查并发安全:调用链中是否存在竞态条件或共享状态问题
Task B — API 可用性评审: 1. 验证 API 序列是否符合直觉(参数顺序、命名、默认值) 2. 检查错误信息是否足够清晰,便于用户调试 3. 检查是否存在容易误用的 API(如参数顺序容易混淆)
输出:issue 标记为 source='rchain',Task A 使用 bug_category 为 bug/security/performance/concurrency/safety/type,Task B 使用 design/exception。
规模控制:
- 每个场景独立运行一个 ReactAgent,最多 _RCHAIN_MAX_PARALLEL_SCENARIOS(3)个并行
- 每个 Agent 超时 _RCHAIN_AGENT_TIMEOUT_SECS(240s),最多重试 _RCHAIN_AGENT_RETRIES(6)次
- 每个场景的 diff 预算受 _RCHAIN_FIXED_OVERHEAD 约束
Checkpoint:每个场景的结果独立缓存(key: rchain_scene_{idx}_{title}),整体结果缓存到 rchain_all(ReviewStage.RChain)。
与主链的关系:RChain 的 issue 绕过 RDedupMerge 去重,在 final_comments 之后直接追加到 all_comments(与 RCov 类似)。这是因为 RChain 的场景驱动视角与 RHunkScan/RArchReview/RAgentVerify 的 diff 视角互补,强制去重会丢失有价值的场景级 bug。
4.8 RDedupMerge:合并去重
目标:将 RHunkScan、RArchReview、RAgentVerify、RMod 的 issue 列表 + lint_issues 合并为最终的、无重复的评论列表。RCov 和 RChain 的 issue 绕过本阶段,在 RDedupMerge 之后直接追加。
工作原理(三步):
- 确定性去重:
- 相同
(path, line, bug_category)的 issue 先按 severity / 来源优先级排序,再用 n-gram 语义相似度(阈值 0.85)判断是否为重复描述:仅当problem文本高度相似时才合并,语义不同的 issue 即使 category 和行号相同也全部保留,交由后续 LLM 步骤做最终判断 -
已在
existing_comments中存在的 issue 直接丢弃(避免重复评论) -
LLM 语义合并:
- 对同一文件内语义相近但行号不同的 issue,用 LLM 判断是否为同一问题的不同表述
-
若是,合并为一条,保留更详细的描述和建议
-
Lint 融合:
lint_issues(来自lint_runner.py,不经过 LLM)直接追加到最终列表- Lint issue 的
source字段为'lint',在平台评论中有特殊标记
输出:final_comments 列表,每条包含完整的 path、line、severity、bug_category、title、description、suggestion、source 字段。
5. RCov:测试覆盖检查(coverage_checker.py)
RCov 在 RDedupMerge 之后运行(独立,不经过去重),检查新增或修改的公开符号是否有充分的测试覆盖。
5.1 三步流程
Step 1 — 识别可测符号(_rcov_identify_symbols):
- 将 diff 裁剪至 SINGLE_CALL_CONTEXT_BUDGET - 20000 字符。
- 调用 LLM 提取需要测试覆盖的公开函数/类列表。
- 过滤掉内部辅助函数(is_internal=True)。
Step 1.5 — 按依赖分组(_rcov_group_symbols):
- 若符号数量 > 1,调用 LLM 将相关符号(如一个类及其关键方法)归为一组,一起评估。
- 分组失败时退化为每个符号单独一组。
Step 2 — Grep + 评估(_rcov_evaluate_groups):
- _find_test_files 扫描 clone_dir 查找测试文件(test_*.py、*_test.py 等)。
- 对每组,_build_grep_results 并行 grep 各符号名。符号名通过 re.fullmatch 校验后以 grep -F 固定字符串模式搜索,防止正则注入。
- LLM 评估现有测试是否充分;issue 标记为 source='rcov'。
- 最多 4 个 worker 并行处理各组;每组有独立超时 _RCOV_GROUP_TIMEOUT_SECS(90s)。
5.2 超时策略
runner.py 中的外层超时根据 diff 大小动态推导:
timeout = clamp(3 × 90 + len(diff_text) // 10000 × 30, min=300, max=900) # 秒
# 注:基础值 270 < min=300,diff 极小时实际取 min=300。
diff 越大,超时越长,避免大 PR 误超时,也避免小 PR 无谓等待。实际超时值会以 INFO 级别打印,便于排查。
5.3 Checkpoint
RCov 结果保存在独立的 ReviewStage.RCov checkpoint key(rcov_issues)下。断点续传时直接加载缓存,跳过所有 LLM 调用。
输出指标:
- rcov_issues_count:发现的 issue 数量;若 RCov 被跳过(超时/无 clone)则为 None。
- rcov_ran:布尔值,标识 RCov 是否实际执行。
6. 静态 Lint 分析(lint_runner.py)
Lint 分析在所有 LLM 轮次(RHunkScan 开始)之前独立运行,不消耗 LLM 调用预算。
工作原理:
- 从 diff 中提取变更的文件和行号范围。
- 对 Python 文件运行
ruff/flake8,对 JS/TS 文件运行eslint(若已安装)。 - 过滤出仅在 diff 变更行上出现的 lint 错误(不报告未改动行的已有问题)。
- 将结果转换为统一的 issue 格式(
source='lint'),注入 RDedupMerge。
Lint issue 不经过 LLM 验证,直接进入最终结果,因此准确率高、无幻觉风险。
7. Checkpoint 系统(checkpoint.py)
7.1 设计目标
- 断点续传:review 中途失败(网络超时、LLM 限流)后,从上次完成的阶段继续,不重复已完成的 LLM 调用。
- 软失效:
resume_from参数可指定从某个阶段重新开始,而不删除其他阶段的缓存。 - 版本控制:
_REVIEW_ROUND_VERSION递增时,自动失效 RDedupMerge(合并去重)阶段的缓存,确保 prompt 变更后重新计算最终结果。
7.2 阶段枚举(ReviewStage)
CLONE → ARCH → SPEC → PR_SUMMARY → RHunkScan → RPrDoc → RArchReview → RAgentVerify → RMod →
RScene → RChain → RDedupMerge → RCov → UPLOAD
每个阶段完成后调用 ckpt.mark_stage_done(stage),重启时通过 ckpt.should_use_cache(stage) 判断是否跳过。
_KEY_TO_STAGE 在类定义时直接初始化(非懒初始化),避免并发场景下的 TOCTOU 竞态。
7.3 Head SHA 轮转
若 PR 在 review 过程中被 force-push(head SHA 变化),checkpoint 自动备份并清除所有 review 轮次数据(保留 clone、arch、spec),从 RHunkScan 重新开始。
8. 评论发布(poster.py)
8.1 发布策略
- 批量提交:优先使用平台的
submit_reviewAPI 一次性提交所有评论(GitHub 的 Pull Request Review)。 - 逐条 fallback:若批量提交失败(如评论行号不在 diff 范围内),逐条使用
create_review_comment重试。 - 速率限制重试:遇到 429 / 403 限流响应时,指数退避重试(最多 3 次)。
- 合并顺序:
all_comments = final_comments + rchain_issues + rcov_issues,三类 issue 均经过_filter_commentable过滤后发布。
8.2 可评论行过滤(_filter_commentable)
平台只允许对 diff 中实际变更的行添加行级评论。_build_commentable_lines 解析 diff,构建 {path: set(lines)} 映射。_filter_commentable 返回三元组 (inline, general, dropped):
inline:有效行号且在 diff 范围内的 issue → 作为行级 review 评论发布。general:line=None的 issue(如 RCov 覆盖问题)→ 作为 PR 级评论,附加在 review body 的"General Review Comments"节中。dropped:行号超出变更范围且无法映射的 issue → 丢弃(避免 GitHub 422 错误)。
8.3 评论正文策略
每条评论正文包含以下强制要求声明:
新引入的架构问题必须在合并前修复;已有问题必须通过 issue(新建或关联)追踪。风格问题也必须修复;测例缺失必须补全。
9. 预算与限流(constants.py)
| 常量 | 默认值 | 说明 |
|---|---|---|
SINGLE_CALL_CONTEXT_BUDGET |
120000 字符 | 单次 LLM 调用的上下文上限 |
R1_DIFF_BUDGET |
95000 字符 | RHunkScan 中 diff 内容的上限(预留 25000 给系统 prompt + 架构文档) |
TOTAL_CALL_BUDGET |
60 次 | 整个 review 会话的 LLM 调用总上限 |
ISSUE_DENSITY_LINE_BLOCK |
100 行 | issue 密度控制的块大小 |
ISSUE_DENSITY_MAX_PER_BLOCK |
5 个 | 每 100 行最多输出 5 个 issue |
R3_MAX_FILES |
20 | RAgentVerify 最多处理的文件数 |
R3_MAX_CHUNKS_PER_FILE |
3 | RAgentVerify 每文件最多 chunk 数 |
R3_MAX_CHUNKS_HARD |
8 | RAgentVerify 每文件硬上限 |
BudgetManager 追踪已使用的 LLM 调用次数,各轮次在调用前检查剩余预算,超出时跳过非关键步骤。
10. Issue 字段规范
所有轮次输出的 issue 遵循统一格式:
| 字段 | 类型 | 说明 |
|---|---|---|
path |
str | 相对于仓库根目录的文件路径 |
line |
int | 问题所在行号(diff 中的新行号) |
severity |
str | critical / high / medium / normal |
bug_category |
str | bug / security / performance / maintainability / design / style |
title |
str | 简短标题(≤80 字符) |
description |
str | 问题详细描述 |
suggestion |
str | 修复建议(含代码示例,使用 markdown 代码块) |
source |
str | rhunk_scan / rarch_review / ragent_verify / rmod / rcov / lint / meta |
11. 已知限制
| 限制 | 说明 |
|---|---|
| Diff 截断 | 超大 PR(>120K 字符)只审查前 N 个文件,被截断的文件通过 meta warning 告知 |
| RAgentVerify 文件上限 | 大 PR 下 RAgentVerify 最多处理 10 个文件,其余文件只经过 RHunkScan/RArchReview |
| RCov clone 依赖 | RCov 需要 clone_dir 来 grep 测试文件;本地模式下无 clone 时,测试文件发现退化为使用本地 repo 路径 |
| 动态引用 | grep_symbol 无法追踪运行时动态生成的符号名(如 getattr(obj, name)) |
| 跨仓库依赖 | 只分析当前仓库,外部依赖的接口变更无法检测 |
| 语言支持 | 当前 Lint 分析主要支持 Python(ruff/flake8),JS/TS 需要本地安装 eslint |
若后续调整 budget 常量、ReviewStage.ordered()、各轮 prompt、检查项或增加新轮次,请同步更新本文档。