Traces
Every agent run, step by step, with tokens, cost and timing.
A trace is one run of your agent, recorded each time a function decorated with @guard.track is called. The Traces page in your dashboard is the first place to look when you want to know what an agent actually did.
@guard.track(action_type=ActionType.CUSTOM)
def handle_ticket(question: str) -> str:
...One call of handle_ticket is one trace. If the function raises, the step is marked error and your exception is re-raised untouched.
What a trace contains
- The agent it belongs to, its status, start time and total duration
- Total tokens and total cost, rolled up from its steps
- An input summary (the function's arguments) and an output summary (its return value), both scrubbed of PII and cut to 500 characters
- Any
tagsyou set on theGuard - A list of steps
The trace status is the status of its root step. Full prompts and responses are not stored in beta, only these previews.
Steps and nesting
@guard.track creates the root step. While your function runs, integrations add child steps under it:
| Source | Step type | Sits under |
|---|---|---|
| Gemini | llm_call | Root step |
HTTP monitor (guard.monitor_http()) | http_request | Root step |
| LangChain handler | llm_call, tool_call, retriever_call | The chain or tool it ran in |
| LangGraph handler | graph_node, plus the LangChain steps inside it | The enclosing node |
handle_ticket custom
├── retrieve_docs retriever_call
├── call_llm llm_call 1200 in / 340 out
└── POST api.example.com http_requestEach step has an action type that says what it was:
| Action type | What it represents |
|---|---|
llm_call | A call to an LLM, with model, tokens and cost |
tool_call | The agent calling a tool or function |
http_request | An outbound HTTP request |
agent_call | A parent agent calling a sub-agent |
graph_node | A LangGraph node running |
retriever_call | A retriever lookup |
circuit_breaker | A circuit breaker decision |
content_safety | A content-safety scan |
custom | Anything else you track |
A step's status is one of started, success, error, blocked or throttled.
Integrations only record inside a @guard.track function. Outside one, nothing is recorded.
Empty input summary?
A tracked function with no arguments has an empty input summary. That is expected.
Tokens and cost
thinking_tokensis part ofoutput_tokens.cached_input_tokensis part ofinput_tokens.total_tokensis input plus output. The two detail fields are never added on top.- Cost is input and output tokens times the model's per-million price. It is calculated on the Guard server, so it is correct even if your SDK has not loaded pricing yet.
- A model with no price is stored with a cost of
$0and the trace is kept. - Cached tokens are charged at the full input rate for now, so cost is slightly overstated for cached calls.
- A trace's total cost is the sum of its step costs. You see it in the Cost column of the Traces list and in the trace header.
- To cap spending, use a
daily_costrule. See Rules and circuit breaker.
If you call a provider that Guard does not patch, report usage yourself from inside the tracked function:
@guard.track(action_type=ActionType.LLM_CALL)
def ask(prompt):
response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt)
guard.track_usage(response) # fills tokens, thinking and cached on the root step
return response.textTraces that call other traces
When a tracked function calls another tracked function, the inner call becomes its own trace, linked to the outer one automatically. You never set the link yourself. See Cross-agent escalation.
The Traces page
The list shows one row per trace with these columns: Trace ID, Status, Agent, Cost, Latency, Started and Summary. You can filter by agent, status, model and date range. The page refreshes about every 10 seconds, so a new trace appears shortly after your agent finishes.
Reading a trace
Click a trace to open it. The header shows Duration, Total tokens, Cost, Steps, Agent and Started. When a model used thinking or cached tokens, it also shows what share of the output and input they were. The trace's input and output summaries are shown below.
Under the header is a tree of steps. Click any step to open its details:
- Status, type, model and source
- Start time, end time and duration
- Input, output, total, thinking and cached tokens
- Cost, step ID and parent step
- Metadata
Use this to find the one expensive call in a run, or the step that failed.
Delivery and limits
- Traces are sent in the background in small batches, about every 2 seconds. Your function never waits for the network.
- A trace is sent when its function finishes, so a long-running agent appears only once it ends.
- If Guard is unreachable, your agent keeps running. The SDK retries, then saves the batch to a local file instead of failing.
- Call
guard.shutdown()before a short script exits. A normal exit flushes automatically, but a hard kill (such asSIGKILL) can lose about 2 seconds of traces. - A trace can hold up to 1000 steps.
Not available yet
Text search, filtering by tag, and grouping runs by session or end user are not part of beta.