genai-code-review

ciroliniMIT活跃维护

用 LLM 自动审查 Pull Request 的代码差异,并把评审意见直接评论到 PR 上的 GitHub Action,省去人工逐行看 diff 的功夫。

GitHub Action that reviews pull request diffs with an LLM and posts the comments on the PR

v3.0.1发布于 2026-09-21 12:32 UTC372star70fork同步于 2026-11-07 07:32 UTC

怎么上手

由 README 提炼,执行前请确认
- uses: cirolini/genai-code-review@v3
  with:
    api_key: ${{ secrets.GEMINI_API_KEY }}
    github_token: ${{ secrets.GITHUB_TOKEN }}
    github_pr_id: ${{ github.event.number }}
硬件要求按 README 说明

README 未说明;这是 GitHub Actions 上运行的 Action,模型推理在所选 provider 侧完成,本地自托管端点(如 Ollama、vLLM)的硬件要求需自行满足

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

用 LLM 审查 PR diff 并发布行内评论的 GitHub Action,靠评论预算只发最值得看的几条

用 LLM 自动审查 Pull Request 的代码差异,并把评审意见直接评论到 PR 上的 GitHub Action,省去人工逐行看 diff 的功夫。

核心能力

  • 按严重度排序,只发 max_comments 条(默认 5)
  • 被压下的发现会在摘要中列出并说明如何放开
  • 重复发现先过滤再排序,不占用评论预算
  • 摘要说明未审查的文件、截断的 diff 等盲区
  • 支持 OpenAI、Anthropic、Gemini 及 OpenAI 兼容端点

容易踩的坑

必须声明 permissions 中的 pull-requests: write,否则能读 PR 但静默发不出评论;不要用 pull_request_target 处理 fork 的 PR,会同时暴露写权限和密钥;panel 模式下每个成员需各自的密钥,且 OpenAI 目前不能作为第二个成员;max_tokens 低于约 4000 时大审查会被截断丢失

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v3.0.1
  1. v3.0.1v3.0.1 — fixes before the announcement最新正式版2026-09-21 · 2026-09-21 12:32 UTC

    A fix release. Nothing new to configure; every input keeps its meaning. If you pin @v3, you get this automatically.

    The summary no longer forgets what it did not review. The last reviewed commit advanced even when files were skipped as too large, the diff was truncated, or the model's output was unusable — so on the next push those files were neither reviewed nor listed under "Not reviewed". It now advances only after a complete run, and the summary says when the next push will review an earlier range again. Findings from a review GitHub rejected are no longer counted as posted: they are listed in the summary and retried.

    Incremental review survives a merge from the base branch or a rebase. Both used to put changes from outside the pull request into the diff, and GitHub rejected the whole review with a 422. The comparison is checked first and falls back to reviewing the whole pull request, saying why.

    One-click suggestions only where they apply. A finding moved a few lines to fit the diff now shows its suggestion as a plain code block, so "Commit suggestion" cannot overwrite code it was not written for.

    Security: .genai-review.yml is read from the base branch. It was read from disk: with a checkout, the pull request controlled it — ignore_paths: ["**"] could review a change out of existence, and base_url could send the API key elsewhere. Without a checkout, as recommended, it was never read at all. It is now fetched over the API from the base commit, and base_url is an action input only. If you set base_url in .genai-review.yml, move it to the workflow.

    The default max_tokens is 8192 (was 2048). At 2048 a review of a large pull request truncated mid-JSON and was lost entirely.

    Also: the action is now named "GenAI Code Review" in action.yml, and the docs no longer claim @v2 was untouched — it points at the fix-only v2.1.

    Full list in the changelog.

  2. v3.0.0v3.0.0 — a code review bot with a comment budget2026-09-19 · 2026-09-19 18:54 UTC
    查看更新说明

    v3 rebuilds this action around a single idea: the scarce resource in code review is the reviewer's attention, and a bot that posts forty comments spends more of it than it returns.

    Two changed defaults. mode now defaults to review, so a workflow that never set mode gets inline comments instead of one summary comment — pin mode: files to keep the old output. And provider now defaults to gemini, though a workflow passing the deprecated openai_api_key without a provider stays on OpenAI.

    Every v2 input still works, and @v2 itself is untouched. See the migration guide.

    Multiple providers

    provider selects Google Gemini (the default), OpenAI, Anthropic, or any OpenAI-compatible server — Ollama, vLLM, Azure OpenAI, OpenRouter — via base_url. New inputs: provider, model, api_key, base_url, temperature, max_tokens. The openai_* inputs remain as aliases.

    Transient failures are retried with backoff; permanent ones are not, because retrying a rejected key only delays the message you need. A provider failure is now reported in the pull request and fails the check, rather than surfacing only in a log — a review that silently did not happen looks exactly like a review that found nothing.

    Inline comments on the right lines

    With mode: review, the model returns findings matching a JSON schema — file, line range, severity, category, confidence, title, rationale, optional suggestion — validated in code, with one repair attempt when it does not match. Findings are posted as inline comments grouped into a single review.

    Review pressure controls
    • max_comments (default 5), ranked by severity then confidence. Suppressed findings are counted and listed, never dropped.
    • min_severity and min_confidence.
    • One sticky summary comment, edited in place instead of a new one per push.
    • Incremental review: on a push, only the new commits are reviewed, and findings already raised are not repeated.
    • ignore_paths, defaulting to lockfiles, generated and vendored code, snapshots and minified assets.
    • Large diffs are chunked, and anything that did not fit is reported rather than silently truncated.
    • panel (experimental): two providers merged, with agreement as a confidence signal. Roughly double the cost.
    Measured
    ModelPrecisionRecallNoise rateCost / PR
    gemini-3.8-flash100%93%0%$0.0009
    gpt-oss-120b70%93%83%n/a

    21 fixtures, 15 with a seeded defect and 6 clean. Recall did not separate the two models; noise did. Full numbers and limits in docs/results/.

    Measurement

    Every run writes a JSON log — provider, model, tokens, estimated cost, latency, findings by severity, posted versus suppressed, coverage — containing no credentials, no diff and no source code.

    evals/ holds 21 fixtures with seeded, labelled defects plus clean diffs, and make eval scores precision, recall, noise rate, cost per pull request and latency.

    Fixed

    Everything in v2.1: the container entrypoint path (#47), the unpinned httpx that broke client construction, and the deprecated default model. Also the patch parser, which split on the bare substring diff and broke on any diff that mentioned the word (#28).

    Security

    The diff is treated as untrusted input: fenced with a per-request random sentinel, with instructions placed after the data, and the model told that injection attempts are themselves reportable. Use pull_request, not pull_request_target, for pull requests from forks.


    Quick start

    on:
      pull_request:
        types: [opened, synchronize]
    
    permissions:
      contents: read
      pull-requests: write
    
    jobs:
      review:
        runs-on: ubuntu-latest
        steps:
          - uses: cirolini/genai-code-review@v3
            with:
              api_key: $
    
  3. v2.1v2.1 — unbreak the action2026-09-19 · 2026-09-19 17:35 UTC
    查看更新说明

    A maintenance release. No new features and no input changes: v2.1 exists because v2 had stopped working, in two independent ways.

    Fixed

    • The action could not start on any repository but this one (#47). GitHub runs container actions with --workdir /github/workspace, which overrides the image's WORKDIR, so the relative CMD ["python", "src/main.py"] resolved against the caller's checked-out repository and failed with can't open file '/github/workspace/src/main.py'. The entrypoint is now absolute. Reported by @Asthethi; fix from @arjunsuresh in #50.

      This repository's own workflow checked the repository out before running the action, which put a src/main.py on the container's working directory and made the action appear to work here while it failed everywhere else. That checkout has been removed, and CI now builds the image and runs it the way GitHub does.

    • The OpenAI client could not be constructed. openai was pinned to 1.30.1 but httpx was not pinned at all. httpx 0.28 removed the proxies argument that openai 1.30 passes, so a fresh build failed with Client.__init__() got an unexpected keyword argument 'proxies'. All dependencies, including transitive ones that affect startup, are now pinned, and the Python base image is pinned to a patch version.

    Changed

    • Default model is now gpt-5.6-luna (was gpt-3.5-turbo, which is deprecated and loses API access on 2026-10-23). gpt-5.6-luna is cheaper than gpt-3.5-turbo on both input and output tokens, so this does not raise the cost of an existing workflow. Pass openai_model to choose a different one.

    • Dependencies: openai 1.30.1 → 1.109.1, PyGithub 2.3.0 → 2.10.0, requests 2.31.0 → 2.34.2, httpx pinned at 0.28.1.

    Documentation

    • The workflow example now includes the required permissions block. Missing pull-requests: write was a recurring cause of "it runs but posts nothing".
    • The configuration reference documented an openai_engine input that the action never read. The correct name is openai_model.
    • Removed actions/checkout from the example. This action reads the pull request over the API and does not need the repository on disk.

    Upgrading

    None required. v2 now points at this release, and every existing input keeps its previous meaning. If you pinned openai_model: "gpt-3.5-turbo" explicitly, change it before 2026-10-23.

  4. v2Enhance Code Review Process with Custom Prompts, Language Selection, and Automated Testing2024-05-21 · 2024-05-21 16:53 UTC
    查看更新说明

    Overview:

    This PR introduces significant enhancements to the code review automation process, including support for custom prompts, language selection, and integration of automated testing using pytest and pylint.

    Changes:

    Custom Prompts:

    • Added support for user-defined custom prompts.
    • Example usage: custom_prompt: "Give a rating from 1 to 10 for this code:".

    Language Selection:

    • ntroduced the ability to specify the language for code review responses.
    • Default language is set to English (en).

    Automated Testing:

    • Implemented unit tests using pytest for core functionalities.

    Refactoring:

    • Modularized the code for better maintainability.
    • Improved error handling and logging.
    • Ensured compliance with PEP8 standards.
  5. v1.4Code Review with ChatGPT2023-12-05 · 2023-12-05 00:36 UTC
    查看更新说明

    Now we do a code review.

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    GenAI Code Review

    A GitHub Action that reviews pull request diffs with an LLM and posts inline comments. It works with OpenAI, Anthropic, Google Gemini, or any OpenAI-compatible server.

    Unlike most such tools, it is built around a comment budget: it posts only the few findings most worth a human's attention, and tells you what it held back.


    Quick start

    One workflow file. Nothing else.

    # .github/workflows/code-review.yml
    on:
      pull_request:
        types: [opened, synchronize]
    
    permissions:
      contents: read
      pull-requests: write
    
    jobs:
      review:
        runs-on: ubuntu-latest
        steps:
          - uses: cirolini/genai-code-review@v3
            with:
              api_key: ${{ secrets.GEMINI_API_KEY }}
              github_token: ${{ secrets.GITHUB_TOKEN }}
              github_pr_id: ${{ github.event.number }}
    

    No actions/checkout step is needed — the action reads the pull request over the API and never uses the repository on disk.

    The permissions block is required. Without pull-requests: write the action authenticates, reads your pull request, and then silently fails to post.


    Why a comment budget

    AI made writing code cheap. It did not make reviewing it cheap. The bottleneck moved from the person writing to the person reading, and that person's attention is now the scarce resource on the team.

    A review bot that posts forty comments does not add forty units of value. It spends forty units of somebody's attention, and it usually returns less than the best five comments would have. Worse, it trains the reviewer to skim past the bot entirely — at which point the two comments that actually mattered are lost along with the thirty-eight that did not.

    So this one is given a budget and made to choose:

    • max_comments (default 5) — findings are ranked by severity, with confidence ordering within a severity, and only the top few are posted.
    • Nothing is silently dropped. The summary says how many findings were held back, lists them, and names the setting to change to see them. A reviewer who knows twelve minor findings were suppressed can ask for them; one who was never told has been quietly misled about coverage.
    • Repeat findings do not consume budget. A finding already raised on an earlier push is filtered out before ranking, so it cannot push a new finding out of the budget.
    • The summary says what was not reviewed — ignored files, files too large for one request, truncated diffs, and the fact that an incremental run only looked at recent commits. This is the section most tools leave out, and it is the one that decides whether silence from the bot can be trusted.

    The same idea drives the rest of the design: one sticky summary comment edited in place rather than a new one per push, one grouped review rather than N separate notifications, and lockfiles and generated code dropped before they ever reach the model.

    Whether this actually produces better reviews is a measurable question, not a claim. See docs/results/ for the harness and docs/case-study.md for the reasoning.


    Providers

    providerDefault modelKey read from
    gemini (default)gemini-3.8-flashapi_key, else GEMINI_API_KEY
    openaigpt-5.6-lunaapi_key, else OPENAI_API_KEY
    anthropicclaude-sonnet-5api_key, else ANTHROPIC_API_KEY
    openai-compatiblenone — model is requiredapi_key, optional

    Defaults are cost-conscious rather than each vendor's strongest model, because this runs on every push to every pull request and the bill is yours. Set model to pick a different one.

    gemini is the default because it is the configuration with measured numbers behind it: 100% precision and 0% noise on the eval set, at $0.0009 per pull request. See docs/results/.

    A workflow that passes the deprecated openai_api_key and does not set provider stays on OpenAI, so moving the default cannot send an OpenAI key to Google.

    - uses: cirolini/genai-code-review@v3
      with:
        provider: anthropic
        api_key: ${{ secrets.ANTHROPIC_API_KEY }}
        github_token: ${{ secrets.GITHUB_TOKEN }}
        github_pr_id: ${{ github.event.number }}
    

    Panel mode (experimental, off by default)

    panel runs two providers over the same diff and merges what they found. A finding both reported has its confidence raised by 15%; one only a single member reported has it cut by 30%, so it is demoted rather than discarded — a model spotting a real blocker the other missed is still worth keeping. Findings are matched across members by file, category and normalised title, because two models describing one defect rarely agree on the line or the wording. The summary reports the agreement rate.

    - uses: cirolini/genai-code-review@v3
      with:
        panel: gemini,anthropic
        api_key: ${{ secrets.GEMINI_API_KEY }}
        github_token: ${{ secrets.GITHUB_TOKEN }}
        github_pr_id: ${{ github.event.number }}
      env:
        ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
    

    Every member needs its own key. The api_key input goes to the first member only; the others read their provider's environment variable (GEMINI_API_KEY, ANTHROPIC_API_KEY). Without it the run fails at startup with provider ... needs an API key rather than quietly reviewing with one model.

    OpenAI cannot currently be the second member. The action declares OPENAI_API_KEY from its own deprecated input, so a value passed through the step's env: is overwritten with an empty one and the member fails to build. Put OpenAI first, as provider/api_key, and the other model second.

    The price is roughly double — two requests, two sets of tokens, and the latency of both. That is why it is off by default and called experimental: there is no measurement yet showing the second opinion is worth what it costs. Panel mode has never been run against two live providers. See docs/results/.

    Self-hosted and third-party endpoints

    openai-compatible covers Ollama, vLLM, Azure OpenAI, OpenRouter, Together and anything else exposing the same API. api_key is optional, so a local server with no auth works:

        provider: openai-compatible
        base_url: http://localhost:11434/v1
        model: llama3
    

    Configuration

    Every setting can be given as an action input or in a .genai-review.yml file at the repository root. Action inputs take precedence over the file, so a workflow can always override the repository's defaults.

    The file is read over the API from the pull request's base branch, not from the pull request, and needs no checkout. A change to it takes effect once it is merged. Otherwise whoever opens a pull request could set ignore_paths: ["**"] and review their own change out of existence.

    See .genai-review.yml.example for a commented file. There is deliberately no api_key key in it: credentials belong in a secret. base_url is also input-only, because it decides which server receives the key.

    Reference

    InputDefaultWhat it does
    github_token—Required. Usually ${{ secrets.GITHUB_TOKEN }}
    github_pr_id—Required. Usually ${{ github.event.number }}
    api_key—Key for the chosen provider
    providergeminigemini, openai, anthropic, openai-compatible
    modelper providerModel ID
    base_url—Required for openai-compatible
    modereviewreview posts inline comments; files/patch are v2 behaviour
    max_comments5Inline comments per review; the rest are counted and listed
    min_severitynitblocker, major, minor, nit
    min_confidence00 to 1
    ignore_pathssee belowGlobs to skip. Unset uses the defaults
    incrementaltrueOn a push, review only the new commits
    panel—Experimental. Two providers, merged. ~2x cost — details
    languageenLanguage the findings are written in
    custom_prompt—Extra instructions for the reviewer
    temperature0.5Sampling temperature
    max_tokens8192Response length cap. Below ~4000 a large review truncates and is lost
    config_path.genai-review.ymlRepository config file, read from the base branch
    runlog_pathgenai-review-runlog.jsonWhere the per-run JSON log is written

    ignore_paths defaults to skipping lockfiles, vendored and generated code, snapshots, migrations and minified assets — files nobody reviews by hand. Set it to a single space to review everything.

    Deprecated inputs

    openai_api_key, openai_model, openai_temperature and openai_max_tokens still work and are used whenever their v3 equivalent is unset. A workflow written for v2 runs on v3 unchanged. See the migration guide.


    Security

    Permissions. The action needs contents: read and pull-requests: write and nothing more.

    Do not use pull_request_target for pull requests from forks. That trigger runs with a token that can write to your repository and with access to your secrets, while the code being reviewed is controlled by whoever opened the pull request. The ordinary pull_request trigger is correct here: forks get a read-only token and no secrets, which is exactly what you want when the input is untrusted. If reviews on fork pull requests matter to you, accept that they will not run rather than reaching for pull_request_target.

    The diff is treated as untrusted data. Code under review can contain text aimed at the reviewer — "ignore previous instructions", a forged system message, a comment claiming the change is pre-approved. The diff is fenced with a randomly generated per-request sentinel it cannot forge, the real instructions come after the data, and the model is told that an injection attempt is itself worth reporting as a security finding. The bot never executes anything from a diff; its only output is findings validated against a schema.

    API keys. Pass them from a secret. The key is never written to the run log, never included in an error message posted to a pull request, and never read from .genai-review.yml. Neither is base_url, the server the key is sent to.

    The config file cannot be changed by the pull request it configures. .genai-review.yml is read from the base branch, so a pull request that edits it is reviewed under the settings already merged.

    What leaves your repository. The diff of the files being reviewed is sent to whichever provider you configure. If that is not acceptable for a given repository, use ignore_paths to exclude the sensitive parts, or point provider: openai-compatible at a model you host yourself.


    Run logs

    Every review writes a JSON file with provider, model, tokens, estimated cost, latency, findings by severity, posted versus suppressed, and what was not reviewed. Upload it to keep a record:

          - uses: actions/upload-artifact@v4
            if: always()
            with:
              name: genai-review-runlog
              path: genai-review-runlog.json
    

    It contains no credentials, no diff and no source code.

    Did anyone act on the comments?

    The run log records what the bot said. To see what reviewers did with it — reactions, replies and whether the thread was resolved — read a repository the action has already run on:

    make feedback REPO=owner/name        # needs $GITHUB_TOKEN with read access
    

    It is read-only and calls no model. The signals are reported side by side rather than averaged into one score: a resolved thread means the conversation ended, which happens both when a finding is fixed and when it is dismissed. The row that carries the most information is the share of comments nobody touched at all.


    How it works

    1. Files matching ignore_paths are dropped before anything is sent.
    2. On a push, only the commits since the last review are considered.
    3. The remaining diff is chunked to fit the model's context, and anything that does not fit is reported rather than silently truncated.
    4. The model returns findings matching a JSON schema — file, line range, severity, category, confidence, title, rationale, optional suggestion. Output that does not validate gets exactly one repair attempt.
    5. Findings are checked against the diff. GitHub rejects an entire review if one comment points outside the diff, so findings that cannot be placed are reported in the summary instead of taking the review down with them.
    6. The top max_comments are posted as one grouped review, and a single sticky summary comment is created or updated.

    Development

    make install   # dependencies
    make check     # ruff + pytest
    make eval-dry  # list the eval fixtures without calling a provider
    make eval      # run the eval set (needs an API key; costs money)
    make feedback  # acceptance signal from a repository the action has run on
    

    Related work

    dlidstrom/genai-code-review is a fork that kept this action working for people while this repository sat unmaintained for two years, and published releases under its own v3.0.x tags. If you are using it today, it still works. That fork and this one are not version-aligned; the tags here are independent of the ones there.

    License

    MIT — see LICENSE.

    Authors