When working with AI agents, one of the most useful ways to understand what is happening behind the scenes is through tracing.
A seemingly simple user request can result in multiple model invocations, tool executions, and event-loop cycles before the agent produces its final response.
In this post, I'll walk through a simple Strands Agent example:
greet rahul using tool
🧠What Actually Happens?
The resulting trace looks roughly like this:
agent.run
|
+-- invoke_agent
|
+-- execute_event_loop_cycle #1
| |
| +-- chat #1
| |
| +-- execute_tool greet
|
+-- execute_event_loop_cycle #2
|
+-- chat #2
Although the user made only one request, the agent performed:
Two event-loop cycles
Two model calls
One tool execution
The obvious question is:
Why is a second model call needed?
⚙️ Event-Loop Cycle #1: Decide and Act
The first chat span represents a model invocation.
The model receives:
The user prompt
Conversation history
Available tools
It decides that the appropriate action is:
greet(name="rahul")
Strands then executes the tool.
chat #1
|
| decides to call
v
execute_tool greet
|
v
"Hello, Rahul"
At this point the tool has completed.
But the agent has not.
🧩 Why Another Model Call?
Tools execute code.
They don't decide what should be shown to the user.
Once the tool returns its result, the model must reason over that result and determine:
Is another tool required?
Is the task complete?
What should the final response be?
That requires another model invocation.
⚙️ Event-Loop Cycle #2: Observe and Respond
The updated context now contains:
User:
greet rahul using tool
Assistant:
toolUse -> greet("rahul")
Tool:
toolResult -> "Hello, Rahul"
The agent enters another event-loop cycle:
execute_event_loop_cycle #2
|
+-- chat #2
|
v
Final Response
The model observes the tool result, determines that no further actions are required, and generates the final response.
The complete execution becomes:
User Prompt
|
v
Cycle #1
|
+-- Model decides
|
+-- Tool executes
|
v
Tool Result
|
v
Cycle #2
|
+-- Model reasons over result
|
v
Final Response
Notice that the first model call decides what to do.
The second model call decides what to say.
🧠Why This Looks Like ReAct
This execution naturally follows the ReAct pattern:
Reason
|
v
Act
|
v
Observe
|
v
Reason
|
v
Answer
For our example:
Reason → Decide to call greet()
Act → Execute greet()
Observe → Receive "Hello, Rahul"
Reason → Determine no further actions are required
Answer → Return final response
ReAct describes how an agent behaves.
OpenTelemetry describes how that behavior is observed.
For example:
Agent Execution OpenTelemetry Representation
Model invocation → chat span
Tool execution → execute_tool span
Tool result → span output / attributes
Next model invocation → next chat span
Agent invocation → trace containing these spans
A ReAct iteration is therefore a logical agent concept—not an OpenTelemetry primitive.
📂 Where Does a Session Fit?
A session can contain multiple user interactions.
Each interaction creates a new trace.
Session
|
+-- Prompt #1
| +-- Trace #1
|
+-- Prompt #2
| +-- Trace #2
|
+-- Prompt #3
+-- Trace #3
Within each trace, the agent may execute multiple event-loop cycles.
Session
|
| 1:N
v
Trace
|
| 1:N
v
Event-Loop Cycles
|
| captured as
v
Spans
The exact relationship between sessions and traces depends on the application's instrumentation, but this is a useful mental model.
🧩 Two Views of the Same Execution
When debugging an agent, think about two different perspectives.
Execution
Event Loop
|
+-- Reason
+-- Act
+-- Observe
+-- Repeat
This explains how the agent works.
Observability
Trace
|
+-- Span
+-- Span
+-- Span
This explains what happened while the agent was working.
🎯 Final Thought
For our simple example:
One User Request
|
v
One Agent Execution / Trace
|
+-- Event-Loop Cycle #1
| +-- chat
| +-- execute_tool
|
+-- Event-Loop Cycle #2
+-- chat
|
v
Final Response
Understanding this distinction makes agent traces much easier to read.
The event loop explains how the agent reasons.
The trace records that reasoning as observable spans.
OpenTelemetry doesn't change how the agent works.
It simply makes that work visible—making it easier to debug behavior, optimize execution, analyze tool usage, and evaluate agent performance.