DEV Community

Mir Arshad Ali Talpur
Mir Arshad Ali Talpur

Posted on

How to Add an Audit Trail to a LangGraph Agent

LangGraph makes it easy to build agents as graphs: nodes that retrieve, reason, call tools, and decide where to go next, with branches and loops between them. That structure is a strength, and it creates a problem. When the agent produces a wrong answer, the cause is usually somewhere in the path it took through the graph, and a final output tells you nothing about that path.

This guide shows how to record every node an agent visits, linked to the node that caused it, so you can answer why in minutes. The setup takes about fifteen minutes, and it uses a small middleware package from ZizkaDB.

What you get, in business terms
Faster debugging. A failure becomes a chain from the final step back to its cause, not a log hunt across nodes.
Visible loops. Graphs can cycle. A node that runs fifteen times in one session shows up immediately, along with its cost.
A defensible record. For customers and auditors, you can show which nodes ran, in what order, for a given request.
Safer changes. When you edit a node or a prompt, you can compare the paths before and after, instead of hoping.
Step 1: Start ZizkaDB

You need Docker. This starts a local instance, and the first image pull can take a few minutes:

bash
curl -fsSL https://raw.githubusercontent.com/Zizka-ai/ZizkaDB/main/scripts/quickstart-remote.sh | bash

On localhost, there is no signup and no API key.

Step 2: Install the integration
bash
pip install zizkadb-sdk zizkadb-langgraph
Step 3: Wrap your nodes

The middleware logs a graph_node event when a node starts and a graph_node_done event when it finishes, and it links each to its cause automatically through the graph state. This is the pattern from the package documentation:

python
from zizkadb import ZizkaDB
from zizkadb_langgraph import ZizkaDBLangGraphMiddleware, wrap_node

async with ZizkaDB(host="http://localhost:8000") as db:
async with db.track(agent="research-bot") as ctx:
mw = ZizkaDBLangGraphMiddleware(db, agent="research-bot", session_id=ctx.session_id)

    async def retrieve(state):
        return {"docs": ["a", "b"]}

    graph.add_node("retrieve", wrap_node(mw, "retrieve", retrieve))
Enter fullscreen mode Exit fullscreen mode

Wrap each node you want to see, and give each a clear name, since the name is what appears in the record. The track block gives you a session, so everything one request does stays together.

For a full working graph, the repository includes a retrieval support bot as a reference at examples/golden-path/langgraph-rag-bot/. It is the best place to see how conditional edges and model calls inside nodes are handled, since those are the details this guide does not confirm.

Step 4: Run it and ask why

Run your graph once. Open the dashboard at http://localhost:3001, go to Activity, find your agent, and open the session. You should see the nodes in order. Then ask why for any step, from the terminal:

bash
zizkadb why

You get the chain of nodes that led there. Try it on an answer you did not like, and see which node supplied the bad context.

Step 5: Use it for the problems graphs actually have

Runaway loops. A retry or review cycle that never settles is a classic graph failure. In the session view, a node repeated many times is obvious. Add a maximum iteration count in your graph, and treat reaching it as an event worth reviewing.

Wrong branch. A router node sends the request down the wrong path. The chain shows the router’s input and the node it chose, so you can tell whether the router was misled or simply wrong.

Silent change after an edit. After a prompt or node change, compare the paths of recent sessions with earlier ones. The behavior view in the dashboard shows shifts in which nodes run and how often, and the same data is available in code through the baseline function.

Human approval steps. If your graph pauses for a person, log the approval as an event, with who decided and what. Then the decision sits inside the same chain as the agent’s actions, which is what oversight reviews want to see.

Step 6: Put it in CI

Two ready-made GitHub Actions work with this setup. ZizkaDB Why runs your graph tests and posts the causal chain of any failure to the pull request. ZizkaDB Audit verifies the hash chain of the recorded history and produces an attested report. Both start ZizkaDB inside the runner, with no account, and both run your test command as a single input:

yaml

  • uses: ZIZKA-AI-SL/zizkadb-why-action@v1 with: run: pytest tests/graph What it saves

These figures are modeled assumptions, not measured results. Replace them with your own.

Assume a team has 12 graph-related failures a month, at 100 euro per engineering hour. Without a recorded path, each takes about 4 hours to diagnose, which is 4,800 euro a month. With the chain and session view, each takes about 1 hour, which is 1,200 euro. That saves about 3,600 euro a month, or 43,000 euro a year.

Add loop control. If one runaway cycle a month burns 300 euro of model calls before anyone notices, and the session view catches it within a day, the saving is small per incident and larger over a year. The setup cost is a few hours, and the tools are open source.

What to keep in mind
One agent per install. Open source ZizkaDB records one agent per install, so a graph with a supervisor and separate worker agents, each logged under its own name, needs ZizkaDB Cloud. A single graph logged under one agent name works fine.
Only wrapped nodes appear. A node you do not wrap leaves a gap in the chain.
Check what happens inside nodes. Model calls and tool calls inside a node may need their own logging, so confirm how the reference example handles them, and whether combining the LangChain callback handler fits your case.
Keep sensitive data out of events. Redact secrets and personal data before logging, and tag people’s data with an identifier so it can be erased later.
Versions move. The LangGraph package is young, so check the current release notes.
Start this week

Take one graph you already run. Wrap three nodes, run a request, and open the session. Then pick a recent wrong answer and run why on it. If the chain points at the cause in under a minute, wrap the rest of the nodes and add the CI action.

Want to test ZizkaDB on your agent? Try our open source version here: https://github.com/ZIZKA-AI-SL/ZizkaDB

Interested in a design partnership? Fill in the form on our site or reach me directly at founder@zizka.ai

Sources: zizkadb-langgraph on PyPI · zizkadb-langgraph 0.1.2 on PyPI · ZizkaDB Python SDK wiki · ZizkaDB changelog · ZizkaDB Why on GitHub Marketplace · ZizkaDB repository

Top comments (0)