Skip to content

Core Concepts

You’ll learn the core mental model behind Opswald: how traces, spans, and decisions work together to give you full visibility into your AI agents.

Traces

A trace represents a single execution of your AI agent — one user request, one agent run, one complete workflow.

with opswald.trace('customer-support-run') as t:
# Everything inside this block belongs to one trace
...

Traces have:

  • A name to identify what the agent was doing
  • A start time and end time
  • A collection of spans (the individual steps)
  • Metadata you attach (user ID, session ID, etc.)

Spans

A span is a single step inside a trace. Every LLM call, tool invocation, or custom operation is a span.

with opswald.span('generate-reply', kind='llm_call', provider='openai', model='gpt-4o') as s:
s.set_input({'messages': [...]})
result = openai.chat.completions.create(...)
s.set_output({'response': result.choices[0].message.content})
s.set_tokens(input_tokens=150, output_tokens=89)

Span Kinds

KindWhat it captures
llm_callLLM API calls (OpenAI, Anthropic, etc.)
tool_callTool/function invocations
errorError events and exceptions
customAny custom operation you want to track

Nesting

Spans nest inside each other to show the call hierarchy:

with opswald.trace('agent-run') as t:
with opswald.span('step-1', kind='llm_call', provider='openai', model='gpt-4o') as s1:
# This span is a child of the trace
with opswald.span('sub-step', kind='tool_call') as s2:
# This span is a child of step-1
...

This creates a tree structure visible in the dashboard, showing exactly how your agent’s execution flowed.

Decisions

When your agent chooses between multiple paths — which tool to call, how to respond, whether to escalate — that’s a decision. Opswald captures these decision points so you can see:

  • What options the agent considered
  • Which option it chose
  • What context led to that choice

Decisions appear as branching points in the Decision Graph.

Tool Calls

Tool calls are spans with kind='tool_call'. They represent your agent invoking external tools — search APIs, databases, calculators, or any function the agent can call.

with opswald.span('search-docs', kind='tool_call') as s:
s.set_input({'query': 'refund policy', 'tool': 'vector_search'})
results = vector_db.search('refund policy')
s.set_output({'results': results, 'count': len(results)})

How They Fit Together

Trace: "customer-support-run"
├── Span: "classify-intent" (llm_call)
│ └── Decision: route to "refund" handler
├── Span: "lookup-policy" (tool_call)
├── Span: "generate-reply" (llm_call)
└── Span: "send-response" (custom)

Every trace tells a story. Opswald helps you read it.

Next Steps