Come limitare i costi di un agente AI su VPS
Evita consumi eccessivi di token con agenti unattended. Scopri come usare prompt caching, batching e hard caps per monitorare lo spend su ogni task.
Come evitare che un agente AI sempre attivo generi costi eccessivi
Il controllo dei costi di un agente AI su un VPS (virtual private server) si basa sui limiti impostati prima dell'avvio dell'agente, poiché non c'è supervisione durante l'esecuzione. Limita ogni risposta con max_tokens, definisci il numero massimo di iterazioni del loop nel codice e memorizza in cache la parte del prompt che non cambia mai. Registra i dati di utilizzo di ogni risposta per monitorare le spese per ogni task. Il costo del server è un prezzo mensile fisso. L'API del modello viene fatturata per token; un loop non supervisionato può consumare molti token senza accorgersene.
Questo presuppone che l'agente sia già esistente e utilizzi la Messages API da un server di proprietà. Creare un agente AI con Claude su un VPS descrive l'architettura necessaria.
Perché un agente unattended ha una struttura di costi differente
Una sessione interattiva coinvolge un essere umano. Se il modello segue un percorso errato o analizza un log di 40,000 righe, l'operatore può interrompere l'esecuzione. Un agente unattended non ha questo limite: l'esecuzione continua fino alla fine del loop, dopodiché un timer lo riavvia.
La frequenza è il moltiplicatore che spesso viene trascurato. Un job con pianificazione ogni cinque minuti viene eseguito 288 volte al giorno e circa 8,640 volte al mese. Il costo totale è il costo di una singola esecuzione moltiplicato per tale frequenza. Molti agenti "always-on" non devono essere attivi costantemente. Devono rispondere entro un certo numero di minuti, il che costituisce una pianificazione.
Un agente comporta costi che una finestra di chat non genera.
- Le tool definitions sono incluse in ogni richiesta. Il system prompt per l'uso dei tool costa 290 token su Claude Opus 4.8 con
tool_choicediautoonone, e 410 conanyotool. Il tool bash aggiunge altri 325 token. Ogni MCP server collegato aggiunge i propri schemi a questo carico; MCP sta per Model Context Protocol. - I risultati dei tool sono token di input. Un comando che stampa 8,000 righe inserisce 8,000 righe nella richiesta successiva e in ogni richiesta successiva di quel turno.
- Le pagine recuperate sono token di input. Una pagina web media da 10 kB corrisponde a circa 2,500 token, mentre un PDF di ricerca da 500 kB corrisponde a circa 125,000.
max_content_tokenstronca solo i contenuti testuali, poiché "si applica al contenuto testuale, non al contenuto binario come i PDF". Per i PDF, utilizza invecemax_useseallowed_domains. - La web search è tariffata per ricerca, al costo di $10 per 1,000 ricerche, indipendentemente dal numero di risultati ottenuti. Una ricerca che genera un errore non viene fatturata.
Nessuno di questi elementi è costoso per una singola esecuzione. L'insieme di essi diventa costoso quando viene ripetuto 8,640 volte.
Hard ceilings and soft ceilings risolvono problemi diversi
max_tokens è applicato. È un limite massimo fisso per l'output totale di una singola richiesta, comprensivo di testo di pensiero e di risposta. Claude non supera mai questo limite e il modello non può visualizzarne il valore. Il raggiungimento del limite causa stop_reason: "max_tokens" e una risposta troncata. Per gli agenti: ogni richiesta in un ciclo di tool-use ha il proprio max_tokens, quindi il limite vincola la singola risposta e non l'intero task. Dieci tool call da 4,000 tokens portano a un limite di 40,000 tokens per il turno.
Un task budget è consultivo. task_budget è contenuto in output_config e indica al modello quanti token ha a disposizione per l'intero ciclo agentico, includendo pensiero, tool calls, risultati dei tool e output.
resp = client.beta.messages.create(
model="claude-opus-4-8",
max_tokens=4096,
betas=["task-budgets-2026-03-13"],
output_config={"task_budget": {"type": "tokens", "total": 64000}},
messages=messages,
)"I task budget sono un suggerimento soft, non un limite fisso." Claude può superarne uno durante un'azione, e il limite applicato all'output rimane max_tokens. "Il countdown è visibile solo al modello" e le risposte non contengono un campo relativo al budget residuo. Il task_budget.total minimo accettabile è di 20,000 tokens; un valore inferiore restituisce un errore 400. Un budget troppo piccolo per il lavoro produce comportamenti simili al rifiuto, quindi il modello riduce l'ambito del task o si interrompe anticipatamente.
Un dettaglio comporta costi invece di risparmi. Se il client decrementa task_budget.remaining ad ogni richiesta successiva, il valore modificato invalida qualsiasi prefix in cache che lo contenga. Impostalo una sola volta, alla prima richiesta.
I task budget sono in beta su Claude Fable 5, Claude Opus 4.8 e Claude Opus 4.7. Claude Sonnet 5 e Claude Haiku 4.5 sono elencati come Not supported, e i task budget non si applicano a Claude Code; pertanto, una sessione Claude Code staccata in tmux dipende dalla gestione della sessione.
Il terzo limite si trova nella Claude Console: assegna all'agente un workspace dedicato, quindi imposta un limite di spesa mensile e limiti di velocità per minuto. "Non è possibile impostare limiti sul Default Workspace" e "I limiti a livello di Organization si applicano sempre, anche se i limiti del workspace sommati sono superiori". Aggiungi le notifiche di spesa in modo che una soglia ti avvisi prima del raggiungimento del cap.
Scelta del modello per singolo job e impatto reale sullo sforzo
La scelta del modello si effettua per singolo job. A luglio 2026, il costo per milione di token (input e poi output) è: Claude Fable 5 a $10 e $50, Claude Opus 4.8 e Opus 4.7 a $5 e $25, Claude Sonnet 5 a $3 e $15, Claude Haiku 4.5 a $1 e $5. Sonnet 5 ha attualmente un costo inferiore al prezzo di listino, poiché "Introductory pricing of $2/$10 per million input/output tokens is in effect through August 31, 2026". Un processo che deve solo classificare le righe di log non richiede l'uso di Opus.
Lo sforzo (effort) è il secondo parametro di controllo. output_config.effort accetta low, medium, high, xhigh e max; il valore predefinito è high, quindi impostare esplicitamente high equivale a ometterlo. Ridurre lo sforzo non riduce solo la lunghezza del ragionamento: la documentazione indica che riduce il numero di tool call e combina più operazioni in una singola chiamata. Per un agente, questo rappresenta il risparmio maggiore, poiché una tool call evitata corrisponde a una richiesta intera che non viene eseguita.
L'errore comune è che lo sforzo è incompatibile con la cache. Cambiare il valore tra una richiesta e l'altra invalida il prompt caching. Nell'esempio documentato, la richiesta 2 ha riportato cache_read_input_tokens: 3546; la richiesta 3, con lo sforzo modificato da high a medium, ha riportato cache_creation_input_tokens di 3546 e cache_read_input_tokens di 0. Pertanto, variare lo sforzo tra diversi carichi di lavoro, mai all'interno di una singola conversazione in cache. Per controllare la profondità senza invalidare la cache, agire sul prompt: una riga come "Answer directly without deliberating." nell'ultimo messaggio dell'utente mantiene intatti i precedenti breakpoint.
I token di pensiero (thinking tokens) vengono fatturati con le tariffe di output e contano ai fini di max_tokens; per questo motivo una risposta troncata spesso indica che il ragionamento ha esaurito il budget. Consultare usage.output_tokens_details.thinking_tokens per il valore esatto. Cosa compone effettivamente la fatturazione di un token Claude analizza nel dettaglio il calcolo.
Memorizza il prefix stabile ed evita di interromperlo accidentalmente
Una scrittura in cache costa 1.25 volte il prezzo base dell'input sulla cache da cinque minuti e 2 volte sulla cache da un'ora. Una lettura in cache costa 0.1 volte; pertanto, "il caching conviene dopo una sola lettura per la durata di 5 minuti (1.25x scrittura), o dopo due letture per la durata di 1 ora (2x scrittura)".
Una riga spiega perché questo sistema è adatto a un agent sempre attivo: "La cache viene aggiornata senza costi aggiuntivi ogni volta che il contenuto memorizzato viene utilizzato." Un job che viene eseguito ogni due minuti sulla cache da cinque minuti mantiene il proprio prefix attivo per tutto il giorno con una sola scrittura.
Tre modi per perdere la cache senza accorgersene.
Un prefix che cambia. "I prefix della cache vengono creati nel seguente ordine: tools, system, poi messages." Qualsiasi modifica a un byte in precedenza in questo ordine invalida tutto ciò che segue; inoltre, modificare le definizioni dei tool invalida l'intera cache. L'errore classico è inserire un timestamp o un run id nel system prompt: ogni richiesta avrà quindi un prefix diverso, scriverà una nuova voce al costo di 1.25x e non troverà alcuna corrispondenza in lettura. Il segnale è usage.cache_read_input_tokens pari a 0 su chiamate apparentemente identiche. Sposta il testo variabile nel messaggio utente più recente.
Un prefix troppo corto. Ogni modello ha una lunghezza minima per il caching; al di sotto di tale soglia la richiesta viene elaborata senza caching e "non viene restituito alcun errore". I valori includono 1,024 token su Claude Opus 4.8 e Claude Sonnet 5, e 4,096 su Claude Haiku 4.5; pertanto, spostare un job da Sonnet a Haiku può disattivare il caching silenziosamente.
Una conversazione che supera la finestra di lookback. "La finestra di lookback è di 20 blocchi." Il sistema controlla al massimo 20 posizioni per breakpoint, poi si ferma. Nell'esempio documentato, un turno che contiene 35 blocchi con un breakpoint sul blocco 35 controlla i blocchi dal 35 al 16; l'entry del turno precedente al blocco 15 esce dalla finestra, quindi non viene trovata alcuna corrispondenza. Un agent che aggiunge diversi blocchi di tool-use e tool-result per turno supera il limite di 20 in due o tre turni. Si hanno quattro breakpoint per richiesta, quindi destina uno di essi ai messaggi recenti.
Invia tutto ciò che può attendere alla Batches API
"Ogni utilizzo è fatturato al 50% dei prezzi standard delle API", sia per l'input che per l'output. L'elaborazione batch è asincrona; "la maggior parte dei batch si completa in meno di 1 ora". I risultati sono disponibili quando tutte le richieste sono terminate o dopo 24 ore, a seconda di quale evento si verifichi per primo. Questo è il comportamento tipico, non garantito.
Monitora processing_status finché non legge ended. Le richieste che restituiscono errored, canceled o expired non vengono fatturate. Una nota se utilizzi un limite di spesa (spend cap): "i batch possono superare leggermente il limite di spesa configurato per il Workspace."
Gli sconti si sommano. Poiché un batch può richiedere più di cinque minuti, la documentazione raccomanda l'uso della cache di un'ora per i batch che condividono lo stesso contesto. Suddividi il lavoro: tutto ciò che richiede l'attesa di una persona o di un webhook deve rimanere sul percorso live; un riepilogo notturno o la classificazione dei log di ieri vanno invece in un batch a metà prezzo.
Registra i campi di utilizzo di ogni risposta nel proprio database
Non è possibile attribuire costi che non sono stati registrati. Ogni risposta indica il costo sostenuto.
u = resp.usage
row = {
"job": job_name,
"model": resp.model,
"uncached_input": u.input_tokens,
"cache_write": u.cache_creation_input_tokens,
"cache_read": u.cache_read_input_tokens,
"output": u.output_tokens,
"stop_reason": resp.stop_reason,
}Aggiungi una riga per ogni chiamata API a un file JSON-lines, etichettata con il nome del job. Dopo una settimana è possibile distinguere i job che generano costi da quelli che sembrano solo occupati. Monitora cache_read: una colonna di zeri è l'errore di costo più comune in un agent self-hosted.
Un campo è facile da interpretare erroneamente. input_tokens conta solo i token dopo l'ultimo breakpoint della cache, quindi la dimensione reale del prompt è total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens. Un agent che riporta input_tokens: 400 su un prompt di grandi dimensioni non è economico: il resto proviene dalla cache.
Conta i token prima di inviare la richiesta. Il conteggio dei token è gratuito e i suoi rate limits sono separati dalla creazione dei messaggi; usa count_tokens per rifiutare un allegato troppo grande invece di pagare per scoprirlo. Il risultato è una stima, quindi ri-misura per ogni modello e non riutilizzare mai il conteggio del tokenizer di un altro vendor. Claude Opus 4.7 e i modelli Opus successivi, Claude Fable 5 e Claude Sonnet 5 utilizzano un tokenizer più recente che "produce circa il 30% di token in più per lo stesso testo". Claude Sonnet 4.6 e versioni precedenti, incluso Claude Haiku 4.5, utilizzano il precedente.
Per una visualizzazione ufficiale, l'Admin API riporta l'utilizzo a https://api.anthropic.com/v1/organizations/usage_report/messages e il costo a https://api.anthropic.com/v1/organizations/cost_report. Entrambi richiedono una admin key (sk-ant-admin01-...) come x-api-key: $ANTHROPIC_ADMIN_KEY con anthropic-version: 2023-06-01, e accettano bucket_width=1d, group_by[]=model e api_key_ids[]=. Un limite: "L'Admin API non è disponibile per gli account individuali."
L'ultimo parametro è un metodo economico per l'attribuzione: assegna a ogni job la propria API key, filtra con api_key_ids[] e dividi il report per chiave con group_by[]=api_key_id. Il filtro è al plurale, la dimensione di raggruppamento è al singolare. Mantieni le chiavi nelle variabili d'ambiente invece che nel codice, come fatto da una prima app Claude API su un VPS.
Limita il loop, perché nessun altro lo farà
Un conteggio di iterazioni limitato è obbligatorio in questo caso. Il loop è sotto il tuo controllo, quindi anche il contatore deve esserlo:
for step in range(MAX_STEPS): # MAX_STEPS = 12, never "while True"
resp = client.messages.create(...)
if resp.stop_reason != "tool_use":
break
else:
log.warning("job %s hit MAX_STEPS=%d, giving up", job_name, MAX_STEPS)Nessuno dei limiti sopra citati risolve il problema: max_tokens limita una singola risposta e il modello riceve solo un budget di task.
Inserisci un secondo limite esterno al processo. Esegui il job tramite un systemd timer invece di un processo permanente, e imposta RuntimeMaxSec= sulla sua service unit. Con RuntimeMaxSec=600, un processo bloccato viene terminato dopo dieci minuti invece di continuare a girare finché non te ne accorgi. Eseguire un programma come systemd service e timer spiega come gestire i file unit. Controlla l'esito di un'esecuzione con journalctl -u triage-agent.service --since "1 hour ago".
Limita anche i tentativi di riprova (retries), poiché un handler che riprova all'infinito consuma risorse per ogni tentativo. Un errore 429 o 500 richiede alcuni tentativi con backoff. Un errore 400 non richiede tentativi, poiché la stessa richiesta fallirà sempre nello stesso modo.
Il controllo dei costi degli agenti AI inizia analizzando i propri dati
Nessuno può prevedere il costo di un agente sempre attivo. Il costo è dato dal numero di token per esecuzione moltiplicato per il numero di esecuzioni al giorno; entrambi i valori dipendono da te. Esegui il processo una volta, leggi la riga relativa all'utilizzo registrata nei log e moltiplica il valore per la tua pianificazione. Dopo due giorni, confronta il report dei costi con il calcolo effettuato. Se i valori non coincidono, la differenza è quasi sempre dovuta a una cache non funzionante o a un loop con una durata superiore a quella prevista.
Questo presuppone l'uso di una API key, poiché l'agente è un programma proprietario che chiama la Messages API. Per l'uso interattivo, quale piano Claude si adatta al tuo modo di lavorare analizza l'aspetto dell'abbonamento. Ogni prezzo e limite riportato è stato verificato rispetto alla documentazione di Anthropic nel luglio 2026; consulta nuovamente la pagina dei prezzi prima di definire un budget.
FAQ
Quanto costa eseguire un agente AI sempre attivo su un VPS?
Ci sono due fatture e solo una è prevedibile. Il server ha un prezzo mensile fisso. L'API del modello è tariffata per token, quindi il costo è il consumo di una singola esecuzione moltiplicato per la frequenza di esecuzione. Anthropic non pubblica dati per un agente self-hosted sempre attivo, quindi considera qualsiasi cifra indicata come una stima. Logga usage da una esecuzione reale e moltiplica il valore per la tua pianificazione.
Qual è la differenza tra max_tokens e un task budget?
max_tokens è applicato ed è invisibile al modello. Limita l'output di una singola richiesta, inclusi i token di pensiero, e il raggiungimento del limite produce stop_reason: "max_tokens". Un task budget è l'opposto: il modello riceve il numero e regola il ciclo agentico in base ad esso, ma "Task budgets are a soft hint, not a hard cap" e il limite applicato rimane max_tokens.
Perché cache_read_input_tokens è sempre zero per il mio agente?
Perché il prefisso cambia tra una chiamata e l'altra, o è troppo breve per essere memorizzato nella cache. La causa comune è un timestamp o un run id interpolato nel system prompt: la cache è basata sul prefisso, quindi qualsiasi modifica di un singolo byte invalida tutto ciò che segue. Modificare le tool definitions o il valore effort produce lo stesso effetto. In altri casi si tratta della dimensione, poiché i prompt più brevi non vengono memorizzati nella cache e non viene restituito alcun errore.
Come posso impedire a un agente AI di andare in loop infinito?
Conta le iterazioni nel codice del loop e interrompi l'esecuzione al raggiungimento di un massimo fisso, poiché max_tokens limita una singola risposta e un agente ne genera molte. Aggiungi un limite di tempo (wall-clock) esterno al processo: avvia il job tramite un systemd timer con RuntimeMaxSec= impostato, in modo che un'esecuzione bloccata venga terminata secondo programmi. Limita anche i tentativi di retry, poiché un ciclo di retry genera costi per ogni tentativo.
Posso impostare un limite di spesa su una singola chiave API di Claude?
Il limite di spesa documentato è per workspace e non per chiave; pertanto, assegna all'agente un workspace dedicato e imposta lì il limite di spesa mensile. "You cannot set limits on the Default Workspace". Aggiungi notifiche di spesa in modo che una soglia ti avvisi preventivamente. Per l'attribuzione, assegna a ogni job la propria chiave, quindi raggruppa il report di utilizzo con group_by[]=api_key_id.