Documents
Home>Documents>AI>Agent

Harness Engineering: The Agent Era Is Actually Here

28 min readApr 29, 2026Apr 29, 2026

Harness Engineering: The Age of Agents Is Here (For Real This Time)

Introduction: The Illusion That the Model Is Everything

2023: "GPT-4 dropped. Everything changes."
2024: "Claude 3 Opus dropped. Everything changes."
2025: "Agents are here. Everything changes."
2026: (quietly) "…So why do 88% of these never reach production?"

Every year something new arrives, and every year we're told everything will change. But anyone who has actually built AI systems that run in production knows the truth: the model was never the problem.

LangChain's Vivek Trivedy recently put it this way:

"If you're not the model, you're the harness."

That single sentence captures the core of AI engineering in 2026.

Models are already smart enough. GPT-4o, Claude Sonnet, Gemini Ultra — pick any of them and they can handle knowledge work at an individual professional level without breaking a sweat. Yet 88% of enterprise AI projects never reach production. And 65% of those failures aren't due to model shortcomings — they're due to harness failures.

A model is an engine. But an engine alone doesn't make a car run.


1. What Exactly Is a Harness?

Let's nail down the definition first.

Agent = Model + Harness

Harness = Everything except the model

System prompts, tool definitions, MCP servers, filesystem access, sandboxes, orchestration logic, context management hooks, retry middleware, memory stores… the entire infrastructure that lets an agent actually do things — that's the harness.

The OS analogy fits best:

  • LLM = CPU (computation)
  • Context window = RAM (working memory)
  • Harness = Operating system (I/O management, memory scheduling, process isolation)

A model on its own cannot do any of the following:

  • Persist state across sessions
  • Execute code
  • Search the internet
  • Read and write files
  • Remember its own mistakes

An LLM is, at its core, just a very well-built next-token predictor. It takes tokens in and predicts the next one. The harness is what gives that predictor hands, feet, memory, tools, and guardrails.

The airplane analogy works too. The pilot (the model) still flies the plane. But modern aircraft have fly-by-wire systems that physically prevent dangerous maneuvers. The harness is that safety system — it constrains what the model can do to what it should do.


2. Why Harness Engineering, and Why Now?

The concept of a harness isn't new. Every LLM application has always had one in some form. What has changed is the recognition.

Before, the harness was treated as boilerplate that came along for the ride when building an AI app. Now, there's a growing understanding that the harness is the competitive advantage.

A few examples make this concrete.

Manus: Five harness redesigns over six months. The model stayed the same. Reliability improved with every redesign.

LangChain Deep Research: Rebuilt four times over a year. Each time, what changed wasn't the model — it was the workflow architecture.

Vercel: Removed 80% of an agent's tools and performance actually improved. They discovered — the hard way — that too many tools confuses the agent.

Claude Code and Cursor: Both run on the same underlying model (Claude Sonnet), yet they deliver completely different experiences, entirely because of their different harnesses.

Models are already commoditizing. GPT-4o-level reasoning will be achievable with open-source models in the not-too-distant future. But a well-built harness represents months or years of engineering work. That's the moat. Even world-class teams take months to build a production-quality harness. There are reports of OpenAI teams of 3–7 engineers spending five months writing roughly one million lines of harness code for a single internal product.


3. Core Components of a Harness

To really understand a Harness, you need to break it down layer by layer.

3.1 Filesystem: The Most Fundamental External Memory

An LLM's context window is large but finite. 128K tokens sounds like a lot, but it fills up fast during any serious long-running coding task.

The filesystem is the most fundamental Harness element for addressing this problem:

  • Offload content that exceeds the context window to files
  • Persist task state across sessions (task_state.json, progress.md, etc.)
  • Serve as a shared collaboration surface for multiple agents and humans
def persist_state(task_id: str, state: dict):
    path = f"tasks/{task_id}/state.json"
    filesystem.write(path, json.dumps(state, ensure_ascii=False))

def resume_state(task_id: str) -> dict:
    path = f"tasks/{task_id}/state.json"
    return json.loads(filesystem.read(path))

Git also belongs in the filesystem layer of a Harness. For a coding agent, Git isn't just version control — it's a "work history + rollback-capable state machine." Even if an agent goes ten steps in the wrong direction, git checkout gets it back to a safe checkpoint.

3.2 Tool Layer: Bash and Code Execution

Tools are the heart of the Harness. They're the only way an LLM can interact with the outside world.

The single most powerful tool here is, in fact, Bash — precisely because it's a general-purpose tool. File manipulation, network requests, package installation, running tests — Bash handles all of it. This lets the agent solve problems autonomously without being constrained to a predefined list of purpose-built tools.

That said, you can't just hand an agent an unrestricted Bash shell. You need a sandbox.

What the sandbox provides:

  • An isolated execution environment (protecting the host system)
  • Allowlist-based command control
  • Network isolation (only whitelisted domains permitted)
  • Infinite scalability via on-demand create/destroy
# 샌드박스 설정 예시
sandbox:
  allowed_commands:
    - python
    - pip
    - git
    - npm
    - pytest
  blocked_commands:
    - "rm -rf /"
    - "git push --force"
  network:
    allowed_domains:
      - pypi.org
      - github.com

MCP (Model Context Protocol) also belongs in this layer. MCP is a standardized interface between agents and external tools and services — often called the "USB-C moment for the agent ecosystem." As this standard takes hold, the way a Harness connects to databases, external APIs, and other services is becoming uniform.

A2A (Agent-to-Agent) protocol, which handles horizontal delegation between agents, is also being standardized. Once both of these standards are established, Harness components will be swappable without vendor lock-in.

3.3 Context Management: Fighting Context Rot

Any agent running a long task will inevitably hit a wall: Context Rot.

As the context window fills up:

  • The model's attention becomes diffuse
  • Early instructions and later instructions start to conflict
  • Signal-to-noise ratio degrades
  • The agent's behavior becomes increasingly erratic

The math makes this concrete. If the probability of taking the correct action at each step is 85%, after 10 steps the probability of overall success is:

0.85^10 ≈ 0.197 = ~20%

Context Rot continuously drives that per-step error probability upward. The longer a task runs, the more likely the agent is to fail — and that's not a sign of a dumb model. It's a structurally inevitable outcome.

Harness Solution 1: Compaction

As the context approaches its limit, the Harness intervenes and summarizes it intelligently. The goal isn't to truncate old messages — it's to compress while preserving "core state."

class CompactionMiddleware:
    def __init__(self, threshold=0.85):
        self.threshold = threshold
    
    def before_model(self, state):
        usage = state.context_used / state.context_limit
        if usage > self.threshold:
            state.history = self.smart_summarize(state.history)
        return state
    
    def smart_summarize(self, history):
        # 핵심 결정사항, 발견된 사실, 현재 진행 상태만 보존
        # "오래된 것 삭제"가 아니라 "중요한 것 보존"이 핵심
        return summarize_preserving_key_decisions(history)

Anthropic's observation is that simple compaction alone isn't enough — what's needed is a "reframing, as if onboarding a new engineer." Even after compaction, the agent needs a clear answer to "why am I doing this in the first place?"

Harness Solution 2: Tool Output Offloading

Dumping large file contents, long API responses, and test logs directly into the context fills it up fast. A more effective approach is to have the Harness write these to the filesystem and pass the agent only a reference — "saved to file → read it when you need it."

class ToolOutputOffloadMiddleware:
    MAX_INLINE_SIZE = 2000  # 토큰 기준
    
    def wrap_tool_call(self, tool_name, tool_input, call_tool):
        result = call_tool(tool_name, tool_input)
        
        if len(result) > self.MAX_INLINE_SIZE:
            path = f"tool_outputs/{tool_name}_{timestamp()}.txt"
            filesystem.write(path, result)
            return f"[결과가 파일에 저장됨: {path}. 필요시 read_file 도구로 접근]"
        
        return result

3.4 Progressive Disclosure: Load Tools Only When Needed

This is currently one of the hottest concepts in Harness engineering.

It's tempting to think that more tools mean a more capable agent. In practice, the opposite is true. It's no coincidence that Vercel saw performance improve after cutting 80% of their tools.

Loading too many tools into the context at agent startup causes:

  • The context window to fill up quickly
  • The model to get confused about which tool to use and when
  • Irrelevant tool descriptions to act as noise

Progressive Disclosure is the design principle that addresses this:

"Don't expose all tools and information upfront. Reveal them only at the moment they're needed."

It might seem like putting all of a company's knowledge into one giant file would make the agent smarter. In most cases, it does the opposite. More noise, a faster-filling context, and attention scattered across irrelevant information.

Skills: The Implementation of Progressive Disclosure

LangChain's middleware architecture implements this through the concept of Skills. A Skill is a bundle of related tools, instructions, and context. When the agent starts a specific task, it loads the corresponding Skill; when the task is done, it unloads it.

class SkillsMiddleware:
    def __init__(self, skills_registry):
        self.registry = skills_registry
        self.active_skills = []
    
    def wrap_tool_call(self, tool_name, tool_input, call_tool):
        # 이 도구를 쓰려면 어떤 Skill이 필요한지 확인
        required_skill = self.registry.get_skill_for(tool_name)
        if required_skill and required_skill not in self.active_skills:
            self.load_skill(required_skill)
        return call_tool(tool_name, tool_input)
    
    def load_skill(self, skill):
        # 해당 Skill의 도구와 지침만 컨텍스트에 주입
        self.active_skills.append(skill)
        skill.inject_context()

Several complementary patterns are commonly used alongside this:

Index-first loading: Instead of providing all materials up front, give the agent an index of what's available. The agent selects what it needs, and only then fetches that material.

Scout pattern: A lightweight pre-inspection step evaluates what the current task requires and returns a list of needed resources — a "prep check" before the main work begins.

Stage-based loading: Load different contexts for different phases — research, planning, execution, review. The execution phase needs coding tools; the review phase needs testing tools.

3.5 AGENTS.md: Externalizing Domain Knowledge

AGENTS.md is a file standard that handles the memory layer of the Harness.

Domain knowledge, project rules, and lessons learned from past failures are stored in a Markdown file. The Harness injects this into the agent's context at startup. This lets you update what the agent "knows" without touching model weights.

Principles for writing a good AGENTS.md:

  • 60 lines or fewer: Keep it concise, like a pilot's checklist. The longer it gets, the less it gets read.
  • Always include the rationale: Not "do it this way" but "do it this way because of incident X."
  • Keep it updated: Every time the agent makes a mistake, add a rule for it.
# AGENTS.md

## 핵심 규칙
- DB 스키마 변경 전 반드시 마이그레이션 파일 생성
  (이유: 2024-03 직접 ALTER TABLE로 프로덕션 장애 발생)
- 외부 API 호출 시 타임아웃 30초 설정
  (이유: 외부 서비스 hang으로 요청 누적 사례 다수)
- 테스트 없이 PR 생성 금지

## 아키텍처 한 줄 요약
(변경될 때마다 업데이트)

## 자주 실수하는 패턴
- import 순서: stdlib → third-party → local
- 비동기 함수는 반드시 await 체크

Applying the Ratchet principle here makes this even more powerful. Every time the agent makes a mistake:

  1. Add a rule to AGENTS.md
  2. Enforce that rule automatically via hooks (linting, tests, etc.)
  3. Prevent the same mistake from recurring

This is a fundamental shift in perspective — from "model problem" to "configuration problem." Instead of fighting the model with prompt tweaks every time it repeats a mistake, you change the environment so that the mistake is structurally impossible.

4. Middleware: The Nervous System of a Harness

The middleware system formalized in LangChain's Deep Agents is one of the core patterns in harness engineering. It consists of hooks that intercept every stage of the agent loop.

Six primary hooks:

HookTimingPrimary Use
before_agentInitial executionMemory loading, resource connection, input validation
before_modelJust before model callHistory pruning, PII detection, context optimization
wrap_model_callWraps the entire model callCaching, retries, dynamic tool selection
wrap_tool_callWraps tool executionContext injection, result interception, access control
after_modelAfter response, before tool executionHuman-in-the-loop implementation
after_agentAfter completionResult persistence, notifications, cleanup

Patterns commonly used in production:

PII masking middleware:

class PIIMiddleware:
    def before_model(self, state):
        state.messages = [mask_pii(msg) for msg in state.messages]
        return state
    
    def after_agent(self, state):
        # 규정 준수를 위한 감사 로그
        audit_log.record(state.run_id, state.actions_taken)
        return state

Loop detection middleware:

class LoopDetectionMiddleware:
    def wrap_tool_call(self, tool_name, tool_input, call_tool):
        recent = self.tool_history[-6:]
        same_count = sum(1 for t in recent if t == tool_name)
        
        if same_count >= 3:
            # 같은 도구를 6번 안에 3번 이상 반복하면 개입
            raise AgentLoopDetected(
                f"도구 {tool_name}이 반복 호출됨. 접근법을 재고하세요."
            )
        
        self.tool_history.append(tool_name)
        return call_tool(tool_name, tool_input)

Domain rule validation middleware:

class PolicyValidationMiddleware:
    def after_model(self, response, state):
        # 보험이 실효 상태인데 모델이 승인하려 한다면
        if state.policy["status"] == "lapsed":
            if '"approved"' in response:
                state.correction_needed = True
                return "보험이 실효 상태입니다. 반드시 거절 처리해야 합니다."
        return response

The core philosophy behind middleware design: handle deterministically what can be handled deterministically. Don't leave it to the model. Compliance requirements, safety constraints, and format validation should be enforced at the code level — not delegated to the model's "good judgment."

Claude Code exposes 22 lifecycle events, each hookable via shell script. These are hard enforcement points that the model cannot skip.


5. Ralph Loop: Preventing Agents from Giving Up

There's a failure mode that surfaces frequently when you hand a long-running task to an agent: midway through, it suddenly stops and says something like "this task is too complex to continue."

This is called context anxiety. As the context window fills up, the model starts feeling like it "can't go on." This isn't a motivation problem — it's a structural response to information overload.

The Ralph Loop is a harness pattern that addresses this:

  1. The harness hook intercepts the agent's attempt to terminate.
  2. The current progress state is saved as a checkpoint to the filesystem.
  3. A clean, fresh context window is opened.
  4. The original goal is re-injected along with "here's how far you got in the previous session — keep going."
class RalphLoop:
    def on_agent_exit_attempt(self, agent_state):
        if not self.task_complete(agent_state):
            # 완료되지 않은 채 종료 시도 → 가로채기
            checkpoint = self.save_checkpoint(agent_state)
            
            resume_prompt = f"""
이전 세션에서 다음까지 완료했습니다:
{checkpoint.completed_summary}

아직 완료하지 못한 작업:
{checkpoint.remaining_tasks}

파일시스템에서 이전 작업 내용 확인 후 계속 진행하세요.
원래 목표: {checkpoint.original_goal}
"""
            return self.restart_with_clean_context(resume_prompt)
    
    def save_checkpoint(self, state) -> Checkpoint:
        checkpoint = Checkpoint(
            completed_summary=state.progress_summary,
            remaining_tasks=state.pending_tasks,
            original_goal=state.initial_goal
        )
        filesystem.write(f"checkpoints/{state.run_id}.json", checkpoint)
        return checkpoint

The key insight here is that the filesystem guarantees state continuity across sessions. Each session reads the artifacts left by the previous one to know where it left off and what still needs to be done. Even if a network failure kills a session, the next session can pick up exactly where things stopped.

The Ralph Loop is what makes multi-hour — or even multi-day — long-running autonomous tasks feasible.


6. Orchestration: Agents Calling Agents

Complex tasks that are too large for a single agent get decomposed using the orchestrator-worker pattern.

Orchestrator Agent (overall coordination)
├── Planning Agent   → task decomposition, planning
├── Research Agent   → information gathering, external lookups
├── Coding Agent     → implementation (coding tools only)
├── Verifier Agent   → independent verification (checks execution results)
└── [Human Approval Gate] → human intervention for high-risk decisions

Each agent only has access to the tools relevant to its role. This is the architectural implementation of the principle of least privilege. There's no reason to give the Coding Agent permission to drop a production database. If the permission doesn't exist structurally, it doesn't matter what the model wants — it's impossible.

The Plan-Execute-Verify (PEV) loop follows naturally from this:

  • High-end model (expensive): planning and final verification
  • Lower-cost model: intermediate execution steps

This preserves quality while significantly cutting costs. Combining KV cache management with semantic routing has been reported to reduce token costs by up to 90%.

There's also the self-evaluation trap. Asking an agent to verify its own work introduces confirmation bias — "I did it, so it's probably right." A well-designed harness separates the planner, executor, and verifier so that independent agents handle verification.


7. Natural-Language Harnesses: Orchestrating with Words, Not Code

An interesting concept that's emerged recently is the Natural-Language Agent Harness (NLAH).

Traditionally, harness logic is written in Python: conditionals, loops, function calls — conventional software. But what if you expressed it in natural language (Markdown documents) instead?

Research has shown that NLAH is a viable approach where an agent's control logic is written as executable Markdown:

# Code Review Agent Harness

## Role Assignment
- Solver: reads code and identifies issues
- Verifier: independently validates findings
- Reporter: produces the final report

## Contract
- Input: PR URL
- Output: JSON-formatted review results
- Constraint: every finding must cite a specific line of code

## Workflow
1. Read the full PR diff (Solver)
2. Produce a categorized list of issues (Solver)
3. Re-validate each item — filter out false positives (Verifier)
4. Sort by severity and generate the report (Reporter)

The upside is clear. Domain experts who don't write code can author and modify harnesses. Business logic is no longer buried in code — it's explicitly visible.

The downside is equally clear. Natural language is less precise than code. Deterministic tasks — data validation, security rules, format enforcement — still need to be handled in code.

Experimental results: migrating a code-based harness to NLAH improved performance on some benchmarks (30.4 → 47.2). Structural clarity helps the model reason better. That said, it's not universally better — results vary depending on the task.


8. The Model-Harness Training Loop: Models Now Learn the Harness

This is the most disruptive development.

The models powering Claude Code, Codex, and Cursor aren't generic LLMs. They've been post-trained to operate within specific harness environments.

  • When and how to use filesystem tools
  • When to run Bash
  • How to decompose tasks into steps
  • How to read and apply AGENTS.md

All of this is internalized in model weights. The model and the harness are trained together.

There are side effects. If the tool interface changes, model performance can degrade — because the model has "overfit" to the harness. A model trained for Claude Code works especially well in the Claude Code harness, and that advantage diminishes in other environments.

What this means is that harness design decisions can no longer be separated from model training decisions. Teams that build good harnesses can build better models, and those models perform especially well on top of those harnesses. This flywheel is the real moat behind products like Claude Code. A competitor can copy the model, but copying the harness is harder. Copying the harness and then training a model specifically to match it is harder still.


9. Real-World Architecture: Harness Impact in Numbers

Let's look at concrete numbers.

An experiment applied two configurations — a pure-prompt agent and a fully harnessed agent using the same LLM — to 16 insurance claims scenarios:

ApproachAccuracyDomain Rule Violations
Pure LLM8/16 (50%)Multiple
With Harness16/16 (100%)0

Take the "claim submission with lapsed policy" scenario in particular:

  • Pure LLM: checks only the loss type and approves — misses the lapsed status
  • With Harness: PolicyVerificationAgent explicitly checks the status; ValidationMiddleware blocks the rule violation

Swapping Gemini for Claude produced the same results. That's because the invariants live in the harness, not in the model.

This is the crux of the paradigm shift:

Old thinking: "How do we get the model to do the right thing?"

Harness thinking: "How do we build a system where doing the wrong thing is structurally impossible?"

Relying solely on the model's "good judgment" without runtime guardrails is like building a calculator without any protection against division by zero. It'll work most of the time — but in the situations where it matters most, it'll break.


10. When Should You Build a Harness?

Not every AI application needs a full-stack harness. If two or more of the following apply, you need one:

  • Real-world impact: financial, legal, medical, or safety-critical domain
  • Long-running tasks: multiple steps, risk of exceeding context
  • Compliance requirements: audit trails and logging are mandatory
  • Cost control: a runaway agent can blow your budget
  • Multiple stakeholders: some decisions require human approval
  • Hard prohibitions: certain actions must be blocked regardless of model reasoning

For short, self-contained, low-risk tasks, a simple system prompt and a few tools is enough. An over-engineered harness just adds unnecessary complexity and latency. It's like rolling out a factory automation line when all you needed was a toolbox.

11. Looking Ahead: Where Harnesses Are Going

Harnesses Don't Shrink — They Move Up

A common question: as models get smarter, won't we need fewer Harnesses? Wrong question.

Addy Osmani's analysis nails it: when models improve, the "anxiety-reduction" scaffolding that Harnesses used to handle does go away. But more complex requirements take its place — multi-agent coordination, longer autonomous runs, more sophisticated domain validation.

Harnesses don't disappear. They move to a higher level of abstraction.

Dynamic Harnesses: The Harness as Compiler

Most Harnesses today are static. You write a tool list in a config file, draft a system prompt ahead of time, register hooks in advance.

The next step is the dynamic Harness — one that assembles itself based on the nature of the task. Which tools are needed, what validation is required, how many sub-agents to spin up: the Harness decides all of this at runtime.

Open Problems

Unsolved problems in Harness engineering:

  • How do you manage parallel orchestration of hundreds of agents?
  • Can an agent analyze its own execution traces to automatically identify failure patterns?
  • How do you objectively benchmark performance across different Harness implementations?
  • How do you handle versioning and rollback of the Harness itself?
  • What's the optimal way to combine natural-language Harnesses with code-based ones?

Closing: The Age of Agents Is Already Here

When people say "the age of agents is coming," it still sounds like a future story. But it's already here.

Products like Claude Code, Cursor, and Devin are writing production code right now. Agents are authoring thousands of lines of code, running tests, fixing bugs. Enterprises are integrating agents into real business processes.

The question is why these agents work as well as they do. Because the models are good? That's half the answer.

The other half is the Harness. Filesystem access, context compression, Progressive Disclosure, AGENTS.md, middleware hooks, the Ralph Loop, sub-agent orchestration — this entire infrastructure is what makes models actually useful.

If you're "building an AI agent" right now, ask yourself:

"Am I spending my time choosing which model to use, or designing the Harness?"

In 2026, competitive advantage won't come from which model you use. It will come from which Harness you've built.


References

Tags
AgentLLMAgentWorkflowMCP