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