SSD Nodes Learn 🎉 VPS mula $5.50/buwan
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-13

Self-host AI PR Review Agent sa VPS

I-host ang AI pull request reviewer sa VPS mo gamit ang self-hosted runner, diff-scoped prompts, path at size filters, inline comments, at aktuwal na cost per PR.

Ginagawa ng self-hosted PR review agent

Ang self-hosted PR review agent ay isang maliit na program na tumatakbo sa server na pagmamay-ari mo. Binabasa nito ang diff ng isang pull request (PR) at ipinapadala lamang sa model ang mga linyang nabago. Ang resulta ay ipinopost bilang mga inline review comment. Hindi nito kailanman chine-checkout ang branch mo, at hindi nito binabasa ang file na hindi ginalaw ng pull request. Ang tanging credential na hawak nito ay isang model API (application programming interface) key at isang token na maaaring mag-post ng comment at wala nang ibang magagawa.

Kayang basahin ng model ang isang diff. Nalutas na ang bahaging iyon. Ang mahalaga ay kung saan napupunta ang diff at kung sino ang may hawak ng key. Kapag hosted review bot ang gamit, lumalabas sa network mo ang bawat diff mula sa bawat private repository, napupunta sa logs ng third party, at saklaw ng retention policy nila. Sa isang VPS (virtual private server) na pagmamay-ari mo, dumadaan ang diff mula GitHub papunta sa server mo at saka sa model API. Mababasa mo rin ang apatnapung linya ng code na nagpapasya kung ano ang ipapadala.

Mga kailangan bago magsimula

  • Isang VPS na nagpapatakbo ng Ubuntu 24.04, na may self-hosted GitHub Actions runner na nakarehistro na sa repository. Idagdag ang label na pr-review kapag nirerehistro ito, dahil ginagamit ng workflow sa ibaba ang label na iyon para sa pagpili.
  • Isang Anthropic API key mula sa Claude Console.
  • Isang repository kung saan kontrolado mo kung sino ang maaaring magbukas ng pull request. Ang private repository ang pinakamadaling kaso. Sinasaklaw ng seksyon sa ibaba tungkol sa fork ang public case, ngunit hindi gaanong komportable ang sagot doon.

I-install ang reviewer sa VPS

Tumatakbo ang runner service gamit ang unprivileged account na ginawa mo noong pinatakbo mo ang ./svc.sh install. I-install ang reviewer gamit ang account ding iyon para maipatupad ito ng job nang hindi gumagamit ng sudo. Palitan ang runner sa ibaba ng pangalan ng account mo.

sudo apt update && sudo apt install -y gh python3-venv
sudo install -d -m 755 -o runner -g runner /opt/pr-review
sudo -u runner python3 -m venv /opt/pr-review/venv
sudo -u runner /opt/pr-review/venv/bin/pip install anthropic
gh --version

Ipinapakita ng gh --version ang gh version 2.45.0 sa Ubuntu 24.04 noong August 2026. Ang anumang release mula 2.20 pataas ay may --input flag na ginagamit sa ibaba. Ang mensaheng Command 'gh' not found ay nangangahulugang hindi naka-enable ang universe component, kaya patakbuhin ang sudo add-apt-repository universe at subukan muli.

Kung Saan Naka-store ang Key at Token

Dalawang secret, dalawang magkaibang lifetime. Hindi dapat mapunta ang alinman sa repository.

Ang ANTHROPIC_API_KEY ay isang repository secret. Itinatakda ito sa Settings, pagkatapos ay Secrets and variables, at Actions. Ine-encrypt ito ng GitHub at ini-inject sa environment ng step kapag tumatakbo na ito. Hindi ito kailanman nagiging file sa disk at hindi rin napupunta sa git history.

Iba ang paraan ng paggana ng GITHUB_TOKEN. Gumagawa ang Actions ng bagong token para sa bawat job at sinisira ito kapag natapos ang job. Ang mga puwedeng gawin ng token ay itinatakda ng permissions: block sa workflow. Dito aktuwal na ipinapatupad ang least privilege:

permissions:
  contents: read
  pull-requests: write

Puwedeng mag-post ng review ang token na iyon. Hindi ito puwedeng mag-push ng commit, mag-merge ng branch, mag-edit ng workflow file, o gumamit ng ibang repository. Ang agent na puwedeng mag-comment ay reviewer. Ang agent na puwedeng mag-push ay committer, at walang sumang-ayon doon. Ingatan ang model key nang kapareho ng pag-iingat sa token, dahil maaari itong gumastos mula sa account mo. May higit pang impormasyon tungkol sa ganitong uri ng problema sa pag-iwas na maabot ng AI agent ang mga secret.

Pinapalitan ng Actions ang eksaktong secret string ng *** sa job logs. Eksaktong string lamang ang hinahanap nito. Kaya kung i-base64 encode mo ang key, hahatiin sa dalawang linya, o ipi-print nang tig-iisang character, lalabas ito nang hindi naka-mask. Huwag magdagdag ng debug step na nagda-dump ng environment.

Bakit hindi kailanman nakikita ng pull request mula sa fork ang iyong API key

Maikli ang tuntunin ng GitHub: maliban sa GITHUB_TOKEN, hindi ipinapasa ang secrets sa runner kapag na-trigger ang workflow mula sa isang forked repository. Kaya ang pull_request run mula sa fork ay nagsisimula ng script nang walang ANTHROPIC_API_KEY, at nabibigo ang unang API call na may invalid x-api-key.

Nakakatuksong solusyon ang palitan ang trigger ng pull_request_target, na tumatakbo sa context ng base repository at nakakatanggap ng secrets. Huwag itong gawin dito. Ayon sa sariling security guidance ng GitHub, ang mga workflow na ito ay “privileged, na nangangahulugang ginagamit nila ang parehong cache ng main branch kasama ng iba pang privileged workflow trigger, at maaaring magkaroon ng write access sa repository at access sa mga referenced secret,” at maaaring “mapagsamantalahan upang maagaw ang kontrol sa isang repository.”

Direkta rin ang gabay tungkol sa runner: “Halos hindi dapat gamitin ang self-hosted runner para sa mga public repository sa GitHub, dahil maaaring magbukas ang sinumang user ng pull request laban sa repository at ma-compromise ang environment.”

Dahil dito, dalawang design choice ang kailangan. May guard ang job upang tumakbo lamang ito sa mga branch na na-push sa sarili mong repository. Wala ring actions/checkout step ang workflow. Hindi kailanman napupunta sa disk ang branch sa agent, kaya ang hostile pull request ay text lamang na ipinapadala sa isang model. Hindi ito makakapagpatakbo ng build script sa iyong VPS, dahil walang anumang bagay sa iyong VPS ang kailanman nagpapatakbo nito. Gayunman, hindi nangangahulugang harmless ang text: ang diff na isinulat ng hindi kilalang tao ay untrusted input na dumarating sa isang model. Ito ang parehong trust boundary na nakikita mo kapag nagbibigay ka sa isang agent ng web search, at ang tanging naglalaman dito sa panganib ay ang katotohanang wala nang magagawa ang agent kundi mag-post ng comment.

Kunin ang diff, hindi ang repository

Isang request ang kumukuha ng buong diff bilang plain text.

export GH_TOKEN=your_token   # in the workflow this comes from secrets.GITHUB_TOKEN
gh api /repos/OWNER/REPO/pulls/42 -H "Accept: application/vnd.github.diff"

Ang Accept: application/vnd.github.diff media type ang nagpapalit sa response mula sa JSON object na naglalarawan sa pull request tungo sa mismong unified diff, at ang gh api ay nagpi-print sa body na iyon nang walang pagbabago. Dapat magsimula sa diff --git a/ ang unang linyang makikita mo. Ang gh: Not Found (HTTP 404) ay nangangahulugang hindi makita ng token ang repository. Sa isang fine-grained personal token, halos palaging nangangahulugan ito na hindi naka-enable ang Pull requests permission.

Mag-filter bago gumastos ng token

Ang seksyong ito ang naghihiwalay sa bot na binabasa ng mga tao at sa bot na vina-mute nila. Tumatakbo ang bawat filter sa ibaba bago makakita ang model ng kahit isang byte.

  • Mga path filter. I-lock ang mga file, vendored directory, minified bundle, at generated code. Puro ingay ang komento ng model sa package-lock.json, at kadalasan, ang mga file na ito ang bumubuo sa malaking bahagi ng mga byte sa diff.
  • Limitasyon sa laki. Kapag lumampas sa limitasyon, laktawan ang review at lumabas na may green status. Sa halip na animnapung hula, isang tapat na linya lang ang ilabas para sa 4,000-line refactor: masyadong malaki ito para awtomatikong ma-review.
  • Severity threshold at limitasyon sa bilang ng komento. I-report ang high at medium findings, hanggang sampu lamang, na mauuna ang may pinakamataas na severity. Walang nagbabasa ng comment number eleven.

Ang script

I-save ito bilang /opt/pr-review/review.py. Binabasa nito ang configuration mula sa environment, kaya maaaring magpalit ng model ang workflow nang hindi binabago ang code.

#!/usr/bin/env python3
"""Review only the changed lines of one pull request."""
import json
import os
import subprocess
import sys

import anthropic

REPO = os.environ["GITHUB_REPOSITORY"]
PR = os.environ["PR_NUMBER"]
MODEL = os.environ.get("REVIEW_MODEL", "claude-haiku-4-5-20251001")
MAX_DIFF_BYTES = int(os.environ.get("MAX_DIFF_BYTES", "120000"))
MIN_SEVERITY = os.environ.get("MIN_SEVERITY", "medium")
MAX_COMMENTS = 10
RANK = {"low": 0, "medium": 1, "high": 2}
SKIP = ("package-lock.json", "poetry.lock", "/vendor/", "/node_modules/", ".min.js")

raw_diff = subprocess.run(
    ["gh", "api", f"/repos/{REPO}/pulls/{PR}",
     "-H", "Accept: application/vnd.github.diff"],
    check=True, capture_output=True, text=True,
).stdout

Ang paghahati ng diff ayon sa file ang nagpapahintulot sa path filtering. Ang paglalagay ng numero sa bawat linya ang nagpapahintulot na mailagay sa tamang lokasyon ang review comments. Tumatanggap ang GitHub ng inline comment lamang sa linyang bahagi ng diff, kaya kailangang magbanggit ang model ng totoong line number. Kapag ibinigay dito ang mga numero, maaari itong kumopya ng isa sa halip na mag-imbento.

def per_file(diff_text):
    """Split a unified diff into one string per file."""
    sections, current = [], []
    for line in diff_text.splitlines():
        if line.startswith("diff --git ") and current:
            sections.append("\n".join(current))
            current = []
        current.append(line)
    if current:
        sections.append("\n".join(current))
    return sections


def annotate(section):
    """Prefix every line that exists in the new file with its line number."""
    out, n, in_hunk = [], 0, False
    for line in section.splitlines():
        if line.startswith("@@"):
            n = int(line.split("+")[1].split(",")[0].split(" ")[0])
            in_hunk = True
            out.append(line)
        elif not in_hunk or line.startswith(("-", "\\")):
            out.append(line)
        else:
            out.append(f"{n}\t{line}")
            n += 1
    return "\n".join(out)


kept = [s for s in per_file(raw_diff)
        if not any(p in s.split("\n", 1)[0] for p in SKIP)]
payload = "\n".join(annotate(s) for s in kept)

if not payload.strip():
    print("every changed file was filtered out")
    raise SystemExit(0)
if len(payload) > MAX_DIFF_BYTES:
    print(f"diff is {len(payload)} bytes, over the {MAX_DIFF_BYTES} cap")
    raise SystemExit(0)

Nasa hunk header ang numbering. Sinasabi ng @@ -12,7 +12,9 @@ na nagsisimula sa line 12 ang hunk ng bagong file, kaya doon nagsisimula ang counter at umuusad lamang sa mga idinagdag at hindi nagbagong linya. Hindi nilalagyan ng numero ang mga inalis na linya dahil wala ang mga ito sa bagong file. Nilalaktawan ng guard sa mga linyang nagsisimula sa backslash ang no-newline marker na isinusulat ng git sa dulo ng file. Kung hindi ito lalaktawan, malilihis ng isa ang bawat kasunod na numero.

Parehong status 0 ang ginagamit ng dalawang exit, hindi 1. Dapat magpakita ng green check ang filtered o oversized na pull request. Hindi pinapansin ang red check na walang aksiyong magagawa ang tao rito. Kapag binalewala ang isang check, binabalewala na rin ang lahat ng ito.

SYSTEM = (
    "You review one pull request diff. Every line that exists in the new file is "
    "prefixed with its line number and a tab character. "
    "Report only defects you can see in the lines shown: a crash, a resource leak, "
    "a security mistake, a wrong boundary condition, a broken contract with code "
    "that is visible in this diff. Do not comment on style, naming or formatting. "
    "Do not guess about code you cannot see. Leave out anything you are not "
    "certain about. An empty findings list is a normal and common answer. "
    'Reply with JSON only, in this shape: {"findings": [{"path": "src/app.py", '
    '"line": 42, "severity": "high", "comment": "what is wrong, then why"}]} '
    "Every line number must be one you can see in the left column of that file."
)

client = anthropic.Anthropic()
message = client.messages.create(
    model=MODEL,
    max_tokens=2000,
    system=SYSTEM,
    messages=[{"role": "user", "content": payload}],
)
print(f"stop={message.stop_reason} in={message.usage.input_tokens} "
      f"out={message.usage.output_tokens}", file=sys.stderr)

text = message.content[0].text
findings = json.loads(text[text.find("{"):text.rfind("}") + 1])["findings"]
findings = [f for f in findings if RANK.get(f["severity"], 0) >= RANK[MIN_SEVERITY]]
findings.sort(key=lambda f: -RANK.get(f["severity"], 0))
del findings[MAX_COMMENTS:]

if not findings:
    print("nothing above the severity threshold; posting no comment")
    raise SystemExit(0)

review = {
    "event": "COMMENT",
    "body": f"Automated review of the changed lines. {len(findings)} finding(s).",
    "comments": [
        {"path": f["path"].removeprefix("b/"), "line": f["line"], "side": "RIGHT",
         "body": f"**{f['severity']}** {f['comment']}"}
        for f in findings
    ],
}
subprocess.run(
    ["gh", "api", "-X", "POST", f"/repos/{REPO}/pulls/{PR}/reviews", "--input", "-"],
    input=json.dumps(review), text=True, check=True,
)

Tatlong detalye sa block na iyon ang mahalaga sa paggana nito. Hinahati ang JSON sa pagitan ng unang { at huling } dahil minsan ibinabalot ng model ang sagot nito sa code fence, at hindi ito kayang i-parse ng json.loads. Tinatanggal ang nangungunang b/ sa path dahil nagmumula ang prefix na iyon sa diff header, samantalang repository-relative path ang kailangan ng GitHub. Ipinapadala ng --input - ang buong review bilang isang API call, kaya dumarating ang sampung finding bilang isang notification sa halip na sampu.

Kapag walang iuulat, walang ipinapadalang post ang script. Ang bot na nagsusulat ng “walang nakitang issue” sa bawat pull request ay nagtuturo sa mga tao na lampasan ito sa pagbasa. Dahil dito, maaari rin nilang lampasan ang isang review na mahalaga.

Iugnay ito sa workflow

I-save ito bilang .github/workflows/pr-review.yml:

name: pr-review

on:
  pull_request:
    types: [opened, synchronize, reopened]
    paths-ignore:
      - '**.md'
      - 'docs/**'

permissions:
  contents: read
  pull-requests: write

concurrency:
  group: pr-review-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  review:
    if: github.event.pull_request.head.repo.full_name == github.repository && !contains(github.event.pull_request.labels.*.name, 'no-ai-review')
    runs-on: [self-hosted, linux, pr-review]
    steps:
      - name: Review the changed lines
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
          REVIEW_MODEL: claude-haiku-4-5-20251001
          MIN_SEVERITY: medium
        run: /opt/pr-review/venv/bin/python /opt/pr-review/review.py

Wala ang GITHUB_REPOSITORY sa env: block na iyon dahil awtomatikong itinatakda ito ng Actions para sa bawat job. Mahalaga ang concurrency group para sa bill: kung wala ito, ang tatlong mabilis na fix na i-push sa isang branch ay magpapatakbo ng tatlong buong review at babayaran mo ang lahat ng iyon. Kapag mayroon ito, ang pinakahuling fix lamang ang matitira.

Dalawang gawain ang ginagawa ng if: line. Nilalaktawan ng unang bahagi ang pull request mula sa fork, na mabibigo rin dahil walang key. Binibigyan naman ng ikalawang bahagi ang team mo ng off switch: idagdag ang no-ai-review label sa isang pull request at hindi tatakbo ang job.

Magbukas ng pull request at obserbahan ang mangyayari:

gh run list --workflow=pr-review.yml --limit 3
gh run view --log
gh pr view 42 --comments

Tama ang paggana kapag natapos ang run sa loob ng ilang segundo at lumitaw ang nothing above the severity threshold; posting no comment sa log. Iyan ang inaasahang resulta para sa maliit at malinis na pull request.

Magkano ang gastos sa automated pull request review?

Halos lahat ng input ay nasa diff, kaya ang laki ng diff ang nagtatakda ng presyo. Nasa ibaba ang isang aktuwal na nasukat na 500-line diff kasama ang system prompt. Binilang ito gamit ang token counting endpoint sa halip na tantiyahin.

ChartTokens for one 500 line pull request diff, measured August 2026
The data behind this chart
[
  {
    "label": "Haiku 4.5",
    "input_tokens": "8,000",
    "output_tokens": "1,200"
  },
  {
    "label": "Sonnet 5",
    "input_tokens": "10,400",
    "output_tokens": "1,560"
  },
  {
    "label": "Opus 5",
    "input_tokens": "10,400",
    "output_tokens": "1,560"
  }
]

Umabot ang diff na iyon sa 8,000 input tokens sa Haiku 4.5 at 10,400 sa Sonnet 5. Pareho ang text, pero magkaiba ang bilang. Gumagamit ang mga Claude model mula 4.7 pataas ng mas bagong tokenizer na lumilikha ng humigit-kumulang 30% mas maraming token para sa parehong input. Inilalahad ito ng Anthropic sa pricing page nito. Isama ito sa kalkulasyon kapag ikinukumpara ang mas bagong model sa mas luma batay lamang sa presyo kada milyong token.

Mga list price noong August 2026: $1 kada milyong input tokens at $5 kada milyong output tokens ang Haiku 4.5. Ang Sonnet 5 ay $2 at $10 sa ilalim ng introductory pricing na hanggang 31 August 2026. Pagkatapos nito, magiging $3 at $15 ang mga presyo. Ang Opus 5 ay $5 at $25.

ChartCost of that one review at list prices, August 2026
The data behind this chart
[
  {
    "label": "Haiku 4.5",
    "cost_per_pr_cents": 1.4,
    "cost_200_prs_usd": "2.80"
  },
  {
    "label": "Sonnet 5",
    "cost_per_pr_cents": 3.64,
    "cost_200_prs_usd": "7.28"
  },
  {
    "label": "Opus 5",
    "cost_per_pr_cents": 9.1,
    "cost_200_prs_usd": "18.20"
  }
]

Iyan ay 1.4 cents kada pull request sa Haiku 4.5 at 9.1 cents sa Opus 5. Ang team na nagme-merge ng 200 pull request bawat buwan ay magbabayad ng humigit-kumulang $2.80 sa Haiku 4.5, $7.28 sa Sonnet 5, o $18.20 sa Opus 5. Simula 1 September 2026, i-multiply ang Sonnet 5 row sa 1.5.

Dalawang bagay ang maaaring magpataas sa aktuwal na bill kaysa sa estimate. Ang synchronize trigger ay nagre-review sa bawat push. Kaya ang active branch na may walong push ay nagkakahalaga ng walong review. Nakakatulong lamang ang concurrency rule kapag halos magkakasabay dumating ang mga push. Ipinapalagay din ng mga figure na gumagana ang path filters. Maaaring doblehin ng isang lock file na hindi na-filter ang input nang mag-isa.

Hindi nakakatulong dito ang prompt caching. Kailangang byte-identical ang cached prefix sa bawat call, pero iba ang diff sa bawat pagkakataon. Ang system prompt lamang ang stable na bahagi, at mas maikli ito kaysa sa minimum cacheable length. Para sa pangkalahatang tuntunin, tingnan ang kung kailan sulit ang prompt caching, at para sa pagpili sa tatlong model sa itaas, tingnan ang kung aling Claude model ang gagamitin para sa bawat trabaho.

Sukatin ang sarili mong diff bago ito i-enable

Idagdag ang linyang ito pagkatapos mabuo ang payload, pagkatapos ay manu-manong patakbuhin ang script laban sa ilang pull request mula noong nakaraang buwan:

print(client.messages.count_tokens(
    model=MODEL, system=SYSTEM, messages=[{"role": "user", "content": payload}]
).input_tokens)

Hindi pinapatakbo ng count endpoint ang model, kaya hindi ito kumokonsumo ng input o output tokens. Ginagamit nito ang tokenizer na kabilang sa model na tinukoy mo. Patakbuhin ito sa sampung aktuwal na pull request mula sa sarili mong repository, at gamitin ang median sa halip na mean upang hindi maapektuhan ang estimate ng isang napakalaking migration.

Bakit nami-mute ang mga review bot, at paano ito maiiwasan

Dalawang behavior ang sumisira sa tiwala sa mga bot na ito, at parehong may solusyon sa code sa itaas.

Lahat ay nire-review nang sabay-sabay. Ang bot na nag-iiwan ng 40 komento ay malamang na walang mabasang komento ang mga engineer. Hindi simpleng usapin ng pagiging magalang ang severity threshold at limitasyong 10 komento. Pinananatili nitong nakikita ang mahahalagang finding. Kapag inayos muna ayon sa severity bago i-truncate, ang hindi gaanong mahalagang finding ang inaalis ng limitasyon, sa halip na random na 10 finding.

May kumpiyansang komento tungkol sa bagay na hindi nito masusuri. Ito ang dahilan kung bakit tuluyan itong pinapatay ng mga engineer. Kapag ipinakita sa isang model ang 200 linya mula sa codebase na may 40,000 linya, magsusulat pa rin ito ng “sinisira nito ang cache invalidation sa redis_client.py” tungkol sa file na hindi naman nito nakita. Malinaw na itinutuwid ito ng system prompt: i-report lamang ang mga defect na nakikita sa ipinakitang mga linya, at alisin ang anumang hindi tiyak. Mas epektibong direktang pangalanan ang failure kaysa humingi lang ng accuracy sa pangkalahatan. Ang pagsasabi sa model na normal ang walang laman na resulta ang pumipigil dito na mag-imbento ng sasabihin tungkol sa dalawang-linyang pagbabago.

I-post ang review bilang COMMENT, hindi bilang REQUEST_CHANGES. Hindi dapat kayang i-block ng opinyon ng model ang isang merge. Kapag kaya na nito, may taong nagmamadali dahil sa deadline na mag-aalis ng buong workflow sa halip na makipagtalo rito.

Mga failure mode at mga string na makikita mo

HTTP 422 kapag ipinapadala ang review. Ipinapakita ng gh ang gh: Unprocessable Entity (HTTP 422), at tinutukoy ng response body ang field: Pull request review thread line must be part of the diff. Hindi ma-anchor ng GitHub ang comment na iyon. Karaniwang sanhi ang line number na inimbento ng model, ang path na may dala pa ring prefix na b/, o comment sa tinanggal na linya. Sa huling kaso, kailangang itakda ang side sa LEFT sa halip na RIGHT. I-print ang review JSON bago ito ipadala, at manu-manong itugma ang isang comment sa diff.

invalid x-api-key mula sa model API. Nabibigo ang step sa unang tawag sa messages.create. Maaaring hindi nakatakda sa repository ang ANTHROPIC_API_KEY secret, o nagmula sa fork ang pull request kaya walang secret na ipinasa ang Actions. Dapat sana itong nilaktawan ng fork guard sa linyang if:, kaya suriin muna ang linyang iyon.

gh: Resource not accessible by integration (HTTP 403). Walang permission ang job token na magsulat sa pull requests. Idagdag ang pull-requests: write sa block na permissions:. Kung naroon na ito, pumunta sa Settings, pagkatapos sa Actions, at pagkatapos sa General. Maaaring nililimitahan ng policy ng organization kung ano ang maaaring hilingin ng anumang workflow token.

json.decoder.JSONDecodeError. Hindi nagbalik ang model ng JSON na ma-parse. Karaniwang sanhi ang response na umabot sa token ceiling at naputol sa kalagitnaan ng object. Para rito mismo ang log line na nagpi-print ng stop_reason: ang halagang max_tokens ay nangangahulugang dapat itaas ang max_tokens o ibaba ang MAX_COMMENTS.

Hindi kailanman tumatakbo ang workflow. Walang ipinapakita ang gh run list para sa pull request. Suriin kung hindi na-filter ng paths-ignore ang lahat ng binagong file. Pagkatapos, suriin ang fork guard at label guard, at tiyaking aktibo ang runner gamit ang sudo systemctl status 'actions.runner.*' sa VPS. Kapag offline ang runner, mananatiling queued ang job nang walang error message saanman sa pull request.

Palaging walang laman ang ibinabalik na review. Itakda ang MIN_SEVERITY sa low para sa isang run. Kung may lumabas na findings, gumagana ang threshold. Kung wala pa ring lumabas, i-print ang payload at tiyaking hindi naalis ng mga filter ang buong diff.

Pagsasabay nito sa iba mo pang agent

Maliit ang reviewer, kaya nakatutuksong ilagay ito sa server na nagpapatakbo na ng lahat ng iba pa. Panatilihin itong hiwalay kung mahalaga ang repository. May hawak ang process na ito na token na maaaring magkomento sa iyong code at key na maaaring gumastos ng pera mo. Ang self-hosted runner ay sadyang lugar kung saan nag-e-execute ang workflow code. Ang dedikadong unprivileged account na walang sudo rights, sa host na walang ibang tumatakbo, ang baseline. Kung nagpapatakbo ka rin ng mga interactive agent na nagche-check out ng code, ang disposable VM para sa bawat agent ang pattern na subok sa aktuwal na paggamit. Para sa pangkalahatang setup, tingnan ang pagpapatakbo ng coding agent sa isang VPS. Kung bago sa iyo ang Anthropic API, mas maliit at mas madaling simulang halimbawa ang unang Claude API app sa isang VPS kaysa rito.

FAQ

Kailangan ba ng write access sa repository ko ang isang AI PR review agent?

Hindi. Kailangan nito ang pull-requests: write upang mag-post ng review at ang contents: read upang kunin ang diff. Iyon lang ang buong listahan, at itinatakda mo ito sa permissions: block ng workflow, na naglilimita sa maaaring gawin ng per-job na GITHUB_TOKEN. Sa dalawang linyang iyon, maaaring magkomento ang agent sa isang pull request pero hindi ito makakapag-push ng commit o makakapag-merge ng branch. Gamitin ang event: COMMENT sa pag-post ng mga review sa halip na REQUEST_CHANGES upang hindi rin nito ma-block ang isang merge.

Bakit nagfa-fail ang review comment ko gamit ang HTTP 422?

Tumatanggap ang GitHub ng inline review comment lamang sa linyang bahagi ng pull request diff, at ibinabalik nito ang Pull request review thread line must be part of the diff kapag hindi ito bahagi nito. Tiyaking repository-relative ang path at walang b/ prefix mula sa diff header, at tiyaking lumilitaw ang line number sa loob ng hunk ng file na iyon. Dapat na RIGHT ang side para sa idinagdag o hindi binagong linya, at LEFT naman para sa tinanggal na linya. Kung lalagyan ng new-file line number ang bawat linya ng diff bago ito ipadala sa model, hindi na basta makakaimbento ng mga numero ang model.

Maaari ko ba itong patakbuhin sa isang public repository na may mga pull request mula sa forks?

Hindi, hindi sa ganitong design. Hindi ipinapasa ng GitHub ang mga secret sa workflow na na-trigger mula sa isang fork, kaya nawawala ang model key at nagfa-fail ang run. Sinasabi rin ng GitHub na ang self-hosted runner ay “halos hindi dapat gamitin para sa public repositories,” dahil maaaring magbukas ang sinuman ng pull request na magpapatakbo ng code sa machine mo. Para sa isang public project, maaari mong paghigpitan ang reviewer sa mga branch na direktang na-push sa repository mismo, na siyang ginagawa ng if: guard, o ilipat ang review step sa GitHub-hosted runner at tanggapin na lalabas ang diff sa sarili mong infrastructure.

Aling model ang dapat kong gamitin para sa pull request review?

Magsimula sa Haiku 4.5. Ang pagbasa ng bounded diff laban sa isang nakapirming listahan ng mga uri ng depekto ay hindi mahirap na reasoning problem, at pinananatili ng pinakamurang model ang buwanang bill sa halagang walang pagtatalunan. Lumipat sa Sonnet 5 kung mapansin mong hindi nito nakikita ang mga totoong bug sa language o framework mo, at sukatin iyon sa halip na basta ipagpalagay. Pinakamahal sa tatlo ang Opus 5 sa bawat pull request, kaya mas madaling bigyang-katwiran ang paggamit nito sa release branch kaysa sa bawat push sa bawat feature branch.