If you've shipped an LLM agent to production, you know the failure mode: a customer says the bot gave a wrong answer, you open your logs, and you see a wall of spans that tell you what happened but not why. The prompt changed three deploys ago. The agent skipped a tool call. A retrieval step pulled a stale document. Nothing in a flat trace tells you the causal chain that led to the bad output.

ZizkaDB is an open-source operational database built specifically for this problem. Instead of storing spans like a tracing tool, it stores agent decisions as a graph, where each event points to the event that caused it, plus session-level replay and drift detection against a baseline. This post walks through the two features that make it different from a generic tracing setup: causal lineage (why()) and session replay, with working code.

Why not just use a tracer?

Distributed tracing tools (Langfuse, LangSmith, Phoenix) give you a span tree: this call started, this call ended, here's the latency. That's useful for performance debugging. It's much weaker for behavioral debugging, where the question isn't how long did this take but what earlier decision caused this one. ZizkaDB models that explicitly by making every logged event optionally declare its parent_id, turning a session into a directed acyclic graph of decisions instead of a list of timestamps.