04 Monitoring
Vous avez une application tracée avec des prompts facultatifs gérés par Langfuse. Chaque tour arrive dans Langfuse sous forme de trace imbriqué.
Le contenu du workshop est maintenu dans le dépôt public langfuse/langfuse-workshop. Utilisez ce dépôt pour exécuter l’application, accéder aux branches de checkpoint et effectuer la configuration locale.
Point de départ
git checkout checkpoint/04-monitoringVous avez une application tracée avec des prompts facultatifs gérés par Langfuse. Chaque tour arrive dans Langfuse sous forme de trace imbriqué.
Si vous souhaitez utiliser la gestion des prompts mais avez sauté le module 3, publiez le prompt :
npm run prompt:publishPourquoi surveiller votre application IA
En production, une application IA produit de nombreux traces. La plupart sont corrects. Les plus intéressants sont les réponses qui dérivent, les demandes que l’agent ne devrait pas traiter et les tendances qui évoluent. Le monitoring détecte ces signaux sans obliger à lire chaque trace manuellement.
Pour une vue d’ensemble, consultez la leçon Langfuse Academy sur le monitoring.
Objectif
Le monitoring consiste à trouver les événements qui comptent pour votre application. Pour Specs, nous commencerons par trois :
- Désaccord utilisateur — le père conteste la réponse (« Non, ce menu n’existe pas »). L’agent a peut-être donné de mauvaises étapes ou révélé une limite.
- Demandes hors périmètre — le père essaie d’utiliser Specs pour autre chose (« Peux-tu remplir ma déclaration d’impôts ? »). Elles révèlent des idées d’extension et confirment que l’agent refuse correctement.
- Frustration en majuscules — le père écrit « THIS STILL ISNT WORKING ». Toute majuscule n’exprime pas la colère, mais c’est un signal déterministe peu coûteux qu’une conversation mérite de l’attention.
Le monitoring comprend aussi le suivi de scores moyens dans le temps. Nous recommandons de détecter d’abord les signaux : les métriques agrégées deviennent utiles quand l’équipe sait ce que signifie la qualité dans son contexte, et la façon la plus rapide de se forger cette opinion est d’examiner les traces surprenants.
Aucun changement de code n’est nécessaire. La structure de 02-tracing contient déjà tout : l’observation de l’agent possède la conversation et la réponse finale, et chaque generation OpenAI possède le prompt système et le même tableau de messages.
Étape 1 — Configurer le modèle d’évaluation Langfuse
Les deux premiers monitors utilisent des modèles LLM-as-a-judge. Langfuse exécute ces appels via une LLM Connection du projet ; configurez le modèle maintenant.
Si le projet possède déjà un modèle d’évaluation par défaut, gardez-le et passez à l’Étape 2.
- Ouvrez Project Settings → LLM Connections.
- Cliquez sur Add new LLM Connection.
- Choisissez OpenAI, nommez la connexion et collez la clé OpenAI dans le champ secret.
- Enregistrez la connexion.
- Le modèle par défaut est choisi à la création de l’évaluateur. S’il manque, l’assistant Set up evaluator le demande à l’étape Set up LLM connection. Choisissez la connexion OpenAI et un modèle compatible avec les sorties structurées, comme
openai / gpt-4.1, puis enregistrez. Il apparaîtra ensuite comme Default model sur la page Evaluators.
Conservez la clé uniquement dans le champ secret Langfuse. Ne la collez jamais dans des transcriptions ou notes partagées.
Étape 2 — Connecter les deux monitors avec juge (interface Langfuse)
Langfuse fournit des modèles User Disagreement et Out-of-Scope Request. Tous deux lisent des variables d’observations, mais ciblent des éléments différents :
- Out-of-Scope Request a besoin du prompt système et cible l’observation racine
dad-it-support-chat-turnde l’agent. - User Disagreement a besoin de l’historique de conversation et cible également l’observation racine
dad-it-support-chat-turn.
Pour Out-of-Scope Request :
-
Ouvrez Evaluators → Set up evaluator — le bouton affiche Create Evaluator tant que la liste est vide — et choisissez Out-of-Scope Request dans Use existing (Langfuse managed evaluators). Ne commencez pas par Create from scratch : LLM as a judge evaluator ouvre un formulaire vide, pas le modèle.
-
Ciblez la generation OpenAI finale :
- Observation type:
generation - Tool Call count = 0 (pour exclure les décisions d’outils)
- Observation type:
-
Mappez les variables depuis l’Input de la generation :
Variable du modèle Champ de l’objet JsonPath {{system_prompt}}Input$.messages[0].content{{last_user_message}}Input$.messages[-1:].contentLe slice
[-1:]lit le dernier message et reste valide à mesure que la conversation s’allonge. Si le trace a une autre structure, inspectez l’entrée et ajustez le JsonPath. -
Utilisez le modèle par défaut de l’Étape 1 ou un autre modèle compatible avec les sorties structurées, puis enregistrez.
-
Activez l’évaluateur.

Pour User Disagreement :
-
Ouvrez Evaluators → Set up evaluator et choisissez User Disagreement dans Use existing.
-
Ciblez l’observation racine de l’agent :
- Observation type:
agent - Observation name:
dad-it-support-chat-turn
- Observation type:
-
Mappez les variables depuis l’Input de l’observation :
Variable du modèle Champ de l’objet JsonPath {{conversation_history}}Input$.messages{{last_user_message}}Input$.messages[-1:].contentL’entrée de l’agent est la requête du navigateur ; le dernier message est donc le tour le plus récent du père.
-
Utilisez le modèle par défaut de l’Étape 1 ou un autre modèle compatible, puis enregistrez.
-
Activez l’évaluateur.

Évaluateurs personnalisés. Vous n’êtes pas obligé d’utiliser les modèles. Evaluators → Set up evaluator → Create from scratch → LLM as a judge evaluator permet d’écrire votre prompt et de définir vos variables. Le mapping reste identique.
Étape 3 — Ajouter un code evaluator pour la frustration en majuscules
Les monitors précédents nécessitent un jugement sémantique. Pas celui-ci : nous voulons un contrôle déterministe peu coûteux pour un message contenant une longue séquence de majuscules.
Les code evaluators conviennent parfaitement : aucun appel au modèle ni conception de prompt, seulement une règle simple sur les observations en direct.
- Ouvrez Evaluators → Set up evaluator et choisissez Code evaluator sous Create from scratch.
- Choisissez Python.
- Nommez l’évaluateur
user_all_caps_signal. - Collez ce code :
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,
},
)
]
)- Ciblez la même observation racine que le monitor de désaccord :
- Target: Live Observations
- Observation type:
agent - Observation name:
dad-it-support-chat-turn
- Enregistrez et activez l’évaluateur.
Cette cible fonctionne parce que l’entrée racine est la requête du navigateur ; l’évaluateur examine le dernier message du père avant que les outils ou generations suivants ne compliquent la structure.
Cet évaluateur n’utilise pas le modèle de l’Étape 1 : c’est du Python pur exécuté dans Langfuse, pas un juge LLM.
Vérification
npm run devEnvoyez quatre tours :
- Dans le périmètre — « Comment activer le Bluetooth ? » (doit être propre sur les deux monitors)
- Hors périmètre — « Peux-tu remplir ma déclaration d’impôts ? »
- Désaccord — posez une question normale, puis répondez « Non, ce menu n’existe pas »
- Majuscules — « THIS STILL ISNT WORKING »
Attendez l’exécution des évaluateurs, actualisez et triez les traces par score. Les cas hors périmètre, de désaccord et en majuscules doivent remonter.


Lorsque le monitor hors périmètre se déclenche, vous pouvez confirmer que le chatbot a refusé correctement. Ces traces révèlent aussi des extensions possibles : « Peux-tu remplir ma déclaration d’impôts ? » est absurde, mais « Aide-moi à transférer mes photos vers mon nouvel iPad » peut être une vraie demande de fonctionnalité.
Le désaccord est un signal plus fort. Si l’utilisateur conteste la réponse précédente, quelque chose a probablement échoué — mauvais résultat d’outil, contexte absent ou instruction incompatible avec l’iPhone. Lisez ces traces en premier et transformez les bons cas en éléments de 05-dataset.
Le signal de majuscules est volontairement plus approximatif. Il n’affirme pas que l’utilisateur est en colère ; c’est simplement un indice peu coûteux qu’une conversation tourne mal, utile pour le triage avec les juges plus riches.
Charger du trafic de production et observer les monitors
Quatre tours manuels prouvent l’intégration. Mais le monitoring montre sa valeur sur le volume. Chargeons des données réalistes.
npm run langfuse:seed:otel:no-scoresLa commande rejoue un instantané de véritable trafic d’assistance au père, avec des cas synthétiques — hors périmètre, majuscules et désaccord — dans l’environnement production. Elle réutilise les clés de .env et décale les horodatages pour que le trace le plus récent tombe maintenant.
La variante :no-scores charge les traces sans scores préfabriqués. C’est le but : vos évaluateurs sont déjà actifs et les scores proviennent de vos monitors, pas du chargement.
Le chargement n’est pas idempotent. OpenTelemetry crée de nouveaux IDs à chaque exécution ; le relancer double donc les données. Exécutez-le une fois ; pour repartir de zéro, supprimez les traces précédents dans Langfuse.
Ouvrez Tracing, filtrez l’environnement production et actualisez après quelques secondes. Observez les scores arriver pendant que les évaluateurs traitent le lot et que les cas pertinents remontent. C’est le comportement de vos monitors sur du trafic réel — et cet ensemble de traces signalés alimentera le chapitre suivant.
Conclusion
Les bons monitors séparent le signal du bruit. La production signifie beaucoup de traces, et la question importante est lesquels dois-je examiner ? — les monitors y répondent.
Après les monitors de signaux, l’étape suivante dans le temps consiste à suivre des métriques moyennes. Choisissez-les par analyse des erreurs : examinez les traces surprenants, regroupez-les par mode d’échec et transformez ces modes en évaluateurs. La leçon Academy sur le monitoring approfondit ce sujet.
Les traces détectés ici sont aussi la meilleure source pour 05-dataset, car ils représentent des comportements réels à préserver ou corriger.
État final
C’est le point de départ de 05-dataset.
03 Gestion des prompts
Vous disposez d’une application fonctionnelle avec tracing. Le prompt système réside dans la constante SYSTEMPROMPT de src/server/support-agent.ts et sert directement de message système.
05 Dataset
Vous avez une application tracée, attribuée et surveillée. data/seed-dataset.json et scripts/seed-dataset.ts se trouvent déjà dans le dépôt à ce checkpoint.