What an Agent Actually Is: Model, Loop, Tools, Context
You've used Claude or ChatGPT. Somewhere between that and the word "agent," people start treating the gap as mysterious, something that takes a research lab to explain. It doesn't. The whole gap fits in one equation, and by the end of this lesson you'll have watched it work in a hundred and twenty lines of Python you could read yourself.
Session 02 opens the part of the semester where you stop reasoning about agents from the outside and start building one. This lesson covers the first block: what a language model is, what it isn't, and the trick, tool use plus a loop, that turns a text-in-text-out function into something that looks like it's taking action in the world.
Start from what a language model is not
Before building anything, get precise about the thing you're building on top of, because almost every mistaken intuition about agents traces back to over-crediting the model underneath them.
A large language model is a stateless function. You give it a sequence of tokens, it returns a plausible continuation. That's the whole contract: text → f(text) → text. You've fit functions before. This is a very large one, trained on a very large amount of text, but the shape of the thing it does is not exotic. Call it twice with the same input and it has no idea it saw you the first time. There is no thread connecting the two calls except whatever you, or the software wrapped around the model, choose to send it again.
That single fact has three consequences worth making explicit, because each one gets casually violated in how people talk about agents.
It has no memory. When a chatbot appears to "remember" your name across a long conversation, what's actually happening is that the application re-sends the entire conversation history, every single turn. The model is amnesiac. The product fakes continuity by re-feeding the past into a function that has none of its own. If you understand nothing else from this lesson, understand that "the AI remembered" is, underneath, "the software resent the transcript."
It has no hands. A language model cannot read your disk, cannot run code, cannot click a button, cannot open a file. It is a function from tokens to tokens. It has never touched a filesystem in its existence, because a stateless function doesn't have the kind of existence that touches things.
It does not learn from your session. Nothing you type today updates any weight inside the model. The weights are frozen at the point training stopped. Whatever gets better over the course of your conversation, it isn't the model.
So when someone tells you an agent "decided" to read a file, pause on that sentence. A stateless text function decided nothing. It can't decide anything, in the sense of taking an action, because it has no way to act. Something else did the deciding and the doing. Hold that suspicion. It resolves in a few sections, once you've met the piece that actually executes anything.
The context window is the whole world
If the model has no memory of its own, then everything it "knows" about your task, right now, this turn, has to live in exactly one place: the text you actually send it. That place has a name, and it is the single most important resource in this course: the context window.
Here's what the model actually receives on a real turn, not a simplified version of it:
- System prompt — who the assistant is, what tools exist, the house rules. This is where a file like CLAUDE.md lands, a detail that matters a great deal once you reach lesson four of this class.
- User message — your actual instruction: "reconcile the invoices against the bank deposits."
- Assistant message — the model's own prior response, including any tool call it made: "I'll start by listing the data directory," followed by a structured request to run
list_files("data/"). - Tool result — pasted back in as though it were a message: the actual file listing, or the actual contents of a CSV.
- And so on: the transcript only grows. Every tool call, every result, every clarifying question, all of it accumulates in the same sequence.
Three properties of that window matter more than anything else you'll learn this class, and all three are consequences of the same fact: it is a sequence of tokens with a hard boundary, not an infinite scratchpad.
It is finite. A couple hundred thousand tokens sounds practically unlimited until you paste three CSVs and forty tool results into it. It fills faster than intuition suggests, especially once an agent starts working autonomously and generating its own tool output.
It is degrading. This is the part that surprises people who assume "more information in the prompt" is strictly good. It isn't. Much the way a regression model gets worse, not better, when you drown it in irrelevant features, a language model's output quality tends to drop as its context window fills with noise. Attention has to spread across everything in the window, useful or not, and a window packed with irrelevant tool output pulls attention away from the parts that actually matter to the current step. Independent long-context benchmarks (the "needle in a haystack" and "lost in the middle" lines of testing that several model labs and outside researchers have published) back this general shape up; treat it as the documented direction of the effect rather than a specific number you can quote for any one model or context length.
It is yours to engineer. Whoever controls what enters that window controls the quality of everything the model produces downstream of it. There is a real name for this job: context engineering. It sounds like a buzzword the first time you hear it, but by the end of this class you'll recognize that half of what Claude Code actually does, CLAUDE.md, skills, subagents, the /compact command, is a context-engineering tool wearing a friendlier name.
"Tool use" is a trick, and a good one
So a stateless function with no hands somehow reads your CSV files. How?
The honest answer removes all the mystery at once: the model never touches anything. What actually happens is a three-step handoff between the model and an ordinary piece of software.
The model emits a request for a tool, formatted as structured text. Not an action, a request. It might say, in its own words, "I'll check the bank export first," and then emit something shaped like this:
{ "type": "tool_use",
"name": "read_file",
"input": { "path": "data/bank_deposits.csv" } }
That's the entirety of the model's contribution to reading a file: a piece of structured text saying, in effect, "I would like read_file called with this path, please." Then it stops. The model does not run read_file. It cannot. It has no hands, remember: it produced text, and the text happens to be shaped like a function call.
A completely ordinary structured message, saying which tool it wants and with what arguments. Nothing executes yet.
A separate, ordinary program, the harness, parses that request and actually runs the function against the real filesystem, the real API, the real database, whatever the tool actually touches. This is a program you, or someone, wrote and can read start to finish. There's nothing inside it that requires a research lab to build. It recognizes "the model asked for read_file," calls the real read_file function, and captures whatever comes back.
The harness pastes its result back into the context window, formatted as though it were a message, and calls the model again. On this new turn, the model's context includes the actual contents of the file, because it's sitting right there in the transcript, and it continues, informed by data it never touched directly.
The model never touches your machine. The harness does, and the harness is just code you can read, inspect, and constrain. This is why safety in an agentic system lives in the harness, not in the model. The harness can refuse a request, ask a human first, log everything that happens, or simply not offer a dangerous tool in the first place. None of that is a property of the language model. All of it is a property of the ordinary program sitting between the model and the world. Later in the course, this becomes its own discipline: harness engineering, choosing and shaping exactly this layer.
One piece is still missing. A single tool call and response is just one exchange. What turns that into something that looks like an agent working autonomously for minutes at a time?
The only equation in this course
A while loop.
That's the missing ingredient, and it is not more complicated than it sounds: call the model; if it requested a tool, execute the tool and append the result to the context; call the model again; repeat until it stops requesting tools and gives you a final answer instead.
That loop, not the model itself, is what produces the appearance of agency, persistence, and autonomous work. The model is exactly as stateless on turn thirty of a long agentic run as it was on turn one. What's different is that the harness keeps calling it, keeps feeding it a growing transcript, and keeps executing whatever it asks for. Put the four pieces together and you get the equation this entire course is built around:
agent = model + loop + tools + context
Memorize this. Not as trivia, as a working diagnostic. When Claude Code impresses you with something that feels almost uncanny, ask which of the four terms actually did the work. Usually it's context: the model had exactly the right information at exactly the right moment. When it fails, ask the same question in reverse. Usually the failure traces to one term breaking down, not a global failure of "the AI." This equation is the debugging framework you'll use for the rest of the semester, on tools far more sophisticated than the one you're about to build.
An agent in a hundred and twenty lines
Theory earns its keep once you've watched it run. Session 02's live class builds an actual working agent from scratch, in roughly a hundred and twenty lines of Python, using nothing more exotic than the Anthropic API, three plain functions acting as tools, and a for-loop capped at twenty turns. If you're working through this course self-paced, the same file, 00-agent-loop/agent.py in the course repository, is yours to open and run. Nothing about the live demo requires a room full of classmates to work.
The file has exactly the shape the last section described. A handful of tools, each one an ordinary Python function paired with a JSON description of its name, its purpose, and the arguments it takes. A loop, for turn in range(20), that calls the model, checks whether it asked for a tool, executes the tool if so, appends the result, and calls the model again. That's the whole program. No hidden machinery.
Point it at a small consulting firm's data directory and ask it something concrete: "How many invoices are in data/crm_invoices.csv, and what's the total amount in MXN? Show your work." Watching the trace stream by is the fastest way to convert the equation from an abstraction into something you actually believe, because you can watch each of its four terms fire in order.
It requests list_files("data/"). It has no idea what files exist until it asks.
Plain Python, nothing more, actually runs the listing and pastes the real directory contents back in.
Now able to see the actual filenames, it requests read_file("data/crm_invoices.csv"). The harness reads the real file and pastes the real rows back in.
It writes a short pandas snippet to sum the amount column and requests run_python(...). The harness executes it and returns the real number, not a guess.
At no point does the model touch the filesystem. At every point, the harness does exactly what was asked, nothing more, and reports back honestly.
The instructive moment comes from breaking it on purpose. Run the same question again, but reference a file that doesn't exist, data/invoices.csv instead of the real data/crm_invoices.csv. The tool call fails. The error message, a plain Python exception, gets pasted into the context exactly like a successful result would have been. And on the next turn, the model reads that error, decides to list the directory to see what's actually there, spots the real filename, and recovers on its own.
This is worth sitting with, because it's the part people most often over-interpret. Nobody wrote a line of code that says "if the filename is wrong, list the directory instead." The recovery happened because the error became more context, and the model, seeing a traceback and a task it hasn't finished, did the same kind of reasoning it would apply to any other piece of unexpected information in its window. It isn't resilience in the sense of the system having a fallback plan. It's the ordinary consequence of feeding a capable text model an accurate description of what went wrong and asking it to keep going.
Watch the running list of messages while any of this happens, and you're looking at the context window filling in real time, turn by turn, exactly as the previous section described it in the abstract. The list only ever grows across a single run. Nothing gets removed. That's the finite, degrading resource from two sections ago, made concrete.
Claude Code, which the rest of this class is about, is this same file, hardened: twenty-plus tools instead of three, a real permission system instead of none, streaming output, the ability to compact a long transcript, and a proper interface around it. But the equation underneath doesn't change. Once you've read and run a hundred and twenty lines that do the whole thing honestly, in front of you, you never again have to wonder what an agent "really" does when it looks like it's thinking. You've read one.
What the loop implies
Four consequences fall directly out of that while-loop, and together they form the operating theory for the rest of this semester. None of them is a separate idea bolted on afterward. Each one follows from the equation itself.
| Consequence | What it means in practice |
|---|---|
| Agency lives in the harness | Everything that looked like the agent "deciding" or "acting" in the demo above was, mechanically, the harness: parsing a request, executing a function, choosing whether to allow it, logging what happened. Safety, permissions, retries, and observability are all properties of that layer, not the model. |
| Everything is context engineering | Since the model's next move is a function of what's currently in its window and nothing else, every technique you'll learn this semester (a well-written prompt, a CLAUDE.md file, a skill, a subagent with its own isolated context) is secretly an answer to the same question: what should be in the window right now, and what shouldn't? |
| Errors are just more context | A traceback pasted into the window is information the model reacts to like any other information, which is why agents feel resilient. It's also why a verifiable feedback signal, a test that actually passes or fails, a control total that either balances or doesn't, is one of the strongest tools you have. |
| Nondeterminism is the price of admission | Run the same prompt twice and you can get two different paths through the loop. This isn't a bug to be engineered away. It's a property of a stochastic system, managed with constraints, checks, and steering rather than wished into determinism. |
Choosing and shaping the harness deliberately, rather than accepting whatever comes by default, is its own real engineering discipline, and it's one of this course's central pillars. The context-engineering consequence turns a scattered list of Claude Code features into one coherent skill, which is where the next three lessons of this class actually live. The errors-as-context consequence is why an agent that can check its own work against something concrete corrects itself, while an agent with no such signal just produces confident, unverified text. And the nondeterminism consequence is why this course studies German Controlling later in the semester: it's a management tradition built specifically around steering a process you can't fully predict in advance, rather than trying to eliminate the unpredictability first.
Where this leaves you
You now have a working, mechanical answer to "what is an agent," built from four ordinary pieces: a stateless function, a loop that calls it repeatedly, a set of tools an external program actually executes, and a context window that both parties read from and write to. None of that required trusting a marketing claim or taking anyone's word for how the black box works, because you watched the box, in a hundred and twenty lines, do exactly what was described.
The next lesson takes this same equation and turns it toward the part of the system you actually control every time you use one of these tools: the text you write. If the model is a pure function of its context, then the portion of that context you author directly is the entire interface you have to it. That's prompting, and it deserves a more serious treatment than the folklore it usually gets.
Reply here and it goes straight to Rod. Same as replying to one of his emails.