Tracing Agent Decisions in Production

Deepak Vishwakarma3 min read

When an agent misbehaves in production, the final answer is rarely enough to diagnose it. You need to reconstruct the run: the user input, the model response, the tool arguments and results, and any retry or failure along the way.

The visibility gap

A streamed response tells you what reached the user. It does not tell you:

  1. Which tools were available to the model
  2. Which tool the model selected and what arguments it generated
  3. What the tool returned
  4. How many model calls the run required
  5. Where time and tokens were spent
  6. Whether the agent loop ended in an error

Agentblit closes that gap with structured events. This is execution telemetry, not hidden chain-of-thought: it records the observable inputs, outputs, tool calls, timing, and errors needed to explain the path a run took.

One timeline per session

The Agentblit SDK assigns every agent instance a session ID. Events from each run are attached to that session and sent to the console in a batch, where sessions show their event count, total tokens, and last activity.

Open a session to inspect its ordered event timeline. Agentblit records:

  • agent_init — the system prompt and tool definitions loaded for the agent
  • user_prompt — the input submitted for a run
  • llm_call — model request context, response content, requested tool calls, finish reason, token usage, and latency
  • tool_call — tool name, arguments, result or error, and execution latency
  • tools_updated — a changed remote tool set during a long-lived session
  • elicitation_request and elicitation_response — structured input requested from a user during tool execution, including user wait time
  • agent_loop_error — the error plus the messages and tools available when the loop failed

Each event also has its own ID and timestamp, along with the agent ID, session ID, token count, and latency in milliseconds. Selecting an event in the console reveals its complete JSON payload.

Reading a failed run

Start with the sequence, not the final error:

  1. Check agent_init to confirm the expected prompt and tools were loaded.
  2. Open each llm_call to see whether the model returned text, requested tools, or stopped for another reason.
  3. Compare the generated arguments in tool_call with the tool result. A valid model request can still expose a connector or permission failure.
  4. Compare model latency with tool latency before deciding where the bottleneck is.
  5. If the loop failed, inspect agent_loop_error for the state the SDK had at the failure boundary.

This separates common failure classes. Bad tool selection points toward prompt or tool-description changes. Correct selection with bad arguments points toward schema or input quality. A good request with an error response points toward the connector. An agent_loop_error after repeated tool rounds may mean the task needs a lower-complexity workflow or a different maxToolRounds limit.

Add domain events

The SDK captures the agent loop automatically. You can add application context with track before a run:

agent.track("support_ticket_received", {
  ticketId: "ticket_4821",
  queue: "billing",
  priority: "high",
});

for await (const chunk of agent.run("Triage ticket_4821")) {
  process.stdout.write(chunk);
}

Use custom events for identifiers and business state that the model should not have to infer. Avoid placing secrets or unnecessary personal data in event properties because trace payloads are operational records.

Structured events turn debugging from guesswork into inspection: find the session, follow the timeline, and inspect the boundary where observed behavior first diverged from what you expected.