fullauto.online

Harnesses

Anatomy of an agent harness

A model is not an agent. The gap between them is six pieces of unglamorous engineering, and almost every agent that fails in production fails in one of them.

Published
10 Aug 2026
Reading
11 min
Class
harnesses

half-life 90dfrom 10 Aug 2026

Ask someone what their agent is built on and you will usually get the name of a model. That answer describes maybe a fifth of the system. The model produces tokens; something else decides what those tokens can touch, what happens when one of them is wrong, and what the model gets to see on the next turn. That something else is the harness, and it is where the engineering actually lives.

The core is embarrassingly simple. Send messages, get back either text or a request to call a tool, run the tool, append the result, repeat until the model stops asking for tools. You can write it in forty lines. People do, on the first afternoon, and it works well enough on a demo that the real work looks optional.

It isn't. Here is what gets added between the demo and the thing you'd leave running unattended, roughly in the order teams discover they need it.

1. The loop, and its stopping conditions

The naive loop runs until the model returns no tool calls. That is one stopping condition and you need several more: a turn cap, a wall-clock budget, a token budget, a repeated-failure detector, and a way for the user to interrupt mid-flight and have the agent keep the work it has already done.

The failure mode that catches everyone is the loop that isn't infinite but is pointless — the agent retries the same broken command eleven times with cosmetic variations. A turn cap eventually stops it, having burned the budget. The better guard is to detect that the last three tool calls were near-identical and their results were near-identical, and inject that observation into the context. Models are good at breaking out of a rut once someone points out they're in one. They are bad at noticing unprompted.

2. Tools, and their descriptions

Tool definitions are prompt. This is the single most under-appreciated fact about building agents. The schema is not just a contract for your code, it is the documentation the model reads to decide whether this is the right tool and what to pass it. A parameter called q with no description will be misused. The same parameter called query with one sentence explaining that it accepts a regular expression and not a glob will not be.

Three rules that hold up across every agent I've seen work:

  • Fewer, larger tools beat many small ones. Twelve tools is a working set. Sixty is a menu the model reads badly. If you have sixty operations, group them behind a handful of tools with a mode parameter, or put them behind a search tool that returns the right one.
  • Error messages are the recovery mechanism. A tool that returns Error: invalid input gives the model nothing to act on. One that returns path must be absolute; you passed "src/main.py", try "/app/src/main.py" gets corrected on the next turn without a human. Write tool errors the way you'd write them for a competent new colleague who cannot see your screen.
  • Results need budgets. A tool that can return four megabytes of log output will, eventually, and it will take the context window with it. Truncate, summarise, or paginate at the tool boundary — never let the size of a tool result be determined by the outside world.

3. Context management

Every turn appends. Left alone, a long-running agent walks into the context limit and dies at the least convenient moment. The harness has to decide what to keep.

The approaches in use, in rough order of how much they cost you:

StrategyWhat it doesCosts you
Truncation Drops the oldest turns The original instruction, usually right when it mattered
Tool-result eviction Keeps the calls, drops old results Little. Start here
Compaction Summarises the transcript, continues from the summary Detail the summary didn't think was important
Externalised state Agent writes notes to a file it can re-read Engineering effort, and a file that can go stale

Compaction is the one worth getting right, because it is the one that decides whether an agent can work for an hour. The trick is that the summary should be written for the agent's future self, not for a human reader: what the goal is, what has been tried, what was ruled out and why, which files are in play, what the next step was going to be. A summary optimised for readability throws away exactly the operational detail the next turn needs.

Load-bearing

Prompt caching interacts with all of this. Anything you rewrite near the start of the context invalidates the cache for everything after it. Append at the end, keep the system prompt and tool definitions stable, and put volatile content last — otherwise you pay full price on every turn and won't understand why.

4. Permissions

An agent that can run shell commands can delete things. The question is not whether to allow that but who approves it and when. The pattern that works is a three-state policy per tool call: allow, ask, deny — evaluated against rules that the user can edit and that persist across sessions.

Two details matter more than the mechanism. First, the approval prompt must show the actual call, fully resolved — not "the agent wants to run a command" but the exact string. Second, an approval must not generalise. Saying yes to one git push is not standing permission to push forever, and a harness that treats it that way will eventually do something the user would not have allowed.

Read-only operations should not prompt at all. If your harness asks permission to list a directory, users will click through every prompt without reading it inside a week, and the permission system has become decoration.

5. Recovery

Things fail: the API rate-limits, a tool times out, the model returns malformed JSON, the process dies. A harness that loses an hour of work to a 429 is not one you can trust with an hour of work.

  • Retry transport errors with backoff and a jittered delay. Do not retry a tool that failed for a business reason — feed the error to the model instead.
  • Malformed tool arguments should be returned to the model as a validation error, not raised. It will usually fix them on the next turn.
  • Persist the transcript after every turn, not at the end. Resuming a session is the difference between a crash costing thirty seconds and costing the whole run.

6. Observability

You cannot debug what you cannot see, and an agent transcript is not a log. What you want, per run: every message, every tool call with its arguments and result, token counts split by cached and uncached, wall-clock per turn, and the final state. Then you want to be able to replay it.

Teams that build this early ship faster, and it isn't close. The alternative is reasoning about failures from a screenshot of the last thing the agent said, which is roughly as effective as debugging a server from its most recent print statement.

What this means when you're choosing one

When you evaluate a harness — off the shelf or the one you're about to write — the questions worth asking are not about which models it supports. Ask what happens when the context fills. Ask what a tool error looks like from the model's side. Ask whether you can resume a run after a crash. Ask what the permission prompt shows. Those six pieces are the whole product; the model is a dependency.

The forty-line loop is real and you should write it once, because understanding it makes every subsequent decision clearer. Just don't mistake it for the thing you're going to run.