12-01-02 — Tracing: spans, runs, threads (OpenTelemetry GenAI conventions)

⏱ 15 minFontes validadas em: 2026-04-29

TL;DR

Tracing distribuido e a espinha dorsal da observabilidade em IA. Com o padrao OpenTelemetry GenAI Semantic Conventions (estabilizado em 2024), voce instrumenta qualquer LLM call, tool call e chain com spans padronizados — e exporta para qualquer backend (Langfuse, Jaeger, Azure Monitor). Run e a execucao completa de um agente. Thread e a conversa (multiplas runs). Span e cada etapa atomica dentro de uma run.

Conceitos fundamentais

Hierarquia: Thread → Run → Span

graph TD T["Thread (conversa completa)\nID: thread_abc123"] --> R1["Run 1\nmensagem: 'Quais vendas?'"] T --> R2["Run 2\nmensagem: 'Filtra por SP'"] R1 --> S1["Span: retrieval\n45ms"] R1 --> S2["Span: llm.chat\n820ms"] R2 --> S3["Span: llm.chat\n610ms"] S2 --> S4["Span: tool_call: query_fabric\n350ms"]
ConceitoO que representaAnalogia .NET
ThreadSessao de conversa com um usuarioHTTP Session
RunUma invocacao do agente (uma mensagem → resposta)HTTP Request
SpanUma operacao atomica dentro da runMethod call em trace
TraceArvore completa de spans de uma runDistributed trace

OpenTelemetry GenAI Semantic Conventions

A spec define atributos padronizados para spans de IA. Os principais:

Atributos de LLM Call (gen_ai.client.*)

# Atributos padrao em um span de LLM call
{
    "gen_ai.system": "openai",               # ou "azure_openai", "anthropic"
    "gen_ai.request.model": "gpt-4o",
    "gen_ai.request.max_tokens": 4096,
    "gen_ai.request.temperature": 0.7,
    "gen_ai.response.model": "gpt-4o-2024-08-06",
    "gen_ai.response.finish_reasons": ["stop"],
    "gen_ai.usage.input_tokens": 1234,
    "gen_ai.usage.output_tokens": 456,
    "gen_ai.usage.total_tokens": 1690
}

Atributos de Tool Call

{
    "gen_ai.tool.name": "query_fabric",
    "gen_ai.tool.call.id": "call_abc123",
    "gen_ai.tool.description": "Executa query SQL no Fabric Lakehouse"
}

Instrumentacao com OpenTelemetry (Python)

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 opentelemetry.instrumentation.openai import OpenAIInstrumentor

# Configurar provider e exporter (aqui exportando para Langfuse via OTLP)
provider = TracerProvider()
exporter = OTLPSpanExporter(
    endpoint="https://cloud.langfuse.com/api/public/otel/v1/traces",
    headers={
        "Authorization": "Basic "
    }
)
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

# Auto-instrumentacao: captura todos os LLM calls automaticamente
OpenAIInstrumentor().instrument()

# A partir daqui, qualquer chamada ao OpenAI SDK gera spans automaticamente
from openai import AzureOpenAI

client = AzureOpenAI(
    azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
    api_key=os.environ["AZURE_OPENAI_KEY"],
    api_version="2024-08-01-preview"
)

# Esta chamada gera um span com todos os atributos GenAI automaticamente
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Quais vendas no Q1?"}]
)

Instrumentacao manual para logica customizada

tracer = trace.get_tracer("sales-agent", "1.0.0")

def executar_agente(user_message: str, thread_id: str) -> str:
    with tracer.start_as_current_span("agent.run") as run_span:
        run_span.set_attribute("gen_ai.thread.id", thread_id)
        run_span.set_attribute("gen_ai.request.input", user_message)
        
        # Etapa de retrieval
        with tracer.start_as_current_span("retrieval.fabric_query") as ret_span:
            docs = query_fabric(user_message)
            ret_span.set_attribute("retrieval.docs_count", len(docs))
            ret_span.set_attribute("retrieval.latency_ms", 320)
        
        # Etapa de geracao
        with tracer.start_as_current_span("gen_ai.chat") as gen_span:
            response = chamar_llm(user_message, docs)
            gen_span.set_attribute("gen_ai.usage.input_tokens", response.usage.prompt_tokens)
            gen_span.set_attribute("gen_ai.usage.output_tokens", response.usage.completion_tokens)
        
        run_span.set_attribute("gen_ai.response.output", response.content)
        return response.content

Instrumentacao em .NET (Semantic Kernel)

using OpenTelemetry;
using OpenTelemetry.Trace;
using Microsoft.SemanticKernel;

// Configurar OpenTelemetry com exportador OTLP
var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSource("Microsoft.SemanticKernel*")  // captura todos os spans do SK
    .AddOtlpExporter(opt =>
    {
        opt.Endpoint = new Uri("https://your-langfuse-endpoint/api/public/otel/v1/traces");
        opt.Headers = "Authorization=Basic ";
    })
    .Build();

// Habilitar telemetria detalhada no Semantic Kernel
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(deploymentName, endpoint, apiKey);
builder.Services.AddLogging(l => l.SetMinimumLevel(LogLevel.Trace));

// AppContext para habilitar conteudo sensivel nos logs (use apenas em dev/staging)
AppContext.SetSwitch("Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnosticsSensitive", true);

var kernel = builder.Build();
// A partir daqui, todas as invocacoes do kernel geram spans automaticamente

Visualizando traces

Com os spans chegando no backend de observabilidade, voce consegue:

  • Waterfall view: Ver exatamente onde o tempo foi gasto (retrieval? LLM? tool call?)
  • Token breakdown: Input vs output tokens por etapa
  • Error traces: Identificar em qual span uma falha ocorreu
  • Session replay: Ver toda a conversa de um usuario com o trace completo de cada mensagem
💡 Regra pratica
Qualquer operacao que demora mais de 10ms merece um span proprio. Isso inclui: chamadas LLM, queries de banco/Fabric, chamadas de tools externas, embedding calls e etapas de pos-processamento.

Como isso se conecta

  • 12-02-01 LangSmith/Langfuse: Backends que consomem esses traces OTLP e os tornam navegaveis
  • 12-02-02 Azure Monitor: Destino alternativo para times full Microsoft stack
  • 08 Semantic Kernel: SK tem instrumentacao OTEL nativa — o setup acima funciona out of the box
  • 05 Agentes: Cada invocacao de agente e uma Run; cada step e um Span

Fontes

  1. OpenTelemetry — Semantic Conventions for Generative AI Systems (2025)
  2. Microsoft Docs — Observability in Semantic Kernel with OpenTelemetry (2025)
  3. Langfuse Docs — OpenTelemetry integration (2025)
  4. OpenTelemetry Python — OpenAI auto-instrumentation (2025)