Controllo dei costi degli agenti IA: routing dei modelli, budget degli strumenti, cache e retry

"OpenAI API Pricing"
Alle 3 di notte, un report Agent in background restituisce una risposta vuota. Lo stato HTTP è 200, ma il body è vuoto. La logica di retry controlla solo lo status code, quindi continua a riprovare. Ogni richiesta invia 500 token in input. Dopo 1.500 retry, sono partiti 750.000 token. La mattina dopo, la fattura chiarisce subito il problema.
La causa non è “abbiamo scelto un modello costoso”. Mancavano tre cose: circuit breaker, controllo del budget e classificazione degli errori. I costi di un Agent tendono a scappare in tre modi:
Retry senza limite. I failure mode non sono classificati, quindi una risposta vuota diventa un errore recuperabile. Manca il circuit breaker, perciò nemmeno 1.500 errori consecutivi fermano l’esecuzione. Ogni retry reinvia tutto il contesto e moltiplica il costo di 2-5x.
Contesto che si gonfia. Un task lungo gira per 6 ore e la cronologia della conversazione cresce fino a 80K tokens. Senza checkpoint, un errore fa ripartire tutto da capo e ogni passaggio viene pagato di nuovo.
Uso eccessivo del modello. Tutti i task usano un frontier model perché non c’è una strategia di routing. Anche una classificazione semplice passa dal percorso più caro e spreca il 70% dei token.
Il controllo dei costi di un Agent non è una singola ottimizzazione. È un design a strati: oggetti di budget, strategia di routing, cache hit, circuit breaker dei retry, log di costo e soglie di alert. Questi sei oggetti di engineering vanno trasformati in tabelle decisionali o check eseguibili.
Oggetti di budget: cosa registrare, dove metterlo e quando interrompere
Il controllo dei costi parte da un oggetto di budget, non da un totale unico. Senza livelli, un picco in fattura non dice quale utente, task o strumento ha bruciato il budget.
Sette livelli di budget
Gli oggetti di budget si dividono dal livello più ampio al più fine:
| Livello di budget | Oggetto di budget | Limite consigliato | Trigger di alert |
|---|---|---|---|
| Layer 1 | user | Limite giornaliero/mensile per utente | Alert se restante < 20% |
| Layer 2 | tenant | Pool di budget separato per tenant | Alert se restante < 30% |
| Layer 3 | workflow | Budget separato per tipo di workflow | Alert se restante < 40% |
| Layer 4 | task | Budget separato per tipo di task | Alert se restante < 50% |
| Layer 5 | tool | Budget per chiamata di strumento | Saltare lo strumento oltre il limite |
| Layer 6 | retry | Limite di retry + circuit breaker | Disattivare lo strumento dopo N errori consecutivi |
| Layer 7 | cache | Monitoraggio del cache hit rate | Alert se l’hit rate è sotto l’atteso |
I limiti esatti dipendono dal modello business e sono configurazione mutevole. La struttura a livelli è più stabile. Il campo di budget residuo va scritto nei log di costo, così alert e circuit breaker possono usarlo.
Campi da registrare per ogni livello
Ogni livello di budget deve registrare questi campi:
| Nome campo | Uso | Tipo | Perché registrarlo |
|---|---|---|---|
model | Identificare il modello | string | Capire se il routing dei modelli è ragionevole |
inputTokens | Numero di token in input | integer | Calcolare il costo dell’input |
outputTokens | Numero di token in output | integer | Il costo dell’output va tracciato a parte |
cachedTokens | Token serviti da cache | integer | Misurare il risparmio della cache |
costEstimate | Stima del costo di questa chiamata | float | Accumulare costi in tempo reale |
budgetRemaining | Budget residuo | float | Base per decisioni del circuit breaker |
Il punto dell’oggetto di budget è contabilizzare per dimensione, non “salvare solo total_cost”. In multi-tenant, il costo va ripartito per tenantId. In un sistema con molti strumenti, toolName è il modo per aprire la scatola nera.
Logica di circuit breaker
Quando il budget residuo scende sotto una soglia, attiva il circuit breaker:
def check_budget_before_retry(budget_remaining, retry_cost_estimate):
if budget_remaining < retry_cost_estimate:
return "skip_retry" # Saltare il retry perché supererebbe il budget
if budget_remaining < threshold: # threshold ad esempio 20%
return "wait_approval" # Budget basso, attendere approvazione
return "continue"
Il circuit breaker controlla il budget prima del retry, non dopo. Prima di ogni retry stima il costo e ferma l’esecuzione se la prossima prova supererebbe il budget. Così eviti il caso “risposta vuota riprovata 1.500 volte”.
L’articolo sul context engineering della stessa serie spiegherà quale contesto entra in un prefix stabile e quale resta variabile di runtime.
Strategia di routing dei modelli: non tutti i task richiedono il modello più caro
Un ticket triage Agent spesso ha questa distribuzione: 70% dei task sono classificazioni semplici, 20% richiedono una bozza di risposta e solo 10% dovrebbe salire a un frontier model prima di inviare un’email al cliente. Una strategia di routing può risparmiare il 40-85% dei costi.
Routing a livello di modello
Il routing per modello segue la complessità del task:
| Livello task | Task tipici | Livello modello consigliato | Quota | Profilo di costo |
|---|---|---|---|---|
| 70% - livello S | Classificazione, estrazione, filtro, Q&A semplice | nano/flash (più economico) | 70% | Output breve, pochi turni, pochi tool call |
| 20% - livello M | Bozza, riassunto, generazione codice, ragionamento medio | mid-tier (prezzo medio) | 20% | Output medio, possibile uso di strumenti |
| 10% - livello L | Review, architettura, ragionamento complesso, coordinamento multi-strumento | frontier (più caro) | 10% | Output lungo, molti turni, strumenti frequenti |
La strategia ha tre passaggi:
Primo passaggio: classificare il task. Per ogni workflow definisci criteri S/M/L: lunghezza dell’output, numero di tool call, profondità di ragionamento e livello di rischio.
Secondo passaggio: partire dal livello S. Si sale a M o L solo quando compaiono segnali di complessità.
Terzo passaggio: routing a cascata. Se S fallisce, sali a M. Se M fallisce, sali a L. Se L fallisce, passa all’intervento umano. Prima di ogni upgrade controlla il budget residuo; se non basta, salta l’upgrade.
Routing a livello di servizio
Lo stesso modello può essere diviso anche per latency priority. Sconti e finestre di completamento cambiano, quindi verifica i prezzi ufficiali prima della pubblicazione.
| Livello servizio | Sconto costo | Tempo di completamento | Caso adatto |
|---|---|---|---|
| Realtime API | Nessuno sconto | Risposta immediata | Chat Agent interattiva, task ad alta priorità |
| Batch API | 50% cost discount (da ricontrollare) | 24-hour turnaround (da ricontrollare) | eval batch, classificazione, embedding, elaborazione di repository di contenuti |
| Flex Processing | Costo più basso (da ricontrollare) | Risposta più lenta, indisponibilità occasionale | Task asincroni a bassa priorità, model evaluations, data enrichment |
Checklist per il routing offline:
- Serve una risposta immediata? Sì -> Realtime API, con routing del modello.
- Può aspettare 24 ore? Sì -> Batch API.
- È a bassa priorità e tollera errori occasionali? Sì -> Flex Processing.
- È lavoro batch come eval, classificazione o embedding? Sì -> Batch API.
Livelli di rischio
I task vanno classificati anche per rischio:
| Livello di rischio | Operazione tipica | Strategia di routing | Ramo di budget |
|---|---|---|---|
| Basso rischio | Classificazione, estrazione, riassunto interno | Modello S + percorso automatico | Nessuna approvazione, limite più largo |
| Rischio medio | Bozza di risposta cliente, suggerimento di modifica codice | Modello M + approvazione opzionale | Se supera il budget, richiedere approvazione |
| Alto rischio | Inviare email al cliente, addebito, modifica architetturale | Modello L + approvazione obbligatoria | Attesa, rifiuto e timeout diventano rami di budget |
L’articolo Human-in-the-Loop della stessa serie copre come attesa, rifiuto e timeout di approvazione cambiano i rami di budget.
Vincoli chiave
Il routing ha bisogno di vincoli chiari:
- Non hardcodare i prezzi: i prezzi cambiano. Registra model + pricingVersion invece di fissare una formula di costo nella logica business.
- Controlla il budget residuo: prima di salire di livello, verifica budgetRemaining. Se non basta, salta l’upgrade o richiedi approvazione.
- Classifica gli errori: separa limiti recuperabili del modello da errori non recuperabili di parametro o permesso.
Budget delle chiamate agli strumenti: per-tool budget, timeout e limiti di retry
Le chiamate agli strumenti hanno un doppio costo: schema e API. Ogni chiamata invia schema, argomenti e contesto di parsing della risposta; inoltre l’API esterna può rate-limitare o andare in timeout. Questi costi sono separati dalla chiamata al modello.
Controllo del budget degli strumenti
Ogni strumento deve avere controlli propri:
| Controllo | Configurazione consigliata | Campo di monitoraggio | Azione |
|---|---|---|---|
| Per-tool budget | Limite per chiamata | tool_cost_estimate | Saltare lo strumento o degradare |
| Tool timeout | Timeout dell’API esterna | tool_duration | Marcare il timeout come errore recuperabile |
| Retry limit per tool | Limite di retry per strumento | tool_retry_count | Abbandonare lo strumento, senza loop di retry |
Gli strumenti che chiamano API esterne, come ricerca, database o servizi terzi, hanno bisogno di contabilità propria. Altrimenti il tool calling diventa una scatola nera di costo.
Classificazione degli errori degli strumenti
Gli errori degli strumenti si dividono in recuperabili e non recuperabili:
| Tipo di errore | Errori tipici | Strategia | Impatto sul costo |
|---|---|---|---|
| Errore recuperabile | Timeout di rete, 503 Service Unavailable, 429 Rate Limit | Retry automatico con exponential backoff e retry-after | Ogni retry invia tutto il contesto |
| Errore non recuperabile | 403 Permission Denied, 400 Bad Request, strumento inesistente | Non riprovare; iniettare l’errore perché il modello decida | Nessun retry, niente spreco ripetuto |
La regola è semplice: si riprovano solo errori temporanei causati da condizioni esterne. Gli errori di configurazione interna non si riprovano.
Circuit breaker
Dopo N errori consecutivi, disattiva lo strumento:
def circuit_breaker_tool(tool_name, consecutive_failures, threshold=5):
if consecutive_failures >= threshold:
return "disable_tool" # Disattivare lo strumento
return "continue"
Lo stato del circuit breaker va nei log, così puoi spiegare perché uno strumento è stato disattivato. Dopo lo scatto, attendi intervento umano o un controllo di recovery automatico, invece di chiamare di nuovo uno strumento instabile.
Le basi del tool calling sono in Tool Calling. Qui le estendiamo con per-tool budget, timeout e limiti di retry.
Design di Prompt Caching: prefix stabile, variabili e soglia di 1024 token
Prompt Caching ottimizza il costo dei token in input per un prompt prefix stabile. Non è una cache di risultato business. Instrada richieste con lo stesso prompt prefix verso un server che ha elaborato di recente quel prefix, riducendo latenza e costo di input.
Prompt Caching non è cache di risultato
Prompt Caching salva un prompt prefix stabile, non un risultato business. La differenza conta:
- Prompt Caching: salva un prompt prefix stabile, come system prompt o tool schema. Un hit risparmia token in input, ma l’inferenza viene comunque eseguita.
- Cache di risultato business: salva un output completo, come risultato di uno strumento o query di database. Un hit risponde direttamente senza chiamare il modello.
Gli obiettivi sono diversi. Prompt Caching riduce il costo dei token in input; la cache business riduce il costo dell’intera chiamata. Possono convivere: prefix stabili su Prompt Caching, risultati frequenti degli strumenti su cache business.
Requisiti strutturali
La chiave è separare prefix stabile e variabili di runtime:
| Tipo di contenuto | Posizione | Probabilità di cache hit | Esempi |
|---|---|---|---|
| Prefix stabile (entra in cache) | Inizio del prompt | Alta | System prompt, Tool schema, documenti Policy, esempi Few-shot |
| Variabili di runtime (fuori cache) | Più avanti nel prompt | Bassa | User input, File fragments, Runtime state (turno corrente, variabili temporanee) |
Passaggi di design:
- Metti System prompt, Tool schema e Policy all’inizio: sono stabili tra chiamate e hanno più probabilità di hit.
- Metti User input, File fragments e Runtime state dopo: cambiano a ogni chiamata e non devono entrare nel prefix stabile.
- Monitora Cache hit rate: registra cachedTokens e token totali in input, poi calcola l’hit rate. Oltre il 40% è sano; sotto il 20%, controlla la struttura del prompt.
Soglia ed effetto
La soglia automatica e l’effetto di Prompt Caching sono fatti mutevoli; ricontrolla la documentazione ufficiale prima della pubblicazione:
- Soglia: attivazione automatica da 1024 tokens in su (da ricontrollare).
- Effetto: gli hit possono ridurre costo e latenza (ricontrollare i rapporti esatti).
- Verifica hit: campo
usage.prompt_tokens_details.cached_tokens.
Modelli supportati, soglie e sconti di Prompt Caching possono cambiare. Verificali sulla pagina pricing ufficiale prima di pubblicare. Il principio stabile resta: contenuto statico davanti, contenuto variabile dietro.
Circuit breaker dei retry: idempotenza, checkpoint e budget residuo
I retry sono una delle maggiori fonti di costo fuori controllo. Un report Agent bloccato su uno strumento instabile può reinviare tutto il contesto a ogni errore; dopo 1.500 retry il costo è lontano dal percorso normale.
Controllare il budget prima del retry
Controlla il budget residuo prima di riprovare, non dopo:
def should_retry(error_type, budget_remaining, retry_cost_estimate):
# Classificazione dell'errore
if error_type in ["403", "400", "tool_not_exist"]:
return False # Errore non recuperabile, non riprovare
# Controllo del budget
if budget_remaining < retry_cost_estimate:
return False # Fuori budget, non riprovare
return True # Retry possibile
Il controllo del budget deve stare prima della logica di retry. Così eviti di spendere il budget giornaliero su una risposta vuota riprovata 1.500 volte.
Idempotenza
Un retry non deve ripetere effetti collaterali come inviare email o addebitare un pagamento:
- Usa un id di idempotenza, come requestId, per le chiamate agli strumenti. Se l’API esterna riceve lo stesso ID, deve restituire il risultato in cache invece di rieseguire l’operazione.
- Scrivi l’id di idempotenza nei log di costo per diagnosticare chiamate ripetute.
L’idempotenza serve a far sì che la stessa operazione non consumi due volte. Senza, i retry amplificano sia costo sia effetti collaterali.
Salvataggio dello stato (Checkpoint)
I task lunghi devono recuperare senza rieseguire tutto il workflow:
- Salva un checkpoint sui nodi importanti, con passaggi completati, stato corrente e sintesi del contesto.
- Dopo un errore, riprendi dal checkpoint, non dall’inizio.
- Persisti il checkpoint. Tenerlo solo in memoria non basta.
Il design di checkpoint e thread state è trattato in Architettura Agent con LangGraph.
Strategia di retry
Ogni tipo di errore richiede una strategia diversa:
| Tipo di errore | Errore tipico | Strategia di retry | Impatto sul costo |
|---|---|---|---|
| Timeout di rete | Nessuna risposta dopo 10 secondi | Exponential backoff + retry-after, massimo 3 retry | Ogni retry invia tutto il contesto |
| 503/429 | Service Unavailable, Rate Limit | Attendere finestra di rate limit + retry-after, massimo 3 retry | L’attesa non consuma token, il retry sì |
| 403/400 | Permission Denied, Bad Request | Non riprovare; iniettare l’errore perché il modello decida | Nessun retry, evita costo non valido |
La regola: si riprovano solo errori recuperabili. Non mandare richieste non valide al modello ancora e ancora.
Circuit breaker
Dopo N errori consecutivi, ferma i retry e attendi intervento:
def circuit_breaker(consecutive_failures, threshold=5):
if consecutive_failures >= threshold:
return "stop_retry" # Fermare i retry
return "continue"
La decisione del circuit breaker va nei log per spiegare perché i retry si sono fermati. Poi attendi intervento umano o recupero del budget, invece di richiamare uno strumento o modello instabile.
Log di costo e alert: quali campi e quali soglie
L’osservabilità dei costi è il prerequisito del controllo dei costi. Se i campi di log sono incompleti, non trovi il problema.
OpenTelemetry trace span attributes
Progetta i log di costo su tre livelli di span:
Agent run span (livello superiore):
| Nome campo | Uso | Tipo | Perché registrarlo |
|---|---|---|---|
runId | Identificare una specifica esecuzione | string | Distingue run ripetuti dello stesso workflow |
tenantId | Identificare il tenant | string | Ripartisce costi in sistemi multi-tenant |
userId | Identificare l’utente | string | Segue trend di costo per utente |
workflowName | Identificare il workflow | string | Segue costi per tipo di workflow |
totalCost | Costo totale stimato | float | Accumula costi in tempo reale |
budgetRemaining | Budget residuo | float | Base per decisioni del circuit breaker |
totalRetries | Numero totale di retry | integer | Mostra l’amplificazione da retry |
Model call span (span figlio):
| Nome campo | Uso | Tipo | Perché registrarlo |
|---|---|---|---|
model | Identificare il modello | string | Capire se il routing è corretto |
pricingVersion | Versione di pricing | string | Evita formule di costo hardcodate |
inputTokens | Token in input | integer | Calcola costo di input |
outputTokens | Token in output | integer | Traccia costo di output separatamente |
cachedTokens | Token in cache | integer | Calcola risparmio della cache |
costEstimate | Stima di costo della chiamata | float | Accumula costi in tempo reale |
latencyMs | Latenza della chiamata | integer | Aiuta a decidere se Batch/Flex è adatto |
Tool call span (span figlio):
| Nome campo | Uso | Tipo | Perché registrarlo |
|---|---|---|---|
toolName | Identificare lo strumento | string | Localizza overhead dei tool call |
toolBudget | Limite budget dello strumento | float | Base per circuit breaker |
toolTimeout | Timeout dello strumento | integer | Classifica errori di timeout |
retryCount | Numero di retry | integer | Misura amplificazione da retry |
errorType | Tipo di errore | string | Separa recuperabile e non recuperabile |
Non registrare solo total_cost. Dividi per dimensione. Senza questi campi, un picco in fattura dice solo “oltre budget”; non dice quale utente, strumento o retry lo ha causato.
Il design completo di log, alert e recovery è in Monitoraggio e recovery degli Agent. Questo articolo aggiunge campi di costo e oggetti di budget.
Soglie di alert
Imposta soglie per dimensione:
| Dimensione alert | Soglia alert | Canale | Azione |
|---|---|---|---|
| Consumo budget globale | 70%, 90%, 100% | Slack/email | 70% avviso, 90% degradazione, 100% interruzione |
| Consumo di un user/tenant | Oltre 3x la media | Slack/email | Controllare chiamate anomale |
| Tasso di errore di un modello | > 5% | Dashboard | Controllare routing o stato servizio |
| Retry di uno strumento | > soglia | Dashboard | Controllare stabilità dello strumento |
| Cache hit rate | < valore atteso | Dashboard | Controllare struttura del prompt |
Le soglie devono stare nella logica di costo, così alert e circuit breaker scattano automaticamente.
Strategia di degradazione
Dopo un alert, la degradazione può seguire questi percorsi:
| Percorso di degradazione | Metodo | Caso adatto | Impatto sul costo |
|---|---|---|---|
| Degradazione del modello | Modello grande -> modello piccolo | Un modello ha alto tasso di errore | Costo più basso, possibile calo qualità |
| Degradazione del percorso | Realtime API -> Batch API -> Flex Processing | Budget globale consumato troppo in fretta | Più latenza, costo minore |
| Degradazione funzionale | Disattivare strumenti non essenziali | Uno strumento ha troppi retry | Riduce overhead degli strumenti |
| Degradazione utente | Rate limit, coda, messaggio “riprova più tardi” | Consumo anomalo di un utente | Evita che un utente bruci il budget |
La degradazione deve vivere nella logica di budget. Quando scatta un alert, il sistema dovrebbe degradare automaticamente, non aspettare intervento manuale.
Prossimi passi
Il controllo dei costi degli Agent dipende anche da monitoraggio, tool calling e context engineering:
- Pubblicato: Monitoraggio e recovery degli Agent: campi di log, alert e recovery degli errori. Questo articolo aggiunge campi di costo e oggetti di budget.
- Pubblicato: Architettura Agent con LangGraph: checkpoint, thread state e recovery per task lunghi.
- Pubblicato: Tool Calling: basi delle chiamate agli strumenti. Questo articolo aggiunge per-tool budget, timeout e limiti di retry.
- Stessa serie: context engineering: prefix stabili, cache hit e quali contesti stanno nel prefix stabile rispetto alle variabili di runtime.
- Stessa serie: Human-in-the-Loop: attesa, rifiuto, timeout di approvazione e impatto su costi e retry.
Parti dalla difesa a livello di sessione: imposta un per-session cost limit e termina automaticamente la sessione quando supera il budget. È la prima protezione più rapida contro un task impazzito che brucia il budget del giorno. Poi estendi il design a livelli di budget, routing dei modelli, budget degli strumenti, Prompt Caching, circuit breaker dei retry e log di costo.
Progettare budget di costo e circuit breaker per Agent
Usa oggetti di budget, routing dei modelli, routing del livello di servizio, cache, budget degli strumenti e circuit breaker dei retry per anticipare il controllo dei costi prima di ogni run.
⏱️ Estimated time: 45 min
- 1
Step 1: Elencare tutti i percorsi di costo
Elenca chiamate ai modelli, chiamate agli strumenti, letture di file, API esterne, processi batch, cache e percorsi di retry dell'Agent. - 2
Step 2: Definire l'oggetto di budget
Registra il budget per tenant, user, run, workflow, model, tool, retry, cache e time window. - 3
Step 3: Impostare la strategia di routing
Definisci routing dei modelli e routing del livello di servizio per diversi tipi di task: online, batch, flex e queue. - 4
Step 4: Progettare i cache hit
Metti il contesto stabile nel prompt prefix, i contenuti variabili dopo, e separa prompt caching, cache di risultato business e cache di risposta degli strumenti. - 5
Step 5: Limitare strumenti e retry
Assegna a ogni strumento timeout, max retries, idempotency key, per-tool budget e fallback. - 6
Step 6: Registrare gli span di costo
Su ogni run/span registra token, cached token, tool, retry, latency, estimated cost, budget remaining e traceId. - 7
Step 7: Configurare degradazione e circuit breaker
Imposta soglie di degradazione, pausa, circuit breaker e alert, poi testale con casi di errore reali.
FAQ
Il costo di un Agent va tracciato per utente, sessione, task o strumento?
Il routing dei modelli consiste solo nel mettere i task semplici su un modello più piccolo?
Che differenza c'è tra Prompt Caching e una normale cache business?
Quante volte conviene riprovare una chiamata di strumento fallita?
Cosa fare se un task Agent lungo sta finendo il budget a metà?
Quali campi deve registrare un log di costo?
15 min di lettura · Pubblicato il: 17 set 2026
Guida all'ingegneria degli AI Agent
Se arrivi dalla ricerca, il modo più veloce per orientarti è passare all’articolo precedente o successivo della stessa serie.
Precedente
Design di Agent human-in-the-loop: quali passaggi richiedono approvazione umana
Guida pratica per definire i punti di approvazione negli agenti IA: azioni automatiche, pause obbligatorie, approve/reject/resume, timeout, compensazione e audit log.
Parte 3 di 6
Successivo
Modello di autorizzazione per Agent IA: identità utente, permessi degli strumenti, audit log e isolamento dei secret
Prima di collegare un Agent IA a strumenti reali, progetta il modello di autorizzazione: identity mapping, service account, per-tool permission, scope, secret vault, rotazione delle chiavi, approval policy, data boundary e audit log.
Parte 5 di 6



Commenti
Accedi con GitHub per lasciare un commento