Blog · Infraestrutura

Observabilidade do AWS Bedrock AgentCore - Pydantic AI + CloudWatch OTEL

Por Fabio Douek

GitHub Veja o código fonte aqui: bedrock-agentcore-otel-pydantic

Visão Geral

Eu estava no meio deste post quando a AWS puxou o tapete. A versão original mostrava como enviar dados OpenTelemetry de um agente Pydantic AI para o CloudWatch usando ADOT (AWS Distro for OpenTelemetry), a distribuição oficial de OpenTelemetry da AWS que inclui auto-instrumentação, autenticação SigV4 e um comando wrapper pelo qual você precisa executar seu código. Funcionava. Mas eram 40 linhas de variáveis de ambiente e uma árvore de dependências que eu não queria explicar.

Então, em 2 de abril, a Amazon anunciou suporte nativo a métricas OpenTelemetry no CloudWatch. Endpoints OTLP nativos. Ingestão direta. Sem lógica de conversão, sem sidecars de collector, sem distro. Meu rascunho passou de “veja como lutar com o ADOT” para “espera, nada disso é mais necessário.” Então eu descartei e recomecei. Você está lendo a versão melhor.

Com o CloudWatch aceitando OTLP diretamente, tudo que você precisa é assinatura SigV4 nos exporters padrão do OpenTelemetry. O pacote requests-auth-aws-sigv4 faz exatamente isso em poucas linhas. Combine com o Agent.instrument_all() integrado do Pydantic AI e o Bedrock AgentCore Observability, e você tem rastreamento completo do agente no CloudWatch com código mínimo e zero dependências pesadas.

O código de exemplo completo está no GitHub. Clone, configure suas credenciais AWS e execute.

Setup

Pré-requisitos AWS

Antes de escrever qualquer código de agente, você precisa habilitar o CloudWatch Transaction Search. Essa é uma configuração única por conta AWS que permite ao CloudWatch receber e indexar traces OpenTelemetry.

# Create resource policy for X-Ray to write to CloudWatch Logs
aws logs put-resource-policy \
  --policy-name TransactionSearchPolicy \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Sid": "TransactionSearchXRayAccess",
      "Effect": "Allow",
      "Principal": {"Service": "xray.amazonaws.com"},
      "Action": "logs:PutLogEvents",
      "Resource": [
        "arn:aws:logs:*:*:log-group:aws/spans:*",
        "arn:aws:logs:*:*:log-group:/aws/application-signals/data:*"
      ]
    }]
  }'

# Route trace segments to CloudWatch Logs
aws xray update-trace-segment-destination --destination CloudWatchLogs

Um detalhe importante: apenas 1% dos spans são indexados como resumos de trace gratuitamente. Para desenvolvimento e testes, aumente para 100%:

aws xray update-indexing-rule --name "Default" \
  --rule '{"Probabilistic": {"DesiredSamplingPercentage": 100}}'

Para produção, mantenha mais baixo para gerenciar custos. As cobranças de indexação do X-Ray acumulam rápido.

Você também precisa do acesso ao modelo Bedrock habilitado para Claude na sua conta AWS. Navegue até o console do Bedrock, vá em Model access e habilite o modelo Claude que você planeja usar.

Setup do projeto

git clone https://github.com/fabiodouek/my2centsai-blog-samples.git
cd my2centsai-blog-samples/bedrock-agentcore-otel-pydantic

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

As dependências são mínimas:

pydantic-ai-slim[bedrock]
opentelemetry-api
opentelemetry-sdk
opentelemetry-exporter-otlp-proto-http
requests-auth-aws-sigv4

O pacote requests-auth-aws-sigv4 lida com a assinatura SigV4 para os endpoints OTLP do CloudWatch. Sem distro pesada, sem comandos wrapper.

Construindo o agente

O agente em si é Pydantic AI direto ao ponto. A linha chave está no topo:

from pydantic_ai import Agent, InstrumentationSettings

Agent.instrument_all(InstrumentationSettings(version=5))

O parâmetro InstrumentationSettings(version=5) opta pela versão mais recente do schema de instrumentação. Essa única chamada habilita instrumentação OpenTelemetry para todo agente que você criar depois. Ela emite spans seguindo as convenções semânticas GenAI do OpenTelemetry: gen_ai.operation.name, gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, e mais.

O agente usa BedrockConverseModel com Claude Sonnet:

agent = Agent(
    "bedrock:us.anthropic.claude-sonnet-4-6",
    system_prompt=(
        "You are a helpful assistant with access to weather, math, and time tools. "
        "Use the appropriate tool to answer the user's question. Be concise."
    ),
)

Eu adicionei três tools para exercitar o rastreamento de chamadas de tool: get_weather retorna dados meteorológicos fixos, calculate avalia expressões matemáticas, e get_current_time retorna a hora atual em um dado offset UTC. Todas autocontidas, sem dependências de API externas, para que o exemplo continue portável.

Configuração OTel

Os endpoints OTLP do CloudWatch exigem autenticação SigV4. O OTLPSpanExporter e o OTLPMetricExporter padrão aceitam um parâmetro session= para um requests.Session. O pacote requests-auth-aws-sigv4 adiciona assinatura SigV4 a qualquer session. Conectar esses dois fatos resulta em um setup limpo:

import os

import requests
from requests_auth_aws_sigv4 import AWSSigV4
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter

region = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
service_name = os.environ.get("OTEL_SERVICE_NAME", "pydantic-ai-agent-demo")

resource = Resource.create({"service.name": service_name})

# Traces: SigV4-signed session for the X-Ray OTLP endpoint
trace_session = requests.Session()
trace_session.auth = AWSSigV4("xray", region=region)

trace_exporter = OTLPSpanExporter(
    endpoint=f"https://xray.{region}.amazonaws.com/v1/traces",
    session=trace_session,
)
tracer_provider = TracerProvider(resource=resource)
tracer_provider.add_span_processor(BatchSpanProcessor(trace_exporter))
trace.set_tracer_provider(tracer_provider)

# Metrics: SigV4-signed session for the CloudWatch OTLP endpoint
metric_session = requests.Session()
metric_session.auth = AWSSigV4("monitoring", region=region)

metric_exporter = OTLPMetricExporter(
    endpoint=f"https://monitoring.{region}.amazonaws.com/v1/metrics",
    session=metric_session,
)
metric_reader = PeriodicExportingMetricReader(metric_exporter)
meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader])
metrics.set_meter_provider(meter_provider)

Os endpoints OTLP do CloudWatch são apenas HTTP 1.1. gRPC não é suportado.

Executando

set -a && source .env && set +a
python main.py

A função setup_otel() no main.py configura os providers no momento do import, Agent.instrument_all() se conecta a eles, e os exporters OTLP padrão cuidam do resto. A saída do terminal mostra as respostas do agente, e nos bastidores os spans OTel estão sendo agrupados e exportados para o CloudWatch.

A saída mostra o agente lidando com chamadas multi-tool e retornando resultados estruturados:

============================================================
Prompt: What's the weather like in Seattle and what's 15% of 340?
============================================================

Response: Here are your answers:

- **Seattle Weather:** It's cloudy and 58°F (14°C) with 72% humidity
  and light rain expected. Don't forget an umbrella!
- **15% of 340:** = **51**
Usage: RunUsage(input_tokens=1839, output_tokens=165, requests=2, tool_calls=2)

============================================================
Prompt: What time is it in UTC-8 right now?
============================================================

Response: The current time in **UTC-8** is **11:32 AM** on April 3, 2026.
Usage: RunUsage(input_tokens=1724, output_tokens=89, requests=2, tool_calls=1)

Note a saída RunUsage: duas requests e duas chamadas de tool para o primeiro prompt (weather + math), uma chamada de tool para o segundo. Esses valores mapeiam diretamente para os spans que você verá no CloudWatch.

Testes

O que aparece no CloudWatch

Uma coisa que me confundiu: traces e métricas aparecem em lugares diferentes no Console AWS. Traces aparecem em X-Ray traces > Transaction Search. A topologia de serviços fica em CloudWatch > Application Signals (APM) > Trace Map. Métricas OTLP vão para CloudWatch Metrics sob um namespace customizado. Não existe uma visão unificada. Você vai ficar alternando entre três seções diferentes do console para ver o quadro completo do comportamento do seu agente.

Após executar o agente, os traces aparecem no CloudWatch em poucos minutos. Navegue até X-Ray traces > Transaction Search e filtre por service = pydantic-ai-agent-demo.

Cada execução do agente produz um trace com uma hierarquia clara de spans:

  • Span de execução do agente (nível superior): a chamada completa agent.run()
    • Span de request ao modelo: cada chamada de API LLM para o Bedrock, com atributos como gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens
      • Spans de chamada de tool: cada invocação de tool, com gen_ai.tool.name e o resultado da tool

Essa hierarquia é o que o instrument_all() do Pydantic AI fornece. Sem isso, você veria apenas chamadas HTTP brutas para o Bedrock, perdendo todo o contexto no nível do agente.

Métricas

Navegue até CloudWatch > Application Signals (APM) > Trace Map para ver uma topologia visual das interações de serviço do seu agente. O trace map mostra o fluxo de requests do cliente, passando pela execução do agente até o Bedrock, com taxas de sucesso e latência em cada salto.

CloudWatch Trace Map mostrando o serviço de execução do agente com 100% de taxa OK chamando o Bedrock

Com o novo suporte nativo a métricas OTLP do CloudWatch, o histograma gen_ai.client.token.usage e o histograma operation.cost do Pydantic AI podem fluir diretamente para o CloudWatch Metrics. Vale configurar agora, já que métricas e consultas OTel são gratuitas durante o preview.

Traces

Navegue até CloudWatch > Transaction Search e filtre por service = pydantic-ai-agent-demo para ver spans individuais. Cada trace contém a hierarquia completa de spans: execução do agente, request ao modelo (chat us.anthropic.claude-sonnet-4-6), e spans de execução de tool, todos correlacionados sob um único trace ID.

CloudWatch Transaction Search mostrando spans para o serviço pydantic-ai-agent-demo

A lista de spans mostra exatamente o que você esperaria pela saída do terminal: dois spans chat para as duas requests ao Bedrock, dois spans execute_tool para as chamadas de tool weather e math, e um span agent run de nível superior conectando tudo. Duração e código de status são visíveis num relance.

Ao clicar em um trace, abre a timeline de spans em CloudWatch > Traces, onde você pode ver o waterfall completo: execução do agente, requests ao modelo para o Bedrock e execuções individuais de tool com suas durações. O painel de detalhes do segmento à direita mostra os atributos brutos do span incluindo gen_ai.request.model e contagens de token.

Timeline de spans do CloudWatch Traces mostrando o waterfall de execução do agente com durações do Bedrock e chamadas de tool

Também existe uma visão dedicada em GenAI Observability > Bedrock AgentCore que mostra traces com uma visualização de trajetória. Essa visão é feita especificamente para depuração de agentes: ela renderiza o grafo de chamadas visualmente e permite clicar em spans individuais como execute_tool get_weather para inspecionar metadata. Esse é mais um lugar no console onde dados de trace aparecem, reforçando o ponto de que você vai navegar entre várias seções.

Visão de trace do Bedrock AgentCore Observability com grafo de trajetória e detalhes de span

Problemas que encontrei

O preview é limitado a 5 regiões. US East (N. Virginia), US West (Oregon), Asia Pacific (Sydney e Singapore) e Europe (Ireland). Se seu endpoint Bedrock está em uma região diferente, a exportação de métricas OTLP pode não funcionar.

As convenções semânticas GenAI são experimentais. Os atributos de span gen_ai.* que o Pydantic AI emite seguem as convenções semânticas GenAI do OpenTelemetry, que estão com status “Development” sem cronograma publicado para estabilidade. Nomes de atributos podem mudar em versões futuras. A boa notícia: a base OTel significa que você pode atualizar a instrumentação sem mudar seu backend de observabilidade.

Como se compara às alternativas

Essa não é a única forma de observar agentes de IA. Logfire da Pydantic é a escolha natural para projetos Pydantic AI. Tem integração nativa e custa $2 por milhão de spans, comparado ao preço variável do CloudWatch que cobra em múltiplas dimensões (ingestão de logs, armazenamento, scanning, métricas, alarmes). Logfire também fornece observabilidade full-stack, então quando uma chamada ao database falha dentro do seu agente, você vê tanto o trace do LLM quanto o erro do database em uma única visão.

Langfuse é a opção open source mais popular com mais de 24.000 estrelas no GitHub.

A vantagem do CloudWatch é zero fornecedores adicionais. Se você já está na AWS, não há conta SaaS extra, nenhum dado saindo do perímetro da sua conta e nenhuma fatura separada. IAM cuida da autenticação. Você obtém métricas de infraestrutura e métricas de agentes de IA no mesmo console. Para indústrias reguladas onde dados de telemetria precisam permanecer dentro da conta AWS, isso pode ser um requisito rígido que nenhuma ferramenta de terceiros atende.

O AgentCore em si suporta roteamento de telemetria para backends de terceiros. A AWS documenta oficialmente a integração com Langfuse, e Dynatrace e Elastic construíram seus próprios conectores. O design nativo OTEL significa que você não fica preso.

Veredito

O suporte nativo a OTLP do CloudWatch, combinado com a instrumentação integrada do Pydantic AI e uma library leve de assinatura SigV4, forma uma stack viável de observabilidade de agentes com setup mínimo. Uma linha de código de instrumentação, uma função setup_otel(), e você tem traces fluindo para o CloudWatch.

Eu recomendaria essa stack para times que já estão na AWS e querem observabilidade de agentes sem integrar outro fornecedor. O argumento de compliance é forte: telemetria fica na sua conta, IAM cuida da autenticação, e você tem métricas de infraestrutura e de agentes em um único lugar. O endpoint OTLP nativo de abril de 2026 é um avanço real, eliminando o sidecar de collector que antes adicionava complexidade.

Vale configurar agora enquanto o preview é gratuito. A base OTel é a aposta certa independentemente de onde você aterrissar nas diferentes plataformas de observabilidade.

Comments