O que é Adicionar Trace?
Adicionar Trace (Add Tracing) é uma refatoração de IA para sistemas com LLM, do grupo Operação. Registrar cada chamada, ferramenta, versão de prompt e custo em um trace navegável.
Add Tracing
Adicionar Trace (Add Tracing) é uma refatoração de IA para sistemas com LLM, do grupo Operação. Registrar cada chamada, ferramenta, versão de prompt e custo em um trace navegável.
Às 3h12, o agente de incidentes reiniciou o serviço de catálogo, que estava saudável. O serviço com problema era o de pagamentos. Na manhã seguinte, o log tinha duas linhas: “alert received” e “done”. Ninguém conseguiu dizer o que o agente leu, o que pensou nem qual versão do prompt estava no ar.
Quando algo dá errado, ninguém consegue ver quais chamadas, ferramentas e contexto levaram à resposta. São os smells Voo Cego e Prompt Sem Versão.
Envolva cada execução num trace, e cada chamada ao modelo, ferramenta e decisão de código num span filho, com os atributos que explicam a decisão: prompt e versão, entrada, saída, tokens, custo, latência e resultado das ferramentas.
def handle_alert(alert):
- log.info(f"alert received: {alert.name}")
- result = agent.run(alert)
- log.info("done")
- return result
+ with tracer.start_as_current_span("incident.handle") as root:
+ root.set_attributes({"alert.name": alert.name, "agent.version": AGENT_VERSION,
+ "prompt.version": PROMPT.version})
+ result = agent.run(alert) # o agente cria spans filhos de modelo e de ferramenta
+ root.set_attributes({"result.action": result.action,
+ "cost.usd": result.cost_usd, "stop.reason": result.stop_reason})
+ return result
+
+# dentro do loop do agente
+with tracer.start_as_current_span(f"chat {MODEL}") as span:
+ reply = client.messages.create(...)
+ span.set_attributes({"gen_ai.operation.name": "chat",
+ "gen_ai.request.model": MODEL,
+ "gen_ai.usage.input_tokens": reply.usage.input_tokens,
+ "gen_ai.usage.output_tokens": reply.usage.output_tokens,
+ "gen_ai.response.finish_reasons": [reply.stop_reason]})
Antes: o log diz que começou e que acabou.
flowchart LR
A(["1. Alerta"]):::io --> M(["2. Agente"]):::llm
M --> L[("3. Duas linhas de log")]:::store
Depois: uma árvore de spans que conta a execução inteira.
flowchart TD
R(["1. Trace: incident.handle"]):::io --> S1(["2. chat"]):::llm
R --> T1["3. execute_tool read_logs"]:::tool
R --> S2(["2. chat"]):::llm
R --> T2["3. execute_tool restart_service"]:::tool
S1 --> A1["4. prompt, versão, tokens, custo"]:::tool
T1 --> A2["4. argumentos, resultado, erro"]:::tool
R --> D[("5. Armazenamento de traces")]:::store
A decisão de um modelo é função do que entrou no contexto. Um trace guarda exatamente isso, em ordem, ligado à ferramenta que gerou cada pedaço. Com ele, “por que o agente reiniciou o catálogo?” vira uma leitura.
Este é o trace da madrugada das 3h12, depois da refatoração (resumido):
incident.handle 4.8s alert=payments-5xx agent=v14 prompt=triage@7 cost=$0.052
├─ chat claude-... 1.9s in=11.840 out=212 finish=tool_use tools=[read_logs]
├─ execute_tool read_logs 0.3s args={"service": "catalog"} result="1.204 linhas, 0 erros"
├─ chat claude-... 2.1s in=13.102 out=148 finish=tool_use tools=[restart_service]
└─ execute_tool restart_service 0.5s args={"service": "catalog"} result="ok"
A leitura leva um minuto. O alerta era de pagamentos, mas o primeiro chat pediu os logs do catálogo. O log veio limpo, e o modelo, em vez de desconfiar, concluiu que o catálogo precisava de reinício. A causa está no primeiro span: o prompt triage@7 traz um exemplo em que o serviço é sempre catalog.
Sem o trace, o time teria discutido “o modelo alucinou”. Com ele, a correção é concreta: trocar o exemplo do prompt e criar um caso de eval com esse alerta.
A estrutura em árvore é o que diferencia trace de log. Cada span sabe quem é o pai, então dá para somar o custo por tarefa, ver o caminho crítico de latência e comparar execuções boas e ruins do mesmo tipo de tarefa. Duas tarefas rodando ao mesmo tempo não se misturam, porque cada linha carrega o identificador da execução.
A versão do prompt em cada span fecha o ciclo: uma resposta ruim aponta para o texto exato que a gerou, e o caso vai para o eval com tudo que é preciso para reproduzir.
gen_ai.* funciona com a maioria das ferramentas de observabilidade (Jaeger, Grafana Tempo, Langfuse, Arize Phoenix, Datadog, entre outras). Essas convenções ainda estão marcadas como em desenvolvimento e já renomearam atributos entre versões, então fixe a versão que você segue.traceparent do padrão W3C Trace Context) para que tudo caia no mesmo trace.Suponha um modelo que cobre US$ 3 por milhão de tokens de entrada e US$ 15 por milhão de saída (valores ilustrativos; use a tabela do seu provedor). Um span com 11.840 tokens de entrada e 212 de saída custa:
11.840 × 3 / 1.000.000 = US$ 0,0355
212 × 15 / 1.000.000 = US$ 0,0032
total do span = US$ 0,0387
Gravado em cada span, esse número soma sozinho no span raiz. Aí “qual tipo de alerta custa mais?” vira uma consulta, não uma planilha.
Separe atributos de conteúdo. Atributos (modelo, tokens, custo, versão, motivo de parada, duração, ids) são pequenos e devem existir em 100% das execuções. Conteúdo (prompt inteiro, resposta, retorno de ferramenta) pesa e tem dados pessoais, então pode ser amostrado.
A amostragem precisa olhar para o fim da execução, não para o começo. Amostrar 5% ao acaso no início perde justamente o caso ruim. Decida depois que a execução termina e guarde sempre as que tiveram erro, custo alto, parada por limite ou ação com efeito.
SimpleSpanProcessor envia cada span na hora; em produção use o BatchSpanProcessor, que envia em lote em segundo plano.Este span tem informação, mas não ajuda a explicar a decisão. O que falta?
with tracer.start_as_current_span("llm") as span:
reply = client.messages.create(model=MODEL, system=SYSTEM, messages=msgs, tools=TOOLS)
span.set_attribute("duration_ms", elapsed_ms)
span.set_attribute("ok", True)
Faltam a versão do prompt, os tokens (para custo), o motivo de parada, as ferramentas pedidas e, quando a política permitir, a entrada e a saída. Duração já é medida pelo próprio span, e ok=True não diz nada sobre a qualidade da decisão.
with tracer.start_as_current_span(f"chat {MODEL}") as span:
reply = client.messages.create(model=MODEL, system=SYSTEM, messages=msgs, tools=TOOLS)
span.set_attributes({
"gen_ai.operation.name": "chat",
"gen_ai.request.model": MODEL,
"prompt.version": SYSTEM_VERSION,
"gen_ai.usage.input_tokens": reply.usage.input_tokens,
"gen_ai.usage.output_tokens": reply.usage.output_tokens,
"gen_ai.response.finish_reasons": [reply.stop_reason],
"tools.requested": [b.name for b in reply.content if b.type == "tool_use"],
})Adicionar Trace (Add Tracing) é uma refatoração de IA para sistemas com LLM, do grupo Operação. Registrar cada chamada, ferramenta, versão de prompt e custo em um trace navegável.
Trata Prompt Sem Versão e Voo Cego.
Ao padrão de projeto Trilha de Auditoria.