Chapter 4 · The Core Agent Loop

Every agent runs
on one loop.

Perceive → think → act → observe — the humble cycle every agent, simple or complex, is built on.

The engine

One loop, turning until the task is done.

No matter how fancy the agent, underneath it's a cycle that keeps spinning: perceive → think → act → observe, over and over.

Perceive take in input Think what's next? Act call a tool Observe read result THE LOOP

The four steps

Perceive, think, act, observe.

Perceive

Take in input — your instruction, a tool's result, or an error. Whatever just landed in context.

Think

The LLM reads everything in context and reasons out the smartest next move.

Act

Call a tool… or decide it's done and answer. One decision, one action.

Observe

Read the result of that action, update its understanding — then loop back to Think.

It keeps looping until the task is done, it hits a max number of steps, or something breaks.

The pattern

ReAct: think out loud before every move.

ReAct = reasoning + acting. Before every action, the agent explicitly writes down its thinking — like forcing yourself to explain your reasoning before you touch anything.

No impulse

Stops random, impulsive tool calls before they happen.

Paper trail

Leaves a written record of the logic behind each step.

Easy to debug

When it fails, you can see exactly where the reasoning went wrong.

A worked trace

Should I bring an umbrella in Tokyo?

Thought — I don't know live weather. I'll use the weather tool for Tokyo.
Action — get_weather("Tokyo")
Observation — 18°C, light rain, humidity 82%
Thought — It's raining, so the user should take an umbrella.
Final Answer — It's 18°C with light rain in Tokyo. Yes — bring an umbrella. ☔

Thought → Action → Observation → (repeat) → Answer. Transparent, logical, debuggable.

In real code

The whole loop is about twelve lines.


tools = {"get_weather": get_weather, "web_search": web_search}
messages = [{"role": "user", "content": goal}]   # PERCEIVE

for step in range(MAX_STEPS):                     # safety belt
    reply = llm(messages, tools=tools)            # THINK — model reasons

    if reply.tool_call:                           # ACT — it wants a tool
        fn  = tools[reply.tool_call.name]
        out = fn(**reply.tool_call.args)
        messages.append(reply)
        messages.append({"role": "tool", "content": out})  # OBSERVE
        continue                                  # loop back and think again

    print(reply.text)                             # no tool call → FINAL ANSWER
    break

MAX_STEPS is your safety belt — without it, one bug becomes thousands of API calls.

Chapter 4 in one breath

Think, act, observe — repeat.

An agent perceives input, thinks about the next move, acts by calling a tool, observes the result, and loops — with ReAct making it reason before each step. It's a dozen lines of code.

Everything else we build just hangs off this loop — starting with the tools it can call.

Next up

Chapter 5 · Tools.

We've got the loop turning. Now we give the agent things to actually do inside it — the tools it reaches for on every Act step.

Press S for speaker notes · F for fullscreen · to advance.