Skip to content

The Step Loop

The built-in ReActAgentStrategy runs as a node-driven Step loop: the LLM decides the plan, the framework walks it, and everything is observable and interruptible.

Anatomy of a Step

intro_step ──► NATIVE_WHILE(iter_cond) ──► STEP_EXEC (one tool round) ──► leave_step
                   ▲                                   │
                   └──────── after_iteration ◄─────────┘
PhaseWhat happens
decomposeFirst intro_step: LLM decides simple vs DAG {needs_decomposition, dag, reason}
intro_stepPick the next ready DAG node (topological order via graphlib.TopologicalSorter); drain peer messages; emit step_intro event + metadata
STEP_EXECOne single_execute() round: model → tools → results; after_iteration() runs stall detection inside the loop
leave_stepSummarize (subject-predicate), complete the node, compress history; emit step_leave event + metadata

Semantic State: AgentRunState

All step-level state lives in AgentRunState (bridged between AgentLoopState.run_state and strategy.run_stateone instance):

FieldMeaning
step_indexGlobal step counter
current_phase / current_step_idThe active DAG node
plan / completed_step_idsThe task DAG + progress
step_tool_signaturesTool signatures in the current Step (stall window)
stall_injectedGive-up prompt injected (once per Step)
last_summarySubject-predicate summary of the previous Step
tokensReal API token accounting (compression trigger)
exec_finishedStrategy done calling tools → iteration loop ends

Stall Protection

  1. _should_cancel_tool_call — before executing, the N-th identical signature is cancelled and returns "Cancelled: Reach the max limit of repeatly calling tool."
  2. after_iteration — per-iteration hook (inside the loop!) injects the give-up prompt when the window repeats; sets stall_injected/exec_finished so iter_cond stops the loop immediately — no more tokens burned.

Historical lesson: stall detection must run inside the loop (after_iteration), not at leave_step (outside) — otherwise a stuck agent never reaches the detector.

Lifecycle Events

EventWhenMutable
agent.step_introStep startsoverride_phase
agent.step_leaveStep endsoverride_verb / override_object
agent.step_iterationEach tool roundend_step
agent.tool_callBefore a regular toolarguments / cancel
agent.tool_returnAfter a regular toolresult / skip_append

Matchers may mutate events or raise StepAbortError (control flow). Built-in tools (REASONING / UPDATE_STEP / STOP) do not fire events.

Step Metadata

Emitted as MessageWithMetadata (type="step"):

extra_typeContent
decomposedecision, DAG ids + descriptions, reason
introphase, step_index, simple_mode, node description
leavephase, stall flag, summary verb/object
stallthe repeated signatures, injected flag
compressprompt tokens, threshold

The update_step Tool

The agent can revise the plan mid-run: replan (replace DAG), mark_done, add_step, remove_step. Each revision bumps plan_revision; execution stays linear (the DAG is a semantic layer, not a parallel graph).

Peer Messages

intro_step drains the reverse stream (send_to_producer) and appends [peer message] user messages — see Suspend & Resume.

Next

Workflow Debugging — step through the interpreter.

Apache 2.0 License