AI (Artificial Intelligence) · Lesson 13 of 20
Agentic AI: From Chatbots to Agents
What an agent really is, the ReAct loop with a runnable demo, workflows vs agents, patterns and failure modes.
- Intermediate
- 30 min read
- 3 objectives
Before this lessonLesson 12: Tool Calling and Structured Output
What you will learn
- Define agentic AI precisely
- Trace a ReAct loop
- Choose between a workflow and an agent
Your Progress
0 of 20 lessons 0%
- Lessons0 / 20
- Completed0
- Est. time left~ 9 hours
Create a free account to keep your progress on every device.
"Agentic AI" is the hottest phrase in the industry and also one of the most abused. Vendors slap it on anything with a chat box. Let us define it properly, watch a real agent loop run step by step, and learn the single most valuable skill in this whole area: knowing when not to build an agent.
A precise definition
An agent is an LLM that decides its own next step, in a loop, using tools, until a goal is reached. Three ingredients: a goal, tools to act on the world, and a loop in which the model chooses what to do based on what it has observed so far.
The key word is decides. In a normal program you write the steps. In an agent, the model controls the control flow.
The autonomy spectrum
Less autonomy on the left, more on the right. Use the simplest thing that works.
- Workflow: LLM calls and tools orchestrated by predefined code paths. Predictable, cheap, testable.
- Agent: the LLM directs its own process. Flexible for open-ended problems where you cannot list the steps in advance, but slower, costlier and harder to control.
The ReAct loop: reason, act, observe
The classic agent pattern is ReAct (Reasoning + Acting). On each turn the model writes a short thought, picks an action (a tool call), you run it, and the observation is fed back. It repeats until the model decides it can give a final answer.
Here it is as running code. The "model" is scripted (a lookup of what a good model would decide) so the demo is deterministic, but the loop, the tool dispatch, the observations and the stopping conditions are exactly what a real agent has:
import re
# ---------- tools ----------
FACTS = {"population of norway": "5.5 million", "population of iceland": "0.38 million"}
def search(query):
return FACTS.get(query.lower(), "no results")
def calculator(expression):
if not re.fullmatch(r"[0-9.\s+\-*/()]+", expression): # never eval arbitrary text!
return "error: only arithmetic allowed"
return str(round(eval(expression), 2))
TOOLS = {"search": search, "calculator": calculator}
# ---------- a scripted stand-in for the LLM's decisions ----------
def decide(goal, history):
"""Returns (thought, action, argument) or (thought, 'final', answer)."""
seen = {a: obs for a, arg, obs in history}
got = [obs for a, arg, obs in history]
if len(history) == 0:
return "I need Norway's population first.", "search", "population of Norway"
if len(history) == 1:
return "Now I need Iceland's population.", "search", "population of Iceland"
if len(history) == 2:
a = float(got[0].split()[0]); b = float(got[1].split()[0])
return "Divide the two numbers with the calculator.", "calculator", f"{a} / {b}"
return "I have everything I need.", "final", f"Norway has about {got[2]} times as many people as Iceland."
# ---------- the agent loop ----------
def run_agent(goal, max_steps=6):
history = [] # (action, argument, observation)
for step in range(1, max_steps + 1):
thought, action, arg = decide(goal, history)
print(f"step {step}")
print(f" thought : {thought}")
if action == "final":
print(f" FINAL : {arg}")
return arg
observation = TOOLS[action](arg)
print(f" action : {action}({arg!r})")
print(f" observation: {observation}")
history.append((action, arg, observation))
return "stopped: step limit reached"
run_agent("How many times bigger is Norway's population than Iceland's?")step 1
thought : I need Norway's population first.
action : search('population of Norway')
observation: 5.5 million
step 2
thought : Now I need Iceland's population.
action : search('population of Iceland')
observation: 0.38 million
step 3
thought : Divide the two numbers with the calculator.
action : calculator('5.5 / 0.38')
observation: 14.47
step 4
thought : I have everything I need.
FINAL : Norway has about 14.47 times as many people as Iceland.Every real agent, from a coding assistant to a research bot, is this loop with a smarter decide() (an LLM call) and more tools. Notice the two guardrails already in the code: max_steps (so it cannot run forever) and the calculator refusing anything that is not arithmetic.
Five workflow patterns you should know
Before reaching for a full agent, these composable patterns solve most problems with far more control:
- 1. Prompt chaining. Break a task into a fixed sequence: draft, then critique, then rewrite. Each LLM call handles one easy step; you can insert checks between steps.
- 2. Routing. Classify the input first, then send it down the right path: refund question, tech support, or sales; easy questions to a cheap model, hard ones to a big model.
- 3. Parallelisation. Run independent subtasks at once (check one document against five criteria simultaneously) or vote (run three times, take the majority).
- 4. Orchestrator-workers. A central LLM breaks a task into subtasks it did not know in advance and delegates them to worker LLMs, then merges results. (Great for coding across many files.)
- 5. Evaluator-optimiser. One LLM produces, another critiques, repeat until the critic is satisfied.
What agents are genuinely good at
- Coding: reading a repo, editing files, running tests, fixing failures. Ideal, because there is a verifiable signal (do the tests pass?).
- Research: search, read, follow leads, synthesise.
- Customer support with tools (look up order, issue refund) plus human escalation.
- Data tasks: explore a database, write queries, fix them when they error.
- Computer/browser use: operating software through screenshots and clicks.
The common thread: tasks with clear success criteria and tools that give feedback. The agent can tell when it is wrong and try again.
How agents fail
- Compounding errors. If each step is 95% reliable, ten steps are only 60% reliable (0.9510). Long chains decay fast.
- Infinite or wasteful loops. Retrying the same failing action forever. Fix with step limits, budgets and loop detection.
- Context bloat. Every observation is appended, the prompt grows, cost climbs, and quality drops ("lost in the middle").
- Tool misuse. Wrong tool, wrong arguments, or a destructive action taken too eagerly.
- Goal drift and over-eagerness. Doing more than asked.
- Prompt injection. A web page the agent reads says "ignore your instructions and..." (safety lesson).
- Cost and latency. Ten LLM calls per task adds up. Measure it.
Let us put a number on that first bullet, because it changes how you design systems:
for per_step in (0.99, 0.95, 0.90):
row = " ".join(f"{n:>2} steps: {per_step ** n:5.0%}" for n in (1, 5, 10, 20))
print(f"each step {per_step:.0%} reliable -> {row}")each step 99% reliable -> 1 steps: 99% 5 steps: 95% 10 steps: 90% 20 steps: 82% each step 95% reliable -> 1 steps: 95% 5 steps: 77% 10 steps: 60% 20 steps: 36% each step 90% reliable -> 1 steps: 90% 5 steps: 59% 10 steps: 35% 20 steps: 12%
A step that is right 95% of the time sounds great until you chain twenty of them (36%). This is why good agent design keeps loops short, adds verification steps, and lets humans approve risky moves.
Autonomy dials: keep a human in the loop
- Read-only agents (research, analysis) can run freely.
- Write-capable agents should propose changes and wait for approval (a diff to review, a draft to send).
- Irreversible actions (payments, deletions, external emails) need explicit confirmation.
- Escalation: when confidence is low or the request is unusual, hand off to a person with the context attached.
So, should you build an agent?
# Write your solution here
