Lesson 3. Harness and Guardrails
A model without a harness is dangerous. A Harness is all the infrastructure around the model: the context, the tools, and three protective layers – guardrails (what you can’t do), checking (did it do it right), and recovery (how to get back on track). The expanded formula: Agent = Model + Harness.
tool call
│
▼
┌───────────────┐
│1. WHITELIST│──▶ “reject”
└───────┬───────┘
│ exists
▼
┌───────────────┐
│ 2. EXECUTION │──────▶ “crash caught”
└───────┬───────┘
│ result
▼
┌───────────────┐
│ 3. CHECKING │──────▶ “ask again”
└───────┬───────┘
│ ok
▼
observation into contextWhat to read
- [Harness-engineering] – why the harness is more important than the model itself
- [Five functions of a Harness] – what the infrastructure consists of
- [Guardrails] – how to forbid an agent from doing dangerous things
In code
The three layers fit into a single wrapper around the tool call:
def run_tool(call):
fn = TOOLS.get(call["tool"])
if not fn: # layer 1: whitelist
return "Tool not found"
try:
result = fn(**call["args"]) # layer 2: catch crash
except Exception as e:
return f"Tool crashed: {e}"
if result is None: # layer 3: check result
return "Tool returned empty"
return resultfunction runTool(call) {
const fn = TOOLS[call.tool];
if (!fn) return "Tool not found"; // layer 1
try {
const result = fn(call.args || {}); // layer 2
if (result === undefined || result === null) // layer 3
return "Tool returned empty";
return String(result);
} catch (e) {
return "Tool crashed: " + e;
}
}Note: tool errors do not crash the agent – they are returned to the context as an observation, and the LLM can correct itself on the next step.
Practice
For the agent from Lesson 1, come up with three guardrails: one “can’t do”, one result check, and one way to recover from an error. Then, wrap any of your functions in the run_tool from the example.
Related: [Lesson 2. ReAct cycle], [Lesson 4. Tools]