Certified Agent Harness Engineer
Course sections

M1.1

What a harness is, and what it is not

984 words · about 6 min

Concept

An agent harness is the program that turns a stateless token predictor into a durable, tool-using, interruptible process that acts on real systems. It owns four things the model cannot own: what the model sees, what happens when the model asks for something, what is allowed, and what happens when something goes wrong.

The clearest way to fix the term is by contrast with four neighbours it is routinely confused with.

A harness is not a model. The model contributes exactly one capability: given tokens, produce plausible next tokens, including tokens that look like a request to call a tool. It has no ability to execute that request. When a model "reads a file", what actually happened is: the model emitted a structured tool call, the harness parsed it, the harness decided it was permitted, the harness called readFile, and the harness appended the bytes to the conversation as a new message. Four of those five steps are yours. Confusing model capability with harness capability is the single most common category error in this field, and it produces a specific failure: teams try to fix loop bugs — runaway retries, context overflow, tools called with stale paths — by editing the system prompt, because they have misattributed a control-flow problem to a persuasion problem.

A harness is not an SDK. An SDK (@anthropic-ai/sdk, openai, a raw fetch wrapper) marshals one request and unmarshals one response. It is transport. It has no opinion about what happens between requests, which is where all the difficulty lives. An SDK will happily let you send a 400,000-token conversation to a 128,000-token model and surface the resulting 400; a harness is the thing that was supposed to prevent that.

A harness is not a chatbot. A chatbot renders a conversation. Its effects are confined to a text box. A harness produces side effects in the world — files written, processes spawned, network calls made, money spent — which means it inherits an obligation a chatbot never has: every action must be attributable, reversible where possible, and refusable. The moment your program can run rm -rf, you are writing a harness, and the permission model is no longer optional.

A harness is not an agent framework. This distinction is the subtlest and the most contested. A framework (LangChain, LlamaIndex, and their successors) gives you abstractions for composing agent-shaped programs — chains, graphs, memory objects. A harness is the runtime underneath: the thing holding the loop, the process boundaries, the cancellation, the log. You can build a harness using a framework, and you can build a harness with no framework at all. The production coding tools people actually use — Claude Code, Codex CLI, Cursor's agent mode, and dsh — are overwhelmingly the second kind. That is not a coincidence: frameworks optimise for expressing many topologies, while a harness optimises for making one topology reliable under interrupt, failure, and adversarial input. Be honest that this is a live disagreement in the field rather than settled fact; the framework camp would argue that harness authors are rebuilding the same abstractions badly, and sometimes they are right.

Mechanism

Operationally, a harness is a loop with four collaborators wrapped around a stateless call:

                  ┌─────────────── harness ───────────────┐
   user input ──> │ compose context                       │
                  │   ↓                                   │
                  │ call model (stateless) ───────────────┼──> provider
                  │   ↓                                   │
                  │ parse response                        │
                  │   ↓                                   │
                  │ policy check ──> execute effects      │──> filesystem,
                  │   ↓                                   │    shell, network
                  │ append observations ──┐               │
                  └───────────────────────┼───────────────┘
                                          └─> back to compose

The loop terminates when the model stops asking for tools, when the user interrupts, when a budget is exhausted, or when a guard fires. Module 2 makes each of those exits precise.

Worked example

Ask "what does the harness own?" of one concrete request: the user types fix the failing test. The model never sees that string alone. It sees a system prompt the harness assembled, a project-instructions block the harness read off disk, a description of every available tool the harness registered, the prior conversation the harness replayed, and only then the user's sentence. The model replies with a tool call — say, run the test suite. The harness decides whether running a subprocess is permitted here, runs it, truncates 40,000 lines of output down to something affordable, appends it as an observation, and calls the model again. The model's contribution is two acts of text generation. Everything else is the product.

Tradeoffs

Drawing the harness boundary wide (the harness owns permissions, context assembly, and persistence) buys you a model-agnostic product and a testable core, and costs you a large amount of code that has nothing to do with AI. Drawing it narrow (leaning on provider-side agent APIs that manage state for you) buys you speed to first demo and costs you portability, offline replay, and the ability to enforce your own policy — the provider's idea of "allowed" becomes yours.

How `dsh` does it

How dsh does it

dsh states the boundary as an architectural rule: "DeepSeek Harness is a plugin-based agent harness on vendored Cordis: everything is a plugin" (AGENTS.md). The model adapter, the tool registry, the session log, and even the agent loop itself are plugins registered into a context. The loop lives in packages/core/agent-loop; the model-facing vocabulary lives in packages/llm/llm; the two are connected only through a service key, ctx.llm. docs/architecture.md describes the consequence: extension packages depend on dsh-agent events and services, not on the concrete loop package.

How others differ

Claude Code ships one engine that is simultaneously CLI, permission authority, and tool host, with an IDE extension acting as a thin client onto the same process. Codex CLI takes a similar local-execution stance. Frameworks like LangGraph invert it: the graph is the artefact, and the runtime is generic. The tradeoff is consistent — the harnesses buy reliability in one shape, the frameworks buy expressiveness across many.


Check yourself

2 questions on M1.1

Start drill