Skip to main content

Chapter 04 · Your First Agent

Part of Part I · Foundations

Fifty lines of Python. That is all that separates you from your first autonomous agent. By the end of this chapter, you will watch it think.

Not a wrapper around an API. Not a chatbot with a fancy name. An actual agent -- a system that observes, thinks, decides, acts, and repeats until the task is done. Here is the complete code. Read it once, even if it does not all make sense yet. Then we will take it apart, line by line, and make it better.

import openai

SYSTEM_PROMPT = """You are an autonomous agent. You operate in a loop:
OBSERVE -> THINK -> ACT. When given a task, reason step by step.
If you have a final answer, begin your response with FINAL_ANSWER:"""

def run_agent(task, max_turns=10):
client = openai.OpenAI()
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": task}
]

for turn in range(1, max_turns + 1):
print(f"\n--- Turn {turn} ---")
response = client.chat.completions.create(
model="gpt-4o",
messages=messages
)
reply = response.choices[0].message.content
print(reply)

if "FINAL_ANSWER:" in reply:
print("\nAgent finished.")
return reply.split("FINAL_ANSWER:")[1].strip()

messages.append({"role": "assistant", "content": reply})
messages.append({"role": "user", "content": "Continue."})

return "Agent did not finish within turn limit."

if __name__ == "__main__":
result = run_agent("What is 15% of 87?")
print(f"\nResult: {result}")

That is it. Forty-seven lines, counting whitespace. An agent that reasons in a loop, decides when it is done, and returns an answer. The rest of this chapter explains every line, breaks the thing, fixes it, and then gives it a real task. By the end, you will understand why this loop is the foundation of every agent system you will ever build.

The Most Important Chapter

This is the most important chapter in the book. Everything that follows -- tools, memory, planning, multi-agent systems, safety -- builds on the agent loop. If you understand this chapter, you understand the core of agentic AI. The rest is details.

Take your time with it. Type the code yourself. Break it. Fix it. Watch it run.

Section 1: The Agent Loop -- Theory

Before you write another line, you need a mental model of what an agent actually does. Strip away the jargon and you are left with three phases in a cycle:

+------------------------------------------+
| |
| +----------+ +----------+ +------+|
| | OBSERVE |--->| THINK |--->| ACT ||
| +----------+ +----------+ +------+|
| ^ | |
| +-------------------------------+ |
| |
+------------------------------------------+

OBSERVE. The agent gathers information. What is the task? What happened on the last turn? Did a tool return a result? Did the user provide new input? Observation is the agent's eyes and ears. In this chapter, observation is simple: the agent reads the conversation history. In later chapters, observation will include tool outputs, database queries, and sensor data.

THINK. The LLM reasons about what to do next. Given everything it has observed, should it act? Should it respond to the user? Is the task complete? This is where the intelligence lives. The model weighs options, breaks down sub-problems, and decides on the next move.

ACT. The agent executes its decision. It might call a tool. It might return an answer. It might ask the user for clarification. The action produces new information, which feeds back into the OBSERVE phase, and the cycle continues.

The loop runs until a termination condition is met. In the simplest case, the agent declares itself done. In production systems, you add guards: a maximum number of turns, a timeout, a budget cap. Without these, an agent can loop forever -- and it will, as you will see shortly.

The loop is the difference between a single LLM call and an agent. A single call gives you one response. An agent loop gives you multi-step reasoning, error recovery, and adaptation to new information. The model sees its own previous outputs and can course-correct. That is the superpower.

Section 2: Building the Loop -- Code

You already saw the full code. Now let us build it from scratch, one piece at a time, so you understand why each line exists.

Step 1: The System Prompt

The system prompt defines the agent's behavior. It is the constitution. Everything the agent does flows from these instructions:

SYSTEM_PROMPT = """You are an autonomous agent. You operate in a loop:
OBSERVE -> THINK -> ACT. When given a task, reason step by step.
If you have a final answer, begin your response with FINAL_ANSWER:"""

Three things happen here. First, you tell the model what it is: an autonomous agent, not a chatbot. Second, you tell it how to operate: observe, think, act. Third, you give it a protocol for signaling completion: the FINAL_ANSWER: token. Without this token, you have no reliable way to know when the agent is done.

The system prompt is the most underrated lever in agent engineering. A well-written prompt prevents loops, reduces hallucinations, and makes the agent's behavior predictable. A bad prompt produces chaos. You will tune this prompt throughout the chapter.

Step 2: The Conversation History

The agent needs memory of what has happened so far. In this first version, memory is a list of message dictionaries:

messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": task}
]

This is the agent's entire world. It starts with the system prompt (the rules) and the user's task (the goal). Every turn, the agent's response is appended to this list, along with a prompt to continue. The history grows, and the agent uses it to track its own reasoning.

This is also the agent's biggest limitation right now. The history resets on every run. The agent cannot learn across sessions. It has no long-term memory. That is a problem for Chapter 7.

Step 3: The Loop

The loop is a for loop with a safety cap:

for turn in range(1, max_turns + 1):
print(f"\n--- Turn {turn} ---")
response = client.chat.completions.create(
model="gpt-4o",
messages=messages
)
reply = response.choices[0].message.content
print(reply)

Each iteration does three things. It calls the LLM with the full conversation history. It prints the response so you can watch the agent think. It stores the reply for the next iteration.

The max_turns parameter is not optional. Without it, a confused agent will loop until your API bill exceeds your rent. Set it to something reasonable -- 10 is a good default for simple tasks.

Step 4: Termination Detection

After each response, the agent checks whether the task is complete:

if "FINAL_ANSWER:" in reply:
print("\nAgent finished.")
return reply.split("FINAL_ANSWER:")[1].strip()

This is crude but effective. The agent signals completion by including FINAL_ANSWER: in its response. Your code splits on that token and returns everything after it. If the token is not present, the agent is still working, so you prompt it to continue:

messages.append({"role": "assistant", "content": reply})
messages.append({"role": "user", "content": "Continue."})

The "Continue." prompt is a nudge. It tells the agent: "You are not done. Keep going." Without it, the agent might stop mid-reasoning, unsure whether it should keep talking or wait for the user.

A Trace: Watching the Agent Think

Here is what happens when you run the agent on "What is 15% of 87?" Turn by turn, with actual LLM output:

--- Turn 1 ---
To find 15% of 87, I need to multiply 87 by 0.15.

87 * 0.15 = 13.05

FINAL_ANSWER: 15% of 87 is 13.05.

Agent finished.

Result: 15% of 87 is 13.05.

One turn. The agent reasoned, computed, and returned the answer. Clean.

Now here is a harder task: "If a train leaves Chicago at 60 mph and another leaves New York at 80 mph, and the distance is 800 miles, when do they meet?"

--- Turn 1 ---
I need to find when two trains moving toward each other meet.
Combined speed = 60 + 80 = 140 mph.
Time = distance / speed = 800 / 140 = 5.714... hours.

That's 5 hours and about 43 minutes.

FINAL_ANSWER: The trains meet after approximately 5 hours and 43 minutes.

Agent finished.

Still one turn. The model has the math in its training data. But watch what happens with a task that requires genuine multi-step reasoning:

"Plan a healthy dinner for four people under $30. List ingredients with estimated costs, then write the recipe."

--- Turn 1 ---
I'll plan a healthy, budget-friendly dinner for four. Let me think through this step by step.

First, I need to choose a main dish that's nutritious and affordable. A lentil and vegetable curry with rice fits well -- lentils are cheap, vegetables add nutrients, and rice is filling.

Let me estimate costs:
- 1 cup dried lentils: $1.50
- 1 onion: $0.50
- 2 carrots: $0.60
- 1 bell pepper: $1.00
- 1 can coconut milk: $2.50
- 2 cups rice: $1.00
- Spices (curry powder, cumin, salt): $0.50
- 2 cloves garlic: $0.20
- 1 tbsp oil: $0.10
- Fresh cilantro: $0.80

Running total so far: $8.70. I have room for a side dish and maybe a simple dessert.

--- Turn 2 ---
Good, I'm well under budget. Let me add a simple cucumber-tomato salad and a dessert.

Cucumber-tomato salad:
- 1 cucumber: $0.80
- 2 tomatoes: $1.20
- Lemon juice: $0.30
- Salt and pepper: negligible

Dessert -- baked apples with cinnamon:
- 4 apples: $3.00
- Cinnamon and a little sugar: $0.40
- Optional yogurt topping: $2.00

Total estimated cost: $8.70 + $4.30 + $5.40 = $18.40. Well under $30.

Now let me write the full recipe.

FINAL_ANSWER:
Dinner: Lentil and Vegetable Curry with Rice, Cucumber-Tomato Salad, and Baked Cinnamon Apples.

Ingredients:
- 1 cup dried lentils ($1.50)
- 1 onion ($0.50)
- 2 carrots ($0.60)
- 1 bell pepper ($1.00)
- 1 can coconut milk ($2.50)
- 2 cups rice ($1.00)
- Curry powder, cumin, salt ($0.50)
- 2 cloves garlic ($0.20)
- 1 tbsp oil ($0.10)
- Fresh cilantro ($0.80)
- 1 cucumber ($0.80)
- 2 tomatoes ($1.20)
- Lemon juice ($0.30)
- 4 apples ($3.00)
- Cinnamon and sugar ($0.40)
- Yogurt ($2.00)
Total: $18.40

Recipe:
[detailed recipe steps follow]

Agent finished.

Two turns. The agent broke the task into sub-problems, worked through the first one, realized it had budget room, added more dishes, and then synthesized the final answer. That is the loop doing its job.

my-first-agent · the loop in action

Section 3: The First Run -- And the First Failure

The agent works on simple tasks. Now let us give it something that breaks it.

The Task

"What is the capital of the country where the Angkor Wat temple complex is located?"

This is a straightforward question. The model knows the answer. But watch what happens:

--- Turn 1 ---
Angkor Wat is located in Cambodia. The capital of Cambodia is Phnom Penh.

FINAL_ANSWER: The capital is Phnom Penh.

Agent finished.

Fine. One turn. Now let us make it harder:

"Count the number of words in the sentence: 'The quick brown fox jumps over the lazy dog.' Then tell me if that number is prime."

--- Turn 1 ---
Let me count the words in the sentence: "The quick brown fox jumps over the lazy dog."

The words are: The, quick, brown, fox, jumps, over, the, lazy, dog.
That's 9 words.

Now, is 9 prime? A prime number is only divisible by 1 and itself. 9 is divisible by 3, so 9 is not prime.

FINAL_ANSWER: There are 9 words, and 9 is not a prime number.

Agent finished.

Still fine. The model can count words and check primality from its training data. Now the real test:

"Write a haiku about programming, then count the syllables in each line to verify it follows the 5-7-5 pattern."

--- Turn 1 ---
Here's a haiku about programming:

Code flows like water
Through the circuits of the mind
Bugs hide in the dark

Now let me count syllables:
Line 1: "Code flows like water" -- Code (1), flows (1), like (1), water (2) = 5 syllables. Correct.
Line 2: "Through the circuits of the mind" -- Through (1), the (1), cir-cuits (2), of (1), the (1), mind (1) = 7 syllables. Correct.
Line 3: "Bugs hide in the dark" -- Bugs (1), hide (1), in (1), the (1), dark (1) = 5 syllables. Correct.

FINAL_ANSWER: The haiku follows the 5-7-5 pattern.

Agent finished.

It worked. But here is the thing: the model did not actually count syllables. It generated a plausible-sounding analysis. If you check carefully, "circuits" is two syllables -- the model got that right by luck. But "through" is one syllable -- also right. The point is, the model is simulating the counting, not actually doing it. It has no tool for counting syllables. It is guessing based on patterns in its training data.

Now the failure:

"Generate a random number between 1 and 100, then tell me if it's even or odd."

--- Turn 1 ---
Let me generate a random number between 1 and 100.

I'll pick 47.

Now, is 47 even or odd? 47 divided by 2 is 23.5, so it's odd.

FINAL_ANSWER: 47 is odd.

Agent finished.

Looks fine, right? Wrong. The agent did not generate a random number. It chose a number. Specifically, it chose 47 -- a number that appears disproportionately often when LLMs are asked to "pick a random number." The model cannot generate randomness. It is a deterministic next-token predictor. Every time you run this, you might get 47, or 42, or 73. Never truly random.

This is not a bug in your code. It is a fundamental limitation of LLMs. The agent thinks it generated a random number, but it did not. It hallucinated the act of randomization.

The Real Failure: Infinite Loop

Now let us break the agent for real. Remove the max_turns guard and give it this task:

"Prove that P = NP."

def run_agent_no_guard(task):
client = openai.OpenAI()
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": task}
]

turn = 1
while True:
print(f"\n--- Turn {turn} ---")
response = client.chat.completions.create(
model="gpt-4o",
messages=messages
)
reply = response.choices[0].message.content
print(reply)

if "FINAL_ANSWER:" in reply:
return reply.split("FINAL_ANSWER:")[1].strip()

messages.append({"role": "assistant", "content": reply})
messages.append({"role": "user", "content": "Continue."})
turn += 1

Run this and you will watch your API budget evaporate. The agent will reason, backtrack, try a new approach, hit a wall, try again, and never stop. It cannot prove P = NP. Nobody can. But the agent does not know that. It just keeps looping.

--- Turn 1 ---
Proving P = NP is one of the most significant open problems in computer science.
Let me approach this systematically...

[500 words of reasoning]

This is a complex problem that requires further analysis.

--- Turn 2 ---
Continuing the analysis. Let me consider the implications of P = NP...

[500 more words]

I need to explore this from additional angles.

--- Turn 3 ---
Let me try a different approach. Consider the Cook-Levin theorem...

[and on, and on, forever]

The Fix

Three things need to change:

1. Always have a max-turns guard. This is non-negotiable. Every agent loop you ever write must have a hard stop.

for turn in range(1, max_turns + 1):
# ... loop body ...

return "Agent did not finish within turn limit."

2. Improve the termination condition. String-matching on FINAL_ANSWER: is fragile. What if the model writes "FINAL_ANSWER:" in the middle of its reasoning? What if it uses a slightly different format? Add a more robust check:

def extract_final_answer(reply):
"""Extract final answer from agent response."""
markers = ["FINAL_ANSWER:", "FINAL ANSWER:", "ANSWER:"]
for marker in markers:
if marker in reply:
return reply.split(marker, 1)[1].strip()
return None

3. Add a "stuck" detector. If the agent repeats itself verbatim, it is stuck. Break the loop:

def is_stuck(reply, history, threshold=0.9):
"""Check if agent is repeating itself."""
if len(history) < 2:
return False
# Simple check: is this reply nearly identical to the last one?
last_reply = history[-1]
if len(reply) == len(last_reply) and reply == last_reply:
return True
return False

With these three fixes, the agent is harder to break. Not unbreakable -- nothing is -- but resilient enough for real use.

Section 4: Adding Structure to the Loop

The raw loop works, but it is fragile. The agent's output is free-form text. You are parsing it with string matching. Every edge case is a potential failure. Let us add structure.

Structured Actions

Instead of hoping the agent formats its output correctly, define an explicit format. The agent must output structured JSON:

STRUCTURED_PROMPT = """You are an autonomous agent. You operate in a loop:
OBSERVE -> THINK -> ACT.

You MUST respond in this exact JSON format on every turn:
{
"thought": "Your reasoning about what to do next",
"action": "continue" | "finish",
"answer": "Your final answer (only when action is 'finish')"
}

Rules:
- If you need more turns to think, set action to "continue" and leave answer empty.
- If you have the final answer, set action to "finish" and put the answer in the answer field.
- Always include your reasoning in the thought field."""

Now the agent's output is machine-readable. You can parse it, validate it, and act on it programmatically:

import json

def run_structured_agent(task, max_turns=10):
client = openai.OpenAI()
messages = [
{"role": "system", "content": STRUCTURED_PROMPT},
{"role": "user", "content": task}
]

for turn in range(1, max_turns + 1):
print(f"\n--- Turn {turn} ---")
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
response_format={"type": "json_object"}
)
reply = response.choices[0].message.content

try:
parsed = json.loads(reply)
except json.JSONDecodeError:
print(f"WARNING: Could not parse agent output: {reply[:100]}...")
messages.append({"role": "assistant", "content": reply})
messages.append({"role": "user",
"content": "Your response was not valid JSON. Please use the required format."})
continue

thought = parsed.get("thought", "")
action = parsed.get("action", "")
answer = parsed.get("answer", "")

print(f"THOUGHT: {thought}")
print(f"ACTION: {action}")

if action == "finish":
print(f"ANSWER: {answer}")
return answer

if action == "continue":
messages.append({"role": "assistant", "content": reply})
messages.append({"role": "user",
"content": "Continue. What is your next step?"})
continue

# Unknown action -- treat as error
messages.append({"role": "assistant", "content": reply})
messages.append({"role": "user",
"content": f"Unknown action '{action}'. Use 'continue' or 'finish'."})

return "Agent did not finish within turn limit."

This is the improved agent. It is about 80 lines. It has:

  • Structured output: JSON with explicit thought, action, and answer fields.
  • JSON mode: The response_format={"type": "json_object"} parameter tells the API to enforce valid JSON output.
  • Error handling: If the JSON does not parse, the agent gets a corrective prompt instead of crashing.
  • Action validation: Unknown actions trigger a helpful error message.
  • Turn limit: Still present. Still non-negotiable.

Why Structure Matters

The structured format gives you three things you did not have before:

Predictability. You know exactly what fields to expect. No more guessing whether the agent is done or just thinking out loud.

Debuggability. When something goes wrong, you can look at the thought field and see exactly what the agent was thinking. The raw loop gave you a wall of text. The structured loop gives you a clean trace.

Composability. In later chapters, you will chain agents together. One agent's output becomes another agent's input. Structured data makes this possible. Free-form text makes it a nightmare.

Here is the structured agent running the dinner-planning task:

--- Turn 1 ---
THOUGHT: I need to plan a healthy dinner for four under $30. Let me start by choosing a main dish and estimating costs.
ACTION: continue

--- Turn 2 ---
THOUGHT: I've chosen lentil curry with rice as the main dish. Estimated cost is $8.70. I have room for sides and dessert. Let me add a salad and baked apples.
ACTION: continue

--- Turn 3 ---
THOUGHT: I have the full meal plan with costs totaling $18.40, well under budget. I can now write the complete recipe and ingredient list.
ACTION: finish
ANSWER: Dinner: Lentil and Vegetable Curry with Rice...

Clean. Parseable. Debuggable. This is the pattern you will use for the rest of the book.

Section 5: The Agent's First Real Task

The agent has solved math problems and planned dinners. Now give it something that demands genuine multi-step reasoning. The agent has no tools yet -- it cannot search the web or query a database -- so it must work entirely from its training data. That is the constraint. Let us see how far it gets.

The Task

"I need to plan a 3-day trip to Tokyo. Figure out: (1) the best time of year to visit, (2) three must-see attractions, (3) a sample 3-day itinerary."

The Trace

--- Turn 1 ---
THOUGHT: The user wants a 3-day Tokyo trip plan with three components. Let me start with the best time to visit. Tokyo's best seasons are spring (March-May) for cherry blossoms and autumn (October-November) for pleasant weather and fall colors. I'll recommend spring as the top choice and explain why. Then I'll move to attractions.
ACTION: continue

--- Turn 2 ---
THOUGHT: I've covered the best time to visit. Now for three must-see attractions. I'll choose a mix of traditional, modern, and cultural: (1) Senso-ji Temple in Asakusa -- Tokyo's oldest temple, (2) Shibuya Crossing and the surrounding district -- the pulse of modern Tokyo, (3) Meiji Shrine and Yoyogi Park -- a serene forest in the heart of the city. Each offers a different facet of Tokyo.
ACTION: continue

--- Turn 3 ---
THOUGHT: I have the best time (spring) and three attractions. Now I need to build a 3-day itinerary that weaves them together logically, with additional stops to fill each day. Day 1: Asakusa and Ueno. Day 2: Shibuya, Harajuku, Meiji Shrine. Day 3: Tsukiji, Ginza, and a flex option. Let me write the full itinerary.
ACTION: finish
ANSWER:
Best time to visit: Spring (late March to early April) for cherry blossoms, or autumn (November) for fall colors and comfortable temperatures. Avoid summer (June-August) -- it is hot, humid, and crowded.

Three must-see attractions:
1. Senso-ji Temple (Asakusa) -- Tokyo's oldest Buddhist temple, with the Nakamise shopping street leading to it. Go early to beat the crowds.
2. Shibuya Crossing and Shibuya Sky -- The iconic scramble crossing, best viewed from the Shibuya Sky observation deck. Surrounding area has incredible food and nightlife.
3. Meiji Shrine and Yoyogi Park -- A massive forested shrine complex in the middle of the city. Walk from Harajuku through the torii gates into total tranquility.

Sample 3-day itinerary:
Day 1 -- East Tokyo / Traditional:
- Morning: Senso-ji Temple and Nakamise-dori (arrive by 8 AM)
- Late morning: Walk along Sumida River to Tokyo Skytree
- Afternoon: Ueno Park and Tokyo National Museum
- Evening: Dinner in Asakusa (try monjayaki, Tokyo's local savory pancake)

Day 2 -- Central Tokyo / Modern:
- Morning: Meiji Shrine and Yoyogi Park (arrive early for quiet)
- Late morning: Harajuku -- Takeshita Street for quirky shops and crepes
- Afternoon: Shibuya -- the crossing, Shibuya Sky at sunset, explore Center-gai
- Evening: Dinner and drinks in Shibuya's non-touristy backstreets

Day 3 -- Markets and Departure:
- Morning: Toyosu Fish Market (the new Tsukiji) -- arrive by 6 AM for the tuna auction
- Late morning: Hamarikyu Gardens, a feudal lord's garden with a teahouse
- Afternoon: Ginza for architecture, department store food halls, and last-minute shopping
- Evening: Departure or one final ramen in Tokyo Station's Ramen Street

Three turns. The agent decomposed a complex request into three sub-problems, solved each one, and synthesized a coherent answer. It did not have a planning module. It did not have a task-decomposition algorithm. It just had a loop and a system prompt.

This is the power of the agent loop. The model, given space to think across multiple turns, breaks down complex problems and solves them step by step. A single LLM call would have produced a shallower answer -- probably all three components crammed into one response, with less reasoning and less structure. The loop gives the model room to breathe.

What Just Happened

The agent did not "know" how to plan a Tokyo trip. It reasoned about it. On turn 1, it decided to tackle the "best time" question first. On turn 2, it picked three attractions that balanced traditional, modern, and cultural experiences. On turn 3, it wove them into a geographically logical itinerary.

Each turn built on the previous one. The agent saw its own output from turn 1 in the conversation history and used it as context for turn 2. That is the loop working. The model is not just generating text -- it is building a chain of reasoning, one link at a time.

Section 6: What the Agent Cannot Do (Yet)

Your agent thinks. It reasons. It decomposes problems. But it is also blind, forgetful, and reckless. Here is what it cannot do -- and where the rest of this book is headed.

No Tools

The agent cannot look anything up. Every fact it uses comes from its training data, which has a cutoff date and contains no real-time information. Ask it for today's weather in Tokyo and it will either refuse or hallucinate. Ask it for the current price of a stock and it will give you a number from months ago.

The agent cannot take action in the world. It cannot send an email, query a database, call an API, or move a file. It is a brain with no hands.

Chapter 5 fixes this. You will give the agent tools -- functions it can call to interact with the world.

No Memory

Every run starts fresh. The agent does not remember you from the last conversation. It does not learn from its mistakes. It does not build up knowledge over time. It is Groundhog Day for AI.

Chapter 7 fixes this. You will give the agent persistent memory -- vector databases, knowledge graphs, and long-term storage.

No Planning

The agent reacts turn by turn. It has no long-term strategy. It does not look ahead and say, "This task will take about five turns, and here is my plan for each one." It just asks, "What should I do right now?" on every iteration. For simple tasks, this works. For complex, multi-hour tasks, it leads to thrashing and dead ends.

Chapter 8 fixes this. You will add planning -- explicit decomposition of tasks into sub-goals with dependencies and milestones.

No Safety

The agent will happily give dangerous advice if that advice exists in its training data. It has no concept of harm. It has no refusal mechanism beyond what the base model provides. It will write phishing emails, explain how to synthesize dangerous compounds, and generate hate speech if prompted cleverly enough.

Chapter 10 fixes this. You will add guardrails -- input validation, output filtering, and safety classifiers.

No Self-Correction

The agent can get stuck in a reasoning rut. It can convince itself that a wrong answer is right. It can double down on a mistake across multiple turns. It has no mechanism for stepping back and saying, "Wait, let me reconsider my entire approach."

Chapter 9 fixes this. You will add reflection -- explicit self-critique and revision steps in the loop.

The Roadmap

Every limitation in this section is a chapter in the rest of the book. You are not building a toy. You are building toward a production agent system, one capability at a time. The loop you wrote in this chapter is the skeleton. Everything else is muscle and organs.

The Turn

You just built something that thinks.

Not a chatbot. Not a script. Not a wrapper around an API. An agent -- a system that observes, reasons, decides, and acts in a loop. It breaks down complex problems. It works through them step by step. It knows when it is done.

And you now understand that the "magic" of agentic AI is just a while loop around an LLM call.

That is not a dismissal. It is an empowerment. The loop is simple. The engineering around it -- the prompts, the parsing, the error handling, the structure, the safety -- is where the craft lives. You now have the foundation. Everything else is details, and you will learn every one of them.

Close

Your agent can think. It can break down complex problems and reason through them step by step. It can plan a trip to Tokyo, plan a dinner, and solve math problems -- all from a 50-line Python script.

But it is still trapped in its training data. It cannot check the weather, search the web, or send an email. It cannot remember you from one conversation to the next. It cannot plan more than one turn ahead. It has no guardrails.

In the next chapter, you will give it hands. You will teach it to use tools -- to call APIs, query databases, and interact with the world beyond its training data. The agent will stop being a brain in a jar and start being a system that can do things.

That is where agentic AI gets interesting.


What you built in this chapter:

ComponentWhat It Does
System promptDefines agent behavior and output format
Conversation historyGives the agent memory within a session
Agent loopEnables multi-step reasoning and error recovery
Termination detectionKnows when the task is complete
Structured outputMakes agent responses parseable and debuggable
Turn limitPrevents infinite loops and budget overruns
Error handlingGracefully recovers from malformed output

Key takeaways:

  • The agent loop is OBSERVE -> THINK -> ACT, repeated until done.
  • The loop is the difference between a single LLM call and an agent.
  • Always have a max-turns guard. Always.
  • Structured output (JSON) is more reliable than free-form text.
  • The agent's power comes from seeing its own previous outputs and building on them.
  • Every limitation (no tools, no memory, no planning, no safety) is solvable -- and you will solve each one in the chapters ahead.