
exploring-llm-traces
Debug and inspect LLM/AI agent traces using PostHog's MCP tools. Use when the user pastes a trace or session URL (e.g. /ai-observability/traces/ or /ai-observability/sessions/ ), asks to debug a trace, figure out what went wrong, check if an agent used a tool correctly, verify context/files were surfaced, inspect subagent behavior, investigate LLM decisions, or analyze token usage and costs. Also use when raw SQL/HogQL against events.properties.$aiinput / $aioutputchoices returns empty — message content lives only on the dedicated posthog.aievents table.
- Installs
- 233
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/posthog/ai-plugin --skill exploring-llm-traces- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads exploring-llm-traces automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
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
| Tool | Purpose |
|---|---|
posthog:query-llm-traces-list | Search and list traces; can return large multi-trace payloads |
posthog:query-llm-trace | Get a single trace by ID with full event tree |
posthog:read-data-schema | Discover custom event/person properties before filtering |
posthog:execute-sql | Ad-hoc SQL for complex trace analysis |
Event hierarchy
See the event reference for the full schema.
$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 withposthog:query-llm-trace./ai-observability/sessions/<session_id>or legacy/llm-analytics/sessions/<session_id>is an AI session, not a trace. Fetch traces withposthog:query-llm-traces-listfiltered 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 — Browse trace summaries
Explicitly set detail: "summary" when browsing traces.
This returns event metadata and leaves the conversation content out,
so browsing does not spend context on prompts and outputs.
Omitting detail still returns full detail for compatibility with existing callers.
For a trace URL, call posthog:query-llm-trace with:
{
"traceId": "<trace_id>",
"detail": "summary",
"dateRange": { "date_from": "-7d" }
}
For a session URL, call posthog:query-llm-traces-list with:
{
"detail": "summary",
"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 trace and event metadata only.
Prompts, outputs, span states, $ai_error and $ai_feedback_text are left out,
and their names are listed in _summaryOmittedKeys beside the property bag.
A trace with _detail: { "mode": "summary" } carries no conversation content at all.
Read the content with detail: "full".
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 — Read the content needed for the investigation
Once you have selected a trace, request posthog:query-llm-trace with detail: "full" before inspecting
exact tool arguments, checking which context the model received, or searching conversation content:
{
"traceId": "<trace_id>",
"detail": "full",
"dateRange": { "date_from": "-7d" }
}
Preserve the date range from the original URL or discovery query instead of copying the example range. Keep relevant property filters to narrow the read. If the user already identified the trace and needs exact content, you can request full detail directly.
Both modes enforce response size limits.
Check the omission and truncation markers before drawing conclusions.
A dropped event, a name listed in _summaryOmittedKeys or _redactedKeys,
or a keyword missing from a summary is not evidence that it was absent from the trace.
If full detail is still truncated, narrow the query to the relevant events or open _posthogUrl for the complete data.
Both detail modes also withhold the properties that are not AI payload, described in
Withheld properties below.
Step 4 — Parse large full-detail 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:
# 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 the returned 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).
Withheld properties
The two trace tools return the AI payload only.
Only the $ai_* properties PostHog's taxonomy defines reach you,
plus $ai_generation_id, $ai_cache_read_cost_usd, $ai_cache_creation_cost_usd, $ai_effort,
$session_id, $lib and $lib_version.
Every other event property, and every person property, is withheld in both detail modes,
and its name is listed in _redactedKeys beside the property bag.
$ai_base_url and $ai_request_url arrive without their query string.
This covers a project's own custom properties, such as project_id or conversation_id.
A withheld property is unchanged in PostHog.
It still works as a filter here, and you can read its value in the PostHog UI or with execute-sql.
A name in _redactedKeys means the property is on the event and only its value is withheld.
search_traces.py cannot find a keyword that appears only in a withheld property.
Investigation patterns
"Did the agent use the tool correctly?"
- Find the
$ai_spanfor the tool call (look at$ai_span_name) - Check
$ai_input_state— what arguments were passed to the tool? - Check
$ai_output_state— what did the tool return? - Check
$ai_is_error— did the tool call fail?
"Was the context correct?" / "Were the right files surfaced?"
- Find the
$ai_generationevent where the LLM made the decision - Check
$ai_input— this is the full message history the LLM saw - Look at preceding
$ai_spanevents for retrieval/search steps - Check their
$ai_output_state— what content was retrieved and fed to the LLM?
"Did the subagent work?"
- In the structural overview, find spans that are children of other spans (via
$ai_parent_id) - The parent span is the orchestrator; child spans are subagent steps
- Check each child's
$ai_output_stateand$ai_is_error - If a child span contains
$ai_generationevents, those are the subagent's LLM calls
"Why did the LLM say X?"
- Use
search_traces.pyto find where the text appears:SEARCH="the text" python3 scripts/search_traces.py FILE - This shows which event and property path contains it
- Check the
$ai_inputof 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
_posthogUrlfromquery-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 with detail: "summary" 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:
- Confirm AI events exist — call
posthog:read-data-schemawithkind: "events"and look for$ai_*events - Find filterable properties — call
posthog:read-data-schemawithkind: "event_properties"andevent_name: "$ai_generation"(or another AI event) to see what properties are captured - Get actual values — call
posthog:read-data-schemawithkind: "event_property_values",event_name: "$ai_generation", andproperty_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.
A custom property works as a filter even though its value is withheld from the response.
Do not confirm $ai_* properties, but confirm any other like email of a person.
By filters
posthog:query-llm-traces-list
{
"detail": "summary",
"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:
posthog:query-llm-traces-list
{
"detail": "summary",
"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"):
posthog:query-llm-traces-list
{
"detail": "summary",
"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:
- Call
posthog:read-data-schemawithkind: "event_properties"andevent_name: "$ai_trace"to find custom properties - Review the returned properties and their sample values
- Construct the filter using the discovered property key and a known value
posthog:query-llm-traces-list
{
"detail": "summary",
"dateRange": {"date_from": "-7d"},
"properties": [
{"type": "event", "key": "project_id", "value": "proj_abc123", "operator": "exact"}
]
}
For more complex SQL patterns, read these references:
- Single trace retrieval — fetches a single trace by ID with all events and properties (renders the
TraceQueryHogQL) - Traces list with aggregated metrics — two-phase query: find trace IDs first, then fetch aggregated latency, tokens, costs, and error counts
Parsing large trace results
Trace tool results are JSON. When too large to read inline, Claude Code persists them to a file. Use a full-detail response for content extraction and keyword searches. The scripts cannot recover content omitted from a summary, withheld from a property bag, or dropped by truncation.
Persisted file format
[{ "type": "text", "text": "{\"results\": [...], \"_posthogUrl\": \"...\"}" }]
Trace JSON structure
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
| Script | Purpose | Usage |
|---|---|---|
print_summary.py | Aggregate list/session totals, trace metadata, tool calls, errors, and final LLM output | python3 scripts/print_summary.py FILE |
print_timeline.py | Chronological event timeline with I/O summaries | python3 scripts/print_timeline.py FILE |
extract_span.py | Full input/output of a specific span by name | SPAN="name" python3 scripts/extract_span.py FILE |
extract_conversation.py | LLM messages with thinking blocks and tool calls | python3 scripts/extract_conversation.py FILE |
search_traces.py | Find a keyword across all event properties | SEARCH="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
_posthogUrlin your response so the user can click through $ai_input_state/$ai_output_stateon spans contain tool call inputs and outputs$ai_input/$ai_output_choiceson 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 theposthog.ai_eventstable, notevents.properties— see the event reference for the column mapping and trace-id-anchored query patterns - Use
filterTestAccounts: trueto exclude internal/test traffic when searching $ai_traceevents are NOT in theeventsarray — their data is surfaced via trace-levelinputState,outputState, andtraceName
FAQ
What does the exploring-llm-traces skill do?
Debug and inspect LLM/AI agent traces using PostHog's MCP tools. Use when the user pastes a trace or session URL (e.g. /ai-observability/traces/ or /ai-observability/sessions/ ), asks to debug a trace, figure out what went wrong, check if an agent used a tool correctly, verify context/files were surfaced, inspect subagent behavior, investigate LLM decisions, or analyze token usage and costs. Also use when raw SQL/HogQL against events.properties.$aiinput / $aioutputchoices returns empty — message content lives only on the dedicated posthog.aievents table.
How do I install exploring-llm-traces?
Run npx skills add https://github.com/posthog/ai-plugin --skill exploring-llm-traces in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/exploring-llm-traces/ (personal) or .claude/skills/exploring-llm-traces/ (project).
Which AI agents can use exploring-llm-traces?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is exploring-llm-traces safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains exploring-llm-traces?
It is published by posthog in posthog/ai-plugin, and it is an official skill from the maker of the technology it covers. It has 233 recorded installs.
More from posthog/ai-plugin
instrument-product-analytics
posthog/ai-plugin
Add PostHog product analytics events to track user behavior. Use after implementing new features or reviewing PRs to ensure meaningful user actions are captured. Also handles initial PostHog SDK setup if not yet installed.
instrument-feature-flags
posthog/ai-plugin
Open the skill to read its SKILL.md instructions.
cleaning-up-stale-feature-flags
posthog/ai-plugin
Identify stale feature flags in a PostHog project and clean up the code that checks them. Use when the user wants to find, audit, or remove unused, fully rolled out, or abandoned feature flags, run a feature flag hygiene review or a weekly cleanup, reduce feature flag tech debt, or asks "which flags can I remove?". When the agent can read and edit a repository it performs the code cleanup itself: tested local changes, and one draft PR per flag when the user authorizes publishing. Agents without repository access generate a tailored cleanup prompt instead. Covers staleness detection, dependency checking, retained-path rules, and the code-first ordering. This skill does not archive or otherwise change a flag in PostHog.
auditing-experiments-flags
posthog/ai-plugin
Audit PostHog experiments and feature flags for configuration issues, staleness, and best-practice violations. Read when the user asks to audit, health-check, or review experiments or feature flags, check flag hygiene, or verify experiment setup.
querying-posthog-data
posthog/ai-plugin
Explains how to query PostHog data, defaulting to typed runners for supported product analytics. Read it before you write HogQL/SQL. Also read it before you call execute-sql against PostHog. Use it to find or aggregate PostHog entities. These entities include insights, dashboards, cohorts, feature flags, experiments, surveys, hog flows, warehouse data, and persons. Use it for trends, funnels, retention, lifecycle, paths, stickiness, web analytics, error tracking, logs, sessions, and LLM traces. Before you calculate a governed business or telemetry measure, check system.informationschema.metrics for an approved definition. Examples include MRR, activation, billable usage, active organizations, and failure rates. Use the approved definition before you derive a measure from raw events or use a typed domain tool. It also covers HogQL differences, system table schemas, functions, query examples, and schema discovery.
creating-experiments
posthog/ai-plugin
Guides agents through experiment creation: reading the project's setup with experiment-setup-context, defining the hypothesis, configuring rollout and bucketing, setting up analytics and running time, and reporting which choices are guesses. Delegates rollout decisions to configuring-experiment-rollout and metric setup to configuring-experiment-analytics. TRIGGER when: user asks to create a new experiment or A/B test, OR when you are about to call experiment-create. DO NOT TRIGGER when: user is updating an existing experiment, managing lifecycle, or only browsing experiments.
Popular ai & agents skills
google-agents-cli-publish
google/agents-cli
This skill should be used when the user wants to "publish an agent", "publish my ADK agent", "register an agent with Gemini", "publish to Gemini" (formerly Gemini Enterprise), or needs guidance on the agents-cli publish gemini command. Also use when the user wants to "manage agents in Agent Registry", "list/update/delete registered agents", or "register an MCP server". Covers ADK vs A2A registration modes, programmatic and interactive usage, flag reference, auto-detection from deployment metadata, Agent Registry fleet management, and troubleshooting. Part of the agents-cli skills suite. Do NOT use for deployment (use google-agents-cli-deploy).
google-agents-cli-eval
google/agents-cli
This skill should be used when the user wants to "run an evaluation", "evaluate my agent", "evaluate my ADK agent", "write an eval dataset", "analyze eval failures", "compare eval results", "optimize agent", or needs guidance on the Gemini platform eval methodology and the Quality Flywheel. Covers eval metrics, dataset schema, LLM-as-judge scoring, and common failure causes. Applies to any agents-cli project, whatever framework the agent is written in. Do NOT use for agent API code patterns (ADK: use google-agents-cli-adk-code), deployment (use google-agents-cli-deploy), or project scaffolding (use google-agents-cli-scaffold).
skill-creator
anthropics/skills
Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
codex-pet
prime-skills/runcomfy-agent-skills
Codex Pet generator on RunComfy. Build a Codex-compatible Codex Pet spritesheet.webp + pet.json from a single reference image, drop it into ${CODEXHOME:-$HOME/.codex}/pets/ / and Codex picks it up as a custom Codex Pet next to the 8 built-ins. This skill produces the exact Codex Pet atlas Codex expects (1536x1872 PNG/WebP, 8 cols x 9 rows, 192x208 cells, 9 animation states — idle, running-right, running-left, waving, jumping, failed, waiting, running, review). Calls OpenAI GPT Image 2 edit ONCE via the local RunComfy CLI as runcomfy run openai/gpt-image-2/edit to produce a canonical Codex Pet pose, then assembles all 9 animation rows programmatically with ImageMagick micro-transforms — no Codex Pro, no $imagegen, no OPENAIAPIKEY required, only RUNCOMFYTOKEN. Triggers on "codex pet", "create codex pet", "make codex pet", "hatch codex pet", "/hatch image", "desktop pet codex", "codex pets", "spritesheet.webp", or any explicit ask to build a custom pet for OpenAI Codex.
full-output-enforcement
leonxlnx/taste-skill
Overrides default LLM truncation behavior. Enforces complete code generation, bans placeholder patterns, and handles token-limit splits cleanly. Apply to any task requiring exhaustive, unabridged output.
executing-plans
obra/superpowers
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available