Skip to content
Tutorials12 min read

Code review in stile PR di GitHub per i coding agent

Configura in Potato l'annotazione di code review in stile PR di GitHub, con commenti inline sui diff, valutazioni di qualità a livello di file e verdetti di approvazione o rifiuto per l'output dei coding agent.

Potato Team

Perché serve annotare la code review

Quasi tutti i benchmark per coding agent riducono la valutazione a un binario: i test passano oppure no? SWE-bench riporta una percentuale di issue risolte. HumanEval riporta il pass@k. Sono metriche utili per le classifiche e inutili per capire la qualità del codice.

Un agente può passare ogni test e scrivere lo stesso codice che nessuno vorrebbe mantenere: codice con un buco di sicurezza, con un percorso lento o con uno stile che va contro il resto della codebase. Un revisore umano chiederebbe modifiche su quella PR anche con i test verdi. Se vuoi agenti che scrivono codice che le persone poi mergiano davvero, devi rivedere il codice, non limitarti a far girare i test.

Lo schema di annotazione code_review di Potato porta l'esperienza della revisione di PR su GitHub dentro uno strumento di annotazione. Gli annotatori vedono diff unificati con evidenziazione della sintassi, cliccano sulle righe del diff per aggiungere commenti inline, valutano i file su un paio di dimensioni di qualità e danno un verdetto di approvazione, richiesta di modifiche o solo commento, esattamente come nella revisione di una pull request vera. Per il riferimento completo dello schema, vedi la documentazione sull'annotazione dei coding agent e la guida alla valutazione degli agenti.

Ecco l'interfaccia di code review in Potato, con i commenti inline sul diff e le valutazioni a livello di file:

Annotazione di code review con commenti inline sul diff e valutazioni dei fileL'interfaccia di code review di Potato, con commenti inline sul diff e valutazioni di qualità a livello di file


Panoramica dello schema code_review

Lo schema code_review ha tre livelli:

  1. Commenti inline sul diff: gli annotatori cliccano su una riga qualsiasi del diff per attaccarci un commento categorizzato (bug, stile, prestazioni, sicurezza, logica, suggerimento, domanda)
  2. Valutazioni a livello di file: ogni file modificato riceve valutazioni indipendenti di correttezza (1-5) e qualità del codice (1-5)
  3. Verdetto complessivo: l'annotatore emette un verdetto finale, cioè approvazione, richiesta di modifiche o solo commento

Il flusso ricalca una code review vera, quindi risulta naturale a chi sviluppa, e l'output strutturato che produce si mappa direttamente sull'addestramento di modelli di code review.


CodingTraceDisplay: come vengono resi i diff

Il componente CodingTraceDisplay mostra le tracce dei coding agent come una sequenza di chiamate a strumenti con i relativi output, e tratta in modo particolare le modifiche ai file. Quando l'agente modifica un file, il display mostra un diff unificato con:

  • Righe rosse: righe eliminate (con prefisso -)
  • Righe verdi: righe aggiunte (con prefisso +)
  • Righe grigie: righe di contesto (invariate)
  • Numeri di riga: sia i vecchi sia i nuovi numeri di riga nella gutter
  • Evidenziazione della sintassi: evidenziazione che riconosce il linguaggio in base all'estensione del file
  • Clic per commentare: cliccando su una riga si apre un form di commento ancorato a quella riga

Il diff viene calcolato in automatico dalle operazioni di modifica dell'agente. Se l'agente ha usato uno strumento di ricerca e sostituzione, Potato ricostruisce gli stati prima e dopo e genera il diff unificato.

Per gli agenti che producono modifiche a più file in una sola traccia (cosa comune nelle correzioni di bug reali), ogni file ha la sua sezione di diff richiudibile, come nella scheda «Files changed» delle PR di GitHub.

CodingTraceDisplay mostra le modifiche al codice con l'evidenziazione della sintassi:

Coding trace display con rendering dei diff e albero dei fileCodingTraceDisplay che mostra diff unificati con evidenziazione della sintassi e una sidebar con l'albero dei file


Categorie di commento

Quando un annotatore clicca su una riga del diff per aggiungere un commento, sceglie una categoria:

CategoriaColoreDescrizioneEsempio
bugRossoIl codice ha un errore funzionale«Questo solleva una NullPointerException se user è None»
styleBluProblema di stile o di convenzione«Il progetto usa snake_case per le funzioni, non camelCase»
performanceArancioneCodice inefficiente«Questo interroga il database dentro un ciclo; usa una query in batch»
securityViolaVulnerabilità di sicurezza«L'input dell'utente finisce direttamente nella query SQL senza sanitizzazione»
logicGialloProblema logico che può non causare un fallimento immediato«Questa condizione dovrebbe essere >= e non >, off-by-one sul limite»
suggestionVerdeSuggerimento di miglioramento, non un errore«Qui valuterei un context manager per gestire la risorsa in modo più pulito»
questionGrigioServe un chiarimento«Perché è stato aggiunto questo import? Non sembra usato»

Ogni commento ha anche un corpo in testo libero, dove l'annotatore spiega il problema nel dettaglio, proprio come scrivendo un commento vero su una PR.


Valutazioni a livello di file

Dopo aver rivisto il diff di ciascun file, l'annotatore lo valuta su due dimensioni:

Correttezza (1-5):

  • 1: non funziona, introduce nuovi bug
  • 2: funziona in parte, ha problemi seri
  • 3: funziona nel percorso felice ma manca i casi limite
  • 4: funziona correttamente, con problemi minori
  • 5: del tutto corretto, gestisce bene i casi limite

Qualità del codice (1-5):

  • 1: non manutenibile, senza struttura
  • 2: qualità scarsa, problemi seri di stile o di progettazione
  • 3: accettabile, segue le convenzioni di base
  • 4: buona qualità, pulito e leggibile
  • 5: ottimo, idiomatico, ben documentato

Opzioni di verdetto

Dopo aver rivisto tutti i file, l'annotatore sceglie uno di tre verdetti:

  • Approve: il codice è pronto per il merge così com'è, o con modifiche minime
  • Request Changes: il codice va rivisto in modo sostanziale prima del merge
  • Comment Only: si lascia un feedback senza prendere una decisione sul merge

Corrispondono direttamente ai tre stati di revisione delle PR di GitHub.


Configurazione passo per passo

Passo 1: convertire le tracce dei coding agent

Le tracce dei coding agent arrivano in molti formati. Ecco gli esempi per tre agenti diffusi.

Da Claude Code (esportazione JSON):

bash
python -m potato.trace_converter \
  --input claude_code_sessions/ \
  --output data/code_traces.jsonl \
  --input-format claude_code

Da Aider (cronologia della chat):

bash
python -m potato.trace_converter \
  --input aider_logs/ \
  --output data/code_traces.jsonl \
  --input-format aider

Da SWE-Agent (directory delle traiettorie):

bash
python -m potato.trace_converter \
  --input swe_agent_trajectories/ \
  --output data/code_traces.jsonl \
  --input-format swe_agent_trajectory

Il convertitore produce un formato JSONL standardizzato. Ogni riga contiene una traccia con il task, i passi dell'agente e i diff dei file:

json
{
  "trace_id": "swe_bench_123",
  "task": "Fix the TypeError in django/db/models/query.py when calling .values() on an empty QuerySet",
  "repo": "django/django",
  "base_commit": "a1b2c3d",
  "steps": [
    {
      "type": "thought",
      "content": "Let me look at the QuerySet.values() method to understand the TypeError."
    },
    {
      "type": "action",
      "tool": "search",
      "input": "def values",
      "file": "django/db/models/query.py"
    },
    {
      "type": "observation",
      "content": "Found at line 234: def values(self, *fields):\n    ..."
    },
    {
      "type": "action",
      "tool": "edit",
      "file": "django/db/models/query.py",
      "old_content": "    def values(self, *fields):\n        clone = self._clone()\n        clone.query.set_values(fields)",
      "new_content": "    def values(self, *fields):\n        clone = self._clone()\n        if not fields and not self.query.values_select:\n            return clone\n        clone.query.set_values(fields)"
    },
    {
      "type": "action",
      "tool": "edit",
      "file": "tests/queries/test_values.py",
      "old_content": "class ValuesTests(TestCase):",
      "new_content": "class ValuesTests(TestCase):\n    def test_empty_queryset_values(self):\n        \"\"\"Calling .values() on empty QuerySet should not raise TypeError.\"\"\"\n        qs = Author.objects.none()\n        result = list(qs.values())\n        self.assertEqual(result, [])"
    }
  ],
  "files_changed": [
    {
      "path": "django/db/models/query.py",
      "diff": "--- a/django/db/models/query.py\n+++ b/django/db/models/query.py\n@@ -234,6 +234,8 @@\n     def values(self, *fields):\n         clone = self._clone()\n+        if not fields and not self.query.values_select:\n+            return clone\n         clone.query.set_values(fields)"
    },
    {
      "path": "tests/queries/test_values.py",
      "diff": "--- a/tests/queries/test_values.py\n+++ b/tests/queries/test_values.py\n@@ -1,4 +1,10 @@\n class ValuesTests(TestCase):\n+    def test_empty_queryset_values(self):\n+        \"\"\"Calling .values() on empty QuerySet should not raise TypeError.\"\"\"\n+        qs = Author.objects.none()\n+        result = list(qs.values())\n+        self.assertEqual(result, [])"
    }
  ]
}

Passo 2: configurare lo schema di code review

Crea il tuo config.yaml:

yaml
annotation_task_name: "Coding Agent Code Review"
 
data_files:
  - "data/code_traces.jsonl"
 
item_properties:
  id_key: "trace_id"
  text_key: "task"
 
# Display coding agent traces with diff rendering
display:
  type: "coding_trace"
  trace_key: "steps"
  diff_key: "files_changed"
  syntax_highlighting: true
  show_line_numbers: true
  collapse_large_diffs: true
  max_uncollapsed_lines: 200
 
annotation_schemes:
  - annotation_type: "code_review"
 
    # Inline comment categories
 
    # File-level ratings
 
    # Overall verdict
 
# Annotator settings
annotator_config:
  allow_back_navigation: true
 
# Output settings
output:
  path: "output/"
  format: "jsonl"

Passo 3: avviare il server di annotazione

bash
potato start config.yaml -p 8000

Vai su http://localhost:8000. Vedrai la prima traccia di coding agent con la descrizione del task, i passi di ragionamento dell'agente e i diff dei file resi con l'evidenziazione della sintassi.

Passo 4: il flusso di lavoro dell'annotatore

Ecco il tipico percorso di revisione:

  1. Leggi il task: capisci che cosa era stato chiesto all'agente (per esempio, «Fix the TypeError in django/db/models/query.py»)
  2. Rivedi la traccia: scorri i passi di ragionamento dell'agente per capirne l'approccio
  3. Rivedi il diff di ogni file:
    • Leggi il diff con l'evidenziazione della sintassi
    • Clicca su una riga qualsiasi per aggiungere un commento inline
    • Scegli una categoria di commento (bug, stile, prestazioni, ecc.)
    • Scrivi il corpo del commento spiegando il problema
    • Valuta il file su correttezza (1-5) e qualità del codice (1-5)
  4. Emetti il verdetto: scegli approvazione, richiesta di modifiche o solo commento
  5. Invia: clicca su «Submit» oppure premi Ctrl+Enter

Le scorciatoie da tastiera velocizzano il lavoro:

ScorciatoiaAzione
j / kNaviga tra i file
cApre un commento sulla riga selezionata
1-5Imposta la valutazione per la dimensione corrente
aImposta il verdetto su approvazione
rImposta il verdetto su richiesta di modifiche
Ctrl+EnterInvia la revisione

Formato di esportazione

Ogni revisione inviata produce un oggetto JSON strutturato:

json
{
  "trace_id": "swe_bench_123",
  "annotator": "reviewer_01",
  "timestamp": "2026-03-22T14:32:11Z",
  "review": {
    "inline_comments": [
      {
        "file": "django/db/models/query.py",
        "line": 236,
        "side": "right",
        "category": "logic",
        "body": "This early return skips set_values entirely, but if fields are provided later via .values('name'), the previous empty .values() call will have returned a clone that never went through set_values. Consider checking if this clone is still valid downstream."
      },
      {
        "file": "tests/queries/test_values.py",
        "line": 5,
        "side": "right",
        "category": "suggestion",
        "body": "Consider adding a test case for .values() followed by .values('name') to verify the chaining behavior after your fix."
      }
    ],
    "file_ratings": [
      {
        "file": "django/db/models/query.py",
        "correctness": 3,
        "code_quality": 4
      },
      {
        "file": "tests/queries/test_values.py",
        "correctness": 4,
        "code_quality": 4
      }
    ],
    "verdict": "request_changes"
  }
}

Questo formato strutturato è utilizzabile subito per addestrare modelli di code review e per l'analisi aggregata.


Analisi: lavorare con i dati delle revisioni

Caricare le revisioni

python
import json
import pandas as pd
from pathlib import Path
 
reviews = []
for f in Path("output/").glob("*.jsonl"):
    with open(f) as fh:
        for line in fh:
            reviews.append(json.loads(line))
 
print(f"Loaded {len(reviews)} code reviews")

Distribuzione delle categorie di commento

python
from collections import Counter
 
all_comments = []
for rev in reviews:
    for comment in rev["review"]["inline_comments"]:
        all_comments.append(comment)
 
category_counts = Counter(c["category"] for c in all_comments)
print("Comment categories:")
for cat, count in category_counts.most_common():
    print(f"  {cat}: {count}")

Valutazioni medie per file

python
ratings = []
for rev in reviews:
    for fr in rev["review"]["file_ratings"]:
        ratings.append(fr)
 
ratings_df = pd.DataFrame(ratings)
print("Average ratings by file:")
print(
    ratings_df.groupby("file")[["correctness", "code_quality"]]
    .mean()
    .round(2)
    .to_string()
)

Distribuzione dei verdetti

python
verdict_counts = Counter(rev["review"]["verdict"] for rev in reviews)
total = sum(verdict_counts.values())
print("Verdict distribution:")
for verdict, count in verdict_counts.most_common():
    print(f"  {verdict}: {count} ({count/total*100:.1f}%)")

Tasso di bug per agente

Se le tue tracce hanno un campo agent, puoi confrontare i tassi di bug tra agenti diversi:

python
agent_bugs = {}
for rev in reviews:
    agent = rev.get("agent", "unknown")
    bug_count = sum(
        1 for c in rev["review"]["inline_comments"]
        if c["category"] == "bug"
    )
    if agent not in agent_bugs:
        agent_bugs[agent] = []
    agent_bugs[agent].append(bug_count)
 
print("Average bugs per review by agent:")
for agent, bugs in sorted(agent_bugs.items()):
    print(f"  {agent}: {sum(bugs)/len(bugs):.2f} (n={len(bugs)})")

Casi d'uso

Addestrare modelli di code review

I commenti inline strutturati, le valutazioni dei file e i verdetti raccolti con l'annotazione di code review di Potato sono dati di addestramento adatti ai modelli di revisione automatica del codice. Ogni revisione fornisce:

  • Feedback localizzato e legato a righe precise del diff
  • Problemi categorizzati (bug, stile, prestazioni)
  • Segnali di qualità a più granularità (riga, file, complessivo)

È il formato di dati che usano strumenti come CodeRabbit e il revisore AI di Graphite, ma generato da esperti umani anziché distillato da un LLM.

Valutare i coding agent su SWE-bench

SWE-bench ti dice se l'agente ha risolto la issue (i test passano), non se il codice è mergiabile. Facendo girare l'annotazione di code review sulle soluzioni SWE-bench puoi distinguere gli agenti che risolvono le issue con codice pulito da quelli che le risolvono con degli espedienti. Ne esce una classifica più sfumata, che correla con l'esperienza reale di chi sviluppa.

Costruire dataset sulla qualità del codice

Aggrega i dati di code review su molte tracce per costruire dataset dei problemi di qualità più comuni nel codice generato dall'AI. Questi dataset servono per:

  • Fare fine-tuning dei modelli di generazione di codice perché evitino gli errori ricorrenti
  • Costruire linter specifici per gli schemi tipici del codice generato dall'AI
  • Addestrare classificatori che segnalano i probabili problemi nell'output dell'agente prima della revisione umana

In sintesi

Lo schema code_review di Potato porta il flusso di revisione delle PR di GitHub dentro la valutazione degli agenti. I commenti inline, le valutazioni dei file e i verdetti che raccogli ti danno dati strutturati sulla qualità del codice, che dicono molto più di un risultato pass/fail dei test. Sono i dati che ti servono, sia che tu stia addestrando un modello di code review, separando le soluzioni SWE-bench pulite da quelle raffazzonate, o semplicemente fissando una baseline di qualità per il tuo agente.