Posts
Back to Basics: Understanding ReAct Agent Pattern
An understanding all of it is basically string :D
Going back to basics with ReAct agent pattern taught me that most of the complexity is about handling a LLM output by parsing and adjusting prompt as to make sure the LLM output pattern matches what we expect while also handling the unexpected.
Output of LLM
Working with agentic framework like crewai, langchain and strands dilluted the how it works behind the scenes. By working from the ground up slowly and seeing that the output of the LLM is simply a string that we need to parse.
Using prompt we can structure the LLM output to match our expected format (Best effort):
ReAct Pattern
ReAct pattern is a simple yet an effective way to structure agentic workflows. It consist of a loop that alternates between the LLM and the tools that we specify. Starting from the initial user input, the loop continues until the final answer are obtained.
This simple example is the things that help me tremendously understand that to utilize the LLM, it’s needed to finding the crucial information from the output where it became a “real” impact.
The pattern consist of simple instructuion where the LLM need to be a helpfull assistant that do help us with the simple arithmetic operation while also has a structured output that we can parse to extract the Thought and Action we need to take next.
System Prompt
The system prompt that we use simply instruct it with the tools or function that are available to it. In this case, the tools are multiplication and addition.
System Prompt:
You are an helpful assistant that do help us with the simple arithmetic operation.
You must always use the multiplication or addition tool for every arithmetic step
You will reply in the form of structured thought and response with below format:
Thought: <your reasoning>
Action: multiplication(<a>, <b>) or addition(<a>, <b>)
Then stop and wait for the Observation before continuing
Only once you have all the numbers you need from Observations, respond with:
Thought: <your reasoning>
Final Answer: <the answer>
Tools
Now that we have the system prompt, we need to define the tools that the LLM will be able to use. In this case as we put multiplication and addition in the tools that are available in the system_prompt we will do map those string to the created function in script:
def multiplication(a: int, b: int) -> int:
print(f"Tool called: multiplication({a}, {b})")
return a * b
def addition(a: int, b: int) -> int:
print(f"Tool called: addition({a}, {b})")
return a + b
Now that we have the tools defined, it will be confusing as to where do we mapping those LLM output to the function that we define, and yes all comes to parsing string as usual :).
As the output is a string with the format Action: multiplication(<a>, <b>) or Action: addition(<a>, <b>) in each reply from the LLM, we need to parse those string to extract the tool name and arguments.
Oh make sure that we do parse the Final Answer: too, as if we not do that then the agent will keep looping :( as it will be used in our loop logic.
def llm_parse_reply(reply: str) -> tuple[str | None, str | None, str | None]:
tool_name = None
action = None
final_answer = None
if "Action: multiplication(" in reply:
tool_name = "multiplication"
action = reply.split("Action: multiplication(")[1].split(")")[0]
if "Action: addition(" in reply:
tool_name = "addition"
action = reply.split("Action: addition(")[1].split(")")[0]
if "Final Answer: " in reply:
final_answer = reply.split("Final Answer: ")[1]
return tool_name, action, final_answer
Agent Loop
Keeping it simple, the agent loop will continue to iterate until it reaches a Final Answer: or the maximum number of iterations (if we didn’t forgot to set it, otherwise it will run indefinitely).
So the loop simply runs in each iteration, do parsing and then call the appropriate tool until it reaches the state that we defined as Final Answer:.
Building blocks and Realization
The ReAct pattern is not a new or complex pattern, it is a straightforward way to teach us the building blocks of a more complex agent pattern. The simple pattern that giving me a realization that to utilizing an LLM as an agent, it’s a simple string output that we need to parse and act upon and understanding all of the complex agent patterns are being built on top of this simple pattern.
Category: technology