Agente AI self-hosted per revisionare le PR su VPS
Scopri come eseguire un revisore AI per PR su una VPS: runner self-hosted, prompt sul diff, filtri per percorso e dimensione, commenti inline e costo per PR.
Cosa fa un agente self-hosted per la revisione delle PR
Un agente self-hosted per la revisione delle PR è un piccolo programma eseguito su un server di tua proprietà. Legge il diff di una pull request (PR) e invia a un modello soltanto le righe modificate. Il risultato viene pubblicato come commenti inline nella revisione. Non esegue mai il checkout del tuo branch e non legge mai un file non modificato dalla pull request. Le uniche credenziali che conserva sono una chiave API (application programming interface) del modello e un token autorizzato ad aggiungere commenti, senza altri permessi.
Un modello è in grado di leggere un diff. Questo problema è risolto. Il punto è dove viene inviato il diff e chi conserva la chiave. Un bot di revisione hosted significa che ogni diff proveniente da ogni repository privato lascia la tua rete, finisce nei log di terzi e resta soggetto alle loro policy di conservazione. Su un VPS (virtual private server) di tua proprietà, il diff passa da GitHub al tuo server e quindi alle API del modello; puoi inoltre leggere le quaranta righe di codice che determinano cosa viene inviato.
Cosa serve prima di iniziare
- Una VPS con Ubuntu 24.04 e un runner self-hosted di GitHub Actions già registrato nel repository. Quando lo registri, assegnagli anche l'etichetta
pr-review, perché il workflow seguente seleziona i runner con questa etichetta. - Una chiave API Anthropic ottenuta dalla Claude Console.
- Un repository in cui puoi controllare chi può aprire una pull request. Un repository privato è il caso più semplice. La sezione seguente sui fork tratta il caso pubblico, ma la risposta è meno rassicurante.
Installare il reviewer sul VPS
Il servizio runner viene eseguito con l'account senza privilegi creato quando hai eseguito ./svc.sh install. Installa il reviewer con lo stesso account, in modo che il job possa eseguirlo senza sudo. Sostituisci runner qui sotto con il nome del tuo account.
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 --versiongh --version restituisce gh version 2.45.0 su Ubuntu 24.04 ad agosto 2026. Qualsiasi release dalla 2.20 in poi include il flag --input utilizzato di seguito. Un messaggio Command 'gh' not found indica che il componente universe non è abilitato; esegui sudo add-apt-repository universe e riprova.
Dove risiedono la chiave e il token
Due secret, due durate diverse. Nessuno dei due deve essere inserito nel repository.
ANTHROPIC_API_KEY è un secret del repository. Si imposta in Settings, quindi Secrets and variables e Actions. GitHub lo cifra e lo inserisce nell'ambiente dello step durante l'esecuzione. Non è mai un file su disco e non compare mai nella cronologia Git.
GITHUB_TOKEN funziona in modo diverso. Actions genera un token nuovo per ogni job e lo elimina al termine del job. Le operazioni consentite a quel token sono definite dal blocco permissions: nel workflow. È qui che si applica effettivamente il principio del privilegio minimo:
permissions:
contents: read
pull-requests: writeQuel token può pubblicare una review. Non può eseguire il push di un commit, fare il merge di un branch, modificare un file di workflow o accedere a un altro repository. Un agent che può aggiungere commenti è un reviewer. Un agent che può eseguire il push è un committer, e nessuno lo ha autorizzato. Proteggi la model key con la stessa attenzione, perché utilizza denaro del tuo account. Per ulteriori informazioni su questo tipo di problema, consulta impedire a un agent AI di accedere ai secret.
Actions sostituisce nei log dei job la stringa esatta del secret con ***. La corrispondenza riguarda solo la stringa esatta. Di conseguenza, una key codificata in base64, suddivisa su due righe o stampata un carattere alla volta viene visualizzata in chiaro. Non aggiungere uno step di debug che scarichi il contenuto dell'ambiente.
Perché una pull request da un fork non vede la tua API key
La regola di GitHub è semplice: ad eccezione di GITHUB_TOKEN, i secret non vengono passati al runner quando un workflow viene attivato da un repository forkato. Di conseguenza, un'esecuzione di pull_request avviata da un fork esegue lo script senza ANTHROPIC_API_KEY e la prima chiamata API fallisce con invalid x-api-key.
La correzione più immediata consiste nello spostare il trigger su pull_request_target, che viene eseguito nel contesto del repository di base e riceve anche i secret. In questo caso non va fatto. Le indicazioni di sicurezza di GitHub specificano che questi workflow "sono privilegiati, cioè condividono la stessa cache del branch principale con gli altri trigger di workflow privilegiati e possono avere accesso in scrittura al repository e ai secret referenziati" e che il risultato "può essere sfruttato per assumere il controllo di un repository".
Le stesse indicazioni sono altrettanto chiare sul runner: "I self-hosted runner non dovrebbero quasi mai essere usati per repository pubblici su GitHub, perché qualsiasi utente può aprire pull request verso il repository e compromettere l'ambiente."
Questo comporta due scelte progettuali. Il job include una condizione che ne consente l'esecuzione solo per i branch inviati al tuo repository. Inoltre, il workflow non include alcun passaggio actions/checkout. L'agent non ha mai il branch sul disco, quindi una pull request ostile è soltanto testo inviato a un modello. Non può eseguire uno script di build sul tuo VPS, perché sul tuo VPS non viene mai eseguito nulla di quella pull request.
Recuperare il diff, non il repository
Una richiesta restituisce l’intero diff come testo normale.
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"Il tipo di contenuto Accept: application/vnd.github.diff converte la risposta da un oggetto JSON che descrive la pull request nel diff unificato vero e proprio; gh api stampa il corpo senza modificarlo. La prima riga visualizzata dovrebbe iniziare con diff --git a/. Un gh: Not Found (HTTP 404) indica che il token non può accedere al repository. Nel caso di un personal token con autorizzazioni granulari, questo significa quasi sempre che non è stata abilitata l’autorizzazione Pull requests.
Filtra prima di consumare token
Questa sezione determina se le persone leggono i messaggi del bot oppure lo silenziano. Ogni filtro seguente viene applicato prima che il modello riceva anche un solo byte.
- Filtri sui percorsi. Escludi i lock file, le directory con dipendenze incluse, i bundle minificati e il codice generato. Un commento del modello su
package-lock.jsonè puro rumore, e questi file spesso contengono la maggior parte dei byte di un diff. - Un limite alle dimensioni. Oltre il limite, salta la revisione e termina con esito positivo. Un refactoring di 4,000 righe produce una sola indicazione corretta, cioè che era troppo grande per una revisione automatica, invece di sessanta ipotesi.
- Una soglia di gravità e un limite al numero di commenti. Segnala i problemi di gravità alta e media, fino a un massimo di dieci, ordinati dalla gravità maggiore. Nessuno legge l'undicesimo commento.
Lo script
Salvare questo contenuto come /opt/pr-review/review.py. Lo script legge la configurazione dall’ambiente, quindi il workflow può cambiare modello senza modificare il codice.
#!/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,
).stdoutSuddividere il diff per file consente di applicare il filtro sui percorsi. Numerare ogni riga consente di posizionare correttamente i commenti della revisione. GitHub accetta un commento inline solo su una riga inclusa nel diff, quindi il modello deve citare un numero di riga reale. Passargli i numeri gli permette di copiarne uno invece di inventarlo.
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)L’intestazione dell’hunk contiene la numerazione. @@ -12,7 +12,9 @@ indica che l’hunk del nuovo file inizia alla riga 12, quindi il contatore parte da quel valore e aumenta solo sulle righe aggiunte e invariate. Le righe rimosse restano prive di numero perché non esistono nel nuovo file. Il controllo sulle righe che iniziano con una barra rovesciata salta il marker di assenza della nuova riga finale che git scrive alla fine di un file; altrimenti tutti i numeri successivi risulterebbero spostati di una posizione.
Entrambe le uscite usano lo stato 0, non 1. Una pull request filtrata o troppo grande deve mostrare un controllo superato. Un controllo fallito su cui una persona non può intervenire viene ignorato e, una volta ignorato un controllo, vengono ignorati tutti.
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,
)In quel blocco ci sono tre dettagli essenziali. Il JSON viene estratto tra il primo { e l’ultimo } perché a volte un modello racchiude la risposta in un blocco di codice e json.loads non gestisce questo delimitatore. Il path viene privato del prefisso iniziale b/, perché quel prefisso proviene dall’intestazione del diff e GitHub richiede un percorso relativo alla repository. Infine, --input - invia l’intera revisione con una sola chiamata API, quindi dieci segnalazioni arrivano come una notifica invece che come dieci.
Quando non ci sono problemi da segnalare, lo script non pubblica nulla. Un bot che scrive «nessun problema rilevato» su ogni pull request abitua le persone a ignorare quel messaggio e, di conseguenza, anche quello che conta davvero.
Collegalo al flusso di lavoro
Salva questo contenuto in .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.pyGITHUB_REPOSITORY non si trova nel blocco env: perché Actions lo imposta già per ogni job. Il gruppo concurrency è importante per i costi: senza questa impostazione, il push di tre correzioni rapide su un branch avvia tre revisioni complete e paghi tutte e tre; con questa impostazione, rimane soltanto l'ultima.
La riga if: svolge due funzioni. La prima parte esclude le pull request provenienti da fork, che fallirebbero comunque perché non dispongono della chiave. La seconda offre al team un modo per disattivare il job: aggiungi l'etichetta no-ai-review a una pull request e il job non viene eseguito.
Apri una pull request e osserva il risultato:
gh run list --workflow=pr-review.yml --limit 3
gh run view --log
gh pr view 42 --commentsUn'esecuzione che termina in pochi secondi e mostra nothing above the severity threshold; posting no comment nel log funziona correttamente. Questo è il risultato previsto per una pull request piccola e pulita.
Quanto costa una revisione automatizzata delle pull request?
Il diff costituisce quasi tutto l'input, quindi la dimensione del diff determina il costo. Di seguito è riportato un diff misurato di 500 righe, insieme al system prompt. Il conteggio è stato ottenuto tramite l'endpoint di conteggio dei token, non tramite una stima.
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"
}
]Quel diff ha prodotto 8,000 token di input su Haiku 4.5 e 10,400 su Sonnet 5. Il testo è identico, ma il conteggio è diverso. A partire dalla versione 4.7, i modelli Claude usano un tokenizer più recente che produce circa il 30% di token in più a parità di input. Anthropic documenta questo comportamento nella pagina dei prezzi. È necessario tenerne conto quando si confronta il costo per milione di token di un modello più recente con quello di un modello precedente.
Prezzi di listino ad agosto 2026: Haiku 4.5 costa $1 per milione di token di input e $5 per milione di token di output. Sonnet 5 costa $2 e $10 con il prezzo promozionale valido fino al 31 agosto 2026; successivamente costerà $3 e $15. Opus 5 costa $5 e $25.
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"
}
]Il costo è di 1.4 centesimi per pull request su Haiku 4.5 e di 9.1 centesimi su Opus 5. Un team che esegue il merge di 200 pull request al mese paga circa $2.80 con Haiku 4.5, $7.28 con Sonnet 5 oppure $18.20 con Opus 5. Dal 1 settembre 2026, moltiplicare per 1.5 il valore relativo a Sonnet 5.
Due aspetti possono portare il costo effettivo oltre questa stima. Il trigger synchronize esegue una revisione a ogni push, quindi un branch attivo con otto push costa quanto otto revisioni. La regola sulla concorrenza è utile solo quando i push arrivano a breve distanza l'uno dall'altro. Inoltre, le cifre presuppongono che i filtri sui percorsi funzionino: un singolo lock file non filtrato può raddoppiare da solo la quantità di input.
Il prompt caching non è utile in questo caso. Il prefisso memorizzato nella cache deve essere identico a livello di byte tra una chiamata e l'altra, mentre il diff cambia ogni volta. Il system prompt è l'unica parte stabile, ma è molto più breve della lunghezza minima richiesta per la cache. Per la regola generale, vedere quando il prompt caching è conveniente; per scegliere tra i tre modelli precedenti, vedere quale modello Claude usare per ogni attività.
Misura i tuoi diff prima di attivarlo
Aggiungi questa riga dopo la creazione di payload, quindi esegui manualmente lo script su alcune pull request del mese scorso:
print(client.messages.count_tokens(
model=MODEL, system=SYSTEM, messages=[{"role": "user", "content": payload}]
).input_tokens)L'endpoint di conteggio non esegue il modello, quindi non consuma token di input o di output e usa il tokenizer corrispondente al modello specificato. Eseguilo su dieci pull request reali del tuo repository e usa la mediana anziché la media, in modo che una singola migrazione di grandi dimensioni non falsi la stima.
Perché i bot di revisione vengono disattivati e come evitarlo
Due comportamenti compromettono la fiducia in questi bot. Entrambi hanno una correzione nel codice precedente.
Esaminare tutto in una volta. Un bot che lascia quaranta commenti non viene letto. La soglia di gravità e il limite di dieci commenti non sono una formalità. Servono a mantenere visibili i problemi effettivi. Ordinare i risultati per gravità prima di troncarli fa sì che il limite elimini i problemi meno importanti, anziché dieci risultati casuali.
Commentare con sicurezza su qualcosa che non può verificare. Questo è il comportamento che porta gli sviluppatori a disattivare definitivamente il bot. Un modello che riceve 200 righe di una codebase di 40,000 righe scriverà comunque «questo interrompe l'invalidazione della cache in redis_client.py» riferendosi a un file che non ha mai visto. Il system prompt lo vincola con istruzioni chiare: segnalare solo i difetti visibili nelle righe mostrate ed escludere tutto ciò di cui non si è certi. Indicare direttamente il possibile errore funziona meglio che chiedere genericamente maggiore accuratezza. Specificare che un risultato vuoto è normale impedisce al modello di inventare un problema da segnalare anche quando la modifica riguarda solo due righe.
Pubblicare la revisione come COMMENT, mai come REQUEST_CHANGES. L'opinione di un modello non deve poter bloccare un merge. Non appena questo diventa possibile, qualcuno con una scadenza imminente rimuoverà l'intero workflow invece di discuterne.
Modalità di errore e stringhe visualizzate
HTTP 422 durante l'invio della review. gh stampa gh: Unprocessable Entity (HTTP 422) e il corpo della risposta indica il campo: Pull request review thread line must be part of the diff. GitHub non riesce ad ancorare quel commento. Le cause più comuni sono un numero di riga inventato dal modello, un path che contiene ancora il prefisso b/ oppure un commento su una riga rimossa, per il quale è necessario impostare side su LEFT invece che su RIGHT. Stampa il JSON della review prima dell'invio e verifica manualmente un commento rispetto al diff.
invalid x-api-key dall'API del modello. Il passaggio non riesce alla prima chiamata messages.create. Il secret ANTHROPIC_API_KEY non è impostato nel repository oppure la pull request proviene da un fork e Actions non ha passato alcun secret. Il controllo sui fork nella riga if: avrebbe dovuto saltare il passaggio; verifica quindi prima quella riga.
gh: Resource not accessible by integration (HTTP 403). Il token del job non può scrivere nelle pull request. Aggiungi pull-requests: write al blocco permissions:. Se è già presente, controlla Settings, quindi Actions e infine General, dove un criterio dell'organizzazione può limitare ciò che ogni token del workflow può richiedere.
json.decoder.JSONDecodeError. Il modello non ha restituito JSON analizzabile. La causa più comune è una risposta che ha raggiunto il limite di token e si è interrotta nel mezzo di un oggetto. La riga di log stampa stop_reason proprio per questo: un valore di max_tokens indica che devi aumentare max_tokens o ridurre MAX_COMMENTS.
Il workflow non viene mai eseguito. gh run list non mostra nulla per la pull request. Verifica che paths-ignore non abbia escluso tutti i file modificati, quindi controlla il controllo sui fork e quello sulle label. Verifica anche che il runner sia attivo sul VPS tramite sudo systemctl status 'actions.runner.*'. Un runner offline lascia il job in coda senza mostrare messaggi di errore nella pull request.
Ogni review risulta vuota. Imposta MIN_SEVERITY su low per un'esecuzione. Se compaiono segnalazioni, la soglia funziona correttamente. Se non compare nulla, stampa payload e verifica che i filtri non abbiano rimosso l'intero diff.
Eseguirlo insieme agli altri agenti
Il reviewer è leggero, quindi può sembrare conveniente installarlo sul server che esegue già tutto il resto. Se il repository è importante, mantenetelo separato. Questo processo dispone di un token che può commentare il codice e di una chiave che può spendere denaro. Inoltre, un runner self-hosted è per definizione un ambiente in cui viene eseguito il codice dei workflow. Un account dedicato senza privilegi sudo, su un host che non esegue altri servizi, è la configurazione di base. Se eseguite anche agenti interattivi che fanno il checkout del codice, una VM temporanea per ogni agente è il modello più affidabile. Per la configurazione generale, consultate eseguire un agente di coding su un VPS. Se non avete mai usato l'API Anthropic, una prima applicazione Claude API su un VPS è un punto di partenza più semplice rispetto a questa configurazione.
FAQ
Un agente AI per la revisione delle PR deve avere accesso in scrittura al mio repository?
No. Gli servono pull-requests: write per pubblicare una revisione e contents: read per recuperare il diff. L'elenco finisce qui e queste autorizzazioni si impostano nel blocco permissions: del workflow, che limita ciò che il GITHUB_TOKEN del singolo job può fare. Con queste due righe, l'agente può commentare una pull request, ma non può eseguire il push di un commit né fare il merge di un branch. Pubblica le revisioni con event: COMMENT invece di REQUEST_CHANGES, in modo che l'agente non possa nemmeno bloccare un merge.
Perché il mio commento di revisione restituisce HTTP 422?
GitHub accetta un commento inline della revisione solo su una riga inclusa nel diff della pull request. In caso contrario restituisce Pull request review thread line must be part of the diff. Verifica che path sia relativo al repository e non contenga il prefisso b/ presente nell'header del diff. Verifica inoltre che il numero di riga si trovi all'interno di un hunk di quel file. side deve essere RIGHT per una riga aggiunta o invariata e LEFT per una riga rimossa. Aggiungere a ogni riga del diff il numero di riga del nuovo file prima di inviarlo al modello impedisce al modello di inventare numeri fin dall'inizio.
Posso eseguire questa procedura su un repository pubblico con pull request provenienti da fork?
Non con questa architettura. GitHub non passa i secret a un workflow attivato da un fork, quindi manca la chiave del modello e l'esecuzione fallisce. GitHub specifica inoltre che i self-hosted runner "dovrebbero essere utilizzati quasi mai per i repository pubblici", perché chiunque può aprire una pull request che provochi l'esecuzione di codice sulla macchina. Per un progetto pubblico, limita il revisore ai branch inviati direttamente al repository, come fa il controllo if:, oppure sposta la fase di revisione su un runner GitHub-hosted e accetta che il diff lasci la tua infrastruttura.
Quale modello devo usare per la revisione delle pull request?
Inizia con Haiku 4.5. Analizzare un diff con dimensioni limitate rispetto a un elenco fisso di tipi di difetto non richiede un ragionamento complesso. Il modello meno costoso mantiene la spesa mensile a un livello difficilmente contestabile. Passa a Sonnet 5 se riscontri che non rileva bug reali nel tuo linguaggio o framework, e misura il risultato invece di darlo per scontato. Opus 5 è di gran lunga il più costoso dei tre per pull request. È quindi più facile giustificarne l'uso su un branch di rilascio che a ogni push su ogni branch di funzionalità.