2026-05-14
Tracing e costi minimi per pipeline AI in produzione
Quali metriche servono davvero per costi e affidabilità di una pipeline LLM prima di stack enterprise.
Prima di comprare una piattaforma “LLM observability”, puoi già capire se la pipeline regge il carico e quanto ti costa con pochi numeri raccolti nel tuo codice o in un log strutturato. Questo pezzo descrive un MVP di metriche allineato al metodo RED (rate, errors, duration), adattato alle chiamate modello.
Premessa verificabile: i prezzi per token cambiano — prendi i valori dalla pagina pricing del provider che usi (OpenAI, Anthropic, Google, ecc.) e ricalcola estimated_cost con la formula documentata (tipicamente input_tokens * price_in + output_tokens * price_out). Qui parliamo di come misurare, non di cifre fisse.
Cosa non misurare all’inizio
Evita questi antipattern nelle prime settimane:
- Dashboard con 30 grafici prima di avere un elenco stabile di
request_idcorrelati a un trace minimo. - Token “approssimati” senza fonte (se il client non espone usage, leggi la risposta ufficiale del provider prima di inventare moltiplicatori).
- Solo latency media: la coda p95 ti dice molto di più su UX e timeout.
- Log del prompt completo in prod senza policy di retention: costo storage + rischio privacy.
Metriche MVP: definizioni e unità
Per ogni invocazione di un modello (una chiamata HTTP al provider o al tuo gateway) registra:
| Campo | Unità | Nota |
|---|---|---|
duration_ms | ms | wall-clock dal send alla risposta completa (o fine stream). |
input_tokens / output_tokens | interi | dal payload usage del provider, se presente. |
ok | boolean | false se status non 2xx o eccezione prima della risposta. |
error_class | stringa corta | es. rate_limit, timeout, invalid_request, provider_5xx. |
model | stringa | id esatto inviato all’API. |
step | stringa | se la pipeline ha più step (retrieve, classify, generate). |
Per batch o job asincroni, stessa riga ma con job_id invece di richiesta HTTP sincrona.
Percentili: calcola p50 e p95 di duration_ms almeno giornalmente (query SQL o metric sink). Il p95 segnala code lente e provider instabili prima che la media si muova.
Error rate: errors / attempts per model e step. Un picco localizzato su uno step spesso indica prompt tool-calling o schema validation, non il modello “in generale”.
Costo stimato vs costo misurato
Il costo misurato deriva da token effettivi * prezzi listino. Il costo stimato può essere un budget statico per richiesta (es. max token dichiarati * costo peggiore). Se stimato e misurato divergono sistematicamente, controlla:
- Caching o prompt prefix lungo che cambia gli input token reali.
- Stream che taglia la risposta: output token più bassi del previsto.
- Retry nascosti nel client: moltiplicano le richieste senza correlazione in UI.
Traccia attempt (1 = primo try, 2 = dopo retry) per non confondere un utente con tre richieste con un job.
Alerting sensato
Tre soglie bastano per molte squadre:
- Budget giornaliero in EUR/USD stimato dai token (somma della giornata > X).
- p95 latency > soglia contrattuale (es. 8s su un endpoint sync).
- Error rate > Y% per 15 minuti sullo stesso
step.
Evita alert sul solo “un 500”: usa error_class e volume.
Quando passare a un tool dedicato
Passa a Langfuse, Helicone, LangSmith o simili quando almeno una di queste è vera:
- Serve tracing distribuito su più servizi (retrieve DB + worker + LLM) con correlazione auto.
- Utenti non tecnici devono esplorare trace senza accesso ai log raw.
- Vuoi dataset di eval versionati accanto ai trace.
Fino ad allora, una tabella llm_calls in Postgres/SQLite con le colonne sopra + indice su created_at risolve l’80% delle domande “perché ieri è esploso il costo?”.
Query SQL di esempio
SELECT
date_trunc('day', created_at) AS day,
model,
sum(input_tokens + output_tokens) AS tokens,
avg(duration_ms) AS avg_ms,
percentile_cont(0.95) WITHIN GROUP (ORDER BY duration_ms) AS p95_ms,
sum(CASE WHEN ok THEN 0 ELSE 1 END)::float / count(*) AS error_rate
FROM llm_calls
WHERE created_at > now() - interval '7 days'
GROUP BY 1, 2
ORDER BY 1 DESC, tokens DESC;
(Adeguare sintassi percentile_cont se usi SQLite—usa window o export verso BI.)
Allinea le metriche a usage e (opzionale) a OpenTelemetry
- Payload dei provider: quando disponibile, salva campi grezzi da
usage(prompt/completion/cached) così riconcili fattura e log. Per OpenAI, oggetti Chat/Responses espongonousagee, con prompt caching attivo, dettagli tipoprompt_tokens_details.cached_tokens—vedi API Reference e note sul prompt caching nei guide aggiornati. Stesso approccio per Anthropic/Google: normalizza ininput_tokens/output_tokensnel tuo schemallm_calls. - OpenTelemetry GenAI: le semantic conventions per span client verso LLM (
gen_ai.operation.name,gen_ai.request.model,gen_ai.usage.*, …) sono in evoluzione—utile se già standardizzi su OTel. Riferimento: Gen AI spans (stato development/experimental al momento della ricerca: verifica versione e opt-inOTEL_SEMCONV_STABILITY_OPT_INse usi SDK datati). - Coerenza con RED: un singolo span “inference” per chiamata modello, attributi stabili, e eventi solo in dev—evita allegare prompt completi in span prod.
Sintesi
Misura durata, token, esito, step per invocazione; calcola p95 e error rate; confronta costo a listino. Il resto è polish, non prerequisito per sapere se il prodotto regge.