Explorer les traces LLM avec les outils MCP
PostHog capture l'activité LLM/agent IA sous forme de traces. Chaque trace est un arbre d'événements représentant une seule interaction IA — de l'invocation de l'agent au niveau supérieur jusqu'aux appels API LLM individuels.
Outils disponibles
| Outil | Objectif |
|---|---|
posthog:query-llm-traces-list |
Rechercher et lister les traces (compact — pas de contenu volumineux) |
posthog:query-llm-trace |
Obtenir une seule trace par ID avec l'arbre complet des événements |
posthog:execute-sql |
SQL ad-hoc pour l'analyse complexe des traces |
Hiérarchie des événements
Voir la référence des événements pour le schéma complet.
$ai_trace (conteneur de haut niveau)
└── $ai_span (regroupements logiques, p. ex. "RAG retrieval", "tool execution")
├── $ai_generation (appel API LLM individuel)
└── $ai_embedding (création d'embedding)
Les événements sont liés via $ai_parent_id → $ai_span_id ou $ai_trace_id du parent.
Flux de travail : déboguer une trace à partir d'une URL
Étape 1 — Récupérer la trace
posthog:query-llm-trace
{
"traceId": "<trace_id>",
"dateRange": {"date_from": "-7d"}
}
Le résultat contient l'arbre complet des événements avec toutes les propriétés. La réponse peut être volumineuse — quand elle dépasse la limite inline, Claude Code la persiste automatiquement dans un fichier.
Du résultat vous obtenez :
- Chaque événement avec son type (
$ai_span,$ai_generation, etc.) - Noms de span (
$ai_span_name) — ce sont les noms des outils/étapes - Latence, indicateurs d'erreur, modèles utilisés
- Relations parent-enfant via
$ai_parent_id _posthogUrl— toujours inclure ceci dans votre réponse pour que l'utilisateur puisse cliquer et accéder à l'interface
Étape 2 — Parser les résultats volumineux avec des scripts
Quand le résultat est persiste dans un fichier (traces volumineuses avec complet $ai_input/$ai_output_choices),
utilisez les scripts de parsing pour l'explorer.
Commencez par le résumé pour avoir une vue d'ensemble, puis approfondissez :
# 1. Aperçu : métadonnées, appels d'outils, output final, erreurs
python3 scripts/print_summary.py /path/to/persisted-file.json
# 2. Chronologie : liste d'événements chronologique avec I/O tronquée
python3 scripts/print_timeline.py /path/to/persisted-file.json
# 3. Approfonddir l'input/output complet d'un span spécifique
SPAN="tool_name" python3 scripts/extract_span.py /path/to/persisted-file.json
# 4. Conversation complète avec blocs de réflexion et appels d'outils
python3 scripts/extract_conversation.py /path/to/persisted-file.json
# 5. Rechercher un mot-clé dans toutes les propriétés
SEARCH="keyword" python3 scripts/search_traces.py /path/to/persisted-file.json
Tous les scripts supportent la variable d'env MAX_LEN=N pour contrôler la troncature (0 = illimité).
Modèles d'investigation
« L'agent a-t-il utilisé l'outil correctement ? »
- Trouver le
$ai_spanpour l'appel d'outil (regarder$ai_span_name) - Vérifier
$ai_input_state— quels arguments ont été passés à l'outil ? - Vérifier
$ai_output_state— qu'a retourné l'outil ? - Vérifier
$ai_is_error— l'appel d'outil a-t-il échoué ?
« Le contexte était-il correct ? » / « Les bons fichiers ont-ils été remontés ? »
- Trouver l'événement
$ai_generationoù le LLM a pris la décision - Vérifier
$ai_input— c'est l'historique complet des messages que le LLM a vu - Regarder les événements
$ai_spanprécédents pour les étapes de retrieval/search - Vérifier leur
$ai_output_state— quel contenu a été récupéré et fourni au LLM ?
« Le sous-agent a-t-il fonctionné ? »
- Dans l'aperçu structurel, trouver les spans qui sont des enfants d'autres spans (via
$ai_parent_id) - Le span parent est l'orchestrateur ; les spans enfants sont les étapes du sous-agent
- Vérifier
$ai_output_stateet$ai_is_errorde chaque enfant - Si un span enfant contient des événements
$ai_generation, ce sont les appels LLM du sous-agent
« Pourquoi le LLM a-t-il dit X ? »
- Utiliser
search_traces.pypour trouver où le texte apparaît :SEARCH="le texte" python3 scripts/search_traces.py FILE - Cela montre quel événement et quel chemin de propriété le contient
- Vérifier le
$ai_inputde cette génération pour voir ce que le LLM a été told avant de dire X
Construire des liens d'interface
Les outils de trace retournent _posthogUrl — toujours présenter ceci à l'utilisateur.
Vous pouvez aussi construire les liens manuellement :
- Détail de trace :
https://app.posthog.com/llm-observability/traces/<trace_id>?timestamp=<url_encoded_timestamp>&event=<optional_event_id> - Liste de traces avec filtres : retourné dans
_posthogUrlparquery-llm-traces-list
Le paramètre de requête timestamp est obligatoire — utiliser le createdAt du premier événement de la trace, URL-encodé (p. ex. timestamp=2026-04-01T19%3A39%3A20Z).
Quand vous présentez les résultats, toujours inclure l'URL PostHog pertinente pour que l'utilisateur puisse vérifier.
Trouver les traces
Utiliser posthog:query-llm-traces-list pour rechercher et filtrer les traces.
CRITIQUE : Ne jamais supposer les noms d'événements, noms de propriétés, ou valeurs de propriétés à partir des données d'entraînement.
Chaque projet instrumente des propriétés personnalisées différentes. Toujours appeler posthog:read-data-schema d'abord
pour découvrir quelles propriétés et valeurs existent réellement dans les données du projet avant de construire les filtres.
Découvrir d'abord le schéma
Avant de filtrer les traces, découvrir ce qui est disponible :
- Confirmer que les événements IA existent — appeler
posthog:read-data-schemaaveckind: "events"et chercher les événements$ai_* - Trouver les propriétés filtrables — appeler
posthog:read-data-schemaaveckind: "event_properties"etevent_name: "$ai_generation"(ou un autre événement IA) pour voir quelles propriétés sont capturées - Obtenir les valeurs réelles — appeler
posthog:read-data-schemaaveckind: "event_property_values",event_name: "$ai_generation", etproperty_name: "$ai_model"pour voir les noms de modèles réels en usage
Ensuite seulement construire l'appel query-llm-traces-list avec les filtres de propriétés.
C'est particulièrement important pour les propriétés personnalisées comme project_id, conversation_id, user_tier, etc. — ces dernières varient par projet et ne peuvent pas être devinées.
Ne pas confirmer les propriétés $ai_*, mais confirmer les autres comme email d'une personne.
Par filtres
posthog:query-llm-traces-list
{
"dateRange": {"date_from": "-1h"},
"filterTestAccounts": true,
"limit": 20,
"properties": [
{"type": "event", "key": "$ai_model", "value": "gpt-4o", "operator": "exact"}
]
}
Plusieurs filtres sont combinés avec AND :
posthog:query-llm-traces-list
{
"dateRange": {"date_from": "-1h"},
"filterTestAccounts": true,
"properties": [
{"type": "event", "key": "$ai_provider", "value": "anthropic", "operator": "exact"},
{"type": "event", "key": "$ai_is_error", "value": ["true"], "operator": "exact"}
]
}
Vous pouvez aussi filtrer par propriétés de personne (les découvrir via read-data-schema avec kind: "entity_properties" et entity: "person"):
posthog:query-llm-traces-list
{
"dateRange": {"date_from": "-1h"},
"filterTestAccounts": true,
"properties": [
{"type": "person", "key": "email", "value": "@company.com", "operator": "icontains"}
]
}
Par identifiants externes
Les clients stockent souvent leurs propres IDs comme propriétés d'événement ou de personne.
Utiliser posthog:read-data-schema pour découvrir quelles propriétés personnalisées existent, puis filtrer :
- Appeler
posthog:read-data-schemaaveckind: "event_properties"etevent_name: "$ai_trace"pour trouver les propriétés personnalisées - Examiner les propriétés retournées et leurs valeurs d'exemple
- Construire le filtre en utilisant la clé de propriété découverte et une valeur connue
posthog:query-llm-traces-list
{
"dateRange": {"date_from": "-7d"},
"properties": [
{"type": "event", "key": "project_id", "value": "proj_abc123", "operator": "exact"}
]
}
Par SQL (pour recherche full-text ou agrégations personnalisées)
Utiliser SQL quand vous avez besoin de quelque chose que query-llm-traces-list ne peut pas exprimer — typiquement recherche full-text sur le contenu des messages ou agrégations personnalisées.
SELECT
properties.$ai_trace_id AS trace_id,
properties.$ai_model AS model,
timestamp
FROM events
WHERE
event = '$ai_generation'
AND timestamp >= now() - INTERVAL 1 HOUR
AND properties.$ai_input ILIKE '%search term%'
ORDER BY timestamp DESC
LIMIT 20
Pour des modèles SQL plus complexes, consultez ces références :
- Récupération de trace unique — récupère une seule trace par ID avec tous les événements et propriétés (affiche le
TraceQueryHogQL) - Liste de traces avec métriques agrégées — requête en deux phases : trouver d'abord les IDs de trace, puis récupérer la latence agrégée, les tokens, les coûts et les comptages d'erreurs
Parser les résultats de traces volumineuses
Les résultats des outils de trace sont JSON. Quand ils sont trop volumineux pour être lus inline, Claude Code les persiste dans un fichier.
Format du fichier persiste
[{ "type": "text", "text": "{\"results\": [...], \"_posthogUrl\": \"...\"}" }]
Structure JSON de trace
results (tableau pour liste, objet pour trace unique)
├── id, traceName, createdAt, totalLatency, totalCost
├── inputState, outputState (état au niveau trace)
└── events[]
├── event ($ai_span | $ai_generation | $ai_embedding | $ai_metric | $ai_feedback)
├── id, createdAt
└── properties
├── $ai_span_name, $ai_latency, $ai_is_error
├── $ai_input_state, $ai_output_state (I/O d'outil de span)
├── $ai_input, $ai_output_choices (messages de génération)
├── $ai_model, $ai_provider
└── $ai_input_tokens, $ai_output_tokens, $ai_total_cost_usd
Scripts disponibles
| Script | Objectif | Usage |
|---|---|---|
print_summary.py |
Métadonnées de trace, appels d'outils, erreurs, et output LLM final | python3 scripts/print_summary.py FILE |
print_timeline.py |
Chronologie d'événements avec résumés I/O | python3 scripts/print_timeline.py FILE |
extract_span.py |
Input/output complet d'un span spécifique par nom | SPAN="name" python3 scripts/extract_span.py FILE |
extract_conversation.py |
Messages LLM avec blocs de réflexion et appels d'outils | python3 scripts/extract_conversation.py FILE |
search_traces.py |
Trouver un mot-clé dans toutes les propriétés d'événement | SEARCH="keyword" python3 scripts/search_traces.py FILE |
show_structure.py |
Afficher les clés et types JSON sans valeurs | cat blob.json \| python3 scripts/show_structure.py |
Conseils
- Toujours définir
dateRange— les requêtes sans plage de temps sont lentes. Utiliser des fenêtres étroites (-30m,-1h) pour les requêtes de listage large ; les fenêtres plus larges (-7d,-30d) conviennent pour les requêtes étroites filtrées par ID de trace ou valeurs de propriété spécifiques - Toujours inclure
_posthogUrldans votre réponse pour que l'utilisateur puisse cliquer et accéder $ai_input_state/$ai_output_statesur les spans contiennent les inputs et outputs des appels d'outils$ai_input/$ai_output_choicessur les générations contiennent la conversation LLM complète — peuvent être plusieurs mégaoctets ; quand le résultat est persiste dans un fichier, utiliser les scripts de parsing- Utiliser
filterTestAccounts: truepour exclure le trafic interne/test lors d'une recherche - Les événements
$ai_tracene sont PAS dans le tableauevents— leurs données sont surfacées viainputState,outputState, ettraceNameau niveau trace