posthog/ai-plugin

exploring-llm-traces

Debug and inspect LLM/AI agent traces using PostHog's MCP tools.

Voir la source

Mode d’emploi

  1. Copiez la commande et examinez le paquet avant de l’installer dans votre environnement agent.
  2. Consultez le document original pour les outils, identifiants et environnements requis.
  3. Décrivez les entrées et le livrable souhaité ; comparez la sortie aux sources.
npx skills add https://github.com/posthog/ai-plugin --skill exploring-llm-traces

Avant de commencer

  • L’installation n’inclut ni abonnement au modèle, ni quota API, ni accès aux fichiers.
  • Étoiles et installations reflètent l’activité, pas un test indépendant de qualité ou de sécurité.
Document Skill original

Rendu depuis le dépôt source en conservant titres, exemples, code, tableaux, liens et images.

Exploring LLM traces with MCP tools

PostHog captures LLM/AI agent activity as traces. Each trace is a tree of events representing a single AI interaction — from the top-level agent invocation down to individual LLM API calls.

Available tools

ToolPurpose
posthog:query-llm-traces-listSearch and list traces; can return large multi-trace payloads
posthog:query-llm-traceGet a single trace by ID with full event tree
posthog:read-data-schemaDiscover custom event/person properties before filtering
posthog:execute-sqlAd-hoc SQL for complex trace analysis

Event hierarchy

See the event reference for the full schema.

text
$ai_trace (top-level container)
  └── $ai_span (logical groupings, e.g. "RAG retrieval", "tool execution")
        ├── $ai_generation (individual LLM API call)
        └── $ai_embedding (embedding creation)

Events are linked via $ai_parent_id → parent's $ai_span_id or $ai_trace_id.

Workflow: debug a trace or session from a URL

Step 1 — Classify the URL

First inspect the path. Do not treat every UUID-looking value as a trace ID.

  • /ai-observability/traces/<trace_id> or legacy /llm-analytics/traces/<trace_id> / /llm-observability/traces/<trace_id> is a single trace. Fetch it with posthog:query-llm-trace.
  • /ai-observability/sessions/<session_id> or legacy /llm-analytics/sessions/<session_id> is an AI session, not a trace. Fetch traces with posthog:query-llm-traces-list filtered by event property $ai_session_id.

Preserve date_from / date_to query parameters from the URL when present. If none are present but the URL has a timestamp query parameter, use that timestamp as the anchor and query an absolute window around it, for example timestamp - 36h to timestamp + 36h. This handles exact session links whose UI timestamp may be offset from the stored event timestamps while keeping the query bounded. If the URL has neither explicit dates nor timestamp, use a safe default like {"date_from": "-7d"}.

For exact trace and session URLs, skip schema discovery for the standard $ai_* fields used below. These are AI observability built-ins, not project-specific custom properties.

Step 2 — Fetch trace data

For a trace URL, call posthog:query-llm-trace with:

json
{
  "traceId": "<trace_id>",
  "dateRange": { "date_from": "-7d" }
}

For a session URL, call posthog:query-llm-traces-list with:

json
{
  "dateRange": { "date_from": "<timestamp_minus_36h>", "date_to": "<timestamp_plus_36h>" },
  "filterTestAccounts": false,
  "limit": 20,
  "properties": [{ "type": "event", "key": "$ai_session_id", "value": ["<session_id>"], "operator": "exact" }]
}

Use the URL's date_from / date_to values in the session query if present. If the URL only has timestamp, calculate the absolute date range from that timestamp instead of using a relative range like -1h. Set filterTestAccounts: false for an exact URL so the requested trace is not hidden by account filters.

The result contains the event tree with all properties. The response may be large — when it exceeds the inline limit, Claude Code auto-persists it to a file.

From the result you get:

  • Every event with its type ($ai_span, $ai_generation, etc.)
  • Span names ($ai_span_name) — these are the tool/step names
  • Latency, error flags, models used
  • Parent-child relationships via $ai_parent_id
  • _posthogUrl — always include this in your response so the user can click through to the UI

Step 3 — Parse large results with scripts

When the result is persisted to a file (large traces with full $ai_input/$ai_output_choices), use the parsing scripts to explore it.

Start with the summary to get the full picture, then drill into specifics:

bash
# 1. Overview: metadata, tool calls, final output, errors
python3 scripts/print_summary.py /path/to/persisted-file.json

# 2. Timeline: chronological event list with truncated I/O
python3 scripts/print_timeline.py /path/to/persisted-file.json

# 3. Drill into a specific span's full input/output
SPAN="tool_name" python3 scripts/extract_span.py /path/to/persisted-file.json

# 4. Full conversation with thinking blocks and tool calls
python3 scripts/extract_conversation.py /path/to/persisted-file.json

# 5. Search for a keyword across all properties
SEARCH="keyword" python3 scripts/search_traces.py /path/to/persisted-file.json

All scripts support MAX_LEN=N env var to control truncation (0 = unlimited).

Investigation patterns

"Did the agent use the tool correctly?"

  1. Find the $ai_span for the tool call (look at $ai_span_name)
  2. Check $ai_input_state — what arguments were passed to the tool?
  3. Check $ai_output_state — what did the tool return?
  4. Check $ai_is_error — did the tool call fail?

"Was the context correct?" / "Were the right files surfaced?"

  1. Find the $ai_generation event where the LLM made the decision
  2. Check $ai_input — this is the full message history the LLM saw
  3. Look at preceding $ai_span events for retrieval/search steps
  4. Check their $ai_output_state — what content was retrieved and fed to the LLM?

"Did the subagent work?"

  1. In the structural overview, find spans that are children of other spans (via $ai_parent_id)
  2. The parent span is the orchestrator; child spans are subagent steps
  3. Check each child's $ai_output_state and $ai_is_error
  4. If a child span contains $ai_generation events, those are the subagent's LLM calls

"Why did the LLM say X?"

  1. Use search_traces.py to find where the text appears: SEARCH="the text" python3 scripts/search_traces.py FILE
  2. This shows which event and property path contains it
  3. Check the $ai_input of that generation to see what the LLM was told before it said X

Constructing UI links

The trace tools return _posthogUrl — always surface this to the user.

You can also construct links manually:

  • Trace detail: https://app.posthog.com/ai-observability/traces/<trace_id>?timestamp=<url_encoded_timestamp>&event=<optional_event_id>
  • Traces list with filters: returned in _posthogUrl from query-llm-traces-list

The timestamp query param is required — use the createdAt of the earliest event in the trace, URL-encoded (e.g. timestamp=2026-04-01T19%3A39%3A20Z).

When presenting findings, always include the relevant PostHog URL so the user can verify.

Finding traces

Use posthog:query-llm-traces-list to search and filter traces.

CRITICAL: Never assume event names, property names, or property values from training data. Every project instruments different custom properties. For open-ended searches and custom filters, call posthog:read-data-schema first to discover what properties and values actually exist in the project's data before constructing filters.

The exception is exact AI observability trace/session URLs: use the built-in $ai_trace_id / $ai_session_id fields directly and skip schema discovery.

Discovering the schema first

Before filtering traces, discover what's available:

  1. Confirm AI events exist — call posthog:read-data-schema with kind: "events" and look for $ai_* events
  2. Find filterable properties — call posthog:read-data-schema with kind: "event_properties" and event_name: "$ai_generation" (or another AI event) to see what properties are captured
  3. Get actual values — call posthog:read-data-schema with kind: "event_property_values", event_name: "$ai_generation", and property_name: "$ai_model" to see real model names in use

Only then construct the query-llm-traces-list call with property filters.

This is especially important for custom properties like project_id, conversation_id, user_tier, etc. — these vary per project and cannot be guessed.

Do not confirm $ai_* properties, but confirm any other like email of a person.

By filters

json
posthog:query-llm-traces-list
{
  "dateRange": {"date_from": "-1h"},
  "filterTestAccounts": true,
  "limit": 20,
  "properties": [
    {"type": "event", "key": "$ai_model", "value": "gpt-4o", "operator": "exact"}
  ]
}

Multiple filters are AND-ed together:

json
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"}
  ]
}

You can also filter by person properties (discover them via read-data-schema with kind: "entity_properties" and entity: "person"):

json
posthog:query-llm-traces-list
{
  "dateRange": {"date_from": "-1h"},
  "filterTestAccounts": true,
  "properties": [
    {"type": "person", "key": "email", "value": "@company.com", "operator": "icontains"}
  ]
}

By external identifiers

Customers often store their own IDs as event or person properties. Use posthog:read-data-schema to discover what custom properties exist, then filter:

  1. Call posthog:read-data-schema with kind: "event_properties" and event_name: "$ai_trace" to find custom properties
  2. Review the returned properties and their sample values
  3. Construct the filter using the discovered property key and a known value
json
posthog:query-llm-traces-list
{
  "dateRange": {"date_from": "-7d"},
  "properties": [
    {"type": "event", "key": "project_id", "value": "proj_abc123", "operator": "exact"}
  ]
}

For more complex SQL patterns, read these references:

Parsing large trace results

Trace tool results are JSON. When too large to read inline, Claude Code persists them to a file.

Persisted file format

json
[{ "type": "text", "text": "{\"results\": [...], \"_posthogUrl\": \"...\"}" }]

Trace JSON structure

text
results (array for list, object for single trace)
  ├── id, traceName, createdAt, totalLatency, totalCost
  ├── inputState, outputState (trace-level state)
  └── 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 (span tool I/O)
              ├── $ai_input, $ai_output_choices (generation messages)
              ├── $ai_model, $ai_provider
              └── $ai_input_tokens, $ai_output_tokens, $ai_total_cost_usd

Available scripts

ScriptPurposeUsage
`print_summary.py`Aggregate list/session totals, trace metadata, tool calls, errors, and final LLM outputpython3 scripts/print_summary.py FILE
`print_timeline.py`Chronological event timeline with I/O summariespython3 scripts/print_timeline.py FILE
`extract_span.py`Full input/output of a specific span by nameSPAN="name" python3 scripts/extract_span.py FILE
`extract_conversation.py`LLM messages with thinking blocks and tool callspython3 scripts/extract_conversation.py FILE
`search_traces.py`Find a keyword across all event propertiesSEARCH="keyword" python3 scripts/search_traces.py FILE
`show_structure.py`Show JSON keys and types without values`cat blob.json \python3 scripts/show_structure.py`

Tips

  • Always set dateRange — queries without a time range are slow. Use narrow windows (-30m, -1h) for broad listing queries; wider windows (-7d, -30d) are fine for narrow queries filtered by trace ID or specific property values
  • Always include the _posthogUrl in your response so the user can click through
  • $ai_input_state / $ai_output_state on spans contain tool call inputs and outputs
  • $ai_input / $ai_output_choices on generations contain the full LLM conversation — can be megabytes; when the result is persisted to a file, use the parsing scripts
  • In raw SQL, heavy content ($ai_input / $ai_output / $ai_output_choices / $ai_input_state / $ai_output_state / $ai_tools) lives only on the posthog.ai_events table, not events.properties — see the event reference for the column mapping and trace-id-anchored query patterns
  • Use filterTestAccounts: true to exclude internal/test traffic when searching
  • $ai_trace events are NOT in the events array — their data is surfaced via trace-level inputState, outputState, and traceName

Tâches connexes

du même dépôt

Autres Skills

Tous les Skills
posthog
Officiel

composing-grid-canvases

Compose PostHog grid canvases — widget grids (including the user's home canvas) built from reusable component canvases. Use when a task asks to add, fill, move, resize, or remove a widget on a grid or home canvas, to compose a whole canvas of widgets from one ask, to build a reusable widget/component, or when a placement id or grid canvas id is the target. Covers the component store search → configure → fork → build ladder, the component placement contract (size, configSchema), the placement lif

Installations / 1h
1
GitHub Stars
96
Mis à jour
31 oct.
posthog
Officiel

consuming-endpoints-from-client-code

Wire a PostHog endpoint into a client app or SDK. Covers fetching the OpenAPI spec, generating a typed client with openapi-generator or @hey-api/openapi-ts, sending the right auth header, shaping the variables payload (HogQL codename vs insight breakdown property), handling rate-limit and materialised-endpoint error responses. Use when the user says "how do I call my endpoint", "generate a client for this", or "what auth header do I use".

Installations / 1h
1
GitHub Stars
96
Mis à jour
31 oct.
posthog
Officiel

designing-email-templates

Author, save, and edit email templates in the PostHog workflows library — compose email design JSON with Liquid personalization and create and round-trip-edit templates over MCP. Use when asked to design, build, update, or fix an email template for workflows, broadcasts, or campaigns.

Installations / 1h
1
GitHub Stars
96
Mis à jour
31 oct.
posthog
Officiel

exploring-llm-evaluations

Investigate AI observability evaluations — hog (deterministic code-based), llmjudge (LLM-prompt-based), and sentiment (user-message sentiment). Find existing evaluations, inspect their configuration, run them against specific generations, query individual results, and set up scheduled reports on an evaluation. Use when the user asks to debug why an evaluation is failing, surface common failure modes, compare results across filters, dry-run a Hog evaluator, prototype a new LLM-judge prompt, inspe

Installations / 1h
1
GitHub Stars
96
Mis à jour
31 oct.