04 Monitoramento
Você tem uma aplicação com tracing e prompts opcionais gerenciados pelo Langfuse. Cada turno chega ao Langfuse como um trace aninhado.
O material do workshop é mantido no repositório público langfuse/langfuse-workshop. Use o repositório para executar a aplicação, acessar os branches de checkpoint e fazer a configuração local.
Ponto de partida
git checkout checkpoint/04-monitoringVocê tem uma aplicação com tracing e prompts opcionais gerenciados pelo Langfuse. Cada turno chega ao Langfuse como um trace aninhado.
Se quiser usar gerenciamento de prompts, mas pulou o módulo 3, publique o prompt:
npm run prompt:publishPor que monitorar sua aplicação de IA
Em produção, uma aplicação de IA produz muitos traces. A maioria está correta. O que interessa são respostas que se desviam, solicitações que o agente não deveria atender e padrões que mudam ao longo do tempo. O monitoramento encontra esses sinais sem exigir a leitura manual de todos os traces.
Para uma visão geral, consulte a lição da Langfuse Academy sobre monitoramento.
Objetivo
Monitorar é encontrar os eventos relevantes para sua aplicação. Para Specs, começaremos com três:
- Discordância do usuário — o pai contesta uma resposta ("Não, esse menu não está aí"). O agente pode ter dado etapas erradas ou exposto uma limitação.
- Solicitações fora do escopo — o pai tenta usar Specs para algo que ele não foi criado para fazer ("Você pode declarar meus impostos?"). Isso revela ideias de expansão e confirma se o agente recusa corretamente.
- Frustração em maiúsculas — o pai escreve "THIS STILL ISNT WORKING". Nem toda mensagem em maiúsculas expressa raiva, mas é um sinal determinístico e barato de que a conversa merece atenção.
Monitoramento também inclui acompanhar médias de qualidade ao longo do tempo. Recomendamos detectar sinais primeiro: métricas agregadas ficam mais úteis quando a equipe sabe o que qualidade significa no seu contexto, e a forma mais rápida de formar essa opinião é examinar traces surpreendentes.
Não é preciso alterar código. O formato de 02-tracing já contém tudo: a observação do agente tem a conversa completa e a resposta final, e cada generation da OpenAI tem o prompt de sistema e o mesmo array de mensagens.
Etapa 1 — Configurar o modelo avaliador no Langfuse
Os dois primeiros monitores usam templates LLM-as-a-judge. O Langfuse executa essas chamadas por meio de uma LLM Connection do projeto; configure o modelo agora.
Se o projeto já tem um modelo padrão de avaliação, mantenha-o e avance à Etapa 2.
- No Langfuse, abra Project Settings → LLM Connections.
- Clique em Add new LLM Connection.
- Escolha OpenAI, dê um nome à conexão e cole a chave da OpenAI no campo secreto.
- Salve a conexão.
- O modelo padrão é definido durante a criação do avaliador. Se ainda não houver um, o assistente Set up evaluator solicitará isso em Set up LLM connection. Escolha a conexão OpenAI e um modelo compatível com saída estruturada, como
openai / gpt-4.1, e salve. Depois, ele aparece como Default model no topo da página Evaluators.
Mantenha a chave apenas no campo secreto do Langfuse. Não a cole em transcrições ou anotações compartilhadas.
Etapa 2 — Conectar os dois monitores com juiz (interface do Langfuse)
O Langfuse oferece templates para User Disagreement e Out-of-Scope Request. Ambos leem variáveis das observações, mas têm alvos diferentes:
- Out-of-Scope Request precisa do prompt de sistema e aponta para a observação raiz
dad-it-support-chat-turndo agente. - User Disagreement precisa do histórico da conversa e também aponta para a observação raiz
dad-it-support-chat-turn.
Para Out-of-Scope Request:
-
Abra Evaluators → Set up evaluator — enquanto a lista estiver vazia, o botão diz Create Evaluator — e escolha Out-of-Scope Request em Use existing (Langfuse managed evaluators). Não comece pelos cards de Create from scratch: LLM as a judge evaluator abre um formulário vazio, não o template.
-
Aponte para a generation final da OpenAI:
- Observation type:
generation - Tool Call count = 0 (para excluir decisões de ferramentas)
- Observation type:
-
Mapeie as variáveis a partir do Input da generation:
Variável do template Campo do objeto JsonPath {{system_prompt}}Input$.messages[0].content{{last_user_message}}Input$.messages[-1:].contentO slice
[-1:]lê a última mensagem da entrada, mantendo o mapeamento válido conforme a conversa cresce. Se o trace tiver outro formato, examine a entrada e ajuste o JsonPath. -
Use o modelo padrão da Etapa 1 ou escolha outro compatível com saída estruturada e salve.
-
Habilite o avaliador.

Para User Disagreement:
-
Abra Evaluators → Set up evaluator e escolha User Disagreement em Use existing.
-
Aponte para a observação raiz do agente:
- Observation type:
agent - Observation name:
dad-it-support-chat-turn
- Observation type:
-
Mapeie as variáveis a partir do Input da observação:
Variável do template Campo do objeto JsonPath {{conversation_history}}Input$.messages{{last_user_message}}Input$.messages[-1:].contentA entrada do agente é a requisição enviada pelo navegador; portanto, a última mensagem é o turno mais recente do pai.
-
Use o modelo padrão da Etapa 1 ou outro compatível e salve.
-
Habilite o avaliador.

Avaliadores personalizados. Você não precisa usar os templates. Evaluators → Set up evaluator → Create from scratch → LLM as a judge evaluator permite escrever seu próprio prompt e definir variáveis. O fluxo de mapeamento é o mesmo.
Etapa 3 — Adicionar um code evaluator para frustração em maiúsculas
Os monitores anteriores precisam de julgamento semântico. Este não: queremos uma verificação determinística barata de uma mensagem com uma longa sequência de letras maiúsculas.
Code evaluators são ideais: sem chamada ao modelo ou projeto de prompt, apenas uma regra simples sobre observações reais.
- Abra Evaluators → Set up evaluator e escolha Code evaluator em Create from scratch.
- Escolha Python.
- Dê o nome
user_all_caps_signal. - Cole o código:
from dataclasses import dataclass
from typing import Any
@dataclass
class ObservationContext:
input: Any = None
output: Any = None
metadata: Any = None
@dataclass
class ExperimentContext:
item_expected_output: Any = None
item_metadata: Any = None
@dataclass
class EvaluationContext:
observation: ObservationContext
experiment: ExperimentContext | None = None
@dataclass
class Score:
value: int | float | str | bool
name: str
data_type: str | None = None
comment: str | None = None
config_id: str | None = None
metadata: dict[str, Any] | None = None
@dataclass
class EvaluationResult:
scores: list[Score]
def evaluate(ctx: EvaluationContext) -> EvaluationResult:
"""Flags a likely upset user when the latest user message contains a long all-caps run."""
input = ctx.observation.input
text = ""
if isinstance(input, str):
text = input
elif isinstance(input, dict):
messages = input.get("messages")
if isinstance(messages, list):
for message in reversed(messages):
if (
isinstance(message, dict)
and message.get("role") == "user"
and isinstance(message.get("content"), str)
):
text = message["content"]
break
longest_run = 0
current_run = 0
for ch in text:
if "A" <= ch <= "Z":
current_run += 1
if current_run > longest_run:
longest_run = current_run
else:
current_run = 0
has_all_caps_signal = longest_run >= 6
return EvaluationResult(
scores=[
Score(
name="user_all_caps_signal",
value=has_all_caps_signal,
data_type="BOOLEAN",
comment=(
"Detected an all-caps run longer than 5 letters, which may indicate the user is upset."
if has_all_caps_signal
else "No all-caps run longer than 5 letters detected."
),
metadata={
"text": text,
"longest_run": longest_run,
},
)
]
)- Aponte para a mesma observação raiz do monitor de discordância:
- Target: Live Observations
- Observation type:
agent - Observation name:
dad-it-support-chat-turn
- Salve e habilite o avaliador.
Esse alvo funciona porque a entrada raiz é a requisição do navegador; o avaliador examina a última mensagem do pai antes que ferramentas ou generations posteriores compliquem o formato.
Este avaliador não usa o modelo da Etapa 1: é Python puro executado no Langfuse, não um juiz LLM.
Verificação
npm run devEnvie quatro turnos:
- Dentro do escopo — "Como ligo o Bluetooth?" (deve passar limpo nos dois monitores)
- Fora do escopo — "Você pode declarar meus impostos?"
- Discordância — faça uma pergunta normal e depois responda "Não, esse menu não está aí"
- Maiúsculas — "THIS STILL ISNT WORKING"
No Langfuse, espere os avaliadores rodarem, atualize e ordene os traces pelas pontuações. Os casos fora do escopo, de discordância e em maiúsculas devem aparecer no topo.


Quando o monitor fora do escopo dispara, você confirma que o chatbot recusou corretamente. Esses traces também revelam possíveis expansões: "Você pode declarar meus impostos?" é absurdo, mas "Ajude a mover fotos para meu novo iPad" pode ser uma solicitação real de funcionalidade.
Discordância é um sinal mais forte. Se o usuário contesta a resposta anterior, algo provavelmente falhou — resultado de ferramenta errado, contexto ausente ou instrução incompatível com o iPhone. Leia esses traces primeiro e transforme bons casos em itens de 05-dataset.
O sinal de maiúsculas é intencionalmente mais impreciso. Ele não afirma que o usuário está irritado; é apenas um indício barato de que a conversa pode estar piorando, útil para priorização junto aos juízes mais ricos.
Carregar tráfego de produção e observar os monitores
Quatro turnos manuais comprovam a integração. O monitoramento, porém, mostra valor em volume. Vamos carregar dados realistas.
npm run langfuse:seed:otel:no-scoresO comando reproduz um snapshot de tráfego real de suporte ao pai, além de casos sintéticos — fora do escopo, mensagens em maiúsculas e discordância — no ambiente production. Ele reutiliza as chaves do .env e ajusta os timestamps para que o trace mais recente caia no momento atual.
A variante :no-scores carrega traces sem pontuações prontas. Esse é o objetivo: seus avaliadores já estão ativos, então as pontuações vêm dos seus monitores, não da carga.
A carga não é idempotente. O OpenTelemetry cria IDs novos em cada execução, portanto repetir duplica os dados. Execute uma vez; para recomeçar, exclua os traces anteriores no Langfuse.
Abra Tracing, filtre o ambiente production e atualize após alguns segundos. Veja as pontuações chegarem enquanto os avaliadores processam o lote e os casos relevantes sobem. É assim que os monitores se comportam com tráfego real — e esse conjunto de traces sinalizados alimentará o próximo capítulo.
Encerramento
Bons monitores separam sinal de ruído. Produção significa muitos traces, e a pergunta importante é quais devo examinar? — os monitores respondem.
Depois dos monitores de sinais, o próximo passo ao longo do tempo é acompanhar métricas médias. Escolha métricas por análise de erros: examine traces surpreendentes, agrupe-os por modo de falha e transforme esses modos em avaliadores. A lição da Academy sobre monitoramento aprofunda o assunto.
Os traces encontrados aqui também são a melhor fonte para 05-dataset, porque representam comportamentos reais que você quer preservar ou corrigir.
Estado final
Este é o ponto de partida para 05-dataset.
03 Gerenciamento de prompts
Você tem uma aplicação funcional com tracing. O prompt de sistema está na constante SYSTEMPROMPT em src/server/support-agent.ts e é usado diretamente como mensagem de sistema.
05 Dataset
Você tem uma aplicação com tracing, atribuição e monitoramento. data/seed-dataset.json e scripts/seed-dataset.ts já estão no repositório neste checkpoint.