HomeAboutOur TeamContact
HomeArtificial Intelligence
AI Agent Loop in Python: Build a ReAct Agent From Scratch

AI Agent Loop in Python: Build a ReAct Agent From Scratch

Artificial Intelligence
June 19, 2026
6 min read
Beginner
Part 3 banner reading The Agent Loop From Scratch with a circular think-act-observe arrow motif, showing the ai agent loop in Python on a dark background
Table of Contents
01
Recap: The Two Pieces You Already Have
02
The AI Agent Loop, in About 30 Lines
03
Where It Stops Being a Workflow and Becomes an Agent
04
Parsing Actions Safely and Knowing When to Stop
05
Common Failures (and the Fixes)
06
Quick Recap
07
Conclusion

Series: AI Agents from Scratch in Python This is Part 3. So far: Part 1 made your first LLM call, and Part 2 let the model call one of your functions. Here we wrap that single call in an AI agent loop so the model can take several steps on its own. If you can run the Part 2 code, you have everything you need.

The most-liked comment under almost every “AI agent” tutorial is the same five words: an agent is a loop. It is the top reply on this month’s breakout agent video, and dev.to, Hacker News, and half the engineering blogs keep repeating it. They are right — and in this post you’ll write that AI agent loop yourself, in plain Python, with no framework.

By the end you’ll have a working AI agent loop that thinks, picks a tool, runs it, reads the result, and keeps going until it can answer.

Part 2 ran a tool exactly once, by hand. A real agent does it again and again, deciding for itself when to stop. That one change — a while loop around the call you already know — is the whole leap from chatbot to agent. Let’s build it.

🎯 Key takeaways
  • An agent is a loop: the model thinks, calls a tool, reads the result, and repeats until it answers.
  • The line that makes it an agent is where the model — not your code — decides the next step.
  • Three guards keep it safe: a max-steps cap, a try/except around tools, and a forced final answer.

Recap: The Two Pieces You Already Have

You are not starting from zero. The loop is just your Part 1 and Part 2 code, repeated.

From Part 1 you have one LLM call: send a list of messages, get a reply. From Part 2 you have one tool call: describe a function, let the model request it, run it, and hand the result back. An AI agent loop is what you get when you stop doing that once and start doing it in a cycle.

The agent loop showing Thought, Action, and Observation as a cycle around the model, with a branch out to a final answer when no tool is needed

That cycle has a name. It is the ReAct pattern — short for Reasoning and Acting — introduced by Yao and colleagues in 2022 (the original paper is here). The model produces a Thought about what to do, takes an Action by calling a tool, sees the Observation that comes back, and repeats.

The classic version parsed all of that out of plain text. Today’s models expose the Action through native tool calling, so you get the same loop with far less glue code — which is exactly why we can build it in about thirty lines.

The AI Agent Loop, in About 30 Lines

Here is the core of every agent. Read it once, then we will walk through the three moving parts.

python
from openai import OpenAI
import json
client = OpenAI()
def search(query: str) -> str:
# Pretend lookup. Real code would call a search API.
return "Mount Everest is 8,849 metres tall."
TOOLS = {"search": search} # name -> real function
tools_schema = [{
"type": "function",
"function": {
"name": "search",
"description": "Look up a fact you do not already know.",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
},
}]
messages = [{"role": "user", "content": "How tall is Everest, in feet?"}]
for step in range(5): # max-steps guard (more on this below)
response = client.chat.completions.create(
model="gpt-5.4-mini", messages=messages, tools=tools_schema,
)
reply = response.choices[0].message
messages.append(reply) # remember what the model just said
if not reply.tool_calls: # no tool wanted -> the loop is done
print(reply.content)
break
for call in reply.tool_calls: # run every tool the model asked for
fn = TOOLS[call.function.name]
args = json.loads(call.function.arguments)
result = fn(**args)
messages.append({
"role": "tool", "tool_call_id": call.id, "content": result,
})

Run that and the model does something it could not do in Part 2: it reaches for search on its own, reads back “8,849 metres,” and then converts the answer to feet in its final reply — about 29,032 feet. You never told it to search and never told it to convert. It decided both.

Five-step flowchart of the agent loop: send messages, check for a tool request, run the tool, append the observation, repeat, and stop on a final answer

The three moving parts are small. The for loop is the agent — it lets the model take more than one step. The if not reply.tool_calls check is the exit — when the model stops asking for tools, it has an answer. And appending every message (both the model’s reply and each tool result) is the memory of this run: each pass through the loop, the model sees everything that happened before. Lose that, and the agent forgets what it just learned.

Where It Stops Being a Workflow and Becomes an Agent

This is the question readers actually ask: are we building a real agent here, or just a script with extra steps? The honest answer is in one line of that code.

In a normal program, you decide the order: call the API, then parse, then format. The control flow lives in your code. In the loop above, the line if not reply.tool_calls hands that decision to the model. The model — not your code — chooses whether to act again or to stop. That is the line where a workflow becomes an agent.

🔑 Key pointIf the model decides what happens next, it's an agent. If your code decides, it's a workflow. That single distinction is the whole point of the loop.

A quick contrast makes it concrete. Imagine the hardcoded version of the same task:

python
# A workflow: YOU decide the steps, every time, in order.
facts = search("height of Mount Everest")
answer = convert_to_feet(facts)
print(answer)

That works, but only for this exact question. Ask it “how tall is Everest and K2?” and it breaks, because the steps are fixed. The agent loop handles both, because the model can choose to search twice before answering.

When people say an agent is a loop where the model decides control flow, this is what they mean — and it is the definition VentureBeat and others settled on across 2026. If you want the full conceptual breakdown, the complete guide to AI agents covers it; here we care that you can see it in your own code.

Parsing Actions Safely and Knowing When to Stop

The bare loop works, but two things will bite you in real use: a tool that throws, and a loop that never ends. Both have simple fixes.

First, never let one tool crash the whole agent. The model fills in the arguments, and it can get them wrong — a missing field, or a function name that does not exist. Wrap the dispatch so a failure becomes a message the model can recover from, not a stack trace:

python
for call in reply.tool_calls:
args = json.loads(call.function.arguments)
try:
fn = TOOLS[call.function.name] # KeyError if the name is invented
output = fn(**args)
except Exception as error:
output = f"Tool error: {error}" # hand the problem back to the model
messages.append({
"role": "tool", "tool_call_id": call.id, "content": output,
})

When the model sees "Tool error: ..." as an observation, it usually corrects itself on the next pass — picks a real tool, or fixes the argument. That recovery is only possible because the result goes back into messages.

Second, the stop condition. The loop ends naturally when reply.tool_calls is empty — the model has nothing left to do, so it answered. The range(5) around the loop is the safety net for when that never happens, which the next section explains.

⚠️ Always cap the loopWithout a max-steps guard, a stuck agent can loop forever and burn real money — I once watched a 2-cent task become a 40-cent runaway. Treat the cap as required, not optional.
💡 Debug by printing each stepA single `print(step, reply.tool_calls)` at the top of the loop turns the agent from a black box into a readable trace — you watch it think, act, and observe in real time, and instantly see a wrong tool, a repeated call, or an early exit.

Common Failures (and the Fixes)

Every from-scratch agent hits the same three problems. Here is each one and the line that fixes it.

1. The infinite loop. The model keeps calling tools and never gives a final answer, burning tokens on every pass. The fix is the for step in range(5) cap you already saw. Pick a number that fits the task — most simple agents finish in two or three steps — and force an answer if you hit it:

python
for step in range(5):
... # the loop body from above
else: # runs only if we never `break`
messages.append({"role": "user",
"content": "Give your best final answer now, no tools."})
final = client.chat.completions.create(model="gpt-5.4-mini", messages=messages)
print(final.choices[0].message.content)

That for/else is real Python: the else runs only when the loop finishes without a break. It guarantees the user always gets an answer, even when the agent runs out of steps.

2. Hallucinated tools. The model asks for a function you never defined. Without a guard, TOOLS[call.function.name] raises KeyError and the program dies. The try/except from the previous section turns that into a recoverable error message instead — the single most common reason a beginner’s agent crashes.

3. No final answer. Sometimes the model calls a tool and then stops mid-thought because of a token limit, leaving no plain reply. Raise max_tokens for the final step, or rely on the for/else fallback above to ask one more time. Between the step cap, the try/except, and the forced final answer, the loop is now hard to break.

One more thing worth saying plainly: this is the loop that every framework wraps. When you eventually compare LangGraph, CrewAI, and AutoGen, you will recognise this exact cycle under all three — they add retries, tracing, and state, but the heartbeat is what you just wrote.

Quick Recap

The whole agent loop, in five points:

  • The loop sends messages to the model, runs any tool it asks for, appends the result, and repeats.
  • It stops when the model replies with no tool call.
  • It’s an agent because the model, not your code, picks the next step.
  • Guard against runaways with a max-steps cap and a forced final answer.
  • Guard against crashes by wrapping every tool call in try/except.

Conclusion

You built a real agent from scratch: a loop that lets the model think, call a tool, read the result, and decide its own next step — about thirty lines, no framework, with guards for the failures that actually happen. The moment your code stopped dictating the steps and let the model choose, it became an agent. That is the whole idea, and now it is yours.

There is one honest limit. Your agent forgets everything the instant the loop ends — start a new question and it has no idea what came before. Giving it memory, so it remembers across turns, is exactly what Part 4 tackles.

When you ran the loop, what was the first tool your agent reached for — a search, a calculator, a database query? Tell me in the comments; the choices people make here are always interesting.

Read next: LangGraph vs CrewAI vs AutoGen (2026) — see the loop you just wrote hiding inside all three frameworks.


References

  1. ReAct: Synergizing Reasoning and Acting in Language Models (arXiv)
  2. Anthropic — Building Effective AI Agents
  3. OpenAI — Function calling guide

Tags

#PythonForAI#AIAgents#ReActAgent#AgentLoop#OpenAI#AgenticAI#AIForDevelopers

Share

Previous Article
AI Agent Memory in Python: Short-Term Memory From Scratch
Sukhveer Kaur
More from this author

Sukhveer Kaur

Part 4 banner reading Give Your Agent a Memory with a stack of chat-bubble messages showing ai agent memory in Python on a dark background
AI Agent Memory in Python: Short-Term Memory From Scratch
June 19, 2026
6 min
Beginner
See all by Sukhveer Kaur

Get new guides in your inbox

Practical AI, software engineering, and cloud articles — straight to your inbox. No spam, unsubscribe anytime.
AI Agent Loop in Python: Build a ReAct Agent From Scratch
6 min left
Sukhveer Kaur

Sukhveer Kaur

Software Developer & AI Engineer

Table Of Contents

1
Recap: The Two Pieces You Already Have
2
The AI Agent Loop, in About 30 Lines
3
Where It Stops Being a Workflow and Becomes an Agent
4
Parsing Actions Safely and Knowing When to Stop
5
Common Failures (and the Fixes)
6
Quick Recap
7
Conclusion

Popular Posts

01
AI Agent Memory in Python: Short-Term Memory From Scratch
Artificial Intelligence
·
6 min read

Related Posts

© 2026, All Rights Reserved.

Quick Links

Advertise with usOur TeamAbout UsEditorial StandardsContact Us

Social Media