DEV Community

Cover image for Harness Engineering - Part 1: The Raw Model Problem
Fikayo Adepoju
Fikayo Adepoju

Posted on

Harness Engineering - Part 1: The Raw Model Problem

Welcome to the Harness Engineering series — a 10-part journey from raw language model to production-ready agentic system. Made by builders. For builders.

Everyone's talking about AI agents. But when you strip away the demos, the hype, and the tweets — what actually is an agent? Where does the "model" end and the "agent" begin? And why do so many prototypes that look magical in a Jupyter notebook fall apart the moment you try to ship them?

Here's the uncomfortable truth: a language model, by itself, cannot do anything at all. It can't touch a file. It can't call an API. It can't remember what you told it yesterday. It can't check its own work. It can't install pandas. What we call "AI agents" are not made of models — they are made of everything wrapped around the model that closes those gaps.

That wrapper has a name. It's called a harness. And harness engineering is the discipline of building that wrapper well.

To help you understand what really goes into one, I've put together a 10-article series that walks straight through every component of a production-grade agentic harness — no framework hand-waving, no vibes-based tutorials. Each article breaks down one component and connects to the next.

What's ahead:

  1. The Raw Model Problem <- You're here
  2. Part 2: Defining the Harness — The Six Components
  3. Part 3: The Control Loop
  4. Part 4: The Tool Layer
  5. Part 5: Context Engineering
  6. Part 6: The Filesystem & Environment
  7. Part 7: The Memory Layer
  8. Part 8: Observability
  9. Part 9: The Harness Architecture
  10. Part 10: Decomposing Claude Code

By the end, you'll have a clear mental model of every component in an agentic harness — what each piece does, why it's there, and how they fit together. Enough to architect your own instead of copying somebody else's stack.

Let's get started.


📚 Want to go deeper than the articles?

While you follow along with this series, I've put together two hands-on resources that go further than any single article can:

Both are optional — the series stands on its own. But if you want the full studio-quality version, that's where it lives.


What Is a Harness?

Raw Model vs Harness

Plain English first. A harness is a support system for a thing — a person, an animal, or an object — that lets it do work it couldn't do on its own. A climber's harness lets a body hang safely off a rope. A horse's harness lets it pull a cart. A wiring harness lets electrical current flow through a car without frying it.

In agentic engineering, the "thing" being harnessed is the language model. And the work it can't do on its own turns out to be… almost everything you'd want an agent to do.

Everything we break down from Part 2 onward is a response to this one limitation. So before we go any further, we need to see the limitation clearly — because if you don't understand what you're working around, you can't tell whether your harness is any good.

What a Raw Model Actually Is

Most developers coming from traditional software engineering carry a mental model of the LLM as a function in a library. You call it, it does something, and it returns a result. Something like:

result = ai.do_thing("summarize this file and save it to disk")
Enter fullscreen mode Exit fullscreen mode

That mental model is wrong. And I don't mean "wrong in some pedantic way" — I mean it's wrong at the level that will silently break every agent you try to build.

Under the hood, a language model is a text transformer. That's it. Its full job description is:

  • Input: a sequence of tokens
  • Output: a probability distribution over the next token
  • Repeat until done.

No side effects. No memory between calls. No access to anything outside the prompt. The intelligence is real — genuinely, remarkably real — but the surface area is limited to "tokens in, tokens out."

The raw model cannot do anything. It can only describe doing things.

Read that sentence again. It's the load-bearing insight for this entire series.

The 5 Gaps

If we take that insight and expand it into concrete deficiencies, we get five gaps that a harness has to close.

# Gap What the raw model cannot do
1 No execution Run code, call APIs, touch files, hit the network
2 No durable state Remember anything between sessions, or across context limits
3 No fresh knowledge Know anything past its training cutoff, or anything private
4 No self-verification Check whether its own output is correct
5 No environment Install dependencies, configure a runtime, set up a workspace

Let's walk through each one.

Gap 1: No Execution

The model cannot read or write a file, run a script, query a database, or make an HTTP request. If the agent needs to act on the world, something other than the model has to do the acting. That "something" is the harness.

Gap 2: No Durable State

Every API call to a language model is stateless on the model's side. What you experience as "conversation memory" in ChatGPT is an illusion — the client is replaying the full message history in every call. Once the context window fills up, or the session ends, the model remembers nothing. Persistence is a harness feature, not a model feature.

Gap 3: No Fresh Knowledge

The model's knowledge is frozen at its training cutoff. It doesn't know:

  • What happened in the world last week
  • Anything about your codebase, your company's docs, or your private data
  • The current version of any library that has shipped updates since it stopped learning

The only way to give it this knowledge is to inject it into the prompt — which means something has to retrieve it first. That's a harness job.

Gap 4: No Self-Verification

The model can generate code, but it cannot run the code to see if it works. It can produce a summary, but it cannot compare the summary to the source to check fidelity. It can claim correctness — often very confidently — but claiming and verifying are different operations, and only one of them is available to the raw model.

The harness is what builds a verification system around the agent's outputs.

Gap 5: No Environment

The model is not sitting inside a Linux box with Python and git installed. It's not sitting anywhere — it's a mathematical function being evaluated on a GPU somewhere in a data center.

If we want the agent to use pandas, something has to install pandas. If we want it to use a browser, something has to give it a browser. Environments are harness-provided.

Demo: The Surface Area Problem

Enough theory. Let's watch a raw model bump into these gaps in the wild.

The plan is simple: we'll give a model the smallest possible task that requires it to reach outside itself — read a file, summarize it, and write the summary to disk — and see what happens.

Setting Up

Create a folder for the project and drop into it:

mkdir harness-project
cd harness-project
Enter fullscreen mode Exit fullscreen mode

Create a Python virtual environment and activate it:

python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
Enter fullscreen mode Exit fullscreen mode

Install the OpenAI Python package:

pip install openai
Enter fullscreen mode Exit fullscreen mode

Now create a file called article.txt at the root of the project and paste the following text into it:

Before Gutenberg's fifteenth-century printing press, books were rare, hand-copied treasures chained to monastery libraries, accessible only to clergy and scholars. That changed irrevocably in Mainz around 1440. By combining movable metal type, oil-based inks, and an adapted screw press, Gutenberg enabled the rapid, uniform production of texts. His famous Bible of 1455 was not merely a beautiful artifact but a declaration: knowledge could now be manufactured. The consequences were explosive. For the first time, information could spread faster than political or religious authorities could suppress it. Within decades, printing shops erupted across Europe, producing millions of books. Humanist ideas, scientific observations, and navigational charts circulated widely. Most critically, the press democratized scripture and criticism; Martin Luther's vernacular pamphlets reached a literate public, igniting the Reformation. Beyond religion, it standardized languages, elevated vernacular literature, and allowed scholars to build upon corrected, shared knowledge—catalyzing the Scientific Revolution. The printing press did not merely make books; it unmoored authority from the few and anchored it in the hands of the many, laying the very groundwork for our modern world of mass media, public education, and individual conscience.
Enter fullscreen mode Exit fullscreen mode

Then create demo.py at the root of the project and add the following code:

from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {
            "role": "user",
            "content": (
                "Read the file article.txt in this directory, "
                "summarize it in 3 bullets, and save the summary "
                "to summary.md."
            ),
        }
    ],
)

print(response.choices[0].message.content)
Enter fullscreen mode Exit fullscreen mode

Look at what this script is asking for. Three concrete actions:

  1. Read a file — a filesystem operation
  2. Summarize it — a text operation, which the model actually can do
  3. Write a file — another filesystem operation

Two out of three are things the model has no way to perform. Watch what it does about that.

Note: This file is deliberately the simplest possible LLM call — one message in, one message out. No tools, no memory, no environment. Everything a harness provides is intentionally absent here, so we can see exactly what a raw model can and cannot do on its own.

Add your OpenAI API key to the environment:

export OPENAI_API_KEY=<your-key-here>
Enter fullscreen mode Exit fullscreen mode

And run the demo:

python demo.py
Enter fullscreen mode Exit fullscreen mode

What Actually Happens

The model responds with something close to this:

I don't have the ability to read files or access directories 
directly. However, I can help you with instructions on how to 
summarize the content of a file. If you provide the text or 
key points from the article, I can assist you in creating a 
summary. Once you have the summary, you can save it to a file 
named summary.md. Would you like to proceed with that?
Enter fullscreen mode Exit fullscreen mode

Note: Exact wording will vary between runs and over time as models are updated — but the structure of the response is the durable part.

Give it credit — this is a well-behaved response. The model:

  • Accurately reports its limitation — it knows it can't access files
  • Offers the capability it does have — summarizing, if you paste the text
  • Routes the I/O back to the user — "you can save it to a file"

Now look at your directory after the call:

ls
# article.txt   demo.py   venv/
Enter fullscreen mode Exit fullscreen mode

The contents are identical to before. No summary.md. The Python script exits with code 0. Everything ran cleanly. And yet…

What This Demonstrates

Nothing happened.

That's the lesson. Not "the model failed" — the model did exactly what it could do, and did it gracefully. But the world outside the prompt is completely unchanged. The directory doesn't care how politely the model declined.

This is the surface area problem in its purest form. The model's only output channel is text. And text can describe action, recommend action, decline action, or claim action — but text is not action. The gap between describing a file write and writing a file has to be closed by something other than the model.

Look at what the model itself suggested we do:

  • "Provide the text" → manual file read
  • "I can assist you in creating a summary" → the model's actual capability
  • "You can save it to a file" → manual file write

That's a workflow with three steps, where the model handles the middle one and a human handles the bookends.

A harness is what automates the bookends so the human doesn't have to be the file system.

Gaps Exposed by This Demo

Three of our five gaps just showed up on stage:

  • No execution — the model couldn't read or write, and correctly said so
  • No environment — there's no filesystem available to it in the first place
  • No durable state — even the summary it offered to help with would vanish the moment the API call ended, unless something outside the model persisted it

Gaps 3 (fresh knowledge) and 4 (self-verification) don't show up here — this task didn't require the model to fetch anything past its training cutoff, and it didn't ask the model to check its own work. Different tasks would surface those gaps just as clearly.

Summary

Let's tighten this down into two definitions to carry into the rest of the series.

The long one:

A harness is every piece of code, configuration, and execution logic that surrounds the model and makes it capable of acting, remembering, learning, verifying, and persisting.

The short one:

Everything that is not the model is the harness.

The harness is what closes the gap between description and action. And starting in Part 2, we're going to break one open — piece by piece, gap by gap — until you can see exactly how a raw model turns into an agent capable of doing the file-summary task your model just apologized for.


Remember that this article is part of a longer 10-part series that walks you through every component of an agentic harness.

Here's the roadmap:

  1. The Raw Model Problem ← You just finished this one.
  2. Part 2: Defining the Harness — The Six ComponentsMove to this one.
  3. Part 3: The Control Loop
  4. Part 4: The Tool Layer
  5. Part 5: Context Engineering
  6. Part 6: The Filesystem & Environment
  7. Part 7: The Memory Layer
  8. Part 8: Observability
  9. Part 9: The Harness Architecture
  10. Part 10: Decomposing Claude Code

See you in the next one.

Happy coding :)

Top comments (0)