Skip to main content

Controlling the Loop

The AgentLoop exposes two methods for running an agent: execute() for simple run-to-completion workflows, and iterate() for step-by-step observation. Both operate on an immutable AgentState and return the resulting state after the agent finishes.

execute() vs iterate()

Running to Completion

The execute() method runs the full loop and returns the final state in a single call. This is the right choice for most application code where you simply need the agent’s answer:
Internally, execute() is a thin wrapper around iterate() — it simply consumes the iterator and returns the last yielded state.

Stepping Through Execution

The iterate() method returns a generator that yields the state after each completed step. This gives you the opportunity to observe progress, log intermediate results, update a UI, or apply custom logic between steps:
Each yielded $stepState is a complete AgentState snapshot. You can inspect messages, tool executions, errors, and token usage at every point in the agent’s run. The final yield includes the post-execution state after the AfterExecution hooks have fired. Use execute() for straightforward application logic. Use iterate() when you need progress updates, streaming indicators, step-level logging, or any form of real-time observation.

Inspecting State After Execution

Once the loop finishes, the returned AgentState provides a comprehensive set of accessors to understand what happened during the run.

Execution Summary

Step History

Every completed step is recorded as a StepExecution in the execution’s step history. You can iterate over all steps to review the full trace of the agent’s reasoning:
For quick access to the most recent step:

Tool Execution Details

When the agent used tools during its run, you can drill into the execution details of each tool call:
To see all tool executions from the last step:

Stop Reason

Every execution ends for a reason. The stop reason tells you whether the agent completed naturally, hit a limit, encountered an error, or was stopped by an external request:
You can also retrieve the full stop signal for additional context:

Reading the Response

AgentState provides two convenience methods for extracting the agent’s output, each suited to different situations.

finalResponse()

Returns the output messages from the last step, but only if that step was a FinalResponse (the model answered without requesting tool calls). If the execution ended mid-tool-use or with an error, this returns an empty Messages collection:

currentResponse()

Returns the most recent visible output regardless of step type. It first checks for a final response; if none exists, it falls back to the output of the current or last step. This is useful during iterate() loops where you want to show the latest output even if the agent has not finished:
A typical pattern after execution combines both:

Listening to Events

The AgentLoop dispatches events at every significant point in the execution lifecycle. You can subscribe to specific event types or listen to all events with a wiretap.

Subscribing to Specific Events

Use onEvent() to register a listener for a particular event class. The listener receives the fully-typed event object:

Wiretap (All Events)

Use wiretap() to observe every event the loop dispatches. This is invaluable for debugging and logging:

Available Events

The loop emits the following events during execution: Events are dispatched through the loop’s EventDispatcher. If you are using the AgentBuilder, the builder can wire a parent event handler so that events propagate up to your application’s event system.

Debugging Execution

For quick diagnostics, AgentState provides a debug() method that returns an array summarizing the execution:
This is particularly useful when logging or when you need a quick overview of what happened without drilling into individual steps.