DEV Community

Cover image for Debugging AI agents by replaying the exact run, not the logs
Yash
Yash Subscriber

Posted on

Debugging AI agents by replaying the exact run, not the logs

The hardest part of building with LLM agents isn't writing the agent. It's the Tuesday afternoon when one misbehaves in production and you have to work out why. The run was non-deterministic, so you can't just re-run it. The logs are spread across your app, your model provider, and your vector DB. And the inputs that actually produced the bad output are already gone.

I hit this loop enough times that I built a tool for it. Retrace records an agent run, lets you replay it step by step like a video, and lets you fork from the step that broke to test a fix — re-running only the part that changed. This post is a walk through the workflow, the design decisions behind it, and where it's still rough. It's open source (MIT) and I built it solo, so the "still rough" section is honest.

Why are agents so hard to debug?

A stack trace tells you the line that threw. An agent failure is rarely that tidy. The real cause is usually one of:

  • a tool call three steps back that returned junk,
  • a prompt that slowly drifted off-task across turns,
  • a retrieval that pulled the wrong context,
  • or a model call that was fine in isolation but wrong given the state.

By the time the bad answer surfaces, the chain that produced it is buried. Print statements don't help either, because the next run takes a different path. You need the actual run preserved, not a summary of it.

How do you record a run without rewriting your agent?

You wrap your agent once. In Python it's a decorator; in TypeScript it's a wrapper. After that, every model call, tool call, and error is captured automatically as a span on a trace. Calls to OpenAI, Anthropic, and Gemini are instrumented for you, so there's no manual logging to maintain.

Retrace: one decorator records the whole agent run — every model call, tool call and error as spans — and the CLI forks a failed step

The capture path is built to not lose data and to not get in your way:

  • an offline buffer queues events and flushes on reconnect,
  • transport falls back WebSocket → HTTP → OpenTelemetry if the socket drops,
  • PII (emails, keys, tokens) is redacted before anything is stored,
  • a sample rate keeps high-volume agents affordable.

Once recorded, the run becomes an interactive timeline you can play, pause, and scrub. That alone changed how I debug: I stopped guessing from logs and started watching where the run actually went wrong.

How does forking actually work?

This is the part I use most. You pick the step that went wrong, change its input, and Retrace re-executes the agent from that point forward. Everything before the fork replays from the recording, so it costs almost nothing. Only the part that genuinely diverges makes real model calls.

Fork and cascade replay: pre-fork spans replay from the recorded tape at near-zero cost, post-fork spans re-execute live, then the two runs are diffed

Then you get a side-by-side diff of the original run versus the fork, down to the first step where they differ. So instead of "I think this prompt change helped," you get a concrete before/after on the exact run that failed.

Can you prove a fix worked before shipping?

Forking gets you a comparison; proving is the next step. You can re-run a proposed change against the failed run and get a verdict: improved, regressed, unchanged, or inconclusive. There's a bisect that binary-searches the run to find the exact step whose change flips the result, and a way to apply one fix across many failed runs at once to check it holds beyond a single example.

If you want this in CI, there's an eval gate: score a run with an LLM judge against your own criteria, and the gate exits non-zero below your threshold, so a regression fails the build instead of reaching users.

Prove the fix: replay your change against the failed run, diff the first divergence, get an improved, regressed or unchanged verdict, and gate CI on the score

- name: Eval Gate
  run: retrace eval gate --evaluation $EVAL_ID --trace $TRACE_ID --threshold 0.8
Enter fullscreen mode Exit fullscreen mode

What's under the hood?

  • Python and TypeScript SDKs, published to PyPI and npm.
  • A Fastify + PostgreSQL backend, with pgvector for semantic search over past runs (find a span or a past failure by meaning, not just by id).
  • Buffered ingestion with backpressure, so a burst of traffic degrades gracefully instead of overloading the datastore.
  • OpenTelemetry ingest, so you can push spans from any language even without the SDK.

Where it's still rough (the honest part)

I built this solo with an AI-assisted workflow, and it's early. Replay re-calls models live, so some divergence reflects provider non-determinism rather than a real regression — you treat diffs as signals to inspect, not gospel. The docs have gaps. And I haven't stress-tested every framework people use.

That's exactly why I'm posting. If you run agents in production: what part of debugging a bad run wastes the most of your time today — reproducing it, finding the root cause, or proving the fix? I want to build against real workflows, not my assumptions. If you try it and it breaks, telling me where is the single most useful thing right now.

Docs

GitHub logo Retraceai-tech / retrace-sdk

Python & TypeScript SDKs for Retrace — the execution replay engine for AI agents.

Retrace

Python TypeScript

Record every LLM call, tool invocation, and error your agent makes. Replay it step-by-step like a video. Fork from any point, change the input, and watch the whole agent re-execute down a new path. Share any run as an interactive, public link.

Python · TypeScript · How It Works · Docs


Python

pip install retrace-sdk
Enter fullscreen mode Exit fullscreen mode
import retrace

retrace.configure(api_key="rt_...")

@retrace.record(name="research-agent", resumable=True)
def run_agent(prompt: str):
    plan = call_planner(prompt)    # captured automatically
    results = call_tools(plan)     # captured automatically
    return summarize(results)      # captured automatically

run_agent("What changed in vector databases this year?")
Enter fullscreen mode Exit fullscreen mode

See python/README.md for the full reference.

TypeScript

npm install retrace-sdk
Enter fullscreen mode Exit fullscreen mode
import { configure, trace } from "retrace-sdk";
configure({ apiKey: "rt_..." });

const
Enter fullscreen mode Exit fullscreen mode

Top comments (0)