An agent harness is the scaffolding around an LLM that turns it into a practical agent. This article explains concepts, architecture, and decision frameworks.
Definition: The Harness Concept
The term "harness" originates from manufacturing and means: the structure that holds a system together and makes it functional.
In AI:
Agent Harness = All components EXCEPT the language model that enable an agent to self-direct, use tools, and achieve goals.
Simplified Formula
Language Model (GPT, Claude, etc.)
β
+ Harness
ββ Memory/Context
ββ Tool Bindings
ββ Planning Logic
ββ Evaluation
ββ Guardrails
β
= Functional Agent
The 5 Core Components of a Harness
1. Memory & Context Management
The agent's memory: What does it know, what must be retained?
# Example: Context window management
class AgentMemory:
def __init__(self, max_tokens=8000):
self.context = []
self.max_tokens = max_tokens
self.token_count = 0
def add_interaction(self, user_msg, assistant_msg):
"""Add interaction, prune old if needed"""
new_tokens = count_tokens(user_msg + assistant_msg)
if self.token_count + new_tokens > self.max_tokens:
# Prune oldest non-critical context
self.context = self._summarize_and_prune()
self.context.append({
"user": user_msg,
"assistant": assistant_msg,
"timestamp": datetime.now()
})
self.token_count += new_tokens
def _summarize_and_prune(self):
"""Use LLM to summarize and compress old context"""
old_context = self.context[:-10] # Keep last 10
summary_prompt = f"Summarize briefly: {old_context}"
summary = llm.call(summary_prompt)
return [{"summary": summary}] + self.context[-10:]
def get_context(self):
"""Return current context for LLM"""
return "\n".join([
f"User: {c['user']}\nAssistant: {c['assistant']}"
for c in self.context
])
Techniques:
- Sliding Window: Only last N tokens in context
- Summarization: Compress old context
- Semantic Chunking: Identify relevant parts
- Vector Stores: Embeddings for retrieval
2. Tool Binding & Execution
The ability to call external functions.
# Tool registry pattern
class ToolRegistry:
def __init__(self):
self.tools = {}
def register(self, name, func, description, parameters):
"""Register a tool the agent can use"""
self.tools[name] = {
"func": func,
"description": description,
"parameters": parameters
}
def call_tool(self, tool_name, **kwargs):
"""Execute tool with safety checks"""
if tool_name not in self.tools:
raise ValueError(f"Tool {tool_name} not found")
tool = self.tools[tool_name]
# Validate parameters
for param, value in kwargs.items():
if param not in tool["parameters"]:
raise ValueError(f"Unknown parameter: {param}")
# Execute with timeout
try:
result = timeout(
tool["func"](**kwargs),
timeout_seconds=30
)
return {"success": True, "result": result}
except Exception as e:
return {"success": False, "error": str(e)}
def get_tool_descriptions(self):
"""Format tools for LLM context"""
descriptions = []
for name, tool in self.tools.items():
desc = f"""
Tool: {name}
Description: {tool['description']}
Parameters: {json.dumps(tool['parameters'])}
"""
descriptions.append(desc)
return "\n".join(descriptions)
# Usage
registry = ToolRegistry()
registry.register(
"search_web",
web_search,
"Search the web for information",
{"query": "string", "max_results": "integer"}
)
3. Planning & Reasoning
How the agent approaches its goal structurally.
# Planning agent with ReAct pattern
class PlanningAgent:
def __init__(self, llm, tools):
self.llm = llm
self.tools = tools
self.plan = []
def create_plan(self, goal):
"""Use LLM to create an execution plan"""
prompt = f"""
Create a step-by-step plan for:
{goal}
Format:
1. [Step]
2. [Step]
...
Available tools:
{self.tools.get_tool_descriptions()}
"""
response = self.llm.call(prompt)
self.plan = self._parse_plan(response)
return self.plan
def execute_plan(self):
"""Execute each step, adapt if needed"""
for i, step in enumerate(self.plan):
print(f"Executing step {i+1}: {step}")
# Reasoning phase (Thought)
reasoning = self.llm.call(f"Reason about this step: {step}")
# Action phase (Action)
tool_call = self._parse_tool_call(reasoning)
if tool_call:
result = self.tools.call_tool(**tool_call)
# Observation
print(f"Observation: {result}")
# Adapt if needed
if not result["success"]:
new_step = self.llm.call(
f"The step failed: {result['error']}. What should we do instead?"
)
self.plan[i] = new_step
4. Evaluation & Feedback
How the agent checks if it reached its goal.
# Evaluation framework
class AgentEvaluator:
def __init__(self, llm):
self.llm = llm
def evaluate_action(self, goal, action, result):
"""Evaluate if action moved towards goal"""
prompt = f"""
Goal: {goal}
Action: {action}
Result: {result}
Was this action helpful? (yes/no/partial)
Explanation:
"""
evaluation = self.llm.call(prompt)
return self._parse_evaluation(evaluation)
def evaluate_completion(self, goal, conversation):
"""Check if goal was achieved"""
prompt = f"""
Original goal: {goal}
Full conversation:
{conversation}
Was the goal achieved? (yes/no/partial)
Remaining tasks:
"""
result = self.llm.call(prompt)
return self._parse_completion(result)
5. Guardrails & Safety
How the agent stays on safe track.
# Guardrails system
class SafetyGuardrails:
def __init__(self):
self.blocked_patterns = [
r'DROP\s+TABLE', # SQL injection
r'rm\s+-rf', # Dangerous commands
r'API_KEY', # Secret exposure
]
def check_tool_call(self, tool_name, args):
"""Check if tool call is safe"""
# Check for dangerous patterns
args_str = str(args)
for pattern in self.blocked_patterns:
if re.search(pattern, args_str, re.IGNORECASE):
raise SecurityError(f"Blocked pattern: {pattern}")
return True
def check_output(self, output):
"""Sanitize output before returning"""
# Remove API keys
output = re.sub(r'sk-\w+', '[REDACTED]', output)
# Remove internal IPs
output = re.sub(r'192\.168\.\d+\.\d+', '[INTERNAL_IP]', output)
return output
Agent Harness vs Framework vs SDK
Differences
| Aspect | Harness | Framework | SDK |
|---|---|---|---|
| Purpose | Single agent | Multi-agent system | Integration |
| Scope | Single LLM coordination | Multiple agents + communication | Developer library |
| Abstraction | High | Medium | Low |
| Complexity | Medium | High | Low |
| Best for | Standalone agents | Complex workflows | Existing code integration |
Examples
Harness:
- Claude Code native system
- OpenAI Assistants API
- LlamaIndex QueryEngine
Framework:
- AutoGen (Microsoft)
- CrewAI
- LangGraph (LangChain)
SDK:
- LangChain SDK
- Anthropic SDK
- OpenAI Python SDK
History: From Prompts to Harnesses
Evolution
1. Era: Raw LLM Prompting (2018-2020)
- Text-in, text-out only
- No memory, no tools
2. Era: Few-Shot Examples (2020-2021)
- In-context learning
- Better prompts, same limitations
3. Era: Function Calling (2021-2023)
- Tool integration (OpenAI, Claude)
- First primitive harnesses
4. Era: Full Agent Harnesses (2023-present)
- Memory management
- Planning + reasoning
- Safety guardrails
- Multi-agent orchestration
5. Era: Production Harnesses (2024-present)
- Enterprise safety
- Cost optimization
- Audit logging
- Compliance integration
Design Patterns for Harnesses
Pattern 1: Single-Agent Linear
Input β Plan β Action β Evaluation β Output
Best for: Simple tasks, classification, standard workflows
Pattern 2: Hierarchical Reasoning
Manager Agent
β β
Worker 1 Worker 2
(specialized)
β β
Aggregator
Best for: Complex multi-domain tasks
Pattern 3: Swarm Intelligence
Agent A β Agent B
β β β β
β Agent C β
Best for: Collaborative problem-solving, brainstorming
Pattern 4: Hierarchical + Feedback Loop
LLM Agent
β
Tool Call
β
Evaluation β Failure β Retry / Adapt
β
Success
Best for: Iterative refinement, learning
Claude Code as a Harness
Claude Code as a harness provides:
class ClaudeCodeHarness:
"""Claude Code = Harness with built-in orchestrator"""
components = {
"memory": "Session context + multi-turn",
"tools": [
"Read", "Write", "Edit", # Filesystem
"Bash", "Glob", # Shell
"Grep", # Search
"Git", # Version control
"MCP Servers" # External APIs
],
"planning": "Automatic multi-step reasoning",
"evaluation": "User feedback loop",
"guardrails": {
"permissions": "Tool whitelist/blacklist",
"safety": "Output sanitization",
"audit": "Full session logging",
"rate_limits": "Configurable"
},
"orchestration": "Native agent sequencing"
}
def create_agent(self, CLAUDE_md_config):
"""Create configured agent from CLAUDE.md"""
return Agent(
model=config.model,
tools=config.tools,
hooks=config.hooks,
skills=config.skills,
permissions=config.permissions
)
Extending the Harness
Claude Code harness can be extended through:
Plugins
# Add new tools
capabilities: [csv-processor, ml-pipeline, blockchain]
Skills
# Specialized workflows
skills:
- data-analysis
- code-generation
- security-audit
Hooks
// Pre/Post execution logic
{
"PreToolUse": [custom_validator],
"PostExecution": [audit_log]
}
MCP Servers
{
"mcp_servers": {
"stripe": "http://localhost:3000",
"slack": "http://localhost:3001",
"salesforce": "http://localhost:3002"
}
}
Evaluation Metrics for Harnesses
How to measure if a harness is good?
| Metric | Description | Goal |
|---|---|---|
| Task Completion Rate | % tasks fully solved | >90% |
| Time to Completion | Average duration | Baseline |
| Tool Accuracy | % correct tool calls | >95% |
| Safety Score | No safety violations | 100% |
| Cost per Task | Average API cost | Minimize |
| User Satisfaction | Feedback score | >4.5/5 |
Community Harnesses (extending Claude Code)
ECC (Extensible Claude Code)
Completely open harness implementation with extensible plugin architecture.
Features:
- Multi-agent coordination
- Advanced memory management
- Custom tool development framework
URL: https://github.com/community/ecc-harness
Ruflo
Focused harness for data processing workflows with optimized memory compression.
Features:
- Token-efficient context windows
- Specialized data transformation tools
- Built-in ML integration
URL: https://github.com/datasets/ruflo
Build vs Buy vs Extend: Decision Framework
βββββββββββββββββββββββ
β Requirements β
β Clear? β
ββββββββββββ¬βββββββββββ
β
βββββββ΄ββββββ
β β
Yesβ βNo
β β
βΌ βΌ
Standard- Custom
Harness (Build)
(Claude Code)
β β
βββββββ¬ββββββ
β
βββββββΌβββββββ
β Complexity β
β High? β
βββββββ¬βββββββ
β
βββββββ΄βββββββ
β β
Yesβ βNo
β β
βΌ βΌ
Framework Extend
(AutoGen) (Plugins)
