Agentic AI · ReAct
ReAct interleaves reasoning ("Thought") with tool use ("Action") and results ("Observation"); the prompt format, a parser for model output, and a full trace with a scripted model.
ReAct (Yao et al., 2022) combines two earlier ideas: chain-of-thought reasoning, and acting with tools. The model writes text in a fixed pattern:
Thought: what I know and what I need next
Action: tool_name[input]
Observation: (filled in by the application, not the model)
... repeat ...
Final Answer: ...
The paper’s argument is that reasoning helps the model plan and handle surprises, and acting grounds that reasoning in real information, which reduces hallucination.
The important engineering point: the application parses each Action: line, runs the tool, and appends the Observation:. The model only produces text. Here’s that parser and loop, driven by a scripted model so the trace is exact. The tool-calling post uses a real model.
import re
TOOLS = {
"lookup": lambda k: {"bert": "BERT was published in 2018.", "gpt-2": "GPT-2 was released in 2019."}.get(k.lower(), "unknown"),
"subtract": lambda s: str(eval(s, {"__builtins__": {}}, {})) if re.fullmatch(r"[\d\s\-]+", s) else "invalid",
}
SCRIPT = iter([
"Thought: I need the year BERT was published.\nAction: lookup[BERT]",
"Thought: Now I need GPT-2's release year.\nAction: lookup[GPT-2]",
"Thought: The gap is 2019 minus 2018.\nAction: subtract[2019 - 2018]",
"Thought: I have everything.\nFinal Answer: GPT-2 came 1 year after BERT.",
])
fake_llm = lambda prompt: next(SCRIPT)
def react(question, llm, max_steps=6):
prompt = f"Question: {question}\n"
for _ in range(max_steps):
out = llm(prompt)
prompt += out + "\n"
if m := re.search(r"Final Answer:\s*(.*)", out):
return m.group(1), prompt
if m := re.search(r"Action:\s*(\w+)\[(.*)\]", out):
tool, arg = m.groups()
obs = TOOLS[tool](arg) if tool in TOOLS else f"unknown tool {tool}"
prompt += f"Observation: {obs}\n"
else:
prompt += "Observation: could not parse an action\n"
return "no answer within step limit", prompt
answer, transcript = react("How many years after BERT was GPT-2 released?", fake_llm)
print(transcript)
print("ANSWER:", answer)
Output:
Question: How many years after BERT was GPT-2 released?
Thought: I need the year BERT was published.
Action: lookup[BERT]
Observation: BERT was published in 2018.
Thought: Now I need GPT-2's release year.
Action: lookup[GPT-2]
Observation: GPT-2 was released in 2019.
Thought: The gap is 2019 minus 2018.
Action: subtract[2019 - 2018]
Observation: 1
Thought: I have everything.
Final Answer: GPT-2 came 1 year after BERT.
ANSWER: GPT-2 came 1 year after BERT.
The years come from my own Transformers notes (BERT 2018, GPT-2 2019). The subtract tool only accepts digits, spaces and minus signs before evaluating, which is a small taste of the safety post at the end of this phase.