Module 6 · AI-native infrastructure

6.3 Agent harnesses: what they are and why the architecture is different

Lesson 6.3 · 10 min read

You have been using an agent for months, possibly without noticing when it stopped being a chatbot.

You asked an AI coding tool to fix a bug, and it read three files, ran the test suite, saw the failure, edited a function, ran the tests again, and reported back. Nobody typed those steps. It decided them, one at a time, based on what it found. That is not a model answering a question. That is a loop, and the loop is the whole subject of this post.

There is a wide gap between a system that calls a model once and a system where a model decides what to do next, repeatedly, taking actions in the world. The second is architecturally different in ways that matter enormously, and if you build one without understanding the difference, it will be slow, expensive, unpredictable, and occasionally destructive.

We are going to build the concept from the bottom, and we are going to talk about mechanism rather than any particular framework. Frameworks in this area are numerous and short-lived. The loop is not.


Start from what a model cannot do

A model takes text in and produces text out. That's the whole of it. It cannot read a file. It cannot search the web. It cannot query your database, send an email, or run a test. It has no hands.

So the first question is: how does an AI coding tool read a file?

The answer is more mundane than people imagine, and understanding it dissolves nearly all the mystery around agents. You tell the model what actions are available, it responds with text saying which one it wants, and your code performs it and hands back the result as more text.

Concretely. Along with the user's question, you send a description of some functions the model may request: read_file(path), run_tests(), search(query). The model, instead of answering, replies with something structured meaning "I want to call read_file with the path src/auth.js." That reply is just text, in an agreed format. Your code parses it, actually opens the file, and sends the contents back to the model as a new message. The model now knows what's in the file, because you told it.

Those advertised functions are tools. And notice what a tool actually is: it's a function in your code, which you wrote, with a description attached so the model knows it exists and what it's for. The model never touches anything. It asks. Your code decides whether and how to comply.

Hold onto that, because it's the safety property that everything else in this post depends on. The model does not have permissions. Your code has permissions, and it chooses which of them to expose as tools. An agent that cannot delete files is one where you did not write a delete tool.


The loop

Now put that in a cycle, and you have an agent.

The model receives the goal and the list of tools. It responds either with a final answer, or with a request to call a tool. If it's a tool call, your code executes it and appends the result to the conversation. Then you call the model again, with everything that has happened so far. It looks at the new information and decides again: another tool, or an answer.

Repeat until it produces an answer, or until you stop it.

That's the entire architecture. An agent is a loop around a model, with tools, and a stopping condition. Everything sold as an agent framework is an implementation of that paragraph with conveniences bolted on.

The word harness describes everything wrapped around the model to make this work: the loop itself, the tool definitions, the code that executes them, the assembly of the conversation on each turn, the limits on how long it can run, the handling of errors, the recording of what happened. The model is one component inside the harness. The harness is the system.

And that reframing matters more than any other sentence in this post. When an agent behaves badly, people say the model is bad. Usually the harness is bad. The tools were poorly described, or the loop had no limit, or an error came back in a form the model couldn't understand, or nothing prevented it from trying the same failing action nine times.

Interactive: agent loop

A hands-on visual for this idea is in the works. The text around it carries everything you need for now.


What makes this architecturally different

Everything you learned in 6.1 about a single model call still applies. Slow, expensive, stateless, non-deterministic, confidently fallible. Now multiply each of those by the number of times around the loop, and add a few new ones.

Cost and latency compound, unpredictably. One call takes four seconds and costs a cent. An agent might loop three times, or thirty. You do not know in advance. And because you resend the entire growing conversation on each turn, the tenth call is far more expensive than the first: everything that happened is being paid for again. The cost of an agent run is roughly quadratic in the number of steps, not linear, and this surprises people when the bill arrives.

The context window fills up. From 6.1: the model remembers nothing, so the harness resends everything. Each tool result adds to the conversation. Read three large files and the window is gone before the work begins. So a real harness must manage what the model sees: summarizing old steps, truncating tool outputs, dropping what's no longer relevant. This is not an optimization. Past a certain length it is the only thing standing between you and a hard failure.

Errors compound. In ordinary code, a wrong value produces a wrong result. In a loop where each step's output becomes the next step's input, a small misunderstanding at step two shapes every decision after it. The agent confidently builds on its own mistake. This is why agents fail in a distinctive way: not with an error, but with a long, coherent, entirely wrong sequence of actions.

And the model takes actions in the world. This is the one that changes the nature of the risk. A wrong sentence is embarrassing. A wrong tool call deletes a table, sends an email to a customer, spends money, or force-pushes over someone's branch. Everything you learned in the security posts about not trusting input applies here, with the model as an untrusted source of instructions, because a plausible-but-wrong action is a real event that no apology reverses.


What a good harness actually provides

So the harness is doing considerably more than looping. Here's what it's responsible for, and each item is a real engineering decision.

Tool design. The description you write for a tool is a prompt, subject to everything in the last post. A vague description produces misuse. Tools should be few, orthogonal, and hard to use wrongly. And they should return errors as informative text the model can act on, because "Error: file not found. Did you mean src/auth.js?" leads to recovery, while a stack trace leads to confusion.

Limits. A maximum number of iterations. A maximum cost. A wall-clock timeout. Without these, an agent that gets stuck will loop until something else breaks, and it will do so while spending money. This is not defensive pessimism; agents get stuck routinely, repeating an action that fails in the same way each time, because nothing told them to stop.

Permission boundaries. Which tools exist at all, and which require a human to approve before they execute. The standard shape is that reading is automatic and writing is confirmed. An agent may search, read files, and run tests freely. It should not delete, deploy, or send without someone saying yes. Where that line falls is the single most consequential decision in the whole system, and it is not a technical decision. It is a decision about how much of a mistake you are willing to absorb.

Context management. Deciding, each turn, what the model gets to see. This is where most of the real engineering in agent systems lives, and it's invisible from outside.

Observability. Recording every step: what the model was sent, what it decided, which tool ran, what came back. Without this, an agent is unfixable, because "it did the wrong thing" is not a debuggable statement. This is important enough that it's the next post but one.


The honest picture of where agents work

There is enormous enthusiasm here, and it is worth being clear-eyed, because the failure modes are structural rather than temporary.

Agents work well when there is a feedback signal. The reason coding agents are the strongest example is not that code is special; it's that code can be run. The agent edits, runs the tests, and gets a genuine, external verdict on whether it succeeded. It is not evaluating its own work with the same faculty that produced it. That feedback loop is what allows it to recover from mistakes rather than compound them.

Agents work poorly when success is unverifiable. Ask an agent to research a topic and write a report, and every step is judged by the same model that wrote it. There is no test to fail. Errors do not surface; they get elaborated.

They work well when actions are reversible or cheap. A wrong file edit is fixed by Git. A wrong email is not fixed by anything.

And they work well when the task is genuinely multi-step and uncertain, which is the honest test of whether you need an agent at all. If you know the sequence of steps in advance, you do not need a model to decide them. Write the steps. Call the model for the parts that need judgment. A great deal of what is built as an agent is a fixed workflow with an expensive, unreliable, slow scheduler inserted where a for loop would do.

That is the most useful design question in this entire area. Does the sequence of steps depend on what is discovered along the way? If no, it's a workflow, and you should build it as one, and it will be faster, cheaper, testable, and correct. If yes, you have an agent, and you should build the harness properly.


Where this leaves you

An agent is not a new kind of intelligence. It is a loop, around a stateless text function, that requests actions your code chooses to perform. The intelligence is real; the architecture is ordinary, and it is your architecture, not the model's.

Which means the quality of an agent product is mostly determined by things that have nothing to do with the model. How well the tools are named and described. How informative the errors are. What the loop is allowed to do without asking. Where the limits sit. What gets shown to the model on turn nine. How the interface represents a machine that is working, uncertainly, on your behalf, and might be wrong.

That last one is yours entirely. An agent that does five things and reports "done" is a different product from one that shows what it's doing as it does it, asks before the irreversible step, and makes it obvious how to intervene. Same model, same loop. Completely different relationship with the person using it.

The people who build good agents are not the ones with a better model. They're the ones who understood that they were building a system, and that the model was a component inside it, doing one job: reading the situation and choosing the next move.


Do this before the next post

Watch an agent work, and read the loop rather than the output.

Use a coding agent on a real, small task, and pay attention to the structure of what happens rather than whether it succeeds. Notice when it reads something before acting: that's a tool call, and its result went back into the conversation. Notice when it runs something and reacts to the failure: that's the feedback signal, and it's why this task suits an agent. Notice how many times it goes around. Count them, and remember that each turn resent everything that came before.

Then find the moment it asks you for permission, and ask yourself who decided that this particular action needed asking. Somebody drew that line. Would you have drawn it in the same place?

Finally, take something you want to build with AI and apply the design question. Write down the steps. If you can write them all down, in order, before running anything, you do not want an agent. You want a workflow with a model call or two inside it, and knowing that will save you weeks and a great deal of money.

If you genuinely cannot know step three until you see the result of step two, then you want an agent, and now you know what you are actually building: a loop, some tools, a stopping condition, and a great many decisions about what it may do without asking.