AI Observability: Monitorare LLM, Token e Agenti
Con l'adozione massiva dei Large Language Model (LLM) in produzione, emerge un nuovo dominio dell'observability: il monitoraggio delle applicazioni basate su AI. Le chiamate agli LLM hanno caratteristiche uniche rispetto alle API tradizionali: costi variabili basati sull'utilizzo dei token, latenze imprevedibili, output non deterministici e rischi di hallucination. L'AI observability applica i principi dell'observability classica a questo nuovo paradigma.
In questo articolo esploreremo come instrumentare le chiamate ai modelli linguistici, tracciare il comportamento degli agenti AI, monitorare i costi in tempo reale e rilevare anomalie nelle risposte dei modelli usando OpenTelemetry e le semantic conventions emergenti per l'AI.
Cosa Imparerai in Questo Articolo
- perchè le applicazioni AI richiedono observability specializzata
- Tracciare le chiamate LLM con span e attributi specifici
- Monitorare l'utilizzo dei token e i costi in tempo reale
- Osservare il comportamento degli agenti AI (tool calls, reasoning)
- Rilevare hallucination e degradazioni della qualità
- Framework e strumenti per l'AI observability
perchè le Applicazioni AI Richiedono Observability Specializzata
Le applicazioni basate su LLM hanno caratteristiche che le rendono fondamentalmente diverse dalle applicazioni tradizionali dal punto di vista dell'observability:
Sfide Uniche dell'AI Observability
| Sfida | Applicazione Tradizionale | Applicazione AI/LLM |
|---|---|---|
| Costi | Fissi (compute, storage) | Variabili (per token, per richiesta) |
| Latenza | Prevedibile (ms range) | Alta e variabile (1-60s per streaming) |
| Output | Deterministico | Non deterministico (temperature, sampling) |
| Errori | Codici di stato chiari | Hallucination, risposte incoerenti |
| Testing | Unit test deterministici | Valutazione qualitativa (eval) |
Instrumentare le Chiamate LLM
L'instrumentazione delle chiamate ai modelli linguistici segue lo stesso pattern di OTel per le API esterne, ma con attributi specifici per catturare i metadata dell'AI: modello utilizzato, conteggio token, temperatura, costo stimato.
from opentelemetry import trace, metrics
import time
import tiktoken
tracer = trace.get_tracer("ai-service", "1.0.0")
meter = metrics.get_meter("ai-service", "1.0.0")
# Metriche specifiche per LLM
llm_token_usage = meter.create_counter(
name="llm.token.usage",
description="Token utilizzati nelle chiamate LLM",
unit="token"
)
llm_request_duration = meter.create_histogram(
name="llm.request.duration",
description="Durata delle chiamate LLM",
unit="ms"
)
llm_cost = meter.create_counter(
name="llm.cost.total",
description="Costo stimato delle chiamate LLM",
unit="USD"
)
def call_llm(messages, model="gpt-4", temperature=0.7, max_tokens=1000):
with tracer.start_as_current_span(
"llm.chat.completion",
attributes={
# Semantic conventions per AI (draft)
"gen_ai.system": "openai",
"gen_ai.request.model": model,
"gen_ai.request.temperature": temperature,
"gen_ai.request.max_tokens": max_tokens,
"gen_ai.request.top_p": 1.0,
# Contesto applicativo
"llm.prompt.messages_count": len(messages),
"llm.prompt.system_prompt_length": len(messages[0]["content"])
if messages[0]["role"] == "system" else 0,
}
) as span:
start_time = time.monotonic()
try:
# Conteggio token input (pre-chiamata)
encoding = tiktoken.encoding_for_model(model)
input_tokens = sum(
len(encoding.encode(m["content"])) for m in messages
)
span.set_attribute("gen_ai.usage.prompt_tokens", input_tokens)
# Chiamata al modello
response = openai_client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
max_tokens=max_tokens
)
# Attributi dalla risposta
output_tokens = response.usage.completion_tokens
total_tokens = response.usage.total_tokens
span.set_attribute("gen_ai.usage.completion_tokens", output_tokens)
span.set_attribute("gen_ai.usage.total_tokens", total_tokens)
span.set_attribute("gen_ai.response.model", response.model)
span.set_attribute("gen_ai.response.finish_reason",
response.choices[0].finish_reason)
# Calcolo costo stimato
cost = estimate_cost(model, input_tokens, output_tokens)
span.set_attribute("llm.cost.estimated_usd", cost)
# Registrare metriche
duration_ms = (time.monotonic() - start_time) * 1000
common_attrs = {
"gen_ai.system": "openai",
"gen_ai.request.model": model
}
llm_token_usage.add(input_tokens,
{**common_attrs, "gen_ai.token.type": "input"})
llm_token_usage.add(output_tokens,
{**common_attrs, "gen_ai.token.type": "output"})
llm_request_duration.record(duration_ms, common_attrs)
llm_cost.add(cost, common_attrs)
span.set_status(StatusCode.OK)
return response
except RateLimitError as e:
span.record_exception(e)
span.set_status(StatusCode.ERROR, "Rate limit exceeded")
span.set_attribute("llm.error.type", "rate_limit")
raise
except Exception as e:
span.record_exception(e)
span.set_status(StatusCode.ERROR, str(e))
raise
Tracciare Agenti AI
Gli agenti AI sono sistemi che usano LLM per ragionare, pianificare e invocare strumenti (tool calls) per completare task complessi. Il loro comportamento e particolarmente difficile da osservare perchè coinvolge cicli multipli di reasoning, decisioni di tool selection e composizione di risultati.
def trace_agent_execution(task, agent):
with tracer.start_as_current_span(
"agent.execute",
attributes={
"agent.name": agent.name,
"agent.model": agent.model,
"agent.task": task.description,
"agent.max_iterations": agent.max_iterations,
"agent.tools_available": ",".join(agent.tool_names)
}
) as agent_span:
iteration = 0
total_tokens = 0
total_cost = 0.0
tools_called = []
while not agent.is_done() and iteration < agent.max_iterations:
iteration += 1
# Span per ogni iterazione del reasoning loop
with tracer.start_as_current_span(
f"agent.iteration.{iteration}",
attributes={
"agent.iteration": iteration,
"agent.state": agent.current_state
}
) as iter_span:
# Span per la chiamata LLM di reasoning
with tracer.start_as_current_span("agent.reasoning") as reason_span:
decision = agent.reason(task)
reason_span.set_attribute("agent.decision.type",
decision.type) # "tool_call" | "final_answer"
reason_span.set_attribute("agent.decision.confidence",
decision.confidence)
total_tokens += decision.tokens_used
# Se l'agente decide di chiamare un tool
if decision.type == "tool_call":
with tracer.start_as_current_span(
"agent.tool_call",
attributes={
"agent.tool.name": decision.tool_name,
"agent.tool.input_summary": decision.tool_input[:200]
}
) as tool_span:
result = agent.execute_tool(decision)
tool_span.set_attribute("agent.tool.success",
result.success)
tool_span.set_attribute("agent.tool.output_length",
len(str(result.output)))
tools_called.append(decision.tool_name)
# Attributi finali dell'agente
agent_span.set_attribute("agent.iterations_total", iteration)
agent_span.set_attribute("agent.tokens_total", total_tokens)
agent_span.set_attribute("agent.cost_total_usd", total_cost)
agent_span.set_attribute("agent.tools_called",
",".join(tools_called))
agent_span.set_attribute("agent.success", agent.is_done())
Monitoraggio dei Costi in Tempo Reale
Il costo delle chiamate LLM può scalare rapidamente, specialmente con modelli come GPT-4 o Claude. Il monitoraggio in tempo reale dei costi permette di rilevare anomalie (un loop infinito che consuma token), impostare budget alert e ottimizzare l'uso dei modelli.
# Alert rules Prometheus per AI cost monitoring
groups:
- name: ai-cost-alerts
rules:
# Alert: spesa oraria sopra il budget
- alert: HighAICostPerHour
expr: |
sum(rate(llm_cost_total[1h])) * 3600 > 50
for: 5m
labels:
severity: warning
annotations:
summary: "AI cost exceeding $50/hour"
# Alert: tasso di errori LLM alto
- alert: HighLLMErrorRate
expr: |
rate(llm_request_duration_count{status="error"}[5m])
/ rate(llm_request_duration_count[5m]) > 0.1
for: 3m
labels:
severity: critical
annotations:
summary: "LLM error rate above 10%"
# Alert: latenza LLM degradata
- alert: HighLLMLatency
expr: |
histogram_quantile(0.95,
rate(llm_request_duration_bucket[5m])
) > 30000
for: 5m
labels:
severity: warning
annotations:
summary: "LLM P95 latency above 30 seconds"
# Alert: anomalia token usage
- alert: AnomalousTokenUsage
expr: |
rate(llm_token_usage[5m]) > 3 * avg_over_time(rate(llm_token_usage[1h])[24h:1h])
for: 10m
labels:
severity: warning
annotations:
summary: "Token usage 3x above 24h average"
Metriche Chiave per l'AI Observability
- Token usage: input/output tokens per modello, per endpoint, per utente
- Costo per richiesta: stima del costo in base al modello e ai token usati
- Latenza TTFT: Time To First Token, critica per applicazioni streaming
- Tasso di errori: rate limit, timeout, errori del modello
- Finish reason: distribuzione di stop, max_tokens, tool_call, content_filter
- Agent iterations: numero di cicli di reasoning per task completato
- Tool call success rate: percentuale di tool call riuscite vs fallite
Rilevamento di Hallucination e qualità
Il monitoraggio della qualità delle risposte e un aspetto unico dell'AI observability. Mentre gli errori tecnici (timeout, rate limit) sono facili da rilevare, le hallucination e le risposte di bassa qualità richiedono metriche proxy e valutazione automatica.
Segnali Proxy per la qualità delle Risposte
Lunghezza della risposta: risposte troppo brevi o troppo lunghe rispetto
alla media possono indicare problemi di qualità.
Finish reason anomalo: un alto tasso di max_tokens indica
risposte troncate; un alto tasso di content_filter indica contenuti bloccati.
User feedback: tracciare le azioni dell'utente dopo la risposta (retry,
thumbs down, abbandono) come segnali indiretti di qualità.
Similarity score: confrontare la risposta con risposte di riferimento
usando embeddings per rilevare drift nella qualità.
Framework per l'AI Observability
Diversi framework stanno emergendo per semplificare l'AI observability, offrendo instrumentazione automatica per le principali librerie AI e dashboard preconfigurate. Tra i più rilevanti: OpenLLMetry (basato su OTel per LLM), LangSmith (per LangChain), Helicone (proxy per OpenAI) e le OTel Semantic Conventions per GenAI (standard emergente).
Conclusioni e Prossimi Passi
L'AI observability e un campo in rapida evoluzione che estende i principi dell'observability classica alle applicazioni basate su LLM. Le sfide uniche (costi variabili, output non deterministico, hallucination) richiedono metriche e pattern di monitoring specializzati.
I tre pilastri dell'AI observability sono: cost monitoring (token usage, costo per richiesta), performance monitoring (latenza, TTFT, tasso di errori) e quality monitoring (finish reason, user feedback, similarity scores).
Nel prossimo e ultimo articolo della serie, presenteremo un case study completo: l'implementazione end-to-end di un sistema di observability per un'architettura a microservizi, con metriche prima e dopo l'adozione di OpenTelemetry.







