Counterfactual replay and causal attribution over LLM-agent trajectories: find which step actually caused a bad outcome by intervening on it.
Project description
causal-agent-replay
Find which step actually caused your agent to fail — by intervening on it and measuring whether the outcome changes.
When an LLM agent does something wrong — issues a refund it shouldn't have, calls the wrong tool, leaks data — observability tools (LangSmith, Langfuse) show you what happened, and eval tools (Promptfoo) score pass/fail. Neither answers the question that actually matters for debugging:
Which step caused the bad outcome?
causal-agent-replay (CAR) answers it the only principled way: it intervenes on a step and
re-runs the agent forward to see if the outcome changes. The step where changing the
decision changes the outcome — but changing later steps does not — is the causal locus.
Proven by counterfactual, not guessed from a trace.
The idea in one picture
A recorded run is modelled as a structural causal model (Pearl):
τ = [ s0, (a1,o1), (a2,o2), …, (an,on), y ]
s_k— the exact state the agent decided from (system prompt, tools, full message history)a_k— the action it took (a tool call or a final answer), drawn from the stochastic policy πo_k— the tool resulty— the outcome, scored by a user-supplied functionY(τ)
An intervention is a do(·) on one variable, after which the agent re-decides everything
downstream. Because the policy is stochastic, running forward K times gives a distribution
over outcomes — never a single path. The causal effect of step k is how much that
distribution shifts versus the observed run. Attribution ranks steps by causal effect.
The headline primitive is do_resample(k): change nothing except re-draw the action at step
k from the same policy. If the bad outcome usually disappears when you resample step k —
but persists when you resample k+1 — then k is where the agent committed to the failure.
The intervention algebra
do(·) |
meaning | question it answers |
|---|---|---|
do_resample |
re-draw a_k from the unchanged policy |
how sensitive is the outcome to this step? |
do_action |
force a specific a_k |
what if it had done X instead? |
do_observation |
replace the tool result o_k |
what if the tool had returned X? |
do_context |
edit the message history at k |
what if the prompt hadn't contained X? |
do_policy |
swap the model from k forward |
did the model upgrade break it? |
See it
uv sync --extra dev
uv run python scripts/make_demo.py # -> examples/demo_report.html (open it)
The demo report attributes a support agent that absorbed a prompt injection. It shows the
causal locus is the decision step — resampling it avoids the bad refund ~half the time,
resampling the already-committed steps never does — with the injection visible in the exact
context that step decided from, an attribution chart with confidence intervals, and a Shapley
toggle. (docs/writeup.md explains the method; examples/gallery.md runs the engine on a live
local model.)
Status
Built in phases (see PLAN.md); each phase is research-gated with a binary done-condition.
Phases 0–4 are complete and validated against synthetic SCMs with known ground truth.
- Phase 0 — faithful recording + deterministic replay. ✅
- Phase 1 — intervention algebra + forward replay. ✅
- Phase 2 — distributional outcomes + effect estimators (with confidence intervals). ✅
- Phase 3 — causal attribution (contrastive + budget-bounded Monte-Carlo Shapley). ✅
- Phase 4 — interactive visualization + technical writeup. ✅
Why this is honest about hard things
- Counterfactual replay yields a distribution, not a path. The policy is stochastic; every effect is an estimate reported with Monte-Carlo confidence intervals.
- Providers are not deterministic — even at temperature 0, and current Claude Opus models
don't accept a temperature at all. CAR does not pretend otherwise: it measures the
action-match rate of replay and reports residual nondeterminism as a metric. See
RESEARCH/phase_0_foundations.md. - Attribution is validated against synthetic SCMs with known ground truth, not just against plausible-looking heatmaps. A method that can't recover a pivotal step you planted isn't trusted.
Install
pip install causal-agent-replay # not yet published
# or, from source:
uv sync --extra dev
Quickstart — free, no API key (local Ollama)
CAR runs against any backend behind the Policy protocol, so you can record and replay agents
with a free local model — no spend. Ollama exposes an OpenAI-compatible
endpoint, so one policy covers it (and Groq / OpenRouter / vLLM / LM Studio too).
ollama serve & # start the local server
ollama pull llama3.1:8b # a tool-capable model (qwen2.5:7b also works)
# record the support-agent demo against the local model, then replay it
uv run python scripts/record.py --backend ollama --model llama3.1:8b
uv run car replay support-injection-demo
Local seeded inference (seed + temperature=0 + a fixed num_ctx) reproduces far more
reliably than hosted APIs — a real advantage for faithful replay. Hosted providers stay
supported (--backend anthropic with ANTHROPIC_API_KEY, or any OpenAI-compatible endpoint via
OPENAI_BASE_URL), and their nondeterminism is measured and reported, not hidden. See
RESEARCH/phase_0_foundations.md.
Intellectual lineage
Pearl's do-calculus and SCMs · counterfactual credit assignment in RL (Mesnard et al.) · causal influence diagrams for agents (Everitt, Carey et al.) · Shapley attribution · record-replay / time-travel debuggers (rr, Pernosco). The novel intersection — causal credit assignment applied to LLM-agent traces as a practical debugging tool — is what CAR is.
License
Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file causal_agent_replay-0.0.1.tar.gz.
File metadata
- Download URL: causal_agent_replay-0.0.1.tar.gz
- Upload date:
- Size: 38.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d1d05742c6b5ea80e17111373fc0e3d9d37823054b53923afb93b98b44784a9
|
|
| MD5 |
e4a98b2271878cc749c527f112251280
|
|
| BLAKE2b-256 |
5454b25784904ed29aced124441bb742a40a121ba083a290b01a222258f9887e
|
File details
Details for the file causal_agent_replay-0.0.1-py3-none-any.whl.
File metadata
- Download URL: causal_agent_replay-0.0.1-py3-none-any.whl
- Upload date:
- Size: 51.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3356a6859dca377442f1cd3413dbbb4cbbb93679e01cee965b8358753ab0a8a
|
|
| MD5 |
7bfb781373d556422376e69f1cc4d98b
|
|
| BLAKE2b-256 |
a6e3a6982ffc3317e0b451dcd246293f0790d7584c20503eb2489137c58081dc
|