O agente devolveu uma resposta plausível, mas ninguém consegue explicar por que ele consultou uma ferramenta, repetiu outra ou parou antes de concluir. O log mostra o texto final. A falha está no caminho que desapareceu.
Um trace útil trata a execução do agente como uma árvore. Um span raiz representa o run. Spans filhos representam chamadas ao modelo, retrieval, tool calls, retries e validações. Assim, você pode descobrir qual etapa demorou, falhou ou mudou o estado sem transformar a conversa inteira em log.
Este artigo mostra uma fronteira pequena em TypeScript para esse registro. O exemplo é ilustrativo: não foi executado contra um agente ou backend de observabilidade. A documentação atual do OpenTelemetry descreve um trace de GenAI com spans de invoke_agent, chat e execute_tool, enquanto o OpenInference define categorias como AGENT, LLM e TOOL sobre o transporte do OpenTelemetry (OpenInference, "Traces").
Resposta curta
- Crie um span raiz para cada execução do agente e um filho para cada operação relevante.
- Propague o contexto do span ativo até a função que executa a ferramenta.
- Registre nome, identificador, duração, tentativa e resultado resumido. Não copie argumentos, prompts ou respostas por padrão.
- Use validação, trace e checkpoint para funções diferentes: telemetria explica o caminho, mas não retoma uma execução.
Por que um log do modelo não explica o agente?
Uma chamada ao modelo é apenas uma parte do run. O modelo pode pedir uma ferramenta, receber um resultado, escolher outra ferramenta e tentar novamente depois de um timeout. Se cada etapa vai para um log separado, o revisor precisa reconstruir a ordem por horário e ainda pode perder a relação entre a decisão e o efeito.
O OpenTelemetry apresenta traces como uma forma de registrar operações relacionadas. A relação pai-filho carrega a estrutura da execução. Para um agente, isso significa que o run pode ser o pai de uma chamada ao modelo, e a chamada da ferramenta pode aparecer como uma operação filha do mesmo ciclo.
Esse modelo também evita um erro de escopo. A conversa que o usuário viu é um registro de produto. O trace é um registro operacional. Eles podem compartilhar um runId, mas não precisam guardar o mesmo conteúdo. Uma discussão recente de praticantes recomenda separar conversa e trace, mantendo no trace IDs, latência, custo, erro e resultado resumido (r/AI_Agents). Isso é linguagem de comunidade, não uma regra técnica do OpenTelemetry, mas a separação é uma boa fronteira de projeto.
O primeiro passo é definir o que um revisor precisa responder. Qual run falhou? Qual tool call ocorreu? Qual tentativa foi repetida? Qual etapa ainda estava ativa? Se a resposta não precisa do texto completo para ser encontrada, não coloque o texto completo no span.
Como deve ser a árvore de spans de um agente?
Comece com um span que represente a execução do agente. Dentro dele, crie spans para operações que mudam o diagnóstico: chamada ao modelo, seleção de ferramenta, execução da ferramenta, acesso a dados, retry e validação. Nem todo helper precisa de um span. Instrumentar cada função pequena produz ruído e deixa o erro importante difícil de encontrar.
Uma árvore mínima pode ter esta forma:
agent.run
├── model.chat
├── tool.execute: search_orders
│ └── http.client
├── model.chat
└── tool.execute: update_order
└── database.client
O nome do span pode seguir o vocabulário do seu runtime. O importante é manter a hierarquia e atributos consistentes. A documentação do OpenTelemetry para GenAI lista atributos como modelo, tokens, mensagens, nome da ferramenta e identificador da chamada. A própria página avisa que esses atributos foram movidos para um repositório dedicado e aparecem como desenvolvimento ou deprecated na visão antiga (OpenTelemetry, "Gen AI attributes"). Por isso, trate os nomes de GenAI como contrato versionado, não como strings eternas.
O OpenInference oferece outra taxonomia útil quando o backend entende suas convenções. Uma operação de ferramenta é TOOL, uma chamada de modelo é LLM e o span que agrupa as duas pode ser AGENT (OpenInference, "Traces"). Você pode adotar essa taxonomia sem abandonar o OTLP. Apenas não misture nomes de um provedor com atributos sem documentar a tradução.
Como instrumentar uma tool call em TypeScript?
O wrapper deve abrir o span dentro do contexto da execução, marcar erro quando a função falhar e encerrá-lo em finally. O código abaixo é uma ilustração curta. Ele usa atributos próprios para resultado e tentativa, e usa o identificador da tool call sem registrar o payload completo.
import {
SpanStatusCode,
trace,
} from "@opentelemetry/api";
const tracer = trace.getTracer("agent-runtime");
type ToolInput = {
name: string;
callId: string;
attempt: number;
};
export async function executeTool<T>(
input: ToolInput,
run: () => Promise<T>,
): Promise<T> {
return tracer.startActiveSpan(
`tool.execute:${input.name}`,
{
attributes: {
"gen_ai.tool.name": input.name,
"gen_ai.tool.call.id": input.callId,
"agent.tool.attempt": input.attempt,
},
},
async (span) => {
try {
const result = await run();
span.setAttribute("agent.tool.outcome", "success");
return result;
} catch (error) {
span.setAttribute("agent.tool.outcome", "error");
span.recordException(error as Error);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error instanceof Error ? error.message : "unknown error",
});
throw error;
} finally {
span.end();
}
},
);
}
O ponto principal não é o nome executeTool. É o contexto ativo. startActiveSpan torna o span atual disponível enquanto run() aguarda a resposta. Se run() cria um cliente HTTP instrumentado, o span da chamada de rede pode virar filho. Se o runtime perde o contexto ao entrar em uma fila, callback ou worker, o trace quebra em duas árvores.
O guia de Node.js do OpenTelemetry mostra a inicialização do SDK e avisa que a instrumentação precisa carregar antes do código que será observado. Em ESM e TypeScript compilado para ESM, a forma de inicialização também precisa respeitar o loader indicado na documentação. Um wrapper correto não compensa um SDK que nunca foi inicializado.
Esse exemplo não valida argumentos, não decide se a ferramenta é segura e não implementa retry. O contrato de validação de tool calls em TypeScript continua sendo a fronteira que decide se a ação pode começar. O trace registra o que aconteceu depois dessa decisão.
Quais atributos merecem entrar no trace?
Registre atributos que ajudam a filtrar e comparar runs. O conjunto exato depende do backend, mas a decisão pode começar com estes campos:
| Campo | Exemplo | Por que guardar |
|---|---|---|
agent.run.id |
run_8f2 |
liga spans ao registro da execução |
gen_ai.agent.name |
order-assistant |
separa agentes com o mesmo serviço |
gen_ai.tool.name |
search_orders |
filtra a ferramenta que falhou |
gen_ai.tool.call.id |
call_42 |
correlaciona pedido e resultado |
agent.tool.attempt |
2 |
revela retries e loops |
agent.tool.outcome |
success |
permite contar falhas sem ler payload |
error.type |
TimeoutError |
agrupa a causa operacional |
O OpenTelemetry define atributos como pares tipados. Prefira valores de baixa cardinalidade para filtros recorrentes. Um ID de run pode ser útil no detalhe de uma ocorrência, mas pode ser caro como dimensão de métrica. Não transforme cada argumento de cliente em label de uma métrica.
O tamanho também importa. Um trace precisa responder a uma pergunta de diagnóstico, não ser um espelho do prompt. Guarde o nome da ferramenta e um identificador do recurso quando isso for permitido. Para payloads grandes, salve um artefato redigido em um armazenamento separado e coloque apenas uma referência com política de acesso. A referência não deve permitir que qualquer leitor do trace descubra dados do cliente.
Como evitar que a observabilidade vaze segredos?
Comece com uma política de exclusão. Prompt do sistema, mensagens do usuário, argumentos de ferramenta, respostas externas e headers podem conter segredos ou dados pessoais. O fato de uma convenção oferecer um atributo para mensagens não obriga sua aplicação a preenchê-lo.
Uma política simples separa três camadas:
- Sempre permitido: nome do serviço, versão, ambiente, nome da ferramenta, tipo do erro, duração e resultado categorizado.
- Permitido com redução: IDs internos, tamanho do payload, código de status, hash não reversível e amostra redigida.
- Bloqueado por padrão: tokens, cookies, prompts completos, dados pessoais, argumentos financeiros e respostas de ferramentas sem classificação.
Faça a redaction antes do exportador. Redigir apenas na interface do backend deixa o dado exposto durante transporte, retenção e acesso administrativo. Também teste exceções: uma mensagem de erro pode incluir o argumento original, e um recordException pode carregar uma mensagem sensível se a aplicação não a normalizar.
O OpenInference explica que aplicações de IA têm requisitos de privacidade e permitem mascarar campos. Use essa preocupação como entrada de projeto, não como promessa de que uma biblioteca resolverá sua política. O backend recebe o que o seu processo de instrumentação decide enviar.
Como verificar se o trace conta a história certa?
Não basta ver um span verde no dashboard. Crie um cenário controlado em que o agente executa uma ferramenta conhecida, force um erro e faça uma segunda tentativa. Depois confira se a árvore mantém o mesmo trace, se a segunda tentativa é filha do run e se o erro aparece na ferramenta correta.
Um checklist curto ajuda:
- O run tem um span raiz que começa antes da primeira chamada ao modelo?
- Cada tool call tem nome, identificador e tentativa?
- A execução da ferramenta aparece como filha do run, mesmo depois de um
await? - O erro tem status e exceção sem carregar segredo?
- Um retry gera um novo span, em vez de sobrescrever a tentativa anterior?
- Uma ferramenta lenta mostra também o cliente HTTP ou banco que a atrasou?
- O trace contém a referência mínima para investigar, sem o prompt completo?
Teste também o que deve faltar. Rode uma ferramenta com um argumento que seria sensível e confirme que o exportador recebe a versão redigida ou nenhum payload. Interrompa a chamada antes do retorno e confirme que o span termina com erro ou cancelamento. Se o trace só fica correto no caminho feliz, ele ainda não é uma prova de observabilidade.
O owner de observabilidade de agentes de código explica como transformar eventos do agente em evidência que o revisor consegue consumir. Aqui, a unidade é menor: o span deve permitir localizar a etapa que precisa ser investigada antes de decidir qual evidência vai para CI.
Quando a pergunta passa de "o que aconteceu?" para "a trajetória respeitou o contrato?", use os spans como entrada para testar a trajetória de um agente de IA. O trace fornece eventos; o teste decide quais eram permitidos.
Trace, log, métrica ou checkpoint?
Eles respondem a perguntas diferentes. O trace explica a ordem e a relação entre operações. O log guarda detalhes textuais de um evento. A métrica agrega contagens e latências para alertas. O checkpoint guarda estado suficiente para retomar uma execução. Um sistema de agente pode precisar dos quatro.
Não use trace como banco de estado. Um exportador pode atrasar, amostrar, descartar ou reter dados por pouco tempo. Se o agente precisa continuar depois de um processo morrer, persista o estado e o contrato de cada etapa fora da telemetria. O artigo sobre execução durável para agentes de IA trata dessa decisão.
Também não use log textual como substituto para a hierarquia. Um JSONL com todos os eventos pode ser útil para auditoria, mas sem contexto pai, IDs consistentes e duração ele vira uma lista que alguém precisa ordenar manualmente. O trace é a estrutura. Logs e artefatos podem completar a história.
Perguntas frequentes
Preciso registrar o prompt inteiro para depurar um agente?
Não. Comece com nome do agente, modelo, tool call, IDs, tentativa, duração, erro e resultado categorizado. O OpenTelemetry oferece atributos para mensagens e chamadas de ferramentas, mas o aplicativo decide o que exportar. Use amostras redigidas ou referências protegidas apenas quando a política de dados permitir.
Cada tool call precisa de um span?
Cada chamada que você precisa investigar merece uma operação observável, mas nem todo helper interno precisa de um span. Comece com o run, modelo, tool call, retrieval, retry e dependência externa. Se a árvore ficar ruidosa, remova spans mecânicos depois de preservar a fronteira que explica a decisão.
OpenTelemetry substitui uma ferramenta de observabilidade de agentes?
Não. OpenTelemetry fornece API, SDK, contexto e transporte para telemetria. Um backend pode oferecer busca, visualização e alertas específicos para agentes. O OpenInference adiciona convenções para spans de agente, modelo e ferramenta. A escolha do backend não muda a necessidade de definir o que é seguro registrar.
Um trace permite retomar o agente depois de uma falha?
Não por si só. O trace mostra o que foi observado e pode ajudar a localizar o último passo. Retomar exige estado persistido, idempotência, política de retry e uma decisão sobre qual etapa pode ser repetida. Trate o trace como evidência da execução, não como checkpoint confiável.
Conclusão
Um agente observável não é o que produz muitos logs. É o que deixa uma trilha curta para responder qual etapa ocorreu, qual ferramenta foi chamada, qual tentativa falhou e o que pode ser compartilhado com segurança.
Comece por um span raiz de execução. Aninhe chamadas de modelo e ferramentas. Propague o contexto assíncrono. Registre IDs, duração, tentativa, status e erro. Depois teste falhas, retries e argumentos sensíveis. OpenTelemetry pode transportar essa estrutura, mas a qualidade do trace depende do contrato que você escreve em volta dele.
Como esta análise foi feita
Samuel Fajreldines é o autor responsável por este artigo. A pesquisa comparou a documentação atual do OpenTelemetry e do OpenInference, o cluster existente de observabilidade e discussões públicas recentes. A árvore de spans e o checklist de redaction são síntese editorial original. O wrapper TypeScript é ilustrativo e não foi executado contra um agente ou backend. A assistência de IA apoiou descoberta, redação, geração da imagem, localização e revisão de consistência; não forneceu experiência de produção nem substituiu a verificação das fontes. O autor também mantém o RemoteCode como ferramenta de trabalho.
Fontes consultadas
- OpenTelemetry, "Inside the LLM Call: GenAI Observability with OpenTelemetry", consultado em 2026-09-24
- OpenTelemetry, "Gen AI attributes", consultado em 2026-09-24
- OpenTelemetry, "Node.js getting started", consultado em 2026-09-24
- OpenTelemetry, "Traces", consultado em 2026-09-24
- OpenTelemetry, "Semantic conventions", consultado em 2026-09-24
- OpenInference, "Traces", consultado em 2026-09-24
- OpenInference, "OpenInference specification", consultado em 2026-09-24
- OpenInference, "OpenInference JS", consultado em 2026-09-24