Here's the one-sentence version: the model reasons; the harness acts. An LLM on its own can tell you what it would do — run this test, edit that file, call that API — but it can't actually do any of it. The harness is everything wrapped around the model that turns those intentions into real actions: the tools, the execution loop, the memory, the guardrails, the logs.
So the equation people keep repeating — Agent = Model + Harness — is worth taking literally. When you use Claude Code, Cursor, or any production agent, you're talking to a harness that happens to have a model inside it. The model is the brain; the harness is the body, the hands, and the adult supervision. And increasingly, the harness is where the engineering happens: on public benchmarks, the same model can land wildly different scores depending entirely on how the harness around it is built.
The Loop Underneath Every Agent
Strip any agent down and you find the same cycle, usually called the ReAct loop (from the 2022 paper ReAct: Synergizing Reasoning and Acting in Language Models):
1. REASON model reads the task + everything so far, decides the next action
2. ACT harness executes it: runs the tool, calls the API, edits the file
3. OBSERVE harness captures the result, feeds it back to the model
4. REPEAT until the model stops asking for actions
Notice who does what. The model never touches your filesystem — it emits a structured request ("call read_file with path=notes.md") and the harness decides whether and how to honour it. That separation is the whole design. It's what lets you put a permission prompt, a sandbox, or an audit log between an intention and its consequence.
What a Real Harness Carries
Production harnesses converge on the same eight parts, each one patching a specific limitation of a raw model:
- System prompt — the standing instructions: who the agent is, what it may do, what rules it must follow.
- Tools — the functions the model can request. The model picks; the harness executes.
- Sandbox — an isolated place to run actions, so a bad command can't hurt anything outside it.
- Filesystem / durable storage — somewhere for work-in-progress to live between steps and sessions.
- Memory and context management — deciding what stays in the model's context as the transcript grows, and what gets summarised or dropped.
- Feedback loops — the harness checks the work: runs the tests, feeds errors back, lets the model retry.
- Guardrails — rules that block or gate actions: human approval before a delete, a hard cap on loop length, a path check on every file write.
- Observability — logs of what the agent did and why, so you can debug it and audit it.
You don't need all eight to understand the idea. You need about eighty lines of Python.
A Minimal Harness You Can Actually Run
Below is a complete, working harness around the Claude API. It gives the model two tools — read a file, write a file — and wraps them in the parts that matter: a workspace sandbox, a human-approval gate on writes, error feedback, a step budget, and a log line per action. pip install anthropic, set ANTHROPIC_API_KEY, and it runs.
# harness.py — a minimal agent harness
import json, pathlib, anthropic
WORKSPACE = pathlib.Path("./workspace").resolve()
client = anthropic.Anthropic()
# 1. SYSTEM PROMPT — the standing rules
SYSTEM = """You are a careful coding assistant. You may read and write
files inside the workspace only. Keep changes minimal."""
# 2. TOOLS — what the model may *request* (it never executes anything itself)
TOOLS = [
{
"name": "read_file",
"description": "Read a text file from the workspace. "
"Call this before editing any file.",
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string",
"description": "Path relative to workspace root"}},
"required": ["path"],
},
},
{
"name": "write_file",
"description": "Write a text file inside the workspace. "
"The user must approve every write.",
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string"},
"content": {"type": "string"}},
"required": ["path", "content"],
},
},
]
# 3. SANDBOX — every path is confined to the workspace, no exceptions
def safe_path(rel: str) -> pathlib.Path:
p = (WORKSPACE / rel).resolve()
if not p.is_relative_to(WORKSPACE):
raise ValueError(f"path escapes workspace: {rel}")
return p
# 4. THE ACT STEP — the harness executes what the model requested
def run_tool(name: str, args: dict) -> str:
if name == "read_file":
return safe_path(args["path"]).read_text()
if name == "write_file":
# GUARDRAIL — human-in-the-loop before anything irreversible
print(f"\n--- agent wants to write {args['path']} ---\n{args['content']}\n")
if input("approve? [y/N] ").strip().lower() != "y":
return "User declined this write. Ask what they'd prefer."
p = safe_path(args["path"])
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(args["content"])
return f"wrote {len(args['content'])} chars to {args['path']}"
raise ValueError(f"unknown tool: {name}")
# 5. THE LOOP — reason, act, observe, repeat
def run_agent(task: str) -> str:
messages = [{"role": "user", "content": task}]
for step in range(1, 21): # GUARDRAIL: hard step budget
response = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
system=SYSTEM,
tools=TOOLS,
messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use": # no more actions: we're done
return next(b.text for b in response.content if b.type == "text")
results = []
for block in response.content:
if block.type != "tool_use":
continue
# OBSERVABILITY — one log line per action
print(f"[step {step}] {block.name}({json.dumps(block.input)[:80]})")
try:
output = run_tool(block.name, block.input)
results.append({"type": "tool_result",
"tool_use_id": block.id, "content": output})
except Exception as e:
# FEEDBACK LOOP — errors go back so the model can recover
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": f"Error: {e}", "is_error": True})
messages.append({"role": "user", "content": results})
return "Stopped: hit the 20-step budget."
if __name__ == "__main__":
print(run_agent("Read notes.md and write a short summary to summary.md"))
Run it and watch the loop breathe. The model asks to read notes.md; the harness executes that and hands back the contents. The model drafts a summary and asks to write it; the harness stops and asks you. Say no, and the refusal goes back into the conversation as an observation — the model adjusts and asks what you'd prefer instead. That's the entire trick, and it's the same trick at every scale.
Map the comments back to the eight building blocks and you'll see six of them in these eighty lines: the system prompt, the tools, the sandbox (safe_path), the guardrails (the approval gate and the step budget), the feedback loop (is_error results), and observability (the log line). The two missing ones — durable memory and context compaction — are exactly what you'd add next as tasks get longer, and they're what separate a demo from a production harness.
Why the Harness Is Where the Leverage Is
Three details in that small script carry more weight than they look like they do:
- The guardrail is structural, not prompted. The system prompt says "workspace only," but the harness enforces it in
safe_path. Prompts are requests; code is a boundary. An optimiser under pressure will find the gap between the two — so the things that must never happen belong in code. - Errors are data, not failures. Because a failed tool call goes back as an observation, the agent self-corrects: wrong filename, it lists what it knows and tries again. Harnesses that hide errors from the model produce agents that confidently stop at the first pothole.
- The loop has a budget. Twenty steps and out. Every production harness has some version of this, because a model with tools and no ceiling is a bill with no ceiling.
This is also why "which model is best?" is becoming the less interesting question. A strong harness around a mid-tier model routinely beats a weak harness around a frontier one on workflow-heavy tasks, because most real-world agent failures — context rot, tool overload, silent truncation, missing verification — are harness failures, not model failures. The discipline even has a name now: after prompt engineering (word the input well) and context engineering (curate what the model sees) comes harness engineering — design the whole system around the model.
And the harness above has an obvious missing organ: it can act, but it can't tell whether it acted well. The moment you add "run the tests and only keep changes that pass," you've bolted an eval into the loop — which is the subject of A Dummy's Guide to Evals, and the difference between an agent that does things and an agent you can trust to do things overnight. If you want to see a harness like this one ratchet a real metric downward on its own, the autoresearch experiment from last week is exactly that: this loop, plus a referee.
The model contains the intelligence. The harness turns it into work you can rely on. Eighty lines is enough to feel the difference.
~ Comments & Discussion ~
Have thoughts on this post? Join the discussion below! Comments are powered by Disqus.