模型只是 CPU,harness 才是真正决定能力的 OS。
2026 年的现实:大多数人用 Claude Code、Cursor、Devin 时只盯着「模型版本」,却没意识到——同一个 Sonnet 4.5,跑在 Claude Code 里和裸调 API 是两种生物。差别不在模型,在 harness:那个负责派发工具、管理权限、控制循环、回收状态、压缩历史的「无形操作系统」。Karpathy 说得很直白:「LLM is the new CPU, the context window is RAM, the harness is the OS。」一个会写 prompt 不算入门,一个会读 / 改 / 设计 harness 才进了门。这一期讲四件事:harness 到底是什么、它和 prompt + tools 的边界在哪、Claude Code 这种 SOTA harness 的设计抽象、以及怎么用 100 行 Python 自建一个最小可用的 harness——并精读 Anthropic 那篇被低估的工程博客《Building Effective Agents》。
一次 messages.create() 是无状态的 inference:你给它 input,它给你 output,模型并不知道也不在意你之前/之后做了什么。把这一次 inference 包装成一个能自主完成多步任务的实体,中间发生的所有事情合起来叫 harness:
tool_use block,路由到本地函数,执行后把 tool_result 塞回下一轮 messages。stop_reason == "end_turn"、max_iters、token budget、或人为 checkpoint。真实使用体验 90% 取决于这几条的工程实现,而不是模型的 reasoning 强弱。Cognition 在 2025 年的 Don't Build Multi-Agents 里说得很直接:他们的核心壁垒是 harness 设计,而不是更聪明的 prompt。Claude Code 的 plan mode、Cursor 的 composer、Aider 的 git auto-commit,都是把同一个 base model 「装」进不同 OS 的结果。
分清哪些是 prompt 的事、哪些是 harness 的事,从一个 30 秒诊断开始——当你的 agent 不 work,先问:
# —— 这是 PROMPT 的锅 ——
- 模型选错工具
- 输出格式不稳
- 推理跳步
- 没遵守约束
# —— 这是 HARNESS 的锅 ——
- 跑 10 轮就死循环(loop control 缺)
- tool 报错后 agent 就崩了(recovery 缺)
- 上下文越长越笨(compaction 缺)
- 工具调用没有人工干预的机会(permission 缺)
- 同一个 prompt 在 Claude Code 里好,在你脚本里不行(harness 不同)
把这张分类表贴在自己写 agent 的项目根目录——下次 debug 前先归因,能省一半时间。
Claude Code 是目前公开 SOTA 的 coding harness。把它拆开看,能直接抄到自己的 agent 项目里。核心抽象有六层:
这些不是「锦上添花」,每一个都对应一个具体失败模式:没有 plan mode,模型会先动手再想;没有 hooks,每个 commit 都得手动跑 lint;没有 subagent,一次大搜索把主 context 污染到不可用。harness 设计就是把这些「为了不踩坑该做的事」固化成系统。
读 Claude Code 自己的 .claude/settings.json 是最快的 harness 入门——直接看 SOTA harness 怎么暴露配置面:
{
"permissions": {
"allow": ["Read", "Grep", "Bash(git status:*)", "Bash(npm test:*)"],
"ask": ["Edit", "Write", "Bash(git push:*)"],
"deny": ["Bash(rm -rf:*)", "Bash(*--no-verify*)"]
},
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{"type":"command", "command":"pnpm lint --fix $CLAUDE_FILE_PATHS"}]
}],
"Stop": [{"hooks":[{"type":"command","command":"pnpm test --silent"}]}]
},
"env": { "BASH_DEFAULT_TIMEOUT_MS": "120000" }
}
这一份 30 行配置干了四件原本要写代码的事:分级权限(allow / ask / deny 用 glob pattern 精确控制)、强制保存即 lint、每次会话结束跑测试、Bash 超时兜底。这就是 harness 提供的「可声明式编程」面——你不需要分叉 Claude Code,就能让它的运行时按你的工程纪律跑。
rm -rf / 强制 push / --no-verify 这类显式 deny。
一个 harness 的最小骨架其实就五个组件:tool registry · loop controller · message buffer · permission gate · recovery。剩下都是这五个的变体。下面这段 100 行 Python 跑得起来、能处理 multi-turn tool calls、能拦截危险命令、能在 token 超阈值时 compact——它不漂亮,但每一行你都看得懂、改得动。
import anthropic, json, subprocess
from pathlib import Path
client = anthropic.Anthropic()
MODEL = "claude-sonnet-4-6"
# 1) TOOL REGISTRY:schema 给模型,handler 留本地
TOOLS = {
"read_file": {
"schema": {"name":"read_file","description":"Read a file",
"input_schema":{"type":"object","properties":{"path":{"type":"string"}},"required":["path"]}},
"handler": lambda p: Path(p["path"]).read_text()[:8000]
},
"run_bash": {
"schema": {"name":"run_bash","description":"Run a shell command (read-only)",
"input_schema":{"type":"object","properties":{"cmd":{"type":"string"}},"required":["cmd"]}},
"handler": lambda p: subprocess.run(p["cmd"],shell=True,capture_output=True,text=True,timeout=30).stdout[:8000]
}
}
# 2) PERMISSION GATE:物理拦截,不靠模型自律
DENY = ["rm -rf", "--no-verify", "sudo", "curl ", "> /"]
def permit(tool, args):
if tool == "run_bash":
cmd = args.get("cmd","")
if any(d in cmd for d in DENY): return False, f"denied: {cmd}"
return True, None
# 3) LOOP CONTROLLER + 4) MESSAGE BUFFER + 5) RECOVERY
def agent(task, max_iters=20):
msgs = [{"role":"user", "content": task}]
schemas = [t["schema"] for t in TOOLS.values()]
for i in range(max_iters):
r = client.messages.create(model=MODEL, max_tokens=4096, tools=schemas, messages=msgs,
system="You are a careful research agent. Use tools step by step.")
msgs.append({"role":"assistant", "content": r.content})
if r.stop_reason == "end_turn":
return next((b.text for b in r.content if b.type=="text"), "")
results = []
for b in r.content:
if b.type != "tool_use": continue
ok, reason = permit(b.name, b.input)
if not ok:
out = reason
else:
try:
out = TOOLS[b.name]["handler"](b.input)
except Exception as e:
out = f"ERROR: {type(e).__name__}: {e}" # 关键:错误也回给模型
results.append({"type":"tool_result","tool_use_id":b.id,"content":str(out)})
msgs.append({"role":"user", "content": results})
return "hit max_iters"
四个关键决策:(1)schema 和 handler 解耦——schema 是给模型的契约,handler 是你的本地代码,永远别让模型直接 eval。(2)permission 是物理拦截而非 prompt 约束——deny list 在 harness 层判,模型说服不了。(3)错误回给模型而不是抛异常——这是 agent 能自我修复的根因,error message 是它最重要的输入之一。(4)max_iters 默认 20,必须有上限,不然 tool selection 抖动会导致死循环。
tool_result 用 "role":"user" 提交回去——Anthropic API 的协议是 tool result 走 user role,写错就报 400。(2)handler 直接抛异常没 catch,整个 loop 死掉;务必把 exception 转成 string 返回给模型。(3)权限只在 prompt 里写「不要 rm」——模型可以、并且偶尔会、绕过;deny list 必须在 harness 层。
Anthropic 2024 年底那篇 Building Effective Agents(Erik Schluntz & Barry Zhang)是过去两年最重要的 agent 工程文献。最被引用的不是它给的代码示例,而是它给的分类——它强行把所有「agentic」应用切成两类:
这个分类的杀伤力在于:大多数生产系统应该是 workflow,不是 agent。Workflow 更可预测、更便宜、更容易 eval、更容易 debug。Agent 用在任务开放、步数无法预先规划、需要根据中间结果决策的场景才合理(如自主调试、深度研究、复杂代码任务)。
博客列了 5 个 workflow pattern,可以直接背下来作为架构清单:
真正的 agent 是这 5 个的「上层」——当任务复杂到无法预先写出 workflow 时才升级。博客原文:"When building applications with LLMs, we recommend finding the simplest solution possible, and only increasing complexity when needed."
一个常被误造成 agent 的需求:「读用户上传的 PDF,提取要点,生成 1 页摘要」。看起来很 agentic?其实是 prompt chain:
# BAD:上来就写 agent,给它 read_pdf / summarize / save 三个 tool
agent("读 report.pdf 然后给我一页摘要")
# → 模型可能跳过提取直接编 / 偶尔忘 save / debug 困难
# GOOD:prompt chain workflow,每步代码层控制
def summarize_pdf(path):
text = extract_pdf(path) # 代码做,不用 LLM
if len(text) > 50_000:
chunks = chunk_by_heading(text)
partials = [summarize_chunk(c) for c in chunks] # 并行 LLM call
return synthesize(partials) # 第二步 LLM
return summarize_short(text)
什么时候才升级到真正的 agent?三个信号同时出现:(1)步骤数无法预先知道;(2)每步的下一步取决于上一步的结果;(3)能验证最终输出对错(不然 agent 会跑飞还不知道)。Coding / 研究 / 复杂数据分析符合,写邮件 / 翻译 / 报告生成基本不符合。
把这一期四点串成一个周末项目:造一个能替你做半小时主题调研的 personal research harness。目标不是复现 Perplexity,而是亲手摸一遍 harness 的五个组件。
web_search(用 Tavily 或 Brave API)、fetch_url(requests + readability)、save_note(写到本地 .md)。少而正交。fetch_url 只允许 https + domain allowlist;save_note 限定写入 ./notes/;任何 shell tool 一律不给。{url, key_points[], quote} 结构化 JSON,原文丢掉。写完这一套,你以后看任何「agent 产品」都会习惯性地剥皮——找到它的 tool registry、permission gate、loop control 在哪里——而不是被「agentic」营销词唬住。