nhatvu148/pr-review-core
GitHub: nhatvu148/pr-review-core
一个可复用的 Rust 核心库,为自托管 AI PR 审查机器人提供跨平台、结构化上下文驱动的代码审查能力。
Stars: 4 | Forks: 0
# pr-review-core
[](https://crates.io/crates/pr-review-core)
[](https://docs.rs/pr-review-core)
[](#license)
自托管建议性 AI pull request 审查器背后的核心引擎。
`pr-review-core` 获取 pull request 的 unified diff,通过
[OpenRouter](https://openrouter.ai) 使用 Claude 模型对其进行审查,
并发布以代码行锚定的行内评论以及总结性评论。
它与 **GitHub**、**GitLab** 和
**Bitbucket** 兼容,并可选择运行 *agentic* 流程,该流程会克隆 repo 并让
模型在写下其发现之前调查跨文件上下文(grep / read_file / list_dir)。
此 crate 是一个 **库** —— 它不包含自身的 bot 身份。使用者
(实际的 bot 二进制文件)依赖于它,并通过 [`Config`] 注入其品牌标识和任何额外的
prompt。
## 使用者
- **🦀 Kaniscope** —— 完全基于此 crate 构建:
- 托管的 **[playground](https://kaniscope.nvnv.app)**(粘贴 diff 或 GitHub PR URL → 获取审查),以及
- Marketplace 上的 **[GitHub Action](https://github.com/marketplace/actions/kaniscope-ai-code-review)**(`uses: nhatvu148/kaniscope-action@v1`)。
## 开箱即用功能
- 跨 GitHub、GitLab 和
Bitbucket 的提供商无关审查流程(`review::run_review`)。
- 来自模型的结构化 JSON 审查,锚定在提供商将接受的 diff 行上(超出 diff 的发现折叠到总结中)。刚刚错失 diff 行(模型偏移几行/漂移)的发现会被**重新锚定**到附近的 diff 行(前提是该行的代码与发现引用的内容匹配),因此微小的漂移仍会作为行内评论发布,而不是折叠到总结中(`REANCHOR_FINDINGS`)。
- 可选的 agentic reviewer,采用双层模型拆分(廉价的探索模型 +
更强大的综合模型)。
- **结构化上下文**:tree-sitter 识别每次更改所属的函数/符号(Rust/TS/TSX/JS/Python/Go),在本地计算且无需克隆,并带有 git hunk-header 作为后备。
- **爆炸半径**(agentic 路径):从克隆中,预先计算每个更改符号的调用者、测试和类型用法,并在 reviewer 中植入这些内容(加上 `references(symbol)` 工具),这样它就不必手动重新发现它们。对于
TS/TSX,它使用 tree-sitter,因此 **JSX 渲染 (` `) 和类型位置
(`: T`, `Foo`)** 都会被算作引用,而不仅仅是 `name(` 调用。Fail-open;通过 `BLAST_RADIUS` / `BLAST_MAX_SYMBOLS` / `BLAST_MAX_REFS` 进行调整。
_实测记录:在典型的、命名规范的 repo 中,这在基准测试中表现出 **没有召回率提升**
(一个能力强的模型已经可以通过名称/类型/文档从 diff 中推断出跨文件破坏);
它可能仍然对大型 monorepos 或命名不佳的代码有帮助。
默认开启 —— 在依赖它之前先在你的 repo 上进行测量。_
- **复杂度指标**:为更改涉及的函数提供确定性的圈复杂度 + 认知复杂度(带有 A–F 评级),通过 tree-sitter 从已经为结构化上下文获取的文件中计算 —— 无需 LLM,无需额外获取。只有达到或超过 `COMPLEXITY_MIN_CYCLOMATIC` 的函数才会作为风险信号被呈现。
通过 `COMPLEXITY_METRICS` 切换。
- **智能 diff 打包**:在大型 PR 上,整个文件会被排序(源码 > 测试 >
文档)并根据预算打包,而不是生硬地截断;被省略的文件会向模型标明。通过 **文件捆绑**(`FILE_BUNDLING`),相关文件 —— 一个源文件及其测试、i18n 同级文件 —— 作为一个单元打包并保持相邻,以便模型一起审查它们,而不是被优先级分散。
- **依赖漏洞扫描**:添加的 lockfile 条目(Cargo/npm/yarn/pnpm/
Go/PyPI/RubyGems/Composer)会与 [OSV.dev](https://osv.dev) 进行比对,已知的 CVE 会在总结中连同严重程度 + 修复版本一起呈现 —— 无需本地 resolver,仅通过 HTTP。
- **PR 命令**:`/ask ` 根据 diff 回答有关 PR 的问题;
`/describe` 以幂等方式(重新)生成 PR 描述,保留人工编辑;`/review-file ` 在 PR head 对整个文件进行深度审查,而不仅仅是针对 diff。
- **按 repo 配置**:repo 根目录下的 `.prbot.toml` 会覆盖模型、globs
(包括 `vendored`,它标记了 reviewer 不得提交规范发现或提出修改意见的第三方源码)、置信度/上限,并添加自由文本审查 `instructions`。
- **基准测试框架**:`examples/bench.rs` 会使用包含已知问题的 PR 语料库
(`examples/bench-corpus.example.json`)对 reviewer 进行评分 —— 报告 precision / recall / F1 和 token 成本,从而可以通过切换标志位重新运行来对某个功能(爆炸半径、
复杂度、后端)进行 A/B 测试。Dry-run;
需要 OpenRouter 密钥。`RunReviewOutput.findings_detail` 为工具提供结构化的发现。
- **噪音控制**:可选的自我批评阶段会剔除误报 / 吹毛求疵,
每个发现的置信度得分驱动排名,而每个 PR 的上限保持审查
聚焦。
- **文件 globs**:lockfile、生成的文件、vendored 和压缩文件会在模型看到它们之前从 diff 中排除
(节省 token 和噪音)。
- **任何兼容 OpenAI 的 endpoint**:通过 `LLM_BASE_URL` + `LLM_API_KEY` 将其指向 OpenRouter,或 Ollama / vLLM /
Together / Groq / 本地服务器。
- Webhook 签名验证和 payload 解析辅助工具。
- Dedupe:bot 在重新审查时会更新自己之前的评论,而不是叠加。
## 注入身份和 prompt
关于 bot 身份的任何内容都不是硬编码的。`Config::from_env()` 读取:
| 字段 | 环境变量 | 默认值 |
| --- | --- | --- |
| `comment_marker` | `COMMENT_MARKER` | `🤖 ai-pr-review` |
| `user_agent` | `USER_AGENT` | `pr-review-core` |
| `http_referer` | `OPENROUTER_HTTP_REFERER` | `https://github.com/nhatvu148/pr-review-core` |
| `x_title` | `OPENROUTER_X_TITLE` | `pr-review` |
| `extra_system_prompt` | `EXTRA_SYSTEM_PROMPT` / `EXTRA_SYSTEM_PROMPT_FILE` | *(空)* |
- `comment_marker` 是附加到每条评论的签名,也是用于查找/更新 bot 自己评论的去重键
。
- `extra_system_prompt` 会附加到内置的 system prompt 中。通过 `EXTRA_SYSTEM_PROMPT` 内联设置,或者将 `EXTRA_SYSTEM_PROMPT_FILE` 指向已内置到 Docker 镜像中的文件,从而无需触及库即可注入大量的约定区块。
其他操作设置(OpenRouter 密钥/模型、提供商 token、agentic 模式、
大小限制)也是从环境中读取的 —— 见 `src/config.rs`。
## 审查质量与成本控制
| 环境变量 | 默认值 | 效果 |
| --- | --- | --- |
| `SELF_CRITIQUE` | `true` | 第二次持怀疑态度的审查,剔除误报 / 低价值的吹毛求疵。 |
| `MIN_CONFIDENCE` | `0` | 丢弃低于此置信度 (0–100) 的发现。 |
| `MAX_FINDINGS` | `20` | 每个 PR 的发现上限(按严重程度然后按置信度排名)。 |
| `REANCHOR_FINDINGS` | `true` | 将刚刚偏离 diff 行的发现捕捉到共享代码符号的最近 diff 行上(否则折叠到总结中)。 |
| `EXCLUDE_GLOBS` | lockfile、生成的文件、vendored、压缩文件 | 在 LLM 调用之前跳过的逗号分隔 globs。 |
| `INCLUDE_GLOBS` | *(空 = 全部)* | 如果设置,则仅审查匹配这些 globs 的文件。 |
| `VENDORED_GLOBS` | `thirdparty/`, `third_party/`, `vendor/`, `vendored/`, `external/`, `node_modules/` | 标记 vendored 第三方源码的 globs。其中的 diff 规范发现会被抑制,并且会告知 reviewer 不要在那里提出修改建议 —— 批量提交 vendored 代码是预期行为,而不是缺陷。设置此项会替换默认值。 |
| `LLM_BASE_URL` | `OPENROUTER_BASE_URL` → openrouter | 兼容 OpenAI 的 endpoint(例如用于 Ollama 的 `http://localhost:11434/v1`)。 |
| `LLM_API_KEY` | `OPENROUTER_API_KEY` | 上述 endpoint 的 API 密钥。 |
| `CI_STATUS` | `true` | 获取 head commit 的 CI 结果(GitHub check runs / Bitbucket build statuses)并在 prompt 中显示它们,这样 reviewer 就不能断言 CI 已经判定为失败的构建。每次审查额外进行一次 API 调用;如果 token 接近其速率限制,请设置为 `false`。 |
| `CVE_SCAN` | `true` | 通过 OSV.dev 扫描已更改的 lockfile,查找已知的有漏洞依赖项。 |
| `CVE_MAX_PACKAGES` | `100` | 每次审查针对 OSV 查询的最大不同包数。 |
| `OSV_API_BASE` | `https://api.osv.dev` | OSV API 基础地址(覆盖以使用镜像/测试替身)。 |
## PR 命令
连接评论 Webhook(见 bot 二进制文件),reviewer 会回答这些
作为 PR 评论发布的命令:
| 命令 | 效果 |
| --- | --- |
| `/review` | (重新)运行完整审查。 |
| `/ask ` | 回答有关 PR 的问题,以其 diff 为依据。 |
| `/describe` | (重新)生成 PR 描述,以幂等方式合并到内容中。 |
| `/review-file ` | 在 PR head 深度审查整个文件(不仅仅是 diff);发现作为总结评论发布。 |
使用 [`command::parse_command`] + [`command::run_command`] 从 bot 二进制文件中路由它们。
## 发布
**在削减版本之前,针对候选发布版本编译每个使用者。**
这是有意的手动步骤。CI 的 `downstream compiles (public consumers)` 作业
只能访问公共使用者 —— 私有使用者对它不可见,并且其中一个
存在于故意不在公共工作流中命名的客户组织中。一个跳过了最可能发生破坏的使用者却仍然报告通过的检查,比不检查还糟糕。
这捕获的失败非常具体并且曾经发生过:**0.11.0 向公共 `PrMeta` 结构体添加了一个字段。这里的每个测试都通过了,而随后一个使用者编译失败。** 对已发布 crate 的破坏性更改对其自身的测试套件是不可见的 —— 只有使用者构建才能看到它。
```
# 在每个 consumer 中:将 dep 重新指向此 checkout,然后进行 build 和 test。
# 重新指向而不是使用 [patch.crates-io] — patch 必须满足 consumer 的
# version requirement,因此每次 version-bump PR 都会因错误的原因而失败。
# # 使用 `perl -i` 而不是 `sed -i`:就地编辑是两个 sed
# 不一致的唯一地方。BSD/macOS 需要 `sed -i ''`,GNU/Linux 会拒绝它;GNU 接受 `sed -i`,
# 而 BSD 会将下一个参数作为 backup suffix。这在两者上都能运行。
perl -i -pe 's|^pr-review-core = .*|pr-review-core = { path = "../pr-review-core" }|' Cargo.toml
cargo check --all-targets # add --features claude-code where the consumer has it
cargo test
```
然后:
1. 在 `Cargo.toml` 中提升 `version`;将 `CHANGELOG.md` 中的 `## Unreleased` 更名为实际版本。
2. **记录每个破坏性更改及其影响的使用者。** 更新日志中的迁移表是使用者用来查找破坏内容的依据。
3. **将行为更改与 API 破坏分开标记。** API 破坏会使构建失败并自动显现;行为更改会静默发布。0.14.0 就是一个例子 —— 通过后端接缝路由自我批评没有破坏任何使用者的编译,但改变了审查期间运行的内容。
4. `cargo publish --dry-run`,然后发布,最后打上 `vX.Y.Z` 标签。
5. 将每个使用者的依赖项提升到已发布版本并提交。
## 许可证
根据以下任一项获得许可
- MIT 许可证 ([LICENSE-MIT](LICENSE-MIT))
- Apache 许可证,版本 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
由你选择。
## 贡献
除非你明确声明,否则你有意提交以包含在本作品中的任何贡献,如 Apache-2.0 许可证中所定义,均应按上述方式进行双重许可,不附加任何额外的条款或条件。
标签:GitHub Action, Rust, 人工智能, 代码审查, 可视化界面, 用户模式Hook绕过, 网络流量审计, 通知系统