nhatvu148/pr-review-core

GitHub: nhatvu148/pr-review-core

一个可复用的 Rust 核心库,为自托管 AI PR 审查机器人提供跨平台、结构化上下文驱动的代码审查能力。

Stars: 4 | Forks: 0

# pr-review-core [![crates.io](https://img.shields.io/crates/v/pr-review-core.svg)](https://crates.io/crates/pr-review-core) [![docs.rs](https://img.shields.io/docsrs/pr-review-core)](https://docs.rs/pr-review-core) [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/pr-review-core.svg)](#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绕过, 网络流量审计, 通知系统