How It Works?#

With so many LLM calls, it may be helpful to visualize the sequence of operations and LLM interactions that occur when using agents. Below is a detailed breakdown of the key steps, iterations, and LLM calls involved in the ReActAgent’s workflow with max_iterations=3 for illustration. Some of the steps are common for CodeActAgent as well, except for the code execution parts.

INITIALIZATION PHASE
├─ Call 1: planner.create_plan()
│  └─ ku.call_llm() → Creates initial plan from task description
│     [AgentPlan response format]
│
└─ (Plan displayed to user, added to message history)


ITERATION 1 (idx=0)
├─ Call 2: _think() → _record_thought(ReActChatMessage)
│  └─ _chat(response_format=ReActChatMessage)
│     └─ ku.call_llm() → Agent thinks about next action
│        [ReActChatMessage response format]
│        [With retry logic: MAX_RESPONSE_PARSING_ATTEMPTS = 3]
│
├─ _act() → Executes tool or returns final answer
│  └─ (No LLM call if action taken; potential LLM call only if parsing fails)
│
├─ Call 3: planner.update_plan()
│  └─ ku.call_llm() → Updates plan based on thought + observation
│     [AgentPlan response format]
│
└─ Call 4: observer.observe()
   └─ ku.call_llm() → Observes agent state for loops/issues
      [ObserverResponse response format]
      [Only if: iteration > 1 AND (iteration - last_correction_iteration) >= threshold]
      [First observation typically skipped since iteration=1 and threshold default=3]


ITERATION 2 (idx=1)
├─ Call 5: _think()
│  └─ _chat(response_format=ReActChatMessage)
│     └─ ku.call_llm()
│
├─ _act() → Executes tool or returns final answer
│
├─ Call 6: planner.update_plan()
│  └─ ku.call_llm()
│
└─ Call 7: observer.observe()
   └─ ku.call_llm()
      [Now eligible since iteration=2, but threshold check: 2 - 0 < 3, so skipped]


ITERATION 3 (idx=2)
├─ Call 8: _think()
│  └─ _chat(response_format=ReActChatMessage)
│     └─ ku.call_llm()
│
├─ _act() → Executes tool or returns final answer
│
├─ Call 9: planner.update_plan()
│  └─ ku.call_llm()
│
└─ Call 10: observer.observe()
   └─ ku.call_llm()
      [Eligible since iteration=3, threshold check: 3 - 0 >= 3, so CALLED]


POST-ITERATION PHASE (if final_answer_found = True)
└─ Call 11: planner.update_plan() [Optional, only if final answer found]
   └─ ku.call_llm()
      [Marks completion steps in plan]


FAILURE RECOVERY PHASE (if max_iterations reached without answer)
└─ Call 12: salvage_response()
   └─ ku.call_llm() → Summarizes progress for incomplete task
      [If summarize_progress_on_failure=True]