Observabilidade, evals e proveniência
Objetivos de aprendizagem
- Logar
usage(input/output/cache tokens) para monitorar custo e cache hit rate em produção. - Explicar por que se avalia (evals) antes de otimizar — e o que medir.
- Preservar claim-source mappings através das etapas de síntese multi-fonte.
- Tratar conflitos entre fontes críveis com anotação e atribuição, nunca escolha arbitrária; usar datas de publicação para interpretar diferenças temporais.
- Estruturar relatórios com seções "bem estabelecido" vs "contestado" e coverage annotations.
Observabilidade: logue o usage de cada resposta
Cada resposta da Messages API traz um objeto usage com os contadores da chamada:
input_tokenseoutput_tokens— a base do custo;cache_read_input_tokens— tokens lidos do prompt cache (≈90% mais baratos);cache_creation_input_tokens— tokens gravados no cache (mais caros na primeira escrita).
Logando esses números por chamada você responde perguntas de produção: quanto custa cada fluxo? O cache está funcionando (cache hit rate)? Qual agente do pipeline consome mais? Uma mudança de prompt invalidou silenciosamente o cache (hit rate despencou)? Sem esses logs, otimização de custo é chute.
Exemplo: logging de usage e cache hit rate
import anthropic
client = anthropic.Anthropic()
metrics = {"calls": 0, "input": 0, "output": 0, "cache_read": 0, "cache_write": 0}
def tracked_call(**kwargs):
"""Wrapper que loga usage por chamada e acumula metricas do pipeline."""
response = client.messages.create(**kwargs)
u = response.usage
metrics["calls"] += 1
metrics["input"] += u.input_tokens
metrics["output"] += u.output_tokens
metrics["cache_read"] += u.cache_read_input_tokens or 0
metrics["cache_write"] += u.cache_creation_input_tokens or 0
print(f"req={response._request_id} in={u.input_tokens} out={u.output_tokens} "
f"cache_read={u.cache_read_input_tokens} cache_write={u.cache_creation_input_tokens}")
return response
def cache_hit_rate() -> float:
total_input = metrics["input"] + metrics["cache_read"] + metrics["cache_write"]
return metrics["cache_read"] / total_input if total_input else 0.0
response = tracked_call(
model="claude-opus-5",
max_tokens=1024,
system=[{"type": "text", "text": "Voce e um analista de pesquisa.",
"cache_control": {"type": "ephemeral"}}],
messages=[{"role": "user", "content": "Liste 3 riscos de sintese multi-fonte."}],
)
print(f"cache hit rate acumulado: {cache_hit_rate():.1%}")
Evals: avalie antes de otimizar
"Melhorar o prompt" sem uma suíte de avaliação é andar no escuro: você não sabe se a mudança melhorou, piorou ou só mudou os erros de lugar. O ciclo maduro:
- Defina critérios de sucesso mensuráveis (acurácia por segmento, taxa de escalação correta, custo por caso);
- Monte um conjunto de casos de teste representativo — incluindo os casos raros e os segmentos fracos;
- Rode a eval como baseline, mude uma coisa, rode de novo e compare;
- Em produção, monitore as mesmas métricas para detectar regressões e padrões novos de erro.
Evals conectam os domínios: os thresholds de confiança da lição 3 só são confiáveis porque foram medidos contra validation set; o custo da lição 2 só cai de verdade quando o cache hit rate é observado.
Proveniência: claim-source mappings (TS 5.6)
Em síntese multi-fonte, a atribuição de fonte se perde no ponto em que achados são comprimidos sem preservar o mapeamento claim → fonte. Depois de dois níveis de resumo, "o mercado cresceu 12%" não tem mais dono — e não pode ser verificado.
O padrão correto: exigir que subagentes emitam saída estruturada em que cada claim carrega sua fonte (URL ou nome do documento, trecho relevante/excerpt, data de publicação) e que os agentes downstream preservem e mesclem esses mapeamentos ao combinar achados — a síntese nunca "achata" o claim descartando a origem.
# Schema de finding com proveniencia que os subagentes devem emitir
# e que a sintese e obrigada a preservar ao mesclar:
finding = {
"claim": "O mercado de X cresceu 12% em 2025",
"source_name": "Relatorio Setorial Alfa 2025",
"source_url": "https://exemplo.com/relatorio-alfa",
"excerpt": "...crescimento de 12% no exercicio de 2025...",
"publication_date": "2026-01-15",
"methodology_note": "amostra de 400 empresas; exclui informais",
}
assert all(finding.get(k) for k in ("claim", "source_name", "publication_date"))
Conflitos entre fontes críveis
- Não escolha arbitrariamente. Duas fontes críveis com estatísticas diferentes (crescimento de 12% vs 15%)? A análise é entregue com ambos os valores incluídos e o conflito explicitamente anotado com atribuição — quem decide como reconciliar é o coordenador (ou o leitor), antes da síntese final.
- Datas de publicação são obrigatórias nos structured outputs: sem elas, uma diferença temporal legítima (dado de 2024 vs dado de 2026) é mal interpretada como contradição entre fontes.
- Estruture o relatório em seções distintas: achados bem estabelecidos (corroborados por múltiplas fontes) separados dos contestados (fontes divergem), preservando a caracterização original de cada fonte e seu contexto metodológico.
- Coverage annotations: a síntese indica quais achados estão bem suportados e quais áreas têm lacunas porque fontes estavam indisponíveis (conectando com a propagação de erros da lição 4).
Formato segue o conteúdo. Na síntese final, renderize cada tipo de conteúdo adequadamente — dados financeiros como tabelas, notícias como prosa, achados técnicos como listas estruturadas — em vez de converter tudo para um formato uniforme que degrada a legibilidade.
Pegadinhas da prova
- Distrator típico: "quando duas fontes divergem, use a mais confiável/recente e siga em frente". Entre fontes críveis, escolher arbitrariamente descarta informação: anote o conflito com atribuição e deixe a reconciliação para o coordenador.
- Distrator típico: tratar dados de anos diferentes como "contradição". Sem datas de publicação no structured output, diferenças temporais viram falsos conflitos.
- Distrator típico: "resuma os achados dos subagentes e descarte os metadados para economizar contexto". É exatamente assim que a proveniência se perde — o mapeamento claim-source deve sobreviver à compressão.
- Distrator típico: otimizar prompt/custo sem baseline de eval — impossível saber se melhorou; primeiro mede, depois otimiza.
- Distrator típico: monitorar custo só pela fatura mensal. O
usagepor chamada (incluindocache_read_input_tokens) é o que permite atribuir custo por fluxo e detectar invalidação silenciosa de cache.
Resumo em 5 linhas
- Logue
usagepor chamada (input, output, cache read/write) para custo por fluxo e cache hit rate — sem isso, otimização é chute. - Evals antes de otimizar: baseline mensurável, mudar uma coisa por vez, monitorar as mesmas métricas em produção.
- Cada claim carrega sua fonte (nome/URL, excerpt, data) em saída estruturada; a síntese preserva e mescla os mapeamentos.
- Conflito entre fontes críveis: incluir ambos os valores anotados com atribuição — nunca escolher arbitrariamente; datas evitam falsas contradições temporais.
- Relatórios separam "bem estabelecido" de "contestado", trazem coverage annotations e renderizam cada tipo de conteúdo no formato adequado.
Documentação oficial
- Prompt caching (usage e cache_read_input_tokens)
- Develop tests (evals)
- Citations (atribuição de fonte)
Questões de fixação
1. Dois subagentes retornam estatísticas conflitantes de fontes igualmente críveis: crescimento de 12% e de 15%. O que o agente de análise deve entregar ao coordenador?
Gabarito: B. Entre fontes críveis, o padrão é anotar o conflito com atribuição — nunca resolver arbitrariamente no meio do pipeline. A descarta informação por heurística (recência não é veredito). C fabrica um número que nenhuma fonte publicou. D pode ser útil depois, mas bloqueia a entrega e não substitui a anotação: a divergência em si é informação.
2. Após dois níveis de sumarização em um pipeline de pesquisa, o relatório final afirma "a adoção dobrou" sem que ninguém consiga dizer de qual fonte isso veio. Qual mudança estrutural previne o problema?
Gabarito: D. A proveniência se perde no ponto de compressão; a correção é estrutural — o mapeamento claim → fonte viaja junto do claim por todas as etapas. A é impraticável (sem sumarização o contexto explode). B chega tarde: no último agente a informação de origem já não existe para ser citada. C lista fontes sem ligá-las aos claims — não permite verificar qual afirmação veio de onde.
3. Um relatório sintetizado marca como "contradição entre fontes" dois números de market share: um de um censo de 2024 e outro de uma pesquisa de 2026. Que requisito de structured output teria evitado o falso conflito?
Gabarito: A. Com as datas presentes, a síntese reconhece evolução temporal em vez de contradição. B empobrece a pesquisa e nem sempre é possível. C altera dados de fonte — a síntese deve preservar caracterizações originais, não reprocessá-las. D joga fora dados históricos válidos; a série temporal costuma ser justamente o insight.
4. Quais duas práticas de observabilidade permitem detectar que uma mudança de prompt invalidou silenciosamente o prompt cache e disparou o custo? (escolha duas)
Gabarito: B e D. O usage de cada resposta é a fonte de verdade sobre cache: hit rate caindo a zero logo após um deploy aponta a invalidação e o momento exato. A detecta o problema semanas depois, sem atribuição de causa. C não funciona: o modelo não tem acesso à contabilidade de cache — isso vem nos metadados da resposta, não do texto. E mitiga o sintoma errado (saída), enquanto o desperdício está no input não cacheado.