Tuesday, 10 November 2026

Beyond the Model Call: The Essential Guide to Harness Engineering for AI Agents

In the rush to build autonomous AI systems, developers often start with a fundamental misconception: assuming a capable language model is by itself a working agent.

A raw language model is simply a statistical inference engine. Abstractly, its entire existence is captured by:

y ∼ pθ(· | c)

Notice what is missing from the above expression: nothing here automatically opens a browser, runs a shell command, retries a timed-out API, preserves state across hours of work, or verifies whether a requested task actually succeeded. pθ(· | c) represents the models complete output probability distribution across possible token/sequences given prompt context c. 

To turn a probabilistic model into a reliable production agent, you need an external control process. The surrounding infrastructure hosting external control process is known as the Agent Harness.

SYSTEM ARCHITECTURE OVERVIEW

+-----------------------------------------------------------------------+
|                            DURABLE SESSION                            |
|             (Task History, State Storage, Audit Logs)                 |
+-----------------------------------------------------------------------+
                                   |
                                   v
+-----------------------------------------------------------------------+
|                            AGENT HARNESS                              |
|  - Context Selection       - Policy & Approvals     - Verification     |
|  - Tool Routing            - Error Recovery         - Budgeting       |
+-----------------------------------------------------------------------+
           |                                             ^
      Active Context                                Action Output
           |                                             |
           v                                             |
+---------------------+                      +--------------------------+
|        MODEL        | -- Action Request --> |   EXECUTION ENVIRONMENT  |
| (Inference Engine)  |                      |    (Sandbox / APIs)      |
+---------------------+                      +--------------------------+

1. The Core Equation & The Minimal Agent Loop

A useful conceptual decomposition for any autonomous AI system is:

Agent System ≈ Model + Harness + Environment

  • The Model: Supplies learned capability and probabilistic decision-making.
  • The Harness: Manages execution, context selection, state updates, approvals, and error recovery over multiple steps.
  • The Environment: Contains external resources (filesystems, databases, remote APIs) that actions inspect or modify.

How the Minimal Agent Loop Operates

Rather than executing a one-shot prompt, the harness drives an iterative loop across discrete steps t:

  1. State & Context Selection: Given durable state st, the harness constructs active context ct = G(st).
  2. Model Invocation: The model generates output at = M(ct), which contains reasoning or action requests.
  3. Policy & Approval: The harness validates whether at is permitted, needs human review, or violates budget limits.
  4. Environment Execution: If approved, the action executes in the environment to yield an observation ot = E(at).
  5. State Update: The harness updates durable state st+1 = U(st, at, ot) and decides whether to continue or terminate.

Real-World Example: If an agent is asked to fix a database migration bug, the model doesn't modify the database directly. The harness intercepts the model's requested SQL patch, checks if it violates safety policies, executes it inside a containerized sandbox environment, captures the SQL stdout/stderr observation, and decides whether the bug is resolved or requires another step.

Implementation: TypeScript Minimal Harness Loop & Structured Execution

To see how the step t control loop and structured tool envelope operate in practice, here is a sample TypeScript implementation of the minimal agent harness and execution plane:

export interface ToolCallAction {
  toolName: string;
  args: Record<string, any>;
}

export interface ToolResultEnvelope<T = unknown> {
  status: "success" | "error";
  data?: T;
  error?: {
    code: string;
    message: string;
    retryable: boolean;
    suggested_actions?: string[];
  };
  meta: {
    toolName: string;
    executionTimeMs: number;
  };
}

export class MinimalAgentHarness {
  private modelClient: any;
  private tools: Map<string, Function>;
  private maxSteps: number;

  constructor(modelClient: any, tools: Map<string, Function>, maxSteps = 10) {
    this.modelClient = modelClient;
    this.tools = tools;
    this.maxSteps = maxSteps;
  }

  public async run(taskPrompt: string): Promise<{ status: string; history: any[] }> {
    // Durable session state s_t
    const state = {
      history: [{ role: "user", content: taskPrompt }],
      step: 0,
      status: "running"
    };

    while (state.step < this.maxSteps) {
      state.step++;

      // 1. Context Selection: Curate active context c_t
      const activeContext = this.selectContext(state);

      // 2. Model Invocation: a_t = M(c_t)
      const actionRequest: ToolCallAction = await this.modelClient.generate(activeContext);

      // 3. Policy & Approval Check
      if (!this.passesSafetyPolicy(actionRequest)) {
        state.history.push({
          role: "system",
          content: "Action blocked by harness security policy."
        });
        continue;
      }

      // 4. Environment Execution: o_t = E(a_t)
      const observation = await this.executeTool(actionRequest);

      // 5. State Update: s_{t+1} = U(s_t, a_t, o_t)
      state.history.push({ role: "assistant", content: actionRequest });
      state.history.push({ role: "observation", content: observation });

      if (observation.status === "success" && observation.data?.taskComplete) {
        state.status = "completed";
        break;
      }
    }

    return state;
  }

  private selectContext(state: any): any[] {
    // Returns curated active context window (last 10 turns)
    return state.history.slice(-10);
  }

  private passesSafetyPolicy(action: ToolCallAction): boolean {
    // Enforce infrastructure boundaries and safety checks
    const forbiddenArgs = ["rm -rf /", "drop database"];
    if (forbiddenArgs.includes(action.args?.command)) {
      return false;
    }
    return true;
  }

  private async executeTool(action: ToolCallAction): Promise<ToolResultEnvelope> {
    const startTime = Date.now();
    const tool = this.tools.get(action.toolName);

    if (!tool) {
      return {
        status: "error",
        error: {
          code: "TOOL_NOT_FOUND",
          message: `The requested tool '${action.toolName}' is not registered in the harness.`,
          retryable: false,
          suggested_actions: ["Query available tools list to select a valid tool identifier."]
        },
        meta: { toolName: action.toolName, executionTimeMs: Date.now() - startTime }
      };
    }

    try {
      const data = await tool(action.args);
      return {
        status: "success",
        data,
        meta: { toolName: action.toolName, executionTimeMs: Date.now() - startTime }
      };
    } catch (err: any) {
      return {
        status: "error",
        error: {
          code: err.code || "RUNTIME_EXCEPTION",
          message: err.message,
          retryable: true,
          suggested_actions: ["Inspect state parameters and verify target resource availability."]
        },
        meta: { toolName: action.toolName, executionTimeMs: Date.now() - startTime }
      };
    }
  }
}

2. Decoupling the Architecture: Model, Harness, Session, and Sandbox

Overlapping component boundaries leads to security gaps and brittle systems. Production architectures decouple responsibilities cleanly:

Term Operational Scope Example
Model Learned inference weights. Claude 3.5 Sonnet, GPT-4o, Llama-3.
Harness Control plane driving the execution loop, context policy, and retries. Anthropic Managed Agents runtime, OpenAI Agents SDK, custom orchestrator.
Session Durable history log surviving process crashes. Append-only database storing execution steps and artifacts.
Sandbox Isolated execution plane for untrusted code and tools. Isolated Docker container, microVM, or WebAssembly runner.
Evaluation Harness Test infrastructure that runs tasks and grades outcomes. τ-bench, ToolSandbox, or SWE-bench grading runners.

Separating the control plane (Harness) from the execution plane (Sandbox) is critical. If model-generated code running inside a sandbox manages to execute a malicious shell payload, keeping credentials, orchestration logic, and session state outside the sandbox ensures the rest of your system remains uncompromised.

3. Tool Design & The Agent-Computer Interface (ACI)

Models do not interact with operating systems directly; they interact with interfaces. The design of this Agent-Computer Interface (ACI) directly dictates agent reliability.

Tool Overhead Costs

Every tool introduced imposes two distinct context costs:

Ctool = Cselection + Cobservation

  • Cselection (Selection Difficulty): The cognitive burden of choosing the right tool and formatting arguments from context descriptions.
  • Cobservation (Observation Payload): The token cost and noise added to the context window by the tool's returned result.

High-Signal vs Low-Signal Tool Design

[ LOW-SIGNAL TOOL RESULT ]
Agent requests customer record --> API dumps 500 lines of JSON metadata, 
raw audit keys, image URLs, and pagination tokens into active context.
Result: Model gets confused by high noise and hits context limits.

[ HIGH-SIGNAL TOOL RESULT ]
Agent requests customer record --> Tool filters response:
{ "id": "USR-102", "status": "active", "balance": "$140.00" }
Result: Model acts immediately with zero token waste.

The Pitfall of Tool Bloat

Exposing six nearly overlapping operations (e.g., search(), find(), lookup(), query(), grep(), retrieve()) does not make an agent smarter. It widens the action space and creates ambiguous decision points where models frequently fail. Tool count is not a monotonic measure of agent capability.

Tool Errors are Model Observations & Formalizing Tool Error Schemas

When a tool operation fails, returning unstructured text or raw stack traces forces the model to guess at the failure mode. Production harnesses must return a structured error envelope with clear machine-readable codes and actionable recovery hints.

Here is a recommended TypeScript/JSON schema contract for tool execution results:

export interface ToolResultEnvelope<T = unknown> {
  // Discriminator status
  status: "success" | "error";
  
  // High-signal payload on success
  data?: T;
  
  // Structured error context on failure
  error?: {
    code: string;                 // e.g., "BRANCH_NOT_FOUND", "RATE_LIMITED"
    message: string;              // Human-readable summary for logging
    retryable: boolean;           // Direct hint: should the model retry?
    suggested_actions?: string[]; // Recommended corrective steps or tool calls
    details?: Record<string, any>;// Target context (e.g., valid branch names, parameter bounds)
  };

  // Execution metadata for the harness control plane
  meta: {
    tool_name: string;
    execution_time_ms: number;
    tokens_consumed?: number;
  };
}

Example Payload Returned to Model on Failure:

{
  "status": "error",
  "error": {
    "code": "BRANCH_NOT_FOUND",
    "message": "Git branch 'release/2.4' does not exist in the remote repository.",
    "retryable": true,
    "suggested_actions": [
      "Check available branches using git_list_branches",
      "Create the branch using git_create_branch"
    ],
    "details": {
      "requested_branch": "release/2.4",
      "available_branches": ["main", "develop", "release/2.3"]
    }
  },
  "meta": {
    "tool_name": "git_checkout",
    "execution_time_ms": 142
  }
}

Providing explicit suggested_actions and details transforms tool errors into self-correction guidance, preventing models from looping on identical invalid inputs.

Where Does MCP Fit?

The Model Context Protocol (MCP) standardizes client-server primitives for resources, prompts, and tools. However, MCP is an interoperability layer, not a complete agent loop. MCP does not specify context compaction, human approval routing, sandbox provisioning, or stopping conditions—those remain the domain of your harness.

4. Context Engineering & Memory Management

Context capacity may be expanding, but relevance always supersedes capacity. Irrelevant tokens compete for model attention and degrade reasoning performance.

CONTEXT COMPACTION FLOW
DURABLE SESSION HISTORY (100k Tokens on Disk)
  [ Turn 1 ] -> [ Turn 2 ] -> [ Turn 3 ] ... -> [ Turn 50 ]
                                 |
                        Harness Compaction
                                 v
  ACTIVE MODEL CONTEXT (8k Tokens in LLM Call)
  +-------------------------------------------------------+
  | System Prompt + Progressive Skill Guidance           |
  | Compacted Summary of Turns 1-45                       |
  | Unaltered Detail of Turns 46-50                       |
  | Active Tool Schemas                                   |
  +-------------------------------------------------------+

Active Context vs. Durable History

  • Durable History: An append-only session log stored externally on disk/database.
  • Active Context: The curated slice of information assembled specifically for the immediate LLM call.

Beyond Naive Summarization: Active Context Compaction Mechanics

As continuous conversation turns approach window limits, relying on simple LLM summarization often backfires by stripping fine-grained variable names, configuration values, or exact error logs needed later in the session. To maintain high recall without exceeding token budgets, modern harnesses implement Hierarchical Memory Management:

+-----------------------------------------------------------------------+
|                       DURABLE SESSION STORE                           |
|      (Append-only log of all raw execution turns & artifacts)        |
+-----------------------------------------------------------------------+
        |                                                       |
        v                                                       v
+-----------------------------------+       +---------------------------+
|     EPISODIC MEMORY (Vector)      |       |  SEMANTIC MEMORY (KV/Graph)|
|  Vector index over past tool outputs|     | Key entities, task specs, |
|  & observation payloads           |       | active state variables    |
+-----------------------------------+       +---------------------------+
        |                                                       |
        +-------------------------+-----------------------------+
                                  |
                                  v
+-----------------------------------------------------------------------+
|                    ACTIVE LLM CONTEXT WINDOW                          |
|  - System Instruction & Core Skill Definitions                       |
|  - Concise Semantic Memory Block (State, Target Objectives)           |
|  - Relevant Episodic Snippets (Retrieved via RAG on current step)     |
|  - Unaltered Recent Execution Window (Last N turns)                   |
+-----------------------------------------------------------------------+
  1. Durable Store (Disk/DB): Append-only storage for raw messages, code execution output, and raw tool outputs.
  2. Episodic Memory (Vector Store): Past tool observations and outputs are vectorized. If an agent needs an error log from 30 steps prior, the harness performs a vector-backed retrieve over episodic storage to inject just that snippet back into active context.
  3. Semantic Memory (Structured State): Essential state parameters (e.g., target branch, modified files, active database URL) are extracted into a key-value or graph representation that is updated deterministically, bypassing lossy text summarization.
  4. Recent Execution Window: The last N turns remain unaltered to preserve exact immediate flow and local context.

Progressive Disclosure & Agent Skills

Rather than dumping a 5,000-line documentation file into system instructions, modern harnesses use progressive disclosure:

  1. Provide a concise entry-point overview.
  2. Expose reusable packages of instructions and scripts (Agent Skills) that the agent dynamically loads only when relevant.

Warning on Skills: Skills should be treated as untrusted executable code. A 2026 empirical study across SkillsBench uncovered 307 skill-induced failures, showing that poorly designed or malicious skills frequently introduce logic failures or massive efficiency regressions.

5. Containment, Credentials, and Security Boundaries

Relying solely on "human approval prompts" or model self-restraint is a recipe for security vulnerabilities.

SECURITY ARCHITECTURE COMPARISON
[ VULNERABLE ARCHITECTURE ]
Model --> [ Sandbox containing SSH Keys & AWS Credentials ]
* Threat: Prompt injection forces agent to read and exfiltrate secrets.

[ CONTAINED HARNESS ARCHITECTURE ]
Model --> [ Isolated Sandbox (NO Secrets) ]
               | (Requests API Call)
               v
          [ Harness Proxy (Holds Credentials) ] --> [ External API ]

Approval vs. Containment

  • Human Approvals: Useful for high-stakes decisions, but users quickly develop approval fatigue (telemetry shows users approve roughly 93% of permission prompts blindly).
  • Containment: Hard infrastructural constraints (network egress allowlists, read-only filesystem mounts, network proxies) that limit damage even if prompt injection succeeds. In red-team testing, environmental containment held even when model-layer intent checks failed 24 out of 25 times.

Credential Isolation

Never place long-lived secrets or API tokens inside the sandbox where model-generated code runs. Instead, keep credentials in the harness control plane and proxy outbound requests. What never enters the sandbox cannot be exfiltrated.

Data Provenance

Not all context tokens carry equal trust. An agent observation can be conceptualized as:

oi = (content, source, trust_level, timestamp)

Text scraped from an untrusted web page or incoming email must be isolated from system instructions to prevent prompt injection attacks (as evaluated in frameworks like AgentDojo).

6. Execution Reliability: Verification, Idempotency, and Long-Running Work

When an agent reports, "I fixed the issue," that is merely a conversational claim—not proof.

VERIFICATION WORKFLOW
[ UNRELIABLE CLAIM ]
Agent Output: "I updated the auth middleware and fixed the bug."
Harness Action: Accepts response and closes task. (Failure Rate: High)

[ EVIDENCE-BACKED VERIFICATION ]
Agent Output: "I updated the auth middleware and fixed the bug."
Harness Action:
1. Spawns test runner: `pytest tests/test_auth.py` -> PASS
2. Inspects git diff -> Verified non-empty change
3. Checks endpoint status -> HTTP 200 OK
Harness Action: Confirms completion. (Failure Rate: Low)

Deterministic Verification Over Self-Reporting

Whenever possible, tie task completion to observable external ground truth:

  • Did reproduction tests pass?
  • Does the database state match expected records?
  • Does the build output assemble without warnings?

Use model judgment for open-ended assessment (like prose quality), but use deterministic checks (linters, unit tests, schema validators) for objective criteria.

Idempotency and Safe Retries

Automatically retrying failed actions is safe only when the operation is idempotent:

f(f(s)) = f(s)

  • Idempotent Actions (Safe to retry): set_user_status("active"), read_file(), overwrite_file().
  • Non-Idempotent Actions (Unsafe to retry blindly): charge_credit_card(), send_email(), create_github_issue().

If a network connection drops mid-flight, the harness knows it requested an action, but doesn't know if the world state committed. Blind retries on non-idempotent actions cause duplicate transactions. Harnesses must enforce idempotency keys, deduplication records, or read-before-retry checks.

Asynchronous & Long-Running Agent Workflows

Production tasks like end-to-end refactoring suites, cloud deployments, or continuous vulnerability remediation frequently outlive standard HTTP request connection limits. An agent harness executing long-running steps cannot rely on continuous synchronous execution. To support durable, multi-hour operations, the harness architecture must separate the Model Inference Loop from the Execution Plane using an event-driven worker framework (e.g., Temporal, Celery, or AWS Step Functions).

[ User Task Request ]
          |
          v
+-----------------------------------------------------------------------+
|                     HARNESS STATE ROUTER                              |
|  1. Persists state checkpoint to durable storage                       |
|  2. Emits execution payload to Message Broker (NATS/SQRS)             |
|  3. Releases active worker / HTTP connection                           |
+-----------------------------------------------------------------------+
                                  |
                                  v
+-----------------------------------------------------------------------+
|                     ISOLATED ASYNC WORKER                             |
|  Executes task inside containerized sandbox                           |
|  - Long-running build                                                 |
|  - Suite execution                                                    |
+-----------------------------------------------------------------------+
                                  |
                   (Task Completion / Webhook Event)
                                  v
+-----------------------------------------------------------------------+
|                   EVENT-DRIVEN RESUMPTION                             |
|  1. Ingests execution observation & exit state                        |
|  2. Reassembles Active Context from Session Store                     |
|  3. Triggers next iteration step in Model Inference Loop              |
+-----------------------------------------------------------------------+

Key Architecture Principles for Long-Running Agents:

  • State Checkpointing: Before triggering any long-running tool, the harness serializes the active state (st), pending steps, and tool execution IDs to a persistent storage layer.
  • Webhook & Callback Resumption: Long-running tool executions issue an asynchronous callback once finished. The harness listens for the incoming webhook payload, reconstitutes the context (ct+1), and triggers the next model evaluation cycle.
  • Graceful Suspension & Timeout Bounds: If a tool call exceeds pre-configured execution budgets, the harness safely suspends execution, updates session state with a structured TIMEOUT observation payload, and invokes model reasoning to choose a fallback path.

7. Multi-Agent Systems & Obsolete Harness Scaffolding

When Subagents Help (and When They Hurt)

Delegating tasks to subagents sounds appealing, but multi-agent coordination introduces overhead: communication latency, token expense, and state conflicts.

A Google Research study evaluating 180 agent configurations revealed a striking contrast:

  • Centralized Coordination (Finance Task): Boosted performance by +80.9% over single-agent baselines.
  • Multi-Agent Delegation (Sequential Task): Degraded performance by -39% to -70% due to coordination friction.

Rule of thumb: Delegate to subagents only when subtasks are cleanly isolated and can run in parallel.

Harness Assumptions Expire

A harness often contains workaround scaffolding designed for a specific model's weaknesses. As underlying models improve, those harness workarounds can become dead weight.

Case Study: Anthropic reported that complex context-resetting and sprint-planning scaffolding built for older models cost $200 across a 6-hour run. When tested with newer models (like Opus 4.6), much of that extra scaffolding proved unnecessary—a lean run achieved comparable quality in 20 minutes for $9.

Regularly evaluate and re-test your harness assumptions whenever you upgrade the underlying language model.

The Production Checklist

When evaluating your agent harness for production readiness, ensure you can answer these key questions:

  • ✓ Control Boundary: Is the harness control plane decoupled from untrusted code execution inside the sandbox?
  • ✓ ACI Design: Are tools concise, distinct, and returning high-signal observations rather than massive data dumps?
  • ✓ Error Semantics: Do failed tool calls return structured, actionable feedback to the model?
  • ✓ State Separation: Is durable session history stored safely outside active context?
  • ✓ Containment: Are network egress, filesystem access, and credentials restricted by infrastructure rather than prompt prose?
  • ✓ Verification: Is task completion verified against ground-truth state changes rather than LLM self-reports?
  • ✓ Retry Safety: Are non-idempotent tool calls protected by idempotency keys or deduplication logic?
  • ✓ Resource Budgets: Does the harness enforce strict turn, token, timeout, and cost bounds?

No comments:

Post a Comment