Osservabilità LLM nel 2026: Tracing, Valutazione e Monitoraggio con LangSmith e Langfuse
Implementa tracing, valutazione e monitoraggio LLM in produzione nel 2026 con LangSmith, Langfuse, Arize Phoenix e OpenTelemetry GenAI semantic conventions. Esempi di codice Python pronti per la produzione.
L'osservabilità LLM è la pratica di tracciare, valutare e monitorare ogni chiamata a un modello linguistico in produzione (input, output, latenza, costo, qualità) per scoprire regressioni, allucinazioni e drift prima che colpiscano gli utenti. Nel 2026 lo stack di riferimento ruota attorno a tre pilastri: tracing distribuito con le semantic conventions GenAI di OpenTelemetry, valutazione continua con LLM-as-judge e ground truth, e monitoraggio di costi e latenza con strumenti come LangSmith, Langfuse e Arize Phoenix. In questa guida ti mostro come implementarli passo passo, con il codice esatto che usiamo nei progetti reali.
OpenTelemetry ha stabilizzato le GenAI semantic conventions nel 2026, rendendo i trace LLM portabili fra LangSmith, Langfuse, Phoenix e qualsiasi backend OTLP.
Per la maggior parte dei team open-source, Langfuse self-hosted offre il miglior rapporto costo/funzionalità; LangSmith è preferibile se sei già nell'ecosistema LangChain/LangGraph.
La valutazione in produzione richiede tre livelli: online evals (LLM-as-judge sul traffico reale), offline evals (dataset di regressione) e guardrail (controlli sincroni sulla risposta).
Tracciare token in/out, costo per request e time-to-first-token è obbligatorio: senza queste metriche non si possono ottimizzare prompt, cache o routing fra modelli.
Il pattern emergente è la session-level evaluation: si valutano conversazioni multi-turno e traiettorie di agenti, non singole chiamate isolate.
Integrare l'osservabilità fin dal primo prototipo evita di dover riscrivere lo strato di logging quando l'app passa in produzione (mi è capitato due volte, non lo consiglio).
Cosa significa osservabilità LLM e perché serve nel 2026
Un'applicazione LLM non è deterministica. Lo stesso prompt può produrre output diversi, e una modifica al system prompt che migliora il 70% dei casi può peggiorare silenziosamente il restante 30%. L'osservabilità LLM è la disciplina che rende visibile questo comportamento: non solo logs e metriche, ma trace strutturati che catturano la catena completa di chiamate dietro ogni risposta, insieme a un giudizio di qualità su quella risposta.
Nel 2026 il problema è amplificato da tre tendenze. Gli agenti multi-step generano traiettorie complesse con decine di tool call per singola richiesta utente, le pipeline RAG introducono retrieval che può fallire silenziosamente, e i router multi-modello (instradare a Claude Haiku 4.5 o Opus 4.8 in base al task) rendono il costo per request molto variabile. Senza osservabilità, queste architetture sono scatole nere.
Una definizione operativa che uso spesso in workshop: hai osservabilità LLM se puoi rispondere a queste cinque domande senza riprodurre il bug. (1) Qual è stato l'input esatto? (2) Quale modello, versione di prompt e parametri sono stati usati? (3) Quali tool sono stati chiamati, e in che ordine? (4) Quanto è costata e quanto è durata la request? (5) La risposta era buona, e secondo quale metrica?
I tre pilastri: tracing, valutazione, monitoraggio
Pensa all'osservabilità LLM come a tre livelli sovrapposti, ognuno con uno scopo diverso e strumenti distinti.
1. Tracing distribuito
Cattura la struttura ad albero di ogni request: span padre per la sessione utente, span figli per ogni chiamata LLM, retrieval vettoriale, tool execution. Ogni span porta attributi standardizzati: gen_ai.system, gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens. Il tracing risponde a "cosa è successo?".
2. Valutazione
Assegna un punteggio di qualità alle risposte. Si divide in offline (dataset di regressione, eseguito in CI quando cambia un prompt) e online (LLM-as-judge che scora un campione del traffico produzione). Risponde a "era una buona risposta?".
3. Monitoraggio
Aggrega metriche in dashboard: p50/p95/p99 di latenza, costo per utente, tasso di errore, drift della distribuzione di input. Risponde a "sta funzionando bene nel complesso?".
Errore comune (l'ho commesso anch'io al primo progetto): implementare solo il primo pilastro. Avere trace senza valutazione significa avere registrazioni di problemi che non si sa di avere. Avere valutazione senza monitoraggio significa scoprire regressioni a campione invece che in aggregato.
LangSmith vs Langfuse vs Arize Phoenix: quale scegliere
I tre strumenti dominanti nel 2026 hanno filosofie diverse. Questa tabella sintetizza le differenze che contano davvero nella scelta.
Caratteristica
LangSmith
Langfuse
Arize Phoenix
Licenza
SaaS proprietario
MIT (self-host) + cloud
Apache 2.0 + cloud
Integrazione nativa
LangChain, LangGraph
SDK Python/JS, OpenTelemetry
OpenTelemetry-first
Self-hosting
Enterprise only
Sì, Docker Compose
Sì, container singolo
Prompt management
Ottimo (versioning + playground)
Ottimo (versioning + A/B)
Base
Dataset & evals
Maturo, integrato
Maturo, integrato
Maturo, focus ML
Costo (10M span/mese)
~$500–1500
$0 self-host / ~$199 cloud
$0 self-host
Quando sceglierlo
Stack 100% LangChain
Default open-source
Team già su Arize/OTel
La regola pratica che applichiamo in produzione: se il team lavora già con LangGraph e ha budget SaaS, LangSmith riduce il time-to-value. Per tutti gli altri, Langfuse self-hosted è il default: SDK stabile, prompt management eccellente, costo zero. Phoenix brilla nelle organizzazioni che hanno già un investimento in OpenTelemetry e vogliono uniformare tracing ML e LLM nello stesso backend (Grafana, Datadog, Honeycomb).
OpenTelemetry GenAI semantic conventions in pratica
Nel 2026 le GenAI semantic conventions di OpenTelemetry sono uscite dallo stato sperimentale per gli attributi core, e i trace LLM sono finalmente portabili. Tradotto: strumenti l'app una volta e cambi backend (da Langfuse a Phoenix a Datadog) senza riscrivere codice. Un'assicurazione utile, in un mercato che si muove veloce. La specifica definisce attributi standard come gen_ai.system, gen_ai.request.model, gen_ai.response.id e nomi di span tipo chat {model} ed execute_tool {name}.
Esempio minimale di strumentazione manuale con il Python SDK su una chiamata Claude:
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from anthropic import Anthropic
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint="https://cloud.langfuse.com/api/public/otel/v1/traces"))
)
tracer = trace.get_tracer("ai-workflow-lab.demo")
client = Anthropic()
def answer(question: str) -> str:
with tracer.start_as_current_span("chat claude-sonnet-4-6") as span:
span.set_attribute("gen_ai.system", "anthropic")
span.set_attribute("gen_ai.request.model", "claude-sonnet-4-6")
span.set_attribute("gen_ai.request.temperature", 0.2)
span.set_attribute("gen_ai.prompt.0.role", "user")
span.set_attribute("gen_ai.prompt.0.content", question)
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
temperature=0.2,
messages=[{"role": "user", "content": question}],
)
span.set_attribute("gen_ai.response.id", resp.id)
span.set_attribute("gen_ai.usage.input_tokens", resp.usage.input_tokens)
span.set_attribute("gen_ai.usage.output_tokens", resp.usage.output_tokens)
return resp.content[0].text
Implementare tracing end-to-end con Langfuse
Vediamo il setup completo per una pipeline RAG con tracing nidificato. Una funzione utente chiama un retriever, poi un LLM, poi una post-elaborazione. Tutti gli step appaiono in un unico trace gerarchico, e Langfuse calcola automaticamente costo e durata cumulati.
from langfuse import Langfuse, observe
from langfuse.openai import openai # wrapper auto-instrumentato
langfuse = Langfuse(
public_key="pk-lf-...",
secret_key="sk-lf-...",
host="https://cloud.langfuse.com",
)
@observe(name="retrieve-context")
def retrieve(query: str, k: int = 5) -> list[str]:
# qui chiamata al vector DB; il decoratore traccia input/output/durata
results = vector_store.similarity_search(query, k=k)
return [r.page_content for r in results]
@observe(name="generate-answer")
def generate(query: str, context: list[str]) -> str:
system = "Rispondi solo usando il contesto fornito. Cita le fonti."
user = f"Contesto:\n{chr(10).join(context)}\n\nDomanda: {query}"
resp = openai.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "system", "content": system},
{"role": "user", "content": user}],
temperature=0.1,
)
return resp.choices[0].message.content
@observe(name="rag-pipeline")
def answer_question(query: str, user_id: str) -> str:
langfuse.update_current_trace(user_id=user_id, tags=["prod", "rag-v2"])
context = retrieve(query)
return generate(query, context)
# uso
answer_question("Qual è il piano gratuito?", user_id="user_42")
langfuse.flush()
Il risultato in Langfuse è un trace gerarchico: rag-pipeline come root, retrieve-context e generate-answer come figli, con la chiamata OpenAI annidata sotto generate-answer grazie al wrapper. Costo e token sono calcolati in automatico. Per integrare questo pattern in un'architettura di agenti IA con LangGraph e MCP, basta avvolgere ogni nodo del grafo nel decoratore @observe.
Valutazione automatica con LLM-as-judge
Una volta che i trace fluiscono, il passo successivo è assegnare loro un punteggio di qualità. La tecnica standard nel 2026 è LLM-as-judge: usare un modello forte (tipicamente Claude Opus 4.8 o GPT-4.1) per scorare l'output di un modello più economico secondo criteri espliciti. Funziona meglio di metriche statistiche come BLEU/ROUGE perché valuta semantica e fedeltà, non sovrapposizione di token.
Esempio di evaluator per fedeltà (faithfulness) di una risposta RAG rispetto al contesto recuperato:
FAITHFULNESS_PROMPT = """Sei un valutatore. Dato un CONTESTO e una RISPOSTA,
determina se la risposta è completamente supportata dal contesto.
Rispondi SOLO con JSON: {"score": 0|1, "reasoning": "..."}.
Score 1 = ogni affermazione è supportata. Score 0 = c'è almeno un'allucinazione.
CONTESTO:
{context}
RISPOSTA:
{answer}
"""
@observe(name="eval-faithfulness")
def score_faithfulness(trace_id: str, context: str, answer: str) -> dict:
judge = openai.chat.completions.create(
model="claude-opus-4-8", # via gateway OpenAI-compatible
messages=[{"role": "user",
"content": FAITHFULNESS_PROMPT.format(context=context, answer=answer)}],
response_format={"type": "json_object"},
temperature=0,
)
result = json.loads(judge.choices[0].message.content)
langfuse.score(
trace_id=trace_id,
name="faithfulness",
value=result["score"],
comment=result["reasoning"],
)
return result
Esegui questo evaluator su un campione del 5–10% del traffico produzione (online eval) e sul 100% di un dataset di regressione (offline eval) ad ogni cambio di prompt. Le tecniche di prompt engineering avanzato con DSPy e output strutturati sono particolarmente utili per costruire judge affidabili, perché lo schema JSON forzato riduce drasticamente il rumore nel punteggio.
Monitorare costi, latenza e token in produzione
I trace e i punteggi popolano le dashboard. Le metriche minime da tenere sotto controllo settimanalmente sono queste:
Costo per request p50/p95: identifica regressioni quando un prompt cresce o un modello costoso viene usato per task semplici.
Token di output medio per intent: spesso si scopre che il 90% del costo viene da risposte troppo verbose, risolvibile con un'istruzione di sintesi.
Time to first token (TTFT) e tokens per second: critici per UX di streaming. Un TTFT > 2s su mobile fa abbandonare gli utenti.
Tasso di errore diviso per tipo: rate limit, timeout, validation error sui tool, refusal del modello.
Distribuzione dei modelli: se il router decide quale modello usare, monitora la percentuale di richieste che finiscono sul modello premium. Un drift verso l'alto indica un cambio nella distribuzione di input o un bug nel routing.
Score di qualità nel tempo: il dato più importante. Una flessione dello score medio a fronte di latenza e costo stabili è una regressione di qualità silenziosa, esattamente il tipo di problema che senza osservabilità scopri solo dai reclami utente.
Configura alert su deviazioni p95 da baseline a 7 giorni, non su soglie assolute. Le soglie assolute generano falsi positivi quando il traffico cambia, e quando i falsi positivi diventano la norma il team smette di guardare gli alert. Onestamente, è uno dei modi più rapidi per rendere inutile una dashboard.
Osservabilità per agenti multi-step e pipeline RAG
Gli agenti rompono il modello "una request = una chiamata LLM". Un singolo turno utente può generare 10–30 chiamate LLM, 5 tool call e 3 retrieval. Il pattern di osservabilità giusto è la session-level evaluation: invece di valutare singole chiamate, si valuta l'intera traiettoria (la domanda originale, gli step intermedi, la risposta finale) come unità.
Tre metriche specifiche per agenti che vale la pena tracciare:
Step count: numero di iterazioni del ciclo ReAct/planner. Se cresce nel tempo, l'agente sta diventando indeciso (spesso un sintomo di prompt o tool description peggiorati).
Tool success rate: percentuale di tool call che ritornano senza errore. Un calo indica drift dello schema di un tool o un servizio esterno degradato.
Goal completion rate: valutato da un judge che riceve la domanda originale e la risposta finale. È la metrica nord-stella di un agente.
Per pipeline RAG, le metriche analoghe sono context precision (quanti dei chunk recuperati erano effettivamente rilevanti), context recall (quanto dell'informazione necessaria era presente nei chunk) e answer faithfulness. Sono lo stesso framework di valutazione descritto nella nostra guida completa alle pipeline RAG di produzione 2026, e si integrano nativamente con Langfuse e LangSmith tramite SDK come RAGAS. Se gestisci anche il deploy degli strumenti che chiamano questi modelli, vale la pena dare un'occhiata alla guida MCP in produzione con OAuth 2.1: le sue convenzioni di tracing combaciano con quelle viste qui.
Errori comuni da evitare
Cinque pattern che vediamo ripetersi nei progetti che iniziano l'osservabilità in ritardo:
Loggare prompt e completion in log testuali invece che in trace strutturati. Rende impossibile filtrare, aggregare e correlare. Usa sempre uno strumento di tracing dedicato.
Tracciare solo le chiamate LLM ignorando retrieval e tool. La maggior parte dei bug RAG vive nel retrieval, non nel modello.
Confondere logs e evals. Avere i trace non significa sapere se le risposte sono buone. Servono entrambi.
Salvare PII in chiaro. Configura masking lato SDK (Langfuse e LangSmith lo supportano) prima del primo deploy produzione, non dopo.
Non versionare i prompt. Senza versioning non puoi correlare una regressione di score a un cambio specifico. Usa il prompt management integrato (LangSmith Hub, Langfuse Prompts) o git, ma scegli e applica una disciplina.
Qual è la differenza tra osservabilità LLM e logging tradizionale?
Il logging tradizionale produce stringhe testuali immutabili e non correlate. L'osservabilità LLM produce trace strutturati gerarchici, con attributi standardizzati (token, costo, modello), correlazione automatica fra step e la possibilità di assegnare punteggi di qualità a posteriori. È una superset, non un'alternativa.
Quanto costa fare osservabilità LLM in produzione?
Con Langfuse o Phoenix self-hosted il costo infrastrutturale è basso: ~$50–200/mese per Postgres e qualche worker fino a 10M span/mese. Il SaaS LangSmith parte da circa $39/utente al mese e scala con il volume; Langfuse cloud Pro è ~$199 forfettario. Il costo dominante è quasi sempre il modello judge, non il backend di tracing.
Posso usare OpenTelemetry per LLM senza un backend specializzato?
Sì: gli attributi GenAI vanno in qualsiasi backend OTLP (Datadog, Honeycomb, Grafana Tempo, Jaeger). Perdi però le viste UI specifiche per LLM, come diff di prompt, playground per replay e dataset di eval. Pattern ibrido: invia gli stessi span a entrambi i backend, uno per APM generale, uno per analisi LLM dedicata.
Quanto traffico devo campionare per la valutazione LLM-as-judge?
Per dashboard di trend bastano il 5–10% delle richieste. Per debugging di edge case alza al 100% temporaneamente su segmenti specifici (utente, intent, modello). Calcola il costo: judge a $15/M token su risposte medie di 500 token significa circa $0.01 a valutazione, sostenibile fino a centinaia di migliaia di chiamate al giorno.
Come gestisco i dati sensibili (PII) nei trace?
Configura il masking lato SDK prima dell'invio: Langfuse offre un parametro mask sul client, LangSmith ha hide_inputs/hide_outputs. Per regex PII affidabili affidati a librerie come Microsoft Presidio. In alternativa, self-host il backend nello stesso perimetro di rete del modello: i dati non lasciano mai l'infrastruttura.
Il prompt caching riduce del 50–90% il costo dei token ripetuti e taglia la latenza p99 delle chiamate LLM. Guida operativa 2026 a Anthropic, OpenAI, Gemini e semantic caching, con codice, tabella comparativa e la dashboard di monitoraggio che vorresti aver costruito il primo giorno.
Confronto pratico tra function calling su OpenAI, Anthropic e Google nel 2026: strict mode, parallel calls, gestione errori, valutazioni e migrazione degli schemi.
Zapier, n8n o Make per i workflow AI? Ho ricostruito quattro pipeline AI identiche sulle tre piattaforme: prezzi reali 2026, latenza, nodo AI Agent, MCP e LangChain a confronto.