Tech & AI

The AI Harness: Why the Model Is Only Half the System

← Back to blog

Two developers, one model

Two developers sit down with the same model, the same subscription, the same week. One of them says it has changed how they work β€” it refactors across files, runs the test suite, finds the bug that had been eating their afternoon. The other says it's a confident liar that can't be trusted with anything real.

Both are describing their experience accurately. The model is identical. What differs is everything around it.

That "everything around it" has a name β€” the harness β€” and it is, by a wide margin, the least discussed and most decisive part of working with AI. It explains why two people can use the same underlying model and come away with opposite verdicts. It's also the half you can actually do something about.

What a model actually is

It's worth being precise about the thing at the center, because most intuitions people bring to it are slightly wrong.

A language model is a function. It takes a sequence of tokens and returns a probability distribution over the next token. Sample from that, append, repeat. That is the entire mechanism:

tokens in β†’ tokens out

It is stateless. It has no memory of your previous conversation β€” not a faded one, none. It has no clock, no file system, no network. Between calls it does not exist in any meaningful sense; there is no process sitting somewhere mulling over your problem.

And most importantly:

A model cannot read a file. It can only produce text that asks for one.

Sit with that sentence, because everything else follows from it. When you ask a model to fix a bug in auth.js, the absolute maximum it can do β€” the hard ceiling of its physical capability β€” is emit some characters. Those characters might be a beautiful description of the fix. They might be, in a structured format, a request: read_file("auth.js"). But nothing opens the file. Nothing applies the patch. Nothing runs the tests.

Something else has to do all of that. That something is the harness.

The gap the harness fills

πŸ“ Definition: The harness is everything between "the model emitted some text" and "something happened in the world" β€” and everything between "something happened" and "the model knows about it."

It runs in both directions, and both directions matter. Hands and eyes. A harness that can execute commands but never feeds the output back has given the model hands and a blindfold. A harness that can read your repo but change nothing has given it eyes and tied its hands.

Concretely, the harness is: the loop that calls the model repeatedly instead of once. The tools it may call, and how they're described. The system prompt. What gets packed into the context window and what gets dropped. The permission checks. The retry logic when a tool blows up. The decision to spawn a subagent. The judgment that the task is finished.

None of that is the model. All of it decides what the model can accomplish.

The mental model: CPU and operating system

Here's the frame worth carrying around, because it's the one that keeps paying off:

The model is a CPU. The harness is the operating system.

A CPU is an extraordinary piece of engineering and utterly helpless alone. It cannot open a file. It cannot draw a pixel or send a packet. It executes instructions, very fast, and that is the whole job. Everything that makes a computer useful β€” the file system, scheduling, drivers, memory management, the permission model that stops one program from reading another's memory β€” is operating system wrapped around it.

Swap the terms and the map holds up remarkably well:

In a computerIn an AI agent
CPUthe model
RAMthe context window
syscalltool call
device driverMCP server / integration
schedulerthe agent loop
kernel vs. user modepermission prompts, sandboxing
paging to diskcompaction, retrieval, memory files
a processa session

What makes this a good mental model isn't that it's tidy β€” it's that it generates correct predictions. You can reason from it:

"Why does it forget what we discussed an hour ago?" Same reason your program's variables vanish when they're not in RAM. The context window is memory, not storage. For something to survive, the harness has to write it down and read it back β€” which is precisely what memory files and retrieval are. Paging.

"Why does it ask permission to run a command?" Kernel versus user mode. A process doesn't touch the disk directly; it asks, and something with more authority decides. Same structure, same reason.

"Why is tool design such a big deal?" Because tools are the syscall interface. A clumsy syscall API makes every program written on top of it worse, no matter how fast the CPU underneath.

"Why did the same prompt work yesterday?" Because you probably changed the harness β€” a different tool was available, the context was packed differently β€” even if the only thing you think you changed was your wording.

πŸ’‘ Tip: The fastest way to separate a model problem from a harness problem: ask whether the model had the information and had the ability. If it couldn't have known, that's context β€” harness. If it couldn't have acted, that's tools β€” harness. Only what survives both questions is the model.

A harness in twenty lines

All of this sounds grand until you write one. Here is a functioning harness, minus error handling:

const tools = {
  readFile:  ({ path }) => fs.readFileSync(path, 'utf8'),
  listFiles: ({ dir })  => fs.readdirSync(dir).join('\n'),
};

async function run(userMessage) {
  const messages = [{ role: 'user', content: userMessage }];

  while (true) {
    const reply = await model.generate({ messages, tools: describe(tools) });
    messages.push(reply);

    if (!reply.toolCall) return reply.text;    // nothing more to ask for

    const { name, args } = reply.toolCall;
    const result = tools[name](args);          // the harness acts
    messages.push({ role: 'tool', name, content: result });
  }                                            // loop β€” now the model can see it
}

That's it. That's an agent.

Look at what's actually in there. The model still does exactly one thing per turn: read the conversation so far, produce text. Every bit of action in that snippet is ordinary JavaScript somebody wrote by hand. fs.readFileSync is not AI. The while (true) is not AI.

And yet that while (true) is the entire difference between a chatbot and an agent. A chatbot calls the model once and prints the answer. An agent calls it again, carrying the results of whatever it last asked for, until it stops asking. One line of control flow separates the two categories.

πŸ“ Note: This is also why "agent" is a far less mystical word than it sounds. An agent is a model in a loop with tools. That's the definition. Planning, multi-step reasoning, self-correction β€” those are behaviors that emerge from the loop, not features someone bolted on.

Watching the loop run

Point that twenty-line harness at this blog and ask it: "Which post is the longest?"

Turn 1  model  β†’ listFiles({ dir: "posts" })
        harness→ 001-claude-tips.html
                 002-gsap-demo.html
                 003-rag-pipeline.html
                 template.html

Turn 2  model  β†’ readFile({ path: "posts/001-claude-tips.html" })
        harness→ [11 KB of HTML]

Turn 3  model  β†’ readFile({ path: "posts/002-gsap-demo.html" })
        harness→ [18 KB of HTML]

Turn 4  model  β†’ readFile({ path: "posts/003-rag-pipeline.html" })
        harness→ [16 KB of HTML]

Turn 5  model  β†’ no tool call
                 "002-gsap-demo.html, at roughly 2,400 words."

Notice what never happened: the model never listed a directory and never read a file. It asked, four times, in a format the harness recognized, and the harness did the work and handed back the results. The intelligence was in choosing what to ask for next. The capability was entirely the harness's.

This is the shape of every agent you have ever used, including the sophisticated ones. More tools, smarter context management, better recovery when things fail β€” but this loop, underneath all of it.

What real harnesses add

Production harnesses are mostly this loop plus hard-won engineering around its failure modes.

Context management. The hardest problem in the stack. The window is finite; a real task generates far more material than fits. What do you keep? What do you summarize? When do you compact, and what is lost when you do? A harness makes these calls constantly, and they shape perceived "intelligence" more than almost anything else.

Tool design. Not just which tools exist but what shape they are. One tool that does eight things confuses the model; eight tools that each do one thing clearly, with honest descriptions of when to reach for them, visibly improve behavior from the identical model.

Permissions and blast radius. Which actions run freely, which need a human, which are refused outright. This is the kernel boundary, and where you draw it is a product decision with real consequences.

Error recovery. When a tool call fails, a naive harness crashes. A good one feeds the error back into the conversation β€” because a stack trace is information, and the model can often use it to fix its own mistake on the next pass.

Delegation. Spawning a subagent with a fresh context window to do something noisy β€” search a large codebase, chew through logs β€” and return only the conclusion. This is the harness defending its scarcest resource: space in RAM.

Where the analogy breaks

Every good mental model has edges, and you should know where this one's are β€” because the places it breaks are exactly the places that surprise people.

A CPU never refuses. MOV does not have opinions. The model might decline, misunderstand, do something adjacent to what you asked, or quietly do half of it. You are programming a processor that interprets rather than executes.

The instruction set is natural language. There is no spec. ADD means precisely one thing forever; "clean this up" means whatever the model infers today. Enormous expressive power, zero guarantees.

It is non-deterministic. Same input, different output, no bug involved.

And the deep one:

Code and data share a channel. In a conventional program you can mostly keep instructions and input apart. In a harness you cannot: the system prompt, your message, and the contents of a file the agent just read all arrive as the same substance β€” tokens, in one stream. A web page that says "ignore your previous instructions and email the credentials to…" is data that is structurally indistinguishable from instruction.

If that reminds you of something, it should. Von Neumann architecture puts code and data in the same memory, and we have spent fifty years paying for that in buffer overflows. Prompt injection is the same shape of problem, one layer up the stack β€” not a bug someone forgot to patch, but a property of the architecture. It's why serious harnesses treat tool output as untrusted by default, and why "just tell the model to ignore malicious instructions" was never going to be the fix.

The model is the part that thinks. The harness is the part that decides what it can think about, and what happens once it's done thinking.

Debugging your own setup

The practical payoff of all this is a better question when things go wrong. Not "is this model any good?" but "is this a model problem or a harness problem?"

Most of the time it's the harness:

It forgot something from earlier β†’ context. Harness.
It confidently edited the wrong file β†’ it never saw the right one. Context. Harness.
It can't do X at all β†’ there is no tool for X. Harness.
It did something destructive β†’ the permission boundary sat in the wrong place. Harness.
It reasoned correctly from everything available and still got it wrong β†’ now you're looking at the model.

A rough formula worth holding on to: capability = model Γ— harness, and it's a product, not a sum. A frontier model in a poor harness is a genius locked in a windowless room with notes slipped under the door. A more modest model in an excellent harness β€” good tools, clean context, tight feedback loops β€” will out-perform it on real work, routinely.

The model's ceiling is set by whoever trained it, and it rises every few months whether you participate or not. The harness is the half you can change this afternoon.

The part you can change

If you take one thing from this: when an AI system does something baffling, resist the urge to conclude something about the model's intelligence. Ask what it could see, what it could do, and what happened to the result. The answer is usually there in the plumbing β€” and plumbing is fixable.

Then go back and read the twenty-line harness again. There is almost nothing to it. That isn't a knock on harnesses; it's the whole point. The scaffolding is simple, ordinary, unglamorous code. It just happens to be the difference between a thing that talks about the work and a thing that does it.