Loop a Thought, Action, Observation cycle — the model thinks, calls a tool, observes the result, and iterates.
A scientist performing an experiment: think (hypothesis), act (run test), observe (review result), refine.
Thought : I need the CEO of Apple.
Action : search('CEO of Apple')
Observation: Result: 'Tim Cook'
Thought : enough; answer the user.
Final : Tim Cook is CEO of Apple.
ReAct (Yao 2022) interleaves reasoning with tool calls. The model emits a Thought, chooses an Action (tool call), receives an Observation, then continues or returns Final. Implement as a stateful loop with iteration caps, per-step logging and progress checks.
import openai
def step(history, tools):
return openai.chat.completions.create(
model='gpt-4o-mini', messages=history, tools=tools)
history = [{'role':'user','content':'Weather in Paris, convert celsius to fahrenheit?'}]
tools = [weather_tool, calc_tool]
for i in range(MAX_ITERS):
r = step(history, tools)
msg = r.choices[0].message
history.append(msg)
if msg.tool_calls:
for tc in msg.tool_calls:
history.append({'role':'tool','name':tc.function.name,'content': run_tool(tc)})
else:
print('FINAL:', msg.content); break
The Thought/Action/Observation loop is the canonical agent frame — fit any tool to it.
last = None
for i in range(MAX_ITERS):
msg = step(history, tools).choices[0].message
args = msg.tool_calls[0].function.arguments if msg.tool_calls else None
if args == last: raise RuntimeError('no progress; same call repeated')
last = args
Progress detection prevents infinite loops; abort on identical repeated calls.
log_to_tracing([(m['role'], m['content']) for m in history])
Log every step; audits and debugging are impossible without per-step traces.
Allowing an agent to loop without a cap or progress check. Even simple bugs in tool semantics drive the model to retry forever.
Use ReAct whenever the agent must plan across multiple tool calls. Cap iterations, log every step, abort on repeated identical calls.