What an AI Harness Is

The model is the smallest part of an AI product. Everything that makes it useful sits around it, and that part has a name.

When developers compare AI coding tools they argue about models. Which one writes better C#, which one hallucinates less, which one is cheaper per million tokens. Then two products built on the same model behave nothing alike: one edits your repository and runs your tests, the other prints a code block and waits. The model is identical. The difference is the harness, and the harness is where most of the engineering lives.

The Term A model turns text into text, and nothing else

Strip away the product and a large language model is one function: context in, next piece of text out. It has no file system, no terminal, no network, no memory between calls, no idea how much it costs. It cannot open a file, run a build, call an API or remember what it did five minutes ago. Everything it appears to do, something else does for it.

That something else is the harness: the runtime that wraps the model, hands it tools, feeds it the results, keeps the history, and decides when to stop. If the model is the engine, the harness is the chassis, the brakes, the gearbox and the dashboard. An engine on a bench makes noise. A car goes somewhere.

The word comes from testing. A test harness is the fixture that runs code under controlled conditions and records what happened. An AI harness does the same job for a model, in production instead of in a test run.

The Loop Think, call a tool, check the result

A developer gives a task to the harness. The harness sends context to the model over HTTPS and gets the next step back, runs tool calls against the file system, shell and tests, feeds results back into the context, and asks the developer before risky actions.
The agent loop. The model only ever sees text and only ever returns text. The harness does everything with hands.

An agent harness is a loop. It sends the current context to the model and asks for the next step. The model answers with text or with a request to call a tool: read this file, run this command, fetch this URL. The harness executes the call, appends the result to the context, and asks again. It stops when the model says it is done or when a guardrail says enough.


// The whole agent, minus the details. The model never leaves this loop on its own.
public async Task<string> RunAsync(string task, CancellationToken ct)
{
    var context = new Context(systemPrompt, task);
    var budget = new Budget(maxTokens: 200_000, maxToolCalls: 50);

    while (true)
    {
        var step = await model.NextStepAsync(context, ct);        // text in, text out, nothing else
        context.Add(step);

        if (step.ToolCall is null)
            return step.Text;                                      // the model is done

        if (!permissions.Allow(step.ToolCall))
            context.Add(await AskDeveloperAsync(step.ToolCall, ct)); // guardrail: a human decides
        else
            context.Add(await tools.RunAsync(step.ToolCall, ct));   // the harness touches the world

        budget.Charge(step);                                       // guardrail: stop before the bill does
        if (context.IsNearLimit) context = context.Compact();      // state: keep the history usable
    }
}

    

Every line that is not the call to the model is the harness. Tool execution, permission checks, budgets, compaction of a context that has grown past what the model can hold. None of it is machine learning. All of it is ordinary software engineering, and it decides whether the product works.

The Guardrails What the model cannot be trusted with

A model will happily propose rm -rf on the wrong directory, loop forever on a failing test, or spend a month of tokens in an afternoon. It is not malicious. It has no notion of consequence, because consequences happen outside the text. The harness owns them.

Concern Model Harness
Reading and editing files Cannot File system tool, scoped to the repository
Running a build or the tests Cannot Shell tool, with a timeout
Remembering step 3 at step 40 Only what still fits in the context History, compaction, memory files
Deleting a database Will suggest it if it looks like a fix Permissions, confirmation before destructive actions
Knowing when to stop No Stop conditions, retry limits
Cost No idea Token and tool call budgets

This is why two products on the same model feel so different. One harness lets the model run the tests and read the failure, the other does not. One asks before it pushes to a branch, the other never pushes at all. Same engine, different car.

The Other Harness Measuring instead of running

The same word covers a second thing. An evaluation harness does not ship features, it grades models. It serves a fixed set of tasks, hundreds or thousands of them, runs each model through the same agent loop with the same tools, and records what came back: pass rate, tokens spent, wall time. SWE-bench, where the task is a real GitHub issue and the pass criterion is the project's own tests, is the best known one for coding.

The reason it needs to be a harness and not a script is fairness. The tasks, the tools and the budget have to be identical for every model, the model must not see the answers, and the run has to be repeatable. When a vendor publishes a benchmark number, the harness is what makes that number mean anything, and a different harness will produce a different number for the same model.

The Rule Compare harnesses, not models

The model is a commodity you rent. The harness is the product you build. When an AI tool works for you, thank the loop, the tools and the guardrails around the model, because that is what you are actually using.

For a .NET team this is good news. Building a harness is building software: a loop, a tool interface, a permission model, budgets, logging. Swap the model behind it when a better one ships. The part that knows your repository, your tests and your rules stays, and that part is yours.


More posts
← All posts  ·  RSS © 2026 DotNET Leet