Contenuti

Claude Code, OpenCode e π (pi): anatomia di una richiesta

Intro

Chi segue da vicino le evoluzioni dei coding agents, avrà sentito parlare di pi, l’harness minimalista diventato molto popolare anche grazie al fatto che è uno dei componenti alla base di OpenClaw.

Uno degli argomenti in favore del minimalismo di pi è la contrapposizione alla presunta “pesantezza” di Claude Code, cioè un uso eccessivo di token per eseguire task anche banali. In questa polemica, pi e altri harness con la medesima filosofia si stanno ritagliando qualche spazio.

Personalmente sono un felice utilizzatore di OpenCode, ma essendo curioso e sempre alla ricerca del setup migliore possibile, volevo capirci qualcosa di più. Poiché non mi fido al 100% di quello che leggo, ho voluto verificare in prima persona le differenze concrete nel payload inviato al modello nel task più elementare possibile: cosa viene effettivamente inviato al modello al primo turno come baseline?

Non volevo fare un benchmark, né tantomento un’analisi sull’impatto di produttività per questo o quell’agent. Volevo solo dare un occhio al payload generato dai tre agent più popolari su un task banale, e capire da dove vengono i token, come vengono spesi, e se davvero “pi è 10x più economico” come si sente dire in giro.

Così ho messo in piedi il setup più semplice possibile: un progetto fittizio, tre coding agent puntati sullo stesso modello su Bedrock attraverso LiteLLM come gateway, e un singolo prompt:

just say “hello”, nothing else

Poi ho catturato l’intera request, la response e i metadati al massimo livello di verbosità per ogni harness. I numeri da soli sono già interessanti. La forma dei payload lo è ancora di più e vale la pena parlarne.

La conclusione che mi sono portato a casa: il mero conteggio degli input token non è un benchmark sensato, soprattutto quando si confrontano gli harness nella loro forma “plain-vanilla”, cioè senza customizzazioni, skills, tools, ed integrazione nell’ecosistema di ciascuno.

Ciò che invece conta davvero è il mix di tool che l’harness espone, lo steering che inserisce dentro le tool description, e l’efficacia che esso ha nello sfruttare le caratteristiche del modello e del provider (es: il prompt caching). Pi è genuinamente economico sulla singola interazione. È anche l’unico che a causa di un vincolo nelle API di AWS ha silenziosamente saltato il prompt caching, e l’unico che spedisce praticamente zero guardrail dentro i suoi tool — due scelte che la maggior parte dei team dovrà tenere in considerazione prima di usarlo in team e per un progetto reale.

Il setup

Il progetto fittizio è semplicemente una cartella che contiene questo AGENTS.md:

This is an example project to make some experiment with coding agents. Key tech stack:
 - python (uv)
 - shell (zsh)
 - aws cli

Tutti e tre gli agent sono stati configurati per parlare con lo stesso modello — eu.anthropic.claude-haiku-4-5-20251001-v1:0 su Bedrock — attraverso un proxy LiteLLM con logging completo di richieste e risposte. Dunque: stesso modello, stesso provider, stesso tokenizer, stesso backend di caching. L’unica cosa che cambia è l’harness in sé.

Altra cosa importante, per tutti e 3 gli harness ho rimosso qualsiasi tool mcp, skills, prompt, agent aggiuntivo. Di conseguenza, stiamo parlando della loro configurazione “out-of-the-box”.

Il flusso è questo:


flowchart LR
    subgraph CLI["Coding agent (CLI)"]
        H[Harness:
system prompt + tools + steering] end H -->|OpenAI-style request| G[LiteLLM Gateway] G -->|converse API| B[AWS Bedrock] B --> M[Claude Haiku 4.5] M --> B --> G --> H G -.->|verbose log:
request / response / metadata| L[(JSON files)]

Per ogni agent ho salvato tre artifact:

  • request.json — il body completo inviato al modello (system, tool, messages, hint di caching)
  • response.json — quello che è tornato indietro
  • metadata.json — la vista di LiteLLM su utilizzo e costi

I payload sanitizzati sono scaricabili qui sotto, se volete dargli un’occhiata voi stessi:

I numeri che saltano all’occhio

Ecco cosa ha effettivamente inviato ciascun harness per quel singolo turno “hello”:

HarnessToolInput tokenToken cache-creationToken cache-readCosto primo turno (USD)
Claude Code2728.40728.4040$0,0391
OpenCode1212.37412.3710$0,0170
pi52.76800$0,0031

Quindi pi è circa 10 volte più economico di Claude Code su un “hello” inviato a freddo, e Claude Code è 2,3x più costoso di OpenCode. Se ci fermassimo qui, la conclusione si scriverebbe da sola: pi vince, OpenCode è il giusto compromesso, Claude Code è il bloated della situazione. Coincide più o meno con quello che ho letto in giro.

Il problema è che il primo turno cold non è il regime in cui questi agent lavorano normalmente.

Perché il numero “economico sulla carta” inganna

Il prompt caching di Anthropic su Bedrock applica un sovrapprezzo di 1,25x per creare una cache entry, e poi 0,10x del prezzo input normale per rileggerla. Questo cambia drasticamente l’economia non appena si supera il primo turno di una sessione.

Per Haiku 4.5 (input $1,10/M, cache write $ 1,375/M, cache read $0,11/M), il costo per turno del solo invio di system prompt + tool + contesto progetto è questo:

HarnessPrimo turno (cache write)Steady state (cache read)Steady-state vs pi
Claude Code$0,0391$0,003121,01x
OpenCode$0,0170$0,001360,44x
pi$0,0031$0,003101,00x

In altre parole, se consideriamo un secondo turno con stesso minimalismo di richiesta/risposta il caching entra in funzione almeno per OpenCode e Claude Code e qui OpenCode è più economico per turno di pi, e Claude Code costa più o meno quanto pi nonostante spedisca dieci volte più token tra system prompt e tooling.

Ovviamente quando si scala su un utilizzo reale, con richieste utente da centinaia o migliaia di tokens, tutti e 3 gli harness lavorano a regime con il prompt caching attivato, dunque questo gap iniziale viene fortemente smussato e mi aspetto che a regime pi sia sempre e comunque più economico (almeno nella versione “plain-vanilla”.

Ok, ma perché pi non ha utilizzato la cache?

La risposta a questo domanda la si può trovare nella documentazione ufficiale di Anthropic e di AWS Bedrock dove si parla appunto di prompt caching. In entrambi i casi, la documentazione dice chiaramente che esiste un limite minimo di dimensione del prompt per far sì che si attivi il prompt caching. Per Haiku 4.5, il limite è pari a 4096 tokens, dunque quasi il doppio di quello inviato da pi verso l’LLM.

Si tratta davvero di overhead?

Lasciando da parte il discorso sul caching, la domanda successiva è: da dove vengono quei 28k token di Claude Code? Il pensiero comune ed anche il più immediato è che facciano parte del System Prompt, ma questo è vero solo in parte. In effetti, pur essendo abbastanza verboso, non è il System Prompt a rappresentare la componente preponderante.

Breakdown caratteri/tokens su tutte le componenti

Fortunatamente, i log di LiteLLM ci forniscono tutti i dettagli che ci servono per investigare meglio la questione.

ComponenteClaude CodeOpenCodepi
System prompt~26,5k char (~6,6k tok)~9,5k char (~2,4k tok)~3,5k char (~0,9k tok)
Tool catalog (27 / 12 / 5)~75,6k char (~18,9k tok)~38,6k char (~9,7k tok)~4,5k char (~1,1k tok)
Contesto lato utente (system reminder, AGENTS.md, prompt)~4,0k char (~1,0k tok)~30 char (~7 tok)~30 char (~7 tok)
Totale dichiarato28.40712.3742.768

Il System prompt è abbastanza lungo sia per Claude Code che per OpenCode, ma è circa un terzo rispetto alle tool definitions. In entrambi i casi, il tool catalog è infatti di gran lunga la voce più importante del budget di token.

Andando nel dettaglio dei tools, ci sono altre due cose saltano all’occhio:

  • Numero di tool. Claude Code spedisce ben 27 tool out-of-the-box, fra cui cose come Agent, EnterPlanMode, EnterWorktree, CronCreate, Monitor, PushNotification, RemoteTrigger, ScheduleWakeup, TaskCreate/Update/Stop. Molti di questi sono elementi di prodotto basilari e quasi indispensabili (sub-agent, cron job, scheduled wakeup, notifiche). OpenCode è più conservativo con 12 tools. Pi espone solo 5 tool: read, bash, edit, write, mcp.
  • Lunghezza delle description. Lo stesso nome di tool porta con sé un peso radicalmente diverso a seconda della harness. L’esempio più chiaro è bash:
HarnessLunghezza description di bash
Claude Code10.637 char
OpenCode9.936 char
pi248 char

Claude Code e OpenCode introducono entrambi circa 10 KB di policy in una singola tool description. La description di bash di pi, per intero, è invece estremamente elementare:

Execute a bash command in the current working directory. Returns stdout
and stderr. Output is truncated to last 2000 lines or 50KB (whichever is
hit first). If truncated, full output is saved to a temp file. Optionally
provide a timeout in seconds.

Stiamo parlando di una differenza di 40x, dunque a questo punto viene naturale chiedersi se davvero quei 10 KB extra sono puro overhead o se nella pratica siano in qualche maniera indispensabili per un coding harness moderno.

I guardrails di Claude Code e OpenCode

Leggendo le bash description fianco a fianco, è difficile sostenere che si tratti di uno spreco di token. Non stiamo parlando di documentazione verbosa, ma di reali policy di utilizzo, incorporate nel punto in cui è più probabile che il modello lo utilizzi. Per esempio, ecco un estratto rappresentativo dal tool bash di Claude Code:

Avoid using this tool to run cat, head, tail, sed, awk, or echo unless
explicitly instructed... Use Read / Edit / Write instead.

Try to maintain your current working directory throughout the session by
using absolute paths and avoiding usage of cd... never prepend
cd <current-directory> to a git command — git already operates on the
current working tree, and the compound triggers a permission prompt.

Long leading sleep commands are blocked. To poll until a condition is met,
use Monitor with an until-loop... Do not chain shorter sleeps to work
around the block.

Git Safety Protocol:
- NEVER update the git config
- NEVER run destructive git commands (push --force, reset --hard, ...)
  unless the user explicitly requests them
- NEVER skip hooks (--no-verify) ...
- NEVER run force push to main/master ...
- Always create NEW commits rather than amending ...

When staging files, prefer adding specific files by name rather than using
"git add -A" or "git add .", which can accidentally include sensitive
files (.env, credentials) or large binaries

La description di bash di OpenCode è strutturalmente simile: regole di verifica della directory, esempi di quoting, un blocco esplicito <good-example>/<bad-example> per workdir vs cd, lo stesso git safety protocol, la stessa regola del “no --no-verify”, e così via.

La description di bash di pi non ha niente di tutto questo. Dice “execute a bash command” e ti informa che l’output viene troncato. Niente concetto di git safety, nessuna preferenza per tool dedicati di read/edit, nessuna regola contro git add -A, nessun warning sulle operazioni distruttive.

Si può leggere la cosa in due modi:

  1. “Pi è pulito e ti lascia costruire le tue policy sopra.”
  2. “Pi dà al modello un invito di 200 caratteri a fare quello che vuole nella tua shell.”

Sono entrambe vere. Quale conta dipende da chi lo sta usando. Per un senior engineer che pilota una VM in sandbox, il minimalismo è il top. Per un team in cui l’agent potrebbe girare su un laptop con credenziali di produzione, l’assenza di prevenzione di git push --force o di warning sui file sensibili è un problema che prima o poi si finisce per riscoprire nel modo peggiore.

In altre parole: quei 10 KB di tool description in Claude Code e OpenCode non sono bloat — sono un misto di Guardrails e Steering. Sono il modo in cui la harness codifica le decine di piccole regole “non fare questo” che un team di engineering reale ha interiorizzato dopo aver visto agent comportarsi male per due anni.

Quindi il confronto non è davvero “verboso vs snello”. È “spedito con guardrail vs spedito senza”.

Cosa c’è fuori dal tool catalog

Qualche osservazione minore che mi sembra valga la pena segnalare.

System-reminder. Il messaggio utente di Claude Code non è solo just say "hello". Si porta dietro due blocchi <system-reminder> (~3 KB e ~0,9 KB) che reiniettano la lista delle skill disponibili e il contenuto di AGENTS.md / CLAUDE.md del progetto, più la data di oggi. Si tratta di una scelta deliberata di context engineering: invece di affidarsi al modello per ricordare regole sepolte nel system prompt, l’harness ridichiara i pezzi più operativamente rilevanti proprio accanto al turno utente. OpenCode incorpora l’AGENTS.md del progetto direttamente nel system prompt stesso (che è il motivo per cui il suo tool catalog è più piccolo ma il blocco di system non è banale). Pi fa la stessa cosa nel suo blocco di system, più un blocco di “Pi documentation” che punta a doc locali.

Skill discovery. Tutte e tre gli harness iniettano qualche nozione di “skill disponibili” nel prompt, ma lo fanno in modo diverso:

  • Claude Code elenca le skill invocabili dall’utente in un system-reminder, in modo che vengano riscoperte a ogni turno.
  • OpenCode le elenca sotto <available_skills> dentro al system prompt con una description di quando caricare ciascuna.
  • pi le elenca in fondo al system prompt e chiede al modello di leggere i file SKILL.md relativamente alla directory delle skill.

I pattern sono diversi, ma il design è abbastanza convergente e coerente con quello che già sapevo delle skills: si tratta di puntatori economici iniettati nel prompt, il cui body viene caricato a runtime e on demand.

Memoria e persistenza. Il system prompt di Claude Code include una sezione corposa su un sistema di memoria basato su file (~/.claude/projects/...), con categorie esplicite (user, feedback, project, reference) e regole su cosa salvare e cosa no. Solo questo pesa circa 5 KB di system prompt. OpenCode non ha un blocco equivalente nel payload catturato. Pi non ha niente del genere.

Non è davvero una decisione di “verbosità”. È una decisione di prodotto: Claude Code sta spedendo una feature di memoria cross-session e paga il costo di prompt necessario a farla funzionare. Se non vuoi la feature, quel prompt è overhead. Se la vuoi, costruirla sopra a pi significa ripagarsi lo stesso costo di prompt da soli.

Tool-choice policy. OpenCode è l’unico che imposta esplicitamente tool_choice: auto nella request. Claude Code e pi si affidano ai default del provider. Dettaglio minore, ma vale la pena notarlo se state debuggando comportamenti non deterministici delle tool call.

Una piccola nota sulla response

Il lato response è genuinamente poco interessante, ed è proprio questo il punto. Tutti e tre i modelli hanno risposto con hello (Pi l’ha messo in maiuscolo: Hello) in 4 token di completion. L’unico pseudo-finding qui è che la risposta di pi era capitalizzata anche se il prompt diceva esplicitamente just say "hello". Con un campione di uno e una risposta di 4 token, è rumore. Lo segnalo solo perché ho dato un’occhiata anche lì e non ho trovato nulla :).

Cosa mi porto a casa

1. Il conteggio dei token nelle considerazioni complessive

Il confronto interessante non è “quanti input token spedisce la harness” ma “quanti di quei token sono in cache, quanti guardrails codificano, e quali feature di prodotto si portano dietro”.

2. La verbosità delle tool descriptions

I 10 KB che Claude Code e OpenCode mettono nelle loro description di bash codificano git safety, regole sulle azioni distruttive, e preferenze del tipo “usa il tool giusto”. Non si tratta di “bloat” e toglierli non rende l’agent più snello; lo rende meno sicuro. Se si vuole usare un coding agent nel mondo reale, anche partendo da un harness minimalista come pi, è inevitabile ricostruire la maggior parte di quelle regole — o come system prompt aggiuntivo, o come AGENTS.md, o come middleware attorno al gateway.

3. Il minimalismo di pi

La leggerezza di pi nasce da una impostazione di default che non sopravvive al contatto con la realtà. Non riesco davvero ad immaginare come si possa lavorare senza guardrails su tools ad alto rischio come ad esempio bash, ma anche write e altri. Volendo integrare questi elementi, pi inizia a somigliare molto di più a OpenCode, magari con meno default tools.

4. OpenCode resta il mio preferito

Non ha il minimalismo di pi, ma è meno verboso di Claude Code, ha le guida di sicurezza nella definizione dei tool, che a mio avviso ha un livello di attenzione al dettaglio simile a Claude Code, ma ha un system prompt grande circa un terzo. Rispetto a Claude Code emerge anche l’assenza di quelle fetaure di prodotto potenzialmenten non necessarie (sub-agent, scheduling, monitor, memoria), ed è esattamente per questo che il suo prompt è più piccolo. Se non vi servono quelle feature, è un trade-off equo.

5. Claude Code: non parlerei di bloat

Sebbene System Prompt e tool description siano evidentemente più verbosi, il grosso dei token extra codifica feature di prodotto e scelte razionali: un sistema di memoria, task pianificati, sub-agent, plan mode, worktree. Se valga la pena pagare per quelle feature dipende dalle necessità di ognuno. Chiamare il prompt “bloated” senza vedere il quadro complessivo mi sembra sbagliato.

Limiti di questo esperimento

Vale la pena essere espliciti su cosa è e cosa non è:

  • È un singolo turno su un progetto fittizio. Cattura il comportamento cold-state della harness, non come ciascun agent si comporta su un task di coding reale con molte tool call e turni successivi.
  • Non ho fatto girare gli agent su lavoro di coding vero, quindi non sto facendo nessun claim su qualità dell’output, latenza o success rate.
  • I metadati di LiteLLM e i conteggi di token riportati dal provider sono presi così come sono; piccole differenze contabili tra Anthropic, Bedrock e LiteLLM sono possibili.
  • Tutte e tre le harness si muovono velocemente. I tool catalog esatti, i system prompt e i default di caching cambieranno tra le release. Trattate i numeri specifici qui come uno snapshot di maggio 2026.

Riferimenti