If you’re still repeatedly refining prompts for the stability of production-grade AI Agents, the conclusion of this article may overturn your intuition: by 2026, what truly creates differentiation is no longer prompt techniques, but AI Agent Harness Engineering. The core reason is simple and brutal—the model is responsible for “deciding the next step,” but ensuring the system can continuously, reliably, and audibly “get things done” in real environments depends on the control harness built around the model. In other words, Agent = Model + Harness, and it is the latter that determines whether an Agent can be deployed and scaled. Harness Engineering turns a bare model that easily hallucinates or goes off track into an execution system with permission boundaries, error recovery, state persistence, and observability through context management, tool invocation, constraint mechanisms, and validation loops. This is why countless teams impress at the demo stage and collapse after launch: the problem isn’t that the model isn’t smart enough, but that the Agent Infrastructure lacks control loops, leading to state loss, tool misuse, and uncatchable errors. For readers, this implies a key shift: if you care about Production AI Agents rather than one-off demos, your investment should move from “writing better prompts” to building system capabilities such as Agent Harness, Model Harness, and Agent Validation Loops. Model capability sets the ceiling, but Harness Engineering determines the floor and sustainability; the former makes an Agent look smart, while the latter decides whether it can run long-term in complex businesses without losing control. This is the real moat forming in today’s top-tier Agent architectures.
What Is AI Agent Harness Engineering (Core Definition and Formula)
AI Agent Harness Engineering is about building a layer of runtime infrastructure around an AI model. This layer provides context, invokes tools, applies constraints, and corrects outputs through validation loops, enabling the model to complete tasks in real production environments in a stable, controllable, and recoverable way.
The most important conclusion can be compressed into a single formula:
Agent = Model + Harness
This does not mean “the model is unimportant.” Rather, it means: the model is only responsible for generating the next judgment or action; what truly turns it into a usable agent is the external control harness. As emphasized in Martin Fowler’s description of harnesses, a harness essentially refers to “everything except the model itself.” And Addy Osmani’s summary is even more direct: a bare model is not an agent—only after being wrapped with state, tools, feedback loops, and constraints does it start to resemble an executable system.
From an engineering perspective, a harness typically takes on four categories of responsibilities:
- Context
Determines what the model “sees” in a given turn: user goals, historical state, business rules, task stage, and external retrieval results. - Tools
Determines what the model “can do”: query databases, call APIs, search knowledge bases, write files, send messages, and execute code. - Constraints
Determines what the model “must not do”: permission boundaries, parameter whitelists, sandboxes, human approvals, and output format requirements. - Validation Loops
Determines how the system “detects and corrects errors”: structural validation, rule checks, tests, retries, rollbacks, and human review.
If the model is likened to a “brain,” then the harness is more like a nervous system + tool interfaces + braking system + dashboard. The core value of a production-grade agent often lies not in “whether it can speak,” but in “whether it can keep doing the right things in complex environments.”
Dimension | Bare Model Invocation | Agent with Harness |
|---|---|---|
Input | One-off prompt | Dynamic context + historical state + external data |
Capability | Can only output textual suggestions | Can call tools and execute multi-step tasks |
Control | Mostly prompt-based constraints | Permissions, rules, sandboxes, approval mechanisms |
Error Handling | Humans fix errors afterward | Can validate, retry, roll back, or terminate |
Production Readiness | Demos are easily impressive | Better suited for real business workflows |
A simple example makes the difference clear.
Suppose you want to build an agent that “automatically generates weekly reports.”
With a bare model invocation, the usual approach is to stuff “please generate a weekly report based on this week’s project progress” into a prompt. It might produce text that looks decent, but problems quickly emerge:
- It doesn’t know which system to pull real data from;
- It may fabricate progress or omit blockers;
- It cannot verify whether numbers match Jira, Git, or CRM;
- Once the output format changes, downstream systems can no longer consume it.
A harnessed agent, by contrast, runs through a much more stable process:
- Pull this week’s ticket status from the project management tool;
- Aggregate code commits and release records from the code repository;
- Generate a structured draft based on a template;
- Validate that key numbers, owners, and dates are complete;
- Flag anomalies and send them for human confirmation if necessary;
- Finally output the report or write it into the enterprise system.
You will find that what truly makes the workflow reliable is not “writing a more elaborate prompt,” but data integration, tool invocation, permission control, and closed-loop validation. This is precisely the job of the harness.
Therefore, Harness Engineering is the key engineering layer that solves the problem of “demos that work but production systems that don’t.” Models determine the upper bound, while harnesses determine whether the system can be deployed, can keep running, and can be reined in when something goes wrong. Nearly all agent architecture discussions that follow can essentially be traced back to this central principle: don’t just optimize model outputs—design the runtime environment in which the model operates.
Why Prompt Engineering Cannot Support Production-Grade Agents
Prompts matter, but they solve “what to say in this turn”, not “how the entire system runs.”
Production-grade Agents fail, usually not because the prompts aren’t elegant enough, but because you’ve shoved capabilities that belong at the system layer onto the model, forcing it to “remember on its own, constrain itself, and correct itself” across conversations.
Put more directly:
Prompt Engineering optimizes single-step reasoning quality; Harness Engineering manages multi-step execution reliability.
Once tasks enter the real world—calling tools, preserving state across multiple turns, handling interruptions, enforcing permissions, validating results, recording execution traces—relying on prompts alone quickly hits a ceiling. Even research focused on real Agent workflows has begun separating “model capability” from “harness configuration,” because final performance is clearly not determined by the model alone; the execution layer’s context, tools, state, constraints, tracing, and recovery mechanisms are themselves performance variables.
The Boundary of Prompt Engineering Is Exactly the Starting Point of Production Systems
No matter how refined a prompt is, it’s hard for it to reliably shoulder the following responsibilities:
- State Management
Models do not natively possess reliable long-term state machines. As multi-step tasks grow longer, they forget goals, skip steps, repeat work, or overwrite prior conclusions. - Tool Invocation Control
Prompts can “suggest” how a model should use tools, but it’s difficult to enforce that tools are called only under specific conditions, with correct parameters, and under least-privilege principles. - Error Recovery
Real systems will inevitably encounter API timeouts, dirty tool outputs, permission denials, and format errors. Prompts can request “retry on failure,” but without runtime control, it’s hard to implement verifiable rollback, retries, and branch handling. - Long-Running Task Execution
Long tasks aren’t solved by simply making prompts longer. Context windows overflow, tasks span sessions, environments drift, and external resource states change. Anthropic’s experience with long-running Agents clearly points out that even with context compression, high-level prompts alone are insufficient to reliably push tasks to production-usable outcomes.
Common Failure Scenarios: It’s Not That the Model Is “Dumb,” but That the System Didn’t Catch It
These issues are often ignored in demos but erupt all at once in production:
Failure Scenario | Typical Behavior with Prompts Alone | Missing System Capability |
|---|---|---|
Hallucinated actions | Inventing nonexistent APIs, database fields, or tool parameters | Context injection, tool schemas, pre-execution validation |
Infinite loops / pseudo-effort | Repeated searching, summarizing, or calling the same tool | Step limits, stop conditions, state machines |
Context overflow | Forgetting user goals, missing constraints, overwriting completed steps | Memory management, summary compression, task decomposition |
Long-task interruption | Restarting from scratch after failure or stopping at a half-finished state | Checkpoints, recovery points, progress tracking |
Plausible-looking but wrong output | Generating “answers that look right” with no interception mechanism | Tests, validators, secondary review |
These failure modes closely match observations from engineering practice. For example, deepset’s analysis of production Agents broadly categorizes failures into three layers: context failure, constraint failure, and verification failure. This is critical, because it shows that many problems can’t be fixed by tweaking prompts further; instead, they need to be addressed separately at the levels of context engineering, constraint mechanisms, and verification loops.
A Typical Misconception: Confusing “Can Talk” with “Can Execute”
Consider a simple but common task: automatically generate a weekly report and send it to the team lead.
An Agent relying solely on prompts:
1. Read chat input: “Help me compile this week’s project report and email it”
2. The model decides on its own what information is needed
3. It chooses whether to call calendar, ticketing, or email tools
4. It generates a report that looks reasonable
5. It sends it directly, or claims “completed”This workflow may look impressive in a demo, but it’s risky in production:
- It may miss a data source
- It may mark unfinished items as completed
- It may send to the wrong recipient
- It may terminate after an email send failure
- It may mistake “draft generated” for “task completed”
An Agent with a Harness:
1. Create a task ID, record goals, recipient scope, and deadline
2. Pull Jira / Git / Calendar data via whitelists
3. If key fields are missing, enter a clarification branch; guessing is not allowed
4. Generate a draft report
5. Run format validation and fact checks
6. High-risk actions (sending email) require confirmation or policy approval
7. Retry or roll back on send failure, and write execution logs
8. Output final status: success / partial success / requires human interventionThe difference isn’t “who wrote the more advanced prompt,” but that the latter turns the task into a controllable, recoverable, auditable execution process.
Why the Prompt-First Path Always Gets Stuck at “Demo Works, Production Doesn’t”
Many teams start down the same path:
Better system prompts
→ Longer few-shot examples
→ Stricter output format requirements
→ More complex “retry if failed” instructions
→ More patchwork rulesIn the short term, results improve; in the long term, the system becomes brittle. There are three reasons:
- Rules exist only in natural language, not as hard constraints.
Writing “do not modify files outside the scope” doesn’t mean the system actually forbids it. - Exception handling stays at the semantic level, not the execution level.
You ask for “retry on failure,” but who decides what counts as failure? How many retries? Should state be cleaned up before retrying? - Behavior is unobservable, and failures are hard to localize.
When something breaks in production, it’s difficult to tell whether it was missing context, tool misuse, or an absent validation step.
This is why production environments need not just “people who are good at writing prompts,” but engineering capability to design runtime control planes.
What Production-Grade Agents Need Is Not Longer Prompts, but Complete Control Loops
A minimally viable production control loop should include at least:
- Observability: logging inputs, decisions, tool calls, latency, and failure reasons at every step
- Retry strategies: distinguishing retryable from non-retryable errors to avoid blind loops
- Constraint mechanisms: permission boundaries, tool whitelists, budget caps, step limits
- Verification mechanisms: format validation, test execution, fact checking, result review
- State persistence: allowing tasks to pause, resume, and continue instead of restarting every time
This isn’t theoretical fastidiousness; it’s a production necessity. Prototype environments usually have clean inputs, single paths, and low error rates. Real environments bring nondeterminism, external system volatility, malicious inputs, and silent quality degradation. Engineering discussions on production Agent metrics emphasize that prototype success doesn’t equal production reliability—many issues only surface under real usage.
The Criterion Is Simple: Does Your Agent “Answer,” or Does It “Complete”
If an Agent relies mainly on prompts to maintain correctness, what you usually get is a model interface that generates explanations.
If an Agent has state, constraints, recovery, validation, and observability, you’re much closer to an execution system that reliably completes tasks.
So Prompt Engineering hasn’t become obsolete; it has simply returned to its proper place:
an input-layer optimization for Agents, no longer the main pillar of production reliability.
What truly determines whether a system can move from demo to production is the Harness.
Core Components of an Agent Harness

If you think of the model as a “reasoning engine,” then the Harness is the runtime control system wrapped around it. As Addy Osmani’s breakdown of Agent Harness Engineering points out, what truly determines whether an Agent is usable in production is often not the model itself, but the surrounding outer system of context, tools, constraints, feedback, and observability built around the model.
A typical Agent Harness can be viewed as the following execution chain:
User goal
↓
Context Management (providing the model with the right, controllable task context)
↓
Tooling Layer (turning “can talk” into “can act”)
↓
Constraints & Sandboxing (limiting permissions, isolating risk)
↓
Validation Loops (checking results, finding errors, triggering retries)
↓
Observability (recording what happened, why it failed, and how much it cost)
↓
Stable Agent behaviorThese layers are not independent plugins, but together form the Agent’s “control harness”: they turn model outputs into executable actions, and then feed execution results back into the next round of decision-making. Remove any one of them, and the system may look smart in a demo yet quickly spiral out of control in production.
Component | Core Responsibility | Common Implementation Approaches |
|---|---|---|
Context Management | Determines what the model “sees, remembers, and ignores” at any moment | system prompts, task state, summary compression, retrieval-based memory, conversation state persistence |
Tooling Layer | Provides callable capabilities instead of just generating text | function calling, API wrappers, database/browser/filesystem tools, MCP server |
Constraints & Sandboxing | Limits what the agent can do to avoid overreach or destructive actions | permission lists, allowlists, approval gates, virtual filesystems, container/sandbox execution |
Validation Loops | Verifies results at each step and corrects errors instead of letting them compound | schema validation, test replay, lint/typecheck, self-checks, retries and rollbacks |
Observability | Makes system behavior diagnosable, auditable, and optimizable | logs, traces, tool-call records, cost/latency metrics, loop detection |
State / Orchestration | Connects all of the above, managing task flow, pause/resume, and multi-step execution | planners, subtask scheduling, handoffs, checkpoints, resume mechanisms |
From an engineering perspective, these six layers can be further reduced to three questions:
- What does the model know? — decided by Context Management.
- What can the model do? — determined by the Tooling Layer and permission boundaries.
- What happens when the model is wrong? — closed by Validation Loops and Observability.
This is why “bare tool calling” is not yet a complete harness. A wrapper that only provides tool invocation usually solves “can an action be executed,” but not “is the action safe, is the state continuous, are the results trustworthy, and how do we recover after failure.” On this point, many engineering practices also distinguish maturity levels from “bare calls” up through “session-aware, rollback-capable, recoverable harnesses.” Related architectural analyses consistently emphasize that state persistence, sandbox isolation, and context management are the dividing line for production-grade Agents.
A practical rule of thumb is: if your Agent is only “take a prompt → call the model → output an answer,” it is not yet a complete production system; if it can already carry state, call tools, execute under constraints, validate results, and leave an auditable trail, then you are truly entering the scope of Harness Engineering.
As the following sections break down each module, you can think of them as a clearly divided control harness:
- Context Management is responsible for “feeding the right information”
- Tooling Layer is responsible for “providing hands and feet”
- Constraints & Sandboxing is responsible for “installing fuses”
- Validation Loops is responsible for “rechecking after execution”
- Observability is responsible for “making incidents traceable”
The differences among top-tier Agents usually come down to whether this harness is designed with sufficient precision, robustness, and alignment to the specific task.
Context Management: Memory, State, and Context Window Control
In production-grade agents, the core of context management is not “letting the model remember more,” but deciding what should enter the window, what should be persisted, and what should be discarded. Many demos fail not because the prompt isn’t fancy enough, but because once a task exceeds 10–30 turns, conversation history starts crowding the context window, old information conflicts with new state, and the model treats already-invalid plans as current facts. As Anthropic’s summary on long-running agents clearly points out: relying solely on context compaction is insufficient to support long tasks; truly usable systems also need structured progress files, state handoff, and recovery mechanisms.
A practical approach is to split context into three layers, rather than stuffing everything back into the prompt:
Layer | What to Store | Lifecycle | Typical Implementation |
|---|---|---|---|
Short-term working context | Recent dialogue turns, current subtask, latest tool results | Within the current session | rolling context |
Long-term retrievable memory | Historical decisions, user preferences, past cases, knowledge snippets | Across sessions | vector memory / retrieval store |
Task state object | Structured facts of the current task: goals, steps, completion status, failure reasons, artifact paths | Task-level | JSON / DB row / state store |
Among these three, the most easily overlooked yet most critical is the task state object. Vector memory is suitable for “finding related content,” but not for serving as the “current source of truth.” For example, if an agent writes status=waitingforapproval into the state at step 12, but vector retrieval pulls back “continue deployment” from step 4, then without a clear state object the model may keep running forward and skip the approval gate entirely.
There are three common context strategies, which usually need to be combined:
- Rolling context
Keep only the most recent high-value messages, and compress earlier history into a summary.
Suitable for: multi-step execution, limited windows, maintaining current coherence.
In practice, don’t do a “full-text summary,” but an execution-oriented summary, which should at least retain:
- Current goal
- Completed steps
- Pending steps
- Most recent failure and error messages
- Explicit constraints (e.g., cannot write to production DB, must run tests first)
Milvus’s discussion on harness engineering mentions two common techniques: compaction (compress history and continue running) and context reset (clear the window and restart using structured handoff documents). The former is cheaper; the latter is cleaner, but requires sufficiently complete handoff artifacts.
- Vector memory
Write information that “might be useful later” into a retrievable store, and recall it semantically when needed.
Suitable for: cross-session preferences, knowledge snippets, recurring problem patterns.
But you must control what gets written—don’t store every bit of the model’s self-talk. A more robust approach is to store only:
- Explicitly stated user preferences
- Verified facts
- Reusable solutions
- Conclusions confirmed by humans or programs
- Task state object
Use a structured object to record task execution state, read and written at each step.
Suitable for: long tasks, resumable execution, multi-tool orchestration.
It should be serializable, auditable, and recoverable, rather than being “implicit memory” hidden in the prompt.
A simple engineering pattern looks like this:
taskstate = {
"taskid": "deploy4821",
"goal": "修复结算服务超时并完成灰度发布",
"currentstep": "analyzelogs",
"completedsteps": [],
"blockedon": None,
"artifacts": {
"logreport": None,
"patchpr": None
},
"constraints": [
"禁止直接改生产配置",
"发布前必须通过集成测试"
],
"lasterror": None
}
recentmsgs = loadrecentmessages(limit=8)
summary = loadrunningsummary(taskid="deploy4821")
memoryhits = vectorsearch(query=taskstate["goal"], topk=3)
promptcontext = {
"taskstate": taskstate,
"summary": summary,
"recentmsgs": recentmsgs,
"memoryhits": memoryhits
}
response = agent.run(promptcontext)
if response.toolresult:
updatetaskstate(taskstate, response.toolresult)
if tokencount(recentmsgs, summary) > COMPACTTHRESHOLD:
summary = compact(recentmsgs, taskstate)
archiveold_messages()There are two key points in this pattern:
- What the model sees is a “trimmed context,” not infinite history;
- The real task progress lives in
task_state, not in the model’s own recollection.
To give a more concrete example: a bug-fixing coding agent needs to work across 20 steps. In step 1 it reads requirements, step 2 runs tests, step 3 locates files, step 4 modifies code, step 5 reruns tests. If you rely only on chat history, by step 15 the model may have forgotten “which test initially failed” or “which files were just modified.” A more robust approach is to update state at every step:
{
"goal": "修复 checkout timeout",
"currentstep": "runregressiontests",
"completedsteps": [
"readbugreport",
"reproduceissue",
"patchretrylogic"
],
"artifacts": {
"failingtest": "tests/testcheckouttimeout.py::testretry",
"modifiedfiles": [
"checkout/retry.py",
"checkout/client.py"
]
},
"last_error": "integration test failed: payment gateway mock mismatch"
}This way, even if a context reset occurs, a new agent instance can recover from this state instead of guessing from a long stream of natural language.
In practice, I recommend using the following decision framework:
- Affects current execution decisions → Put into short-term context
- May be relevant later, but not current truth → Put into vector memory
- Must be accurate, recoverable, and auditable → Write into the task state object
- Outdated, redundant, or purely noisy → Delete or archive
The four most common mistakes are:
- Stuffing all history directly into the prompt: short-term it feels “more complete,” long-term it inevitably becomes more expensive, slower, and messier.
- Treating vector memory as a database: retrieval gives you “similar content,” not “current real state.”
- Doing only summaries without state persistence: summaries are good for compression, not for precise process control.
- Failing to distinguish facts, plans, and drafts: a plan proposed in the previous turn does not mean it has been successfully executed.
If you remember just one sentence, let it be this:
Context management is not a “window-expansion trick,” but an information-layering design at the harness level.
The gap between top-tier agents often lies not in prompt wording, but in whether they can, after dozens of turns and hundreds of tool calls, still know: which step they are on, which facts are trustworthy, which memories to retrieve, and which history to forget.
Tooling Layer: How Agents Safely Call External Tools

Without tools, an Agent is essentially still “a model that generates text.” With tools, it truly gains the ability to read systems, query data, and execute actions. Order lookup, database retrieval, web search, code execution, ticket creation, calling internal APIs—these are not things a prompt alone can accomplish; they require a tool execution layer built outside the model by the Harness. Many demos fail not because the model cannot “think,” but because it is allowed to directly output operations that look reasonable yet are actually non-executable or high-risk.
From an engineering perspective, safe tool invocation is usually a fixed closed loop, not “whatever the model thinks of gets executed directly”:
- Declare tool contracts: Each tool has a clear name, description, parameter schema, and return format.
- The model only selects tools and fills parameters: It outputs structured
tool_call, not arbitrary shell commands. - The Harness validates and authorizes: It checks parameter types, ranges, permissions, budgets, and context state.
- The executor runs the tool: The actual call is completed in a sandbox, read-only connection, or restricted network.
- Results are returned to the model: Raw or normalized results are sent back so the model can proceed to the next step.
A minimal viable tool definition usually looks like this:
{
"name": "searchorders",
"description": "Query recent orders by email",
"inputschema": {
"type": "object",
"properties": {
"email": { "type": "string" },
"limit": { "type": "integer", "minimum": 1, "maximum": 20 }
},
"required": ["email"]
}
}Here, the schema is not just documentation for the model, but an execution contract for the Harness:
- It constrains parameter shapes, preventing dirty inputs like
"limit": "all"; - It makes calls observable, enabling statistics on each tool’s error rate, latency, and retries;
- It allows permission control to apply to specific fields, rather than staying at the level of “please be careful” in the prompt.
A typical invocation loop can be written like this:
tools = [searchorders, getshipment, websearch]
state = {
"toolbudget": 8,
"approvedwriteops": False,
"userrole": "supportreadonly"
}
while state["toolbudget"] > 0:
response = llm.chat(messages, tools=toolschemas)
if response.type != "toolcall":
return response.text
call = response.toolcall
validateschema(call.name, call.args) # Parameter validation
authorize(state["userrole"], call.name, call.args) # Permission check
checkbudget(state, call.name) # Call budget / loop detection
result = executetool(
call.name,
call.args,
timeout=5,
sandbox=True,
maxrows=50
)
state["toolbudget"] -= 1
messages.append({"role": "tool", "name": call.name, "content": result})The key point behind this flow is: the model never directly touches real systems. It can only request “which tool to call, with what parameters”; whether it can actually execute is decided by the Harness. This is also the foundational pattern used by many production systems. AIQuinta’s summary of this loop captures it well: task enters → model requests tools → Harness executes → results returned → model updates plan.
In real systems, it’s best to divide tools into three layers instead of throwing everything at the model:
Tool Type | Examples | Default Strategy |
|---|---|---|
Read-only tools |
| Open by default, limit rows/pages/timeouts |
Controlled write tools |
| Parameter allowlists, human confirmation when needed |
High-risk execution tools |
| Sandbox execution, strong approval, least privilege |
For example, database access should not be exposed as a single all-purpose run_sql. A more robust approach is to split it into:
querycustomerorders(email, limit)getorderdetail(order_id)listrefundstatus(order_id)
Such “narrow tools” are easier to validate and easier to enforce permission boundaries than “omnipotent tools.” Browser tools are similar: search_web, open_page, and extract_text are usually much safer than a browser agent that is allowed to freely click and submit forms.
Here’s a more realistic scenario: a support Agent handles “Help me check the recent orders for alice@example.com and determine whether they have shipped.”
- The model first calls
search_orders(email="alice@example.com", limit=3) - The Harness queries the order system via a read-only API and returns a list of order IDs
- The model then calls
getshipment(orderid="O12345") - The Harness queries the logistics interface and returns the status
in_transit - The model generates a natural-language response based on the tool results
There are two common pitfalls here:
- Pitfall 1: Letting the model write SQL or shell commands directly
- It looks flexible, but is actually the hardest to control.
- In production, it’s far more common to have “the model selects constrained tools + the Harness executes fixed logic.”
- Pitfall 2: Treating permission control in the prompt as sufficient
- Prompts can only “remind,” not truly block incorrect execution.
- What actually works is allowlists, timeouts, approvals, and sandboxes at the Harness layer.
Most risks also originate at this layer, and they are mostly engineering problems rather than language problems:
- Infinite or looping calls
The Agent repeatedly doessearch -> summarize -> search, burning through API quotas.
At a minimum, you should have:
maxtoolcalls- Detection of repeated calls with identical parameters
- A total execution time budget
- Per-tool cooldowns or deduplication caches
- Parameters are valid but semantically wrong
For example, passing acustomer_idas anorder_id; the schema won’t fail, but the business logic will.
Solutions include:
- Narrowing tool responsibilities
- Using stricter field names and enums
- Adding business validation before execution, such as “does this
order_idbelong to the current user?”
- Destructive operations triggered accidentally
Actions like dropping databases, force-pushing branches, or sending mass emails should never be reachable in one hop from the model.
Addy Osmani’s advice on Harness hooks is very practical: attach hooks before and after tool calls to directly block commands likerm -rf,git push --force, andDROP TABLE, and require approval before pushes or opening PRs. - Loss of control over code execution environments
run_codemay look like “just run a script for me,” but it is actually the most dangerous, because it turns model output into real execution.
For single-tenant internal scenarios, containers are already much better than bare metal; for multi-tenant or high-risk code execution, Blaxel’s discussion on microVM isolation is closer to production requirements: limit CPU, memory, file systems, and network egress, and treat the execution environment as untrusted by default.
A simple but effective set of tool-layer rules usually includes at least:
- Expose only necessary tools; don’t give all internal APIs to the Agent
- Separate reads and writes, default to read-only
- Per-tool timeouts
- Per-round / per-task call budgets
- Structured tool return values, not just large blocks of text
- Approval required for high-risk actions
- Log all calls: tool name, parameter summary, duration, result status, retry count
- Zero trust for external content: search results, web pages, and code repositories may all contain prompt injection
If you can only do one thing, do this first: do not let the model directly execute arbitrary commands; only allow it to call tools you have defined, validated, and can audit.
This step is often the dividing line between an Agent that “can demo” and one that “can go live.”
Constraints and Sandboxing: Preventing Agents From Running Amok

Once you actually put an agent into production, you’ll quickly discover that prompt constraints like “don’t do dangerous operations” barely count as constraints at all.
A model can only “tend to comply,” but it cannot be trusted as the sole execution boundary. The boundary must live in the harness: validation before tool calls, runtime budgets, isolated execution environments, approval gates, and rollback mechanisms. Otherwise, a single bad plan, a prompt injection, or a typo in a tool parameter can escalate from “gave a wrong answer” to “broke the system.”
A practical rule of thumb is:
Prompts define intent; the harness defines permissions.
If permission control exists only in the system prompt instead of being enforced in the executor, it isn’t control—it’s just a suggestion.
In production, the most common constraints usually include at least four layers:
- Token / turn limits
Control how many turns a single task can run and how many tokens it can consume, to avoid agents burning money in a loop of “can’t figure it out → keep trying → keep failing.” - Tool permission control
Not “giving the model a shell,” but giving it a set of tools with explicit schemas and allowlists. For example, it canread_fileorrun_tests, but not directlyrm -rf; it can query a read-only database, but not execute write operations. - Execution budgets
Including maximum tool calls, maximum wall-clock time, maximum API cost, and maximum browser steps. Exceed the budget and interrupt—don’t let the agent attempt infinite self-repair. - Sandboxed isolation environments
All model-generated code, scripts, and scraping behavior should be treated as untrusted by default. At a minimum, run them in a temporary container; in multi-tenant or high-risk scenarios, a zero-trust isolation model such as a microVM-level sandbox is more robust.
You can think of this as a “tool-call firewall”:
POLICY = {
"maxturns": 20,
"maxtoolcalls": 50,
"maxruntimesec": 300,
"maxcostusd": 2.0,
"tools": {
"dbquery": {"mode": "readonly"},
"websearch": {"domainsallowlist": ["docs.python.org", "api.example.com"]},
"shell": {"allow": ["pytest", "python", "ls", "cat"], "deny": ["rm", "sudo", "curl | sh"]},
},
"requireshumanapproval": ["deployprod", "sendemail", "dbwrite", "gitpushmain"]
}
def executetoolcall(call, state):
enforcebudget(state, POLICY) # turns / cost / tool calls / runtime
validatetoolname(call, POLICY["tools"]) # check allowlist
validateargs(call) # argument schema validation
checkpermission(call, POLICY) # read-only, domain allowlist, command allowlist
mayberequireapproval(call, POLICY) # human confirmation for high-risk actions
result = runinsandbox(
call,
cpulimit="2 cores",
memlimit="2GiB",
fsscope="/workspace/session123",
networkallowlist=["api.example.com"]
)
logevent(call, result, state)
detectloop(state, call, result)
return resultIn this flow, the model never receives “raw permissions.” It can only propose tool calls; before execution, the harness performs five actions:
- Check whether budgets are exceeded;
- Validate tools and parameters;
- Verify permissions;
- Execute inside a sandbox;
- Record results and perform anomaly detection.
This is fundamentally different from “reminding the model to be careful” in a prompt.
Without These Constraints, How Does Loss of Control Usually Happen?
The most common cases aren’t dramatic “hacker attacks,” but very ordinary engineering accidents:
- Infinite loops: to fix a failing test, the agent repeatedly
runtests -> editfile -> run_tests, making hundreds of tool calls in 20 minutes; - Erroneous write operations: intending to query orders, but accidentally running an UPDATE SQL statement;
- Environment contamination: one task installs dependencies or changes configs, and the next task inherits the dirty state, making issues harder and harder to reproduce;
- External system abuse: a browser agent gets stuck on pop-ups, redirects, or CAPTCHA pages and keeps retrying;
- Prompt injection derailment: after reading a malicious repository README or webpage, the agent starts accessing forbidden domains or executing dangerous commands.
The difference between session-aware harnesses and sandboxing puts it bluntly: without persistent state, rollback, and sandboxes, agent errors often become permanent side effects, leaving humans to clean up the mess manually.
In Practice, Write Rules as “Executable Policies,” Not Team Conventions
This set of rules is far more effective than a single line like “please operate cautiously”:
agentpolicy:
limits:
maxturns: 20
maxtoolcalls: 50
maxruntimesec: 300
maxcostusd: 2.0
tools:
dbquery:
access: readonly
browseropen:
domainsallowlist:
- app.internal.example
- docs.example.com
shell:
commandallowlist:
- python
- pytest
- pip show
- ls
- cat
commanddenylist:
- rm
- sudo
- chmod 777
- curl
- wget
sandbox:
ephemeralworkspace: true
networkegressdefault: deny
cpulimit: 2
memorymb: 2048
filesystemroot: /tmp/agent-run
approvals:
- gitpushmain
- deployprod
- dbwrite
- sendexternalemailSeveral details here are critical:
- Default deny is much safer than default allow;
- Read-only tools first, making “write” a privileged exception;
- Ephemeral workspaces, where each run gets a fresh directory that is destroyed afterward;
- Network allowlists, rather than unrestricted outbound access;
- Destructive actions require human confirmation, such as production deploys, database writes, sending emails, or pushing to the main branch.
Many teams add a gatekeeper here: when an agent triggers a high-risk action, the harness pauses execution and sends a Slack/CLI confirmation request. This kind of “approval gate” is a practical compromise, and some production practices treat it as a standard component rather than an afterthought.
Sandboxing Is More Than “Just Use Docker”
For many internal single-machine tools, containers are already far better than running on bare metal. But if your scenario includes any of the following, you should seriously consider stronger isolation:
- Multi-tenancy;
- Executing model-generated code;
- Allowing network access;
- Access to real repositories, secrets, or customer data;
- Running agents as long-lived background tasks.
The reason is simple: LLM-generated code should be treated as untrusted by default.
For high-risk scenarios, zero-trust sandboxes and microVM isolation are more robust than standard containers sharing a host kernel, at the cost of increased infrastructure complexity.
A pragmatic layered approach looks like this:
Scenario | Minimum Recommendation | More Robust Option |
|---|---|---|
Local personal experiments | Docker container + temporary directory | Container + network allowlist |
Internal team tools | Container + resource limits + approval gates | One container per task + snapshot rollback |
Multi-tenant / high-risk code execution | Strongly isolated sandbox | microVM + zero-trust network policies |
Constraints Aren’t Just About “Blocking,” They’re About “Observability”
Limits without observability will still lead to loss of control. At a minimum, you should track these metrics:
- Tool calls per task;
- Error rate per tool;
- Average task duration;
- Loop detection trigger count;
- Number of calls rejected by policy;
- Frequency of human approval triggers.
If an agent averages 80 tool calls per task, or a particular tool has an error rate consistently above 30%, the problem is usually not that the model is “dumb,” but that the harness design is flawed: tools are too coarse-grained, parameter schemas too vague, too many actions are allowed, or explicit validation is missing. Some production hardening checklists list loop detection, tool error rates, and latency logging as mandatory items.
A final, very practical takeaway: don’t think of “preventing loss of control” as an extra demand from the security team—it is fundamentally reliability engineering.
Agents aren’t harder to control because they’re “smarter,” but because they start touching real systems. Once they can write files, run commands, query databases, or click through browsers, constraints and sandboxing stop being optional and become core infrastructure of the harness.
Validation Loops: Enabling Agents to Self-Check and Self-Correct

A validation loop essentially turns a single Agent output into an engineering loop that is verifiable, reversible, and repairable—rather than “generate once and deliver directly.” In production environments, the most common structure looks like this:
Generate
↓
Validate
↓
Repair
↓
Re-validate
↓
Pass / Terminate / Escalate to HumanThe key to this mechanism is not “making the model better at reflection,” but subjecting outputs to external constraints. Many stability issues are not because the model can’t write, but because no one—or no program—checks immediately after it writes: whether the syntax is correct, whether interfaces exist, whether tests pass, whether the page still runs, or whether security rules are violated.
A practical validation loop usually includes four stages:
- Generate a candidate result
The Agent first completes a minimal, checkable unit, rather than producing everything at once. For example, modifying a single function, adding a set of SQL statements, or writing one API handler. - Run validators
The harness triggers external checks instead of relying only on the model’s “self-evaluation.” Validators can include:
- Rule checks: JSON schema, output format, field completeness, permission rules, forbidden command lists
- Static checks: linting, type checking, AST rules, SQL syntax checks
- Programmatic tests: unit tests, integration tests, API contract tests, UI smoke tests
- Model review: using another reviewer prompt to evaluate output against a clear rubric
- Environment validation: starting services, running a real request, checking whether pages error
- Feed structured failure signals back to the Agent
Don’t just return “it failed.” Return actionable error context, such as:
- Failed test names
- Error stacks
- Violated rule IDs
- Expected vs. actual values
- Relevant files and line numbers
- Limited repair and retry
The Agent fixes issues based on the failure signals, then re-enters validation. There must be retry limits, termination conditions, and human takeover conditions; otherwise, it’s easy to fall into low-value loops.
Here is a most typical example: a code-fixing Agent.
Task: Fix a bug in a backend API
1. Agent reads the issue and related files
2. Agent modifies the code
3. Harness automatically runs:
- typecheck
- unit tests
- API contract test
4. If it fails, return stderr, failed test names, and diff to the Agent
5. Agent fixes only the current failure, without expanding the scope of changes
6. Run validation again
7. After all pass, run a smoke test
8. If it still fails after N attempts, stop and request human interventionThis approach is very close to engineering hooks and automated checks. In Addy Osmani’s summary of agent harnesses, he notes that a safer approach is to automatically run type checks, linting, and tests at key lifecycle points, and feed failures directly back to the model—rather than relying on the model to “remember to check itself.” For long-running tasks, Anthropic has also emphasized a very practical pattern: start each session by running a basic executable check to confirm the system is not in a broken state before continuing to implement new features. In practice, they start a local development server and use browser automation to perform a minimal interaction check, allowing the Agent to discover and fix legacy issues first instead of piling new code on top of a broken state (Anthropic’s experience with long-running agent harnesses).
When choosing validators, it’s best to order them by “the more objective, the earlier”:
Validation Method | What It’s Good For | Advantages | Limitations |
|---|---|---|---|
Rule checks | Format, fields, permissions, dangerous operations | Fast, stable, low cost | Only catches explicit rules |
Static checks | Syntax, types, style, dependency errors | Automatable, clear feedback | Doesn’t guarantee business correctness |
Programmatic tests | Functional correctness, regressions | Closest to real delivery standards | Requires upfront test assets |
Model review | Documentation quality, design consistency, readability | Covers subjective dimensions | Can share the same errors as the generator |
Real-environment smoke tests | End-to-end usability | Finds integration issues | Higher cost, slower |
A common misconception is treating “model self-review” as a validation loop. Self-review can be kept, but at best it is an auxiliary check and should never be the only one. The reason is simple: the generation model and the review model often share the same biases, and may “confidently approve their own wrong answers.” If a task has objective criteria, prioritize objective validators; if it lacks complete objective standards, then use reviewer models to supplement subjective quality judgment.
There are also several common engineering pitfalls worth avoiding upfront:
- Validation granularity is too large: Letting the Agent change 12 files at once and then running full tests makes failures hard to locate. A better approach is small commits and small validations.
- Feedback is too raw: Dumping 2,000 lines of logs back into the model usually just wastes tokens. Extract a failure summary first, then attach necessary context.
- No stopping conditions: If the same error is fixed six times and still loops, it’s not something “one more try” will solve—context may be insufficient, the plan may be wrong, or the test design may be flawed.
- Only validating locally, not system state: Passing unit tests doesn’t mean the service can start, the page is usable, or dependencies are intact.
- Poor validation order: Running expensive E2E tests before cheap schema/lint/type checks increases both cost and latency.
If you’re building a minimal viable version from scratch, the recommended order is:
- Start with rule checks + type checking/linting
- Add deterministic unit tests
- Add a smoke test for one critical path
- Finally consider model review as a softer form of validation
The reason is very practical: the more deterministic the validator, the more suitable it is as the “first gate” of a harness. As emphasized by Martin Richards when building agent harnesses, with TDD and a final validation stage, the essence is separating “implementation” from “validation,” ensuring the Agent is not guessing while delivering, but advancing within a checkable closed loop.
In one sentence: a validation loop is not about giving an Agent a chance to “reflect,” but about adding an externally executable error-correction circuit to the system. Without this loop, an Agent relies only on prompts and luck; with it, the system begins to approach production-ready engineering.
A Production-Grade Agent Harness Architecture Example

Turning an Agent from something that “can run a demo once” into a “system that can reliably complete tasks” is not about writing yet another prompt, but about placing the model into a controllable execution loop. A sufficiently practical blueprint usually looks like this:
User Input
↓
Planner
↓
Tool Executor
↓
Validation
↓
Memory Update
├─ Pass → Deliver Result
└─ Fail → Return to Planner / Executor and continue iteratingIn this flow, the harness is not a single-point module, but the orchestration layer of the entire task loop: it decides when to plan, when to call tools, which operations must be intercepted, what counts as “done,” how to roll back after failure, and which information should be persisted into the next round.
Module | Primary Responsibility | Typical Input | Typical Output | What Happens Without It |
|---|---|---|---|---|
User Input | Receive user goals and standardize them | Natural-language requests, tickets, error logs | Structured task object | Task boundaries are vague, and the Agent easily misinterprets goals |
Planner | Decompose tasks, define acceptance criteria, arrange steps | Task object, historical state, environment summary | plan.json / plan.md, subtask list | The model thinks while doing, keeps changing direction, high cost and instability |
Tool Executor | Execute tool calls and environment operations | Subtasks, tool permissions, context | File changes, command output, API responses | The Agent can only “talk,” not “do” |
Validation | Objectively check results | Code, artifacts, logs, test results | pass/fail, error details, fix suggestions | The model easily becomes “confidently wrong” |
Memory Update | Write progress, decisions, summaries, and state | Current round results, validation conclusions | Progress notes, long-term memory, next-round context | Amnesia across rounds, repeated mistakes |
Why This Architecture Works
The core lies in two things:
- Separation of planning and execution
In production environments, studying the context first, then forming a plan, and finally executing is usually far more stable than “generate while acting.” Martin Richards’ practice explicitly emphasizes that studying the codebase first, then writing a plan, and iterating on that plan between humans and the Agent can significantly reduce wasted back-and-forth during implementation. - External validation in every round
Letting the model merely “feel like it’s done” is not enough. A truly reliable harness attaches hooks at lifecycle points, such as automatically running type checks, linting, and tests after file changes, or intercepting dangerous commands. These mechanical checks are far more trustworthy than “model self-assessment.”
---
Below, we walk through the entire execution path with a code-fixing Agent. Assume the user input is:
“Fix the intermittent duplicate charge issue in the payment service and submit a mergeable patch.”
1. User Input: Turn Natural-Language Goals into Executable Tasks
The input is not handed directly to the model to improvise freely; instead, it is first standardized into a task object, for example:
{
"goal": "Fix the duplicate charge bug in the payment service",
"repo": "payments-service",
"constraints": [
"Must not change the public API",
"Must add regression tests",
"Must not skip CI"
],
"acceptance_criteria": [
"Reproduce and identify the root cause",
"Unit and integration tests pass after the fix",
"Add a regression test preventing duplicate charges"
]
}The purpose of this step is to turn “intent” into a “constrained task.” Many demo failures are not due to weak models, but because the system never clearly defined the completion criteria from the start.
2. Planner: Generate a Plan First, Instead of Editing Code Immediately
The Planner should not start editing files right away. A more robust approach is to output a plan first, for example:
- Read payment flow–related modules and recent error logs
- Search for order idempotency keys, retry logic, and transaction boundaries
- Design a minimal reproduction path
- Write a failing test to confirm the bug is reproducible
- Modify the implementation
- Run tests and check for side effects
- Generate a change summary and commit message
This step is best written to a structured file rather than kept only in the model’s context. The reason is simple: the plan is shared state. It can be consumed by the tool executor, checked against by the validation module, and recalculated on failure. This “plan first, then implement” pattern essentially turns the Agent from improvisation into a state-machine–driven system.
3. Tool Executor: Execute in a Controlled Environment, Not with Unrestricted Power
The Tool Executor is responsible for real actions, such as:
- Reading code:
grep,ripgrep, AST queries - Running tests:
pytest,go test,npm test - Modifying files: restricted file editor
- Querying logs: logging platform APIs
- Version control:
git diff, creating commits - Browser or interface validation: headless browser / API client
The key here is not “the more tools, the better,” but that permissions and order must be controlled by the harness. For example:
- Forbid dangerous commands:
rm -rf,git push --force - Restrict writable directories: only the current service directory can be modified
- Force automatic validation after changes
- Require secondary confirmation before large changes
In other words, the Tool Executor does not give the model a computer with unlimited privileges, but a workbench with guardrails.
4. Validation: Use Objective Tests to Decide Whether to Enter the Next Round
After the fix is implemented, the system does not let the model say “I’ve solved it,” but instead enters the validation layer. For this payment bug, validation typically includes at least:
- Rule checks: linting, type checks, code style, forbidden sensitive file changes
- Programmatic tests: unit tests, integration tests, idempotency regression tests
- Runtime validation: start the service, simulate repeated requests, confirm no duplicate charges
- Model review: have another model check whether “the implementation meets the acceptance criteria,” but only as a supplement, never a replacement for tests
A common validation loop is:
Generate fix → Run failing tests → Get errors → Feed error text back → Agent fixes → Run tests againIn long tasks, this loop is more stable than a one-shot large change. Anthropic’s practice with long-running Agents emphasizes that context compression alone is insufficient to guarantee quality; each time an Agent starts working, it should first run a basic round of tests to ensure the environment is not in a “broken but unrecorded” state, otherwise subsequent changes will only compound the problem.
5. Memory Update: Persist the Current Round into the Next Round’s Input
After validation passes, the system should not only return the result, but also update the memory layer. At a minimum, three types of information should be written:
- Progress status: which steps are complete and what comes next
- Key decisions: why this fix was chosen and which alternatives were rejected
- Environment snapshot: current test status, changed files, known risks
For example:
{
"taskstatus": "fixedpendingreview",
"rootcause": "Idempotency key validation occurred after retry logic, causing duplicate charges within a concurrency window",
"fileschanged": [
"payments/idempotency.py",
"tests/testidempotentcharge.py"
],
"validation": {
"unittests": "pass",
"integrationtests": "pass",
"smoketest": "pass"
},
"next_step": "open PR with risk summary"
}This kind of memory update is critical, especially in multi-round, multi-session tasks. One of Anthropic’s approaches is to have the Agent read progress records and commit history at the start of a session, and write back progress notes at the end, so the system does not “forget where it was” due to context switches.
---
If you distill the above process into one sentence:
The essence of a production-grade Agent Harness is to make the model work within the closed loop of “plan–execute–validate–remember,” rather than gambling on a single prompt.
Finally, here are a few of the most common pitfalls in implementation:
- Planner without strong validation: The plan looks great, but the final output is untested and still unreliable.
- Tool calls without state persistence: The current round works, the next round forgets everything.
- Equating validation with model self-review: Self-review can exist, but it must come after rule checks and programmatic tests.
- Letting the Agent freely change everything: Without hooks, permission boundaries, and dangerous-operation interception, the environment will eventually be broken.
- Hard-coding the harness: As model capabilities change, the harness must adapt as well. LangChain’s analysis of harness anatomy notes that model capabilities and harness design co-evolve; some old guardrails become burdens, while new capabilities introduce new failure modes.
If you want to judge whether an Agent system is close to production-ready, don’t first look at how fancy the prompt is. First check whether it has this main chain:
Input is structured, plans are explicit, execution is constrained, results are validated, and state is remembered.
Only when all five are in place can it be called a harness; otherwise, it is most likely just a chatbot that happens to call tools.
From Demo to Production: Engineering Steps to Build an Agent Harness
Turning an Agent that “can run” into one that “can go live” is not about endlessly polishing prompts. The key is to systematically fill in task boundaries, tool contracts, execution loops, validation mechanisms, and observability systems. A practical approach is to move forward in the following 7 steps, defining a clear “definition of done” for each step, and avoiding piling all complexity on at once.
1. Narrow the task first—don’t start with a “general-purpose Agent”
The first step is not choosing a framework, but compressing the task into a single, verifiable, replayable workflow.
At a minimum, clearly answer five questions:
- What is the input: natural language, structured parameters, or a code repository path?
- What is the output: a SQL statement, a fix patch, or an analysis report?
- What is the success criterion: accuracy, test pass rate, human acceptance rate, or ticket completion rate?
- What is forbidden: cannot write to production databases, cannot access the public internet, cannot delete files.
- What is the budget: maximum steps, maximum tokens, maximum latency, maximum cost.
If these are not clear, the harness that follows can only become “automation of uncertainty.”
An example of a concrete, actionable scope:
“Only handle single-file bug fixes in Python repositories. Allowed to read the repository, run tests, and modify workspace files; not allowed to access the internet or execute destructive shell commands. The success criterion is that the target test goes from fail to pass, and no new failures are introduced in regression tests.”
The principle at this stage is simple: make the task narrow first, then make the loop stable.
---
2. Design tool interfaces first, then let the model call them
Many Demo problems are not that the model cannot reason, but that the tool layer is too ad hoc: unstable parameters, unparseable return values, and inconsistent error messages. A production-grade harness must treat tools as API products.
Each tool should at least satisfy:
- Schema-defined inputs: field types, required fields, and enum values are explicit
- Machine-readable outputs: do not return only natural language
- Tiered errors: distinguish timeouts, permission failures, parameter errors, and system exceptions
- Auditable operations: who called it, with what arguments, and with what result must be logged
- Idempotence where possible: retries should not corrupt the system
For example, a code testing tool should not be just:
{"cmd": "pytest"}but closer to:
{
"tool": "runtests",
"args": {
"target": "tests/testpayment.py::testrefund",
"timeoutsec": 120
}
}The return value should also be structured rather than a long narrative:
{
"ok": false,
"exitcode": 1,
"passed": 24,
"failed": 1,
"failures": [
{
"test": "tests/testpayment.py::test_refund",
"message": "AssertionError: expected 200 got 500"
}
]
}If your Agent executes code or shell commands, sandboxing is not an enhancement to “add later” but foundational infrastructure. For coding agents in particular, a safer approach is to treat LLM-generated code as untrusted by default and execute it in an isolated environment. Blaxel’s sandbox practices for AI coding agents also emphasize the importance of zero trust and stronger isolation boundaries.
---
3. Write deterministic tests before connecting a model
This is a step many teams skip, but it delivers the highest value. You need to prepare a set of deterministic test cases to define the minimum level your Agent must reach.
It is recommended to prepare at least three layers of tests:
- Unit-level: tool schemas, permission checks, state machine transitions
- Flow-level: given an input, whether the full loop executes as expected
- Task-level: a batch of real samples to check whether final results meet criteria
A minimal test set usually includes:
Type | Goal | Example |
|---|---|---|
Happy path | Verify the main flow works | Clear bug, reproducible test, Agent generates a patch and passes |
Known failure | Verify rejection and degradation | Requests to delete production data must be rejected |
Edge case | Verify boundary handling | Tool timeout, overly long test output, empty results |
Adversarial | Verify security | Prompt injection attempting to leak system prompts or escalate privileges |
Why must this come first? Because before going live, you need a stable baseline. Otherwise, today it “seems better,” tomorrow you switch model versions or tweak a system prompt, and tool call success rates and costs collapse together—without knowing which layer regressed.
For non-deterministic Agents, offline evaluation should not run just once. Towards Data Science’s guidance on production Agent evaluation harnesses recommends running the same sample 3–5 times and recording the mean and variance. High variance itself is a bug signal, indicating the harness has not sufficiently constrained behavior.
---
4. Implement a minimal execution loop: Plan → Act → Observe → Exit
Only now does the actual Agent loop come into play. The focus here is not “how intelligent,” but how controllable the loop is.
A minimal viable state machine typically looks like this:
receivetask
-> buildcontext
-> makeplan
-> executenextaction
-> observeresult
-> validate_progress
-> decide(next / repair / stop)
-> finalizeTwo engineering principles are critical:
First, separate planning from execution.
Have the planner output a structured plan, rather than letting the model think and change its mind during execution. Martin Richards repeatedly emphasizes in his practical guide to building an agent harness that researching context, writing a plan, and then implementing it significantly reduces token waste from mid-execution drift and error propagation.
Second, enforce hard exit conditions.
At a minimum, limit:
- Maximum steps
- Maximum retries
- Maximum consecutive no-op cycles
- Maximum total tokens
- Maximum wall-clock time
Without these, a Demo can easily turn into a production incident: the Agent gets stuck endlessly “retrying a fix,” burning money, increasing latency, and producing no result.
A simple execution loop pseudocode might look like:
for step in range(MAXSTEPS):
action = planner.nextaction(state)
if not policy.allow(action):
return fail("policyblocked")
result = toolexecutor.run(action)
state = state.update(result)
if validator.done(state):
return success(state)
if validator.irrecoverable(state):
return fail("unrecoverable")
return fail("steplimitexceeded")---
5. Add validation and repair inside the loop, not manual cleanup afterward
The stability of a production harness often comes from the validation loop, not from “smarter” prompts. The basic pattern is:
- Generate: produce a plan, SQL, patch, or answer
- Check: rule validation, model review, programmatic tests
- Repair: feed structured failure reasons back to the Agent and retry
- Exit: stop when thresholds are reached—do not repair infinitely
Validation methods usually fall into three categories and are best used together:
- Rule checks: JSON schemas, SQL blacklists, field completeness, permission policies
- Programmatic tests: unit tests, regression tests, static analysis, compilation checks
- Model review: check whether explanations are coherent and whether the user’s question is answered
In terms of priority: programmatic validation > rule checks > model self-evaluation.
Relying on the model to “feel good about its own answer” is almost never sufficient. Model self-evaluation can assist, but cannot replace objective tests.
For example, a repair loop for a code-fixing Agent might be:
Generate patch
-> Run target test
-> If failed, extract failure stack trace
-> Feed failure reason back to Agent
-> Regenerate patch
-> Exit and escalate to human if still failing after 2–3 attemptsThe key is not allowing infinite self-repair, but ensuring each repair is driven by clear, external, structured failure signals.
---
6. Put memory, permissions, and context compression in the harness—not in the prompt
Demos tend to shove everything into context; Production must start doing “context budget management.”
At a minimum, split state into three categories:
- Short-term runtime state: current task, executed actions, recent tool results
- Working memory: current plan, to-do items, intermediate conclusions
- Long-term memory: user preferences, historical tasks, reusable knowledge
More important from an engineering standpoint is: which state must enter the model context, and which should remain only in the harness state.
For example, tool call logs, raw large files, and full test outputs generally should not be fed back to the model every round, but handled via summarization, indexing, or truncation.
Permission control should also be enforced at the harness layer, not by a single line saying “please operate carefully.” You can borrow common engineering tiers:
- Read operations allowed by default
- Write operations require approval or secondary confirmation
- Destructive operations denied by default
- External network access controlled by an allowlist
Atlan’s practical summary on AI agent harnesses treats loops, tools, state, permissions, and evals as part of the same infrastructure layer. This perspective is correct: permissions are not an add-on feature, but part of the runtime control plane.
---
7. Complete observability, evaluation, and rollout—don’t “just ship and watch feedback”
If the first six steps are done, the system can run. But to go live, one last thing is missing: you must know when it starts getting worse.
At a minimum, record these metrics:
- Task success rate
- Tool call success rate
- Average / 95th / 99th percentile latency
- Cost per request
- Retry count
- Loop detection count
- Human takeover rate
- Distribution of validation failure reasons
Some metrics are especially actionable:
- If tool success rate stays below a threshold, inspect tool design before blaming the model
- A steadily rising cost/query usually indicates loop loss of control or context bloat
- Increasing loop detection often means unclear boundaries between planner and validator
Evaluation methods for production AI agents typically split assessment into offline and online layers:
offline evaluation intercepts regressions, while online monitoring detects new issues in real traffic. Neither can replace the other.
For production hardening, some checks can be turned directly into a release checklist. For example, several items in this Agent Harness Engineering guide are very practical:
- Tool latency, cache hit rate, and loop detection must be instrumented
- If a tool’s error rate exceeds a threshold, prioritize refactoring or deprecating it
- Long-running tasks should configure memory compaction and log trigger points
Avoid all-at-once rollouts. A recommended order is:
- Local replay on the test set
- Shadow runs in a pre-production environment
- Read-only mode in production
- Small-traffic canary rollout
- Gradually open write permissions or high-risk operations
---
A more pragmatic implementation order
If you currently only have a Demo, the most time-efficient path is not “filling in all capabilities,” but:
- Lock onto one high-value single task
- Write 20–50 deterministic samples
- Refactor tool interfaces into structured schemas
- Implement a minimal loop with hard exits
- Add programmatic validation
- Fully instrument logs and metrics
- Only then optimize prompts, models, and frameworks
Do not reverse the order.
Prompt tuning should happen after the harness takes shape, not before. Without tests, state machines, validation, and observability, any “improvement” is an illusion—you are not seeing capability, but luck.
Common Failure Modes and Debugging Methods
In production, Agents rarely fail simply because the “model isn’t smart enough.” More often, the problem is this: things that should have been externalized into the harness were mistakenly left for the model to handle implicitly on its own. In practice, I recommend triaging by failure layer first, rather than tweaking the prompt upfront:
- Context failure: the model didn’t receive the right information, or received it in the wrong order or at the wrong granularity;
- Constraint failure: the model knows what it should do, but does things it shouldn’t;
- Verification failure: the result looks correct, but there is no mechanism to catch errors.
This is also the most useful takeaway from deepset’s classification of production failures: first identify which layer is broken, then decide where to fix it. The table below lists the six most common problems that, in my view, most deserve guardrails to be added first.
Failure Mode | Typical Symptoms | Common Root Causes | Debugging & Fixes |
|---|---|---|---|
1. Hallucinated tool calls / unauthorized actions | Calling the wrong tool, passing wrong parameters, modifying files or environments that shouldn’t be touched | Too many tools with ambiguous names; unclear permission boundaries; the model is forced to “guess” which tools are available | Create capability whitelists for tools and dynamically expose them by phase; run high-risk tools through |
2. Infinite loops / local optimum spinning | Repeatedly doing “search → read → minor tweak → search again”; token and tool costs keep rising but the task doesn’t advance | No stopping conditions; failure signals are too weak; the Agent can’t see that repeated attempts add no new value | Implement loop detection: if the same tool with similar parameters is called N times in a row, break immediately; set step budgets / time budgets; treat “no new evidence” as a failure branch; after 3 similar errors, automatically switch strategies or escalate to humans. |
3. Context explosion / goal drift | After long tasks, original constraints are forgotten, completed work is repeated, and the task drifts off-topic | Conversation history keeps piling up; important state is scattered across chat; key decisions are lost during compression | Pull “state” out of the conversation: |
4. Silent failure / pretending to be done | The Agent replies “completed” or “tests passed,” but the result is unusable; no one notices immediately | No executable verification; state relies only on natural-language self-reporting; the system monitors errors but not “incorrect success” | Bind executable validators to every stage: tests, linting, format checks, interface probes, end-to-end smoke tests. As LangChain points out when improving deep Agents, it’s far from enough for an Agent to “look correct”—it must run tests. |
5. Self-verification bias | After generating an answer, the Agent reviews it itself and still gives a high score; obvious bugs are glossed over | The generator and reviewer share the same context and tend to defend existing conclusions | Separate roles for execution and review: a dedicated reviewer model or verifier step; give the reviewer only the artifact, not the original reasoning; prefer objective assertions over “do you think this is right?” As discussed by Milvus, Agent self-evaluation is usually overly optimistic. |
6. Inability to recover after interruption | After network hiccups, API timeouts, or container restarts, the Agent loses its place; subsequent runs repeat work or leave tasks half-done | No checkpoints; tool calls aren’t idempotent; progress exists only in the model’s short-term memory | Make key steps idempotent; write checkpoints / resume tokens; record intent before any external side effect, and record results after completion; write a progress log at the end of each round. On recovery, read progress first and run a basic health check instead of blindly continuing. |
A Very Common Production Case
Suppose you ask a coding agent to “add a refund status page to the payment service and deploy it to the test environment.” In reality, failures are usually not a single mistake, but a chain of them:
- The Agent modifies the frontend first, but mistakenly calls a deployment script it shouldn’t touch;
- While fixing bugs, it falls into a loop—restarting services repeatedly and re-editing the same configuration;
- As the session grows longer, context compression drops the constraint that “the test environment must use a read-only database”;
- In the end, it glances at the diff and replies “completed,” without actually visiting the page to verify it.
If you rely only on “optimizing the prompt,” these incidents are rarely fixed at the root. More robust fixes include:
- Deployment tools are hidden by default and exposed only when clearly entering the deploy phase;
- Three consecutive identical commands or similar patches immediately trigger a loop breaker;
- Constraints are written into a structured task card, not buried in chat history;
- Smoke tests run both at the start and at the end of a session;
- Final “completion” must include verification artifacts: test results, page screenshots, or API responses.
When Debugging, Don’t Look Only at the Final Output—Watch 5 Process Signals
Many teams troubleshoot slowly because their logs record only “what was asked” and “what was answered,” not how the Agent went off the rails. At a minimum, you should log these five signals:
- Tool call traces: in chronological order, record tool names, parameter summaries, latency, and return codes.
- Repetition rate: counts of consecutive identical calls, similar patches, or similar search queries.
- Context health: token growth, number of compression triggers, and the last update time of handoff artifacts.
- Verification pass rate: step-by-step results of tests, linting, schema checks, and review gates.
- Recovery success rate: after timeouts or interruptions, whether automatic recovery succeeded or required human takeover.
Without these signals, when an Agent fails you can almost only “repeat the prompt and pray.” Once you have these observability surfaces, most problems stop being mysterious: either the context design is wrong, permissions are too loose, or the verification loop is missing.
A rule of thumb: if a problem can be solved with rules, state machines, validators, or structured artifacts, don’t keep hoping the prompt will behave itself. This is exactly the value of harness engineering in production environments.
Existing Agent Harness Frameworks and Tooling Ecosystem
When looking at the agent tooling ecosystem, the easiest mistake to make is to compare everything as an “agent framework.” In reality, at the harness layer, these tools solve three different categories of problems:
Frameworks determine how you “build” an agent, orchestrators determine how it “runs,” and evaluation determines how you know it’s still “reliable.”
In real projects, all three layers are often needed together, just with different priorities. Moreover, the boundary between frameworks and harnesses is becoming blurred: many frameworks are adding runtime capabilities, and many harnesses are starting to support custom code injection. So when choosing tools, don’t start by asking “which one is the most popular,” but instead ask: is your core challenge reasoning orchestration, runtime control, or quality validation?
Tool category | Typical representatives / forms | Position at the harness layer | Advantages | Limitations | Better-suited scenarios |
|---|---|---|---|---|---|
Agent frameworks | LangChain / LangGraph, CrewAI, AutoGen, code-first SDKs | Provide agent, tool, memory, state components; the harness often still needs to be filled in | Maximum flexibility; suitable for custom planning, RAG, multi-agent collaboration | Deployment, retries, permissions, auditing, replay, and cost control are usually DIY; high maintenance cost | When agent behavior itself is the product capability, e.g., coding agents, research agents, complex decision systems |
Workflow orchestrators / deployable harnesses | Graph workflows, state machine orchestrators, configuration-first agent platforms | Handle task flow, state persistence, timeouts, retries, human approval, external system integration | Closer to production operations; friendly to long-running tasks, approval flows, async jobs | Reasoning strategies may be weaker; over‑processization can degrade agents into “talking BPMs” | Ticket handling, CRM operations, internal automation, process-heavy tasks requiring stable SLAs |
Evaluation tools | Tracing, replay, offline benchmarks, online monitoring tools | Do not execute agents directly; complete the validation loop: quality, stability, cost, latency | Can detect regressions, pinpoint tool call failures, measure token/cost overruns | Building datasets and metrics has a learning curve; subjective tasks are hard to judge with a single score | Systems already live or about to launch; any agent that needs continuous iteration |
1. Agent frameworks: the most flexible, but also the easiest to postpone “runtime engineering”
The advantages of code-first frameworks are clear: you can precisely control prompts, tool selection, state structures, branching logic, and memory strategies. The problem is that this freedom often postpones harness complexity instead of eliminating it.
Many teams can build a demoable agent in two weeks using a framework, only to discover before launch that they are missing key capabilities:
- Checkpoint / resume after failures
- Idempotency and replay protection for tool calls
- Timeouts, cancellation, and compensation for long-running tasks
- Step-by-step tracing, logging, and cost attribution
- Version management for prompts, tool schemas, and memory policies
- Approval points and audit records for human intervention
This is why many frameworks are excellent for demos but not necessarily production-ready by default. In particular, highly autonomous, infinite-loop-style agent designs are often categorized in public comparisons as more experimental than production-ready.
2. Workflow orchestrators: not about making agents smarter, but making systems more controllable
If your business is essentially “receive request → query systems → call tools → wait for responses → approve → write to database,” then the real difficulty is usually not reasoning, but state management and failure recovery.
In such cases, workflow orchestrators or configuration-first harnesses are often more suitable than pure agent frameworks, because they naturally excel at:
- Breaking processes into observable nodes
- Defining retries, rollbacks, and timeouts for each node
- Persisting durable state across asynchronous events
- Inserting human-in-the-loop steps
- Constraining agent execution boundaries to prevent uncontrolled divergence
A common rule of thumb is:
If, after a failure, your main concern is “which step do we resume from,” rather than “why did the model think this way,” then what you need first is an orchestrator, not a fancier prompt.
A simple example:
- Customer support ticket routing + CRM updates: usually better suited to workflows/orchestrators. The process is clear, system boundaries are well defined, and the focus is on permissions, auditing, and retries.
- Technical research assistants / code-fixing agents: usually better suited to frameworks. You need more open-ended planning, tool selection, and context management.
3. Evaluation tools: the most underestimated, but almost a necessity at the harness layer
Many teams treat evaluation tools as “nice to have later,” which is a classic mistake. Without an evaluation layer, it’s hard to answer three critical questions:
- Is the new version actually better, or just “seems smarter”?
- When a tool call fails, is it a model issue, a schema issue, or an external API issue?
- Are cost and latency quietly getting out of control?
A more robust production practice is to run both offline and online evaluations:
- Offline evaluation: use datasets with reference answers to catch regressions, suitable for pre-release risk gating.
- Online evaluation: observe agent metrics in real traffic, such as hallucination proxies, tool success rates, P99 latency, and per-request cost.
For non-deterministic agents, a single run score has limited meaning. A more practical approach is to run the same task 3–5 times and observe the mean + variance. High variance itself signals an unstable harness: possible causes include context contamination, drifting tool selection, or overly complex planner branching.
4. Don’t choose by “feature lists”; choose where you’re willing to pay engineering debt
If I had to give one practical takeaway, it would be this:
- Choose a framework when your competitive edge lies in agent behavior itself, and your team can bear the costs of coding, deployment, monitoring, and maintenance.
- Choose an orchestrator / harness when your goal is to reliably automate known business processes, prioritizing controllability, recoverability, and auditability.
- Fix evaluation first when you already have agents running, but every upgrade feels like opening a blind box.
Many teams ultimately adopt a hybrid architecture:
use frameworks to implement high-freedom reasoning nodes, use orchestrators to control long-chain execution, and use evaluation tools to guard release quality. This is often more realistic than betting everything on a single “all-in-one” framework.
5. A minimal production-oriented decision checklist
When selecting tools, you can directly filter with these five questions:
- Does it support state persistence and checkpoint recovery?
- Can it provide unified observability for tool calls, tokens, latency, and failure rates?
- Does it support human approval, permission boundaries, and audit logs?
- Can it perform replay testing and version regression comparisons?
- Does it allow you to drill down to the code layer when needed, instead of locking you into visual configurations?
If three of these five questions can’t be answered, the tool is probably better suited for demos than for a production harness. The real gap is usually not “can it call 100 tools,” but whether it can fail safely, reproduce behavior stably after version upgrades, and keep costs and latency under control at scale.
Future Trends: From Agent Harnesses to Long-Running “Lifelong Agents”
If we define “Lifelong Agents” in engineering terms, they are not “superintelligences that never stop,” but rather agent systems that can continuously complete work across sessions, context windows, and environmental changes, and that are recoverable, auditable, and interruptible after failures. The key to such systems is not how fancy the prompts are, but whether the harness can handle state, memory, permissions, validation, and recovery.
Over the next 1–2 years, the real inflection point will very likely emerge in the following four directions.
Trend | Why It Will Happen | Changes at the Harness Layer |
|---|---|---|
1. From conversational agents to persistent runtimes | Single-task success does not equal long-term usability; long tasks span multiple context windows, tool states, and code versions | Requires recoverable execution, progress persistence, periodic self-checks, and exception rollback |
2. Memory evolves from “context stuffing” to layered memory systems | Context compression alone is insufficient; long-running systems face information expiration, contamination, and retrieval noise | Requires session memory, task memory, fact stores, retrieval strategies, and expiration policies |
3. Evaluation shifts from offline benchmarks to online feedback loops | Real environments continuously change, and static scores mask degradation | Requires online assertions, replay testing, time-based re-evaluation, and failure grading |
4. Human oversight moves from approval flows to a governance layer | Long-running agents inevitably face high-risk decisions and ambiguous boundaries | Requires permission layering, escalation paths, interruption mechanisms, and auditable traces |
1. Continuously running agents will first become “recoverable systems,” and only then “smarter systems.”
Many teams initially focus on model capability, but in long-running tasks, runtime issues usually surface first: tasks stop halfway, environments are broken, or the agent mistakenly believes it has finished. Anthropic notes in its practice of long-running agent harnesses that context compaction alone is insufficient to sustain complex tasks; their approach has the agent perform an environment health check, read progress files, and verify the current state each round before deciding the next step. This design highlights a reality: future agent harnesses will look more like job runtimes than chat wrappers.
A very typical production pattern will look like:
heartbeat -> read progress/state -> run health checks -> select a single sub-goal -> execute -> verify -> write logs/snapshots -> wait for next cycleThe real barrier here is not “whether tools can be called,” but whether the task can continue after being interrupted 20 times.
2. Memory systems will shift from “stuffing more into context” to “layered memory with lifecycles.”
The biggest problem for long-running agents is not that they “can’t remember,” but that they remember too much and don’t know what to forget. Many failures stem not from insufficient intelligence, but from stale facts, incorrect summaries, and polluted state being repeatedly retrieved. In discussions of harness architecture, one repeatedly emphasized point is that memory, context management, and state persistence must be controlled by the harness rather than improvised by the model (reference).
More practical designs typically split memory into four layers:
- Scratchpad: Temporary reasoning material for the current turn, with a short lifecycle.
- Session memory: Decisions, constraints, and open items within the current task.
- Project memory: Long-term stable facts, such as system architecture, API contracts, and user preferences.
- Audit log / event log: Immutable execution traces for accountability and postmortems.
This means that by 2026, good harnesses will no longer compete on “how full the context is,” but on retrieval hit rate, memory contamination control, and fact expiration handling. In the long run, memory systems will resemble databases and caching layers more than chat transcripts.
3. Evaluation will upgrade from “single-run success rate” to a “long-term reliability profile.”
Today, many agent evaluations still stop at “it ran successfully once,” but this is almost meaningless for long-running systems. “Towards a Science of AI Agent Reliability” points out that real deployment environments constantly change: API formats shift, databases migrate, tool libraries upgrade, and documentation content updates. A single static evaluation cannot reveal whether an agent will still succeed tomorrow, nor whether its failures are predictable or safe.
This will directly push Harness Engineering in two directions:
- Online validation: Insert assertions and lightweight checks after each tool call, rather than performing a single final acceptance check.
- Time-based replay testing: Periodically replay the same tasks to observe consistency, robustness, and degradation in error severity.
Work such as Harness-Bench has already begun to isolate and measure “execution-layer configuration differences,” signaling that the industry is acknowledging a fact: with the same model, changing the harness can dramatically change performance. In the future, truly valuable metrics will shift from “how many questions were answered correctly” to “how much work can be completed stably under environmental drift.”
4. Human oversight will not disappear, but will upgrade from “step-by-step approval” to “policy-level steering.”
The goal of long-running agents is not to eliminate humans, but to shift humans from operators to governors. As agents run for longer durations, the modes of human intervention will evolve:
- Low-risk actions are executed automatically;
- Medium-risk actions trigger secondary confirmation;
- High-risk actions enter a human escalation queue;
- Anomalous patterns trigger forced interruption or degradation.
At its core, this is about embedding permissions, sandboxing, escalation paths, and auditing into the harness rather than into prompts. Especially in scenarios like code modification, data processing, and external API calls, the harness must be able to limit the blast radius: mistakes can be rolled back, abnormal calls can be circuit-broken, and polluted context can be isolated. Otherwise, the longer an agent runs, the higher the accumulated risk.
---
Why will the harness layer become the true competitive moat? Because model capabilities will increasingly become commoditized, while the following assets will not:
- Recovery strategies for long-running tasks: How to continue after failure instead of restarting;
- Memory and context strategies: What to retain, what to compress, and what must be discarded;
- Tool permission models: Which actions are auto-approved and which require human confirmation;
- Online evaluation and replay sets: Knowing whether the system is “occasionally successful” or “consistently stable”;
- Real execution trace data: Which environmental changes most easily break the agent, and which recovery actions are most effective.
More importantly, a reinforcing feedback loop will form between models and harnesses. LangChain’s breakdown of agent harnesses notes that post-training of models and harness design evolve in a coupled way: which tool primitives are added to the harness and which behaviors are treated as “default capabilities” ultimately feed back into shaping the next generation of models. This means that the advantage of leading teams is not just “having built a good framework,” but accumulating an entire engineering loop that continuously strengthens models within a specific runtime environment.
So, what is worth watching in 2026 is not “which agent is best at writing prompts,” but which agent can still deliver stable results after running for three days, experiencing two rounds of environmental drift and one failure recovery. That moat lies almost entirely in the harness.







