Neste artigo você vai montar a observabilidade de uma aplicação com LLM: o que registrar em cada chamada, como medir latência e custo, como acompanhar qualidade sem gastar uma fortuna avaliando tudo, e como organizar um painel que responde "por que a conta subiu?". Os exemplos são em Python e independem de provedor.
O problema real
O painel de infraestrutura está verde, as chamadas ao provedor retornam 200 e ninguém reclamou. Mesmo assim, a fatura do mês veio bem maior. Depois de muita investigação, descobre-se que uma alteração no prompt passou a produzir respostas bem mais longas, ou que uma nova funcionalidade manda um histórico enorme a cada requisição.
Numa aplicação comum, "respondeu 200" quase sempre significa que deu certo. Com LLM, não. A resposta pode ser cara, lenta, incorreta ou uma recusa educada, e tudo isso volta como sucesso HTTP. Por isso a observabilidade precisa olhar para tokens, custo, latência percebida e qualidade.
O que registrar em cada chamada
O núcleo é um evento estruturado por chamada, com um identificador para correlacionar com o resto do sistema.
import json, time, uuid
from dataclasses import dataclass, asdict
@dataclass
class EventoLLM:
request_id: str
feature: str # ex.: "resumo-de-contrato"
modelo: str
tokens_entrada: int
tokens_saida: int
tokens_cache: int # tokens lidos do cache de prompt, se o provedor reportar
ttft_ms: float | None
latencia_ms: float
motivo_parada: str # fim normal, limite de tokens, recusa, erro
custo_usd: float
Os números de tokens vêm do campo de uso que o próprio provedor devolve na resposta, e o nome do campo muda de um para outro (input_tokens/output_tokens, prompt_tokens/completion_tokens). Não estime por contagem de caracteres: use o que a API informa.
O custo é tokens vezes o preço do modelo. Não coloque preços fixos no código. Guarde numa tabela de configuração com a data de vigência, alimentada a partir da página de preços do provedor, e calcule o custo no momento da consulta ou do registro. Assim, quando o preço mudar, você corrige em um lugar só.
PRECOS = { # USD por milhão de tokens. Preencha com os valores atuais do seu provedor.
"modelo-pequeno": {"entrada": 0.0, "saida": 0.0},
"modelo-de-ponta": {"entrada": 0.0, "saida": 0.0},
}
def calcular_custo(modelo: str, tokens_entrada: int, tokens_saida: int) -> float:
p = PRECOS[modelo]
return (tokens_entrada * p["entrada"] + tokens_saida * p["saida"]) / 1_000_000
Os zeros acima são de propósito: o preço certo é o que está na página do provedor hoje, não o que está num artigo.
Medindo latência: TTFT e tempo total
Em respostas com streaming, o usuário sente duas coisas. Quanto tempo leva até o primeiro token aparecer (TTFT, time to first token) e quanto leva para terminar. Meça as duas.
def chamar_com_stream(cliente, **kwargs):
inicio = time.perf_counter()
ttft = None
partes = []
for pedaco in cliente.stream(**kwargs): # adapte ao SDK que você usa
if ttft is None:
ttft = (time.perf_counter() - inicio) * 1000
partes.append(pedaco.texto)
total = (time.perf_counter() - inicio) * 1000
return "".join(partes), ttft, total
Acompanhe mediana e p95 ou p99, não a média. Não existe um valor mágico de TTFT, ele depende do modelo, do tamanho do prompt e da carga do provedor. O que importa é a tendência: se o p95 sobe de repente, investigue prompts maiores, limites de taxa e mudanças no provedor.
Recusas e erros
Muitos provedores informam o motivo de parada da geração (fim natural, limite de tokens, bloqueio por política). Registre esse campo em vez de procurar frases como "não posso" no texto, o que é frágil e erra em português, inglês e todo o resto. Acompanhe também a proporção de respostas cortadas por limite de tokens, que costuma indicar um max_tokens baixo demais.
Um aumento súbito de recusas pode ter origem num novo texto de prompt, numa troca de modelo ou num tipo de entrada que chegou com um novo cliente. Segmente por funcionalidade para achar a causa.
Qualidade: avaliação online e offline
Latência e custo são fáceis de medir. Qualidade é o difícil, e vale combinar três camadas.
Verificações automáticas baratas rodam em todas as respostas: o JSON é válido, os campos obrigatórios existem, a resposta cita uma fonte que realmente estava no contexto, o tamanho está na faixa esperada.
Um modelo avaliador (LLM-as-judge) pontua uma amostra, digamos de 1% a 10% do tráfego, segundo critérios claros, como "a resposta se apoia só no contexto fornecido?". Ele é útil para tendências, e não para sentenciar uma resposta isolada. Calibre o juiz contra avaliações humanas numa amostra e meça a concordância, porque juízes têm vieses próprios, como preferir respostas longas.
Revisão humana de uma amostra pequena e regular mantém todo o resto honesto. Alimente seu conjunto de testes com os casos ruins que aparecerem em produção, o que fecha o ciclo com avaliação de LLM com evals.
import random
def deve_avaliar(taxa: float = 0.05) -> bool:
return random.random() < taxa
if deve_avaliar():
fila_de_avaliacao.enqueue(avaliar_resposta, request_id) # fora do caminho da requisição
Rode a avaliação de forma assíncrona, numa fila, para não somar latência à resposta do usuário. Veja filas e workers.
Traces e padrões abertos
Uma requisição de usuário em um sistema com LLM costuma atravessar busca de contexto, uma ou mais chamadas de modelo, ferramentas e validações. Um trace mostra essa cadeia com os tempos de cada etapa. O OpenTelemetry tem convenções semânticas para IA generativa (atributos gen_ai.*), que ajudam a manter nomes consistentes, e o projeto ainda evolui, então confira a versão atual na documentação. Existem também plataformas especializadas em traces de LLM, como Langfuse, LangSmith e Arize Phoenix. Escolha pelo que já combina com o resto da sua stack.
Por privacidade, trate prompts e respostas como dados sensíveis: eles podem conter dados pessoais. Registre o conteúdo completo só quando necessário, com retenção curta, mascaramento e acesso restrito, de acordo com a LGPD.
O painel que responde "por que subiu?"
| Pergunta | Visão |
|---|---|
| Quanto gastamos? | Custo por dia, por modelo e por funcionalidade |
| O que mudou? | Tokens de entrada e de saída por requisição, ao longo do tempo |
| Está lento? | TTFT e latência total, mediana e p95 |
| Está pior? | Nota média do avaliador e taxa de falha das verificações |
| Estão recusando? | Proporção por motivo de parada |
| Está sendo reaproveitado? | Fração de tokens vindos do cache de prompt |
Para investigar um aumento de custo, comece decompondo: foi mais requisições, mais tokens de entrada por requisição, mais tokens de saída, ou troca para um modelo mais caro? Quase sempre a causa está num desses quatro.
Para consultar os eventos, qualquer agregação simples serve:
import pandas as pd
df = pd.DataFrame(eventos)
df["dia"] = pd.to_datetime(df["timestamp"]).dt.date
print(df.groupby(["dia", "feature"]).agg(
custo=("custo_usd", "sum"),
requisicoes=("request_id", "count"),
tokens_saida_medio=("tokens_saida", "mean"),
))
Defina alertas de variação, por exemplo "tokens de entrada por requisição subiram bem acima da média da semana" ou "custo diário passou do orçamento", em vez de valores absolutos que envelhecem rápido.
Armadilhas comuns
- Guardar o texto completo de todas as chamadas indefinidamente. Amostre e defina retenção.
- Avaliar tudo com um modelo juiz caro. Amostre.
- Olhar só o TTFT. Resposta rápida e errada continua errada.
- Deixar a instrumentação para depois. Ela é bem mais simples no começo do projeto, e é a base para controlar custos, como mostra FinOps para IA.
Checklist
- Cada chamada registra modelo, tokens, custo, latência, motivo de parada e
request_id. - Existe tabela de preços versionada fora do código de negócio.
- TTFT e tempo total aparecem em percentis.
- Há verificações automáticas em todas as respostas e avaliação por amostragem.
- Conteúdo sensível tem retenção e acesso controlados.
- O painel decompõe o custo por funcionalidade e por modelo.
Próximos passos
Leia FinOps para IA para transformar essas métricas em controle de orçamento e Segurança em aplicações com LLM para registrar sem vazar dados.
Quer aplicar isso na sua empresa? Marque uma conversa de 45 minutos em https://iauaicloud.com.br/consultoria