The Automated agent_loop() Controller
~20 min read
Replace the human controller with a Python function that parses the agent's Thought/Action/Answer output, runs the right tool via regex extraction, and keeps looping until a final Answer appears.
The manual trace makes the ReAct mechanics concrete — the automated agent_loop() function replaces the human controller with code that does the exact same job: read the agent's output, figure out what stage it's in, act accordingly, and keep looping until a final answer appears.
The function takes a query (the user's natural-language question) and the system_prompt (the same ReAct prompt explored in the previous subtopic). Inside, it initializes a new Agent instance with that system prompt, and defines a dictionary of the tools available to the agent — the keys of this dictionary must match exactly what the agent writes in its 'Action:' lines, since that's how the controller knows which function to call.
Two pieces of loop state matter: current_prompt holds the next message to send to the agent (which might be the initial query, a blank string to let it continue, or a formatted Observation), and previous_step tracks what stage the loop was last in, for control flow. The loop itself runs the agent with current_prompt at each iteration, prints the output for visibility, and checks: if the response contains 'Answer:', that's the final answer — print it and break out of the loop entirely.
If instead the response contains a 'Thought:' line, the controller just sets current_prompt to an empty string (to let the agent continue into its next stage) and moves on. If it contains an 'Action:' line, the controller uses a regular expression to extract the tool name and its argument — for example, from 'Action: lookup_population: India', the regex pulls out lookup_population as the tool and India as the argument. If that tool name exists in the tools dictionary, the controller calls it like a normal Python function, formats the result as an 'Observation: ...' string, and feeds that back in as the next current_prompt — mimicking tool execution and response injection, exactly like the human did manually, just automated. If the tool name doesn't exist, the controller asks the agent to retry instead of crashing.
💻 Code example
import re
ACTION_RE = re.compile(r"^Action:\s*(\w+):\s*(.*)$", re.MULTILINE)
def agent_loop(query: str, system_prompt: str, tools: dict, max_turns: int = 6) -> str:
agent = Agent(system=system_prompt)
current_prompt = query
for _ in range(max_turns):
result = agent(current_prompt)
print(result)
if "Answer:" in result:
return result.split("Answer:", 1)[1].strip()
if "Action:" in result:
match = ACTION_RE.search(result)
if not match:
current_prompt = "Observation: could not parse Action — please retry"
continue
tool_name, arg = match.group(1), match.group(2).strip()
if tool_name not in tools:
current_prompt = f"Observation: unknown tool '{tool_name}' — please retry"
continue
observation = tools[tool_name](arg)
current_prompt = f"Observation: {observation}"
else:
current_prompt = "" # e.g. a Thought: line — just continue the loop
return "Agent did not converge to an answer within max_turns"
tools = {"math": lambda e: eval(e), "lookup_population": lookup_population}
answer = agent_loop("What is India's population divided by 2?", REACT_SYSTEM_PROMPT, tools)
print("Final:", answer)
💬 Deep Dive with AI
Key points
- •agent_loop() automates exactly what the human did in the manual trace: read output, act, feed back an Observation, repeat
- •current_prompt and previous_step are the loop's state — tracking what to send next and what stage was last seen
- •A regex extracts the tool name and argument from lines like 'Action: lookup_population: India'
- •The tools dictionary's keys must exactly match what the model writes in its Action lines, or the lookup fails
- •An 'Answer:' line in the response is the signal to stop looping and return the final result