DAY 03 / PHASE 1 · ENGINEERING

Harness Engineering

Agent Runtime · Tool Registry · Permission Gate · Loop Control

2026-05-22 · BigCat

模型只是 CPU,harness 才是真正决定能力的 OS。

// WHY THIS MATTERS

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》。

// 01

什么是 Harness:Agent 的运行时与边界

论断:harness 是把无状态的 LLM API 缝合成「会做事」的运行时;它不是 framework,是 OS。

背景与原理

一次 messages.create() 是无状态的 inference:你给它 input,它给你 output,模型并不知道也不在意你之前/之后做了什么。把这一次 inference 包装成一个能自主完成多步任务的实体,中间发生的所有事情合起来叫 harness:

真实使用体验 90% 取决于这几条的工程实现,而不是模型的 reasoning 强弱。Cognition 在 2025 年的 Don't Build Multi-Agents 里说得很直接:他们的核心壁垒是 harness 设计,而不是更聪明的 prompt。Claude Code 的 plan mode、Cursor 的 composer、Aider 的 git auto-commit,都是把同一个 base model 「装」进不同 OS 的结果。

┌──────────────── Harness = LLM 的运行时 OS ─────────────────┐ │ │ │ USER ──▶ ┌─────────────────────────────────────────┐ │ │ │ HARNESS │ │ │ │ ┌───────────────────────────────────┐ │ │ │ │ │ Loop Controller (while not done) │ │ │ │ │ └───┬───────────────────────────┬───┘ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ ┌──────────┐ ┌──────────────┐ │ │ │ │ │ LLM API │ ◀────── │ Tool Registry│ │ │ │ │ │ (no mem) │ │ + Permission │ │ │ │ │ └────┬─────┘ └──────┬───────┘ │ │ │ │ │ tool_use │ result │ │ │ │ ▼ ▼ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ │ │ Memory · Compaction · Logs │ │ │ │ │ └─────────────────────────────────┘ │ │ │ └─────────────────────────────────────────┘ │ │ ▲ │ │ │ stop / interrupt / approve │ │ HUMAN │ └────────────────────────────────────────────────────────────┘

实战示例

分清哪些是 prompt 的事、哪些是 harness 的事,从一个 30 秒诊断开始——当你的 agent 不 work,先问:

# —— 这是 PROMPT 的锅 ——
- 模型选错工具
- 输出格式不稳
- 推理跳步
- 没遵守约束

# —— 这是 HARNESS 的锅 ——
- 跑 10 轮就死循环(loop control 缺)
- tool 报错后 agent 就崩了(recovery 缺)
- 上下文越长越笨(compaction 缺)
- 工具调用没有人工干预的机会(permission 缺)
- 同一个 prompt 在 Claude Code 里好,在你脚本里不行(harness 不同)

把这张分类表贴在自己写 agent 的项目根目录——下次 debug 前先归因,能省一半时间。

失败模式:把所有问题都归给「模型不够强」。一个常见反例:你换到更新的模型,agent 表现反而下降——通常是因为新模型工具调用风格变了,旧 harness 的 parser 漏过了它新的输出形态。这不是模型问题,是 harness 没跟上 contract。
进阶资源 · Karpathy 关于 LLM-as-OS 的 talk, youtube · Intro to LLMs · Cognition Don't Build Multi-Agents, cognition.ai/blog/dont-build-multi-agents · Anthropic Building Effective Agents, anthropic.com/engineering/building-effective-agents
// 02

Claude Code 的 Harness 拆解:你每天用的东西到底在做什么

论断:Claude Code 的「Agentic 感」90% 来自 harness 设计,不是模型本身。

背景与原理

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,就能让它的运行时按你的工程纪律跑。

失败模式:(1)滥用 hooks 跑长命令——比如把全套 e2e 套进 PostToolUse,每次小改文件都触发,turn latency 飙到几分钟。Hook 应该是亚秒级的轻量验证,重活靠 Stop / 显式 slash command。(2)权限里只写 allow 不写 deny,等于敞着后门——务必把 rm -rf / 强制 push / --no-verify 这类显式 deny。
进阶资源 · Claude Code Hooks 文档, docs.claude.com/.../hooks · Claude Code Settings, docs.claude.com/.../settings · Claude Code Subagents, docs.claude.com/.../sub-agents
// 03

自建最小 Harness:100 行 Python 跑通一个 Agent

论断:把 harness 当黑盒会让你受 Claude Code / Cursor 的设计选择牵着走;写一次 minimal harness 是 AI 工程师的 hello world。

背景与原理

一个 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 抖动会导致死循环。

失败模式:(1)忘记把 tool_result"role":"user" 提交回去——Anthropic API 的协议是 tool result 走 user role,写错就报 400。(2)handler 直接抛异常没 catch,整个 loop 死掉;务必把 exception 转成 string 返回给模型。(3)权限只在 prompt 里写「不要 rm」——模型可以、并且偶尔会、绕过;deny list 必须在 harness 层。
进阶资源 · Anthropic Tool Use 协议, docs.claude.com/.../tool-use · Claude Agent SDK(Python/TS), docs.claude.com/.../agent-sdk · Simon Willison I built a small agent in 70 lines, simonwillison.net
// 04

《Building Effective Agents》精读:什么时候根本不该写 Agent

论断:Anthropic 这篇博客的核心结论不是「怎么造 agent」,是「90% 的需求别造 agent,写 workflow 就够了」。

背景与原理

Anthropic 2024 年底那篇 Building Effective Agents(Erik Schluntz & Barry Zhang)是过去两年最重要的 agent 工程文献。最被引用的不是它给的代码示例,而是它给的分类——它强行把所有「agentic」应用切成两类:

这个分类的杀伤力在于:大多数生产系统应该是 workflow,不是 agent。Workflow 更可预测、更便宜、更容易 eval、更容易 debug。Agent 用在任务开放、步数无法预先规划、需要根据中间结果决策的场景才合理(如自主调试、深度研究、复杂代码任务)。

博客列了 5 个 workflow pattern,可以直接背下来作为架构清单:

  1. Prompt chaining:把任务切成几步,每步用一个 prompt,前一步输出做下一步输入。可以在中间插 gate(不通过就回退)。
  2. Routing:先用一个分类 prompt 把请求分类,路由到不同的下游 prompt / 模型。
  3. Parallelization:把任务切成可并行子任务(sectioning),或者跑多个 voter 投票(voting)。
  4. Orchestrator-workers:一个 orchestrator 动态切分子任务、派给 worker、收集结果——Claude Code 的 subagent 就是这种。
  5. Evaluator-optimizer:一个 LLM 生成,另一个 LLM 评估并给反馈,迭代直到达标。写作 / 翻译 / 复杂代码很合适。

真正的 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 / 研究 / 复杂数据分析符合,写邮件 / 翻译 / 报告生成基本不符合。

失败模式:被「agentic」营销词带跑,把所有 LLM 应用都包成 multi-agent。Cognition 的 Don't Build Multi-Agents 复盘了 Devin 早期 multi-agent 路线踩的坑:context 在子 agent 间漂移 + 决策不一致 + 调试地狱。他们最终砍回到单个长上下文 agent + workflow。
进阶资源 · Anthropic Building Effective Agents, anthropic.com/engineering/building-effective-agents · 配套代码 anthropic-cookbook, github.com/anthropics/anthropic-cookbook · Cognition Don't Build Multi-Agents, cognition.ai/blog/dont-build-multi-agents

// 综合实战 · 给自己造一个「research harness」

把这一期四点串成一个周末项目:造一个能替你做半小时主题调研的 personal research harness。目标不是复现 Perplexity,而是亲手摸一遍 harness 的五个组件。

  1. Tool registry:3 个工具——web_search(用 Tavily 或 Brave API)、fetch_url(requests + readability)、save_note(写到本地 .md)。少而正交。
  2. Permission gatefetch_url 只允许 https + domain allowlist;save_note 限定写入 ./notes/;任何 shell tool 一律不给。
  3. Loop control:max_iters=25,每轮记录 tool 序列;连续 3 次同一个 tool 同一个参数则强制 break(防死循环)。
  4. Memory:超过 60K token 触发 compaction,把 fetched pages 压成 {url, key_points[], quote} 结构化 JSON,原文丢掉。
  5. 是 agent 还是 workflow?先按 workflow 写:plan → search → fetch → write。如果 plan 步骤经常错,再升级成 agent(让模型自己决定何时停)。这一步是你亲身体会 §4 论断的地方
  6. Eval:自己出 10 个能验证答案的问题(已知 ground truth),跑 workflow vs agent 两版,对比 token 成本和准确率。绝大多数人会发现 workflow 版便宜 3-5×、准确率不输甚至更高——这就是 Anthropic 那篇博客的实证。

写完这一套,你以后看任何「agent 产品」都会习惯性地剥皮——找到它的 tool registry、permission gate、loop control 在哪里——而不是被「agentic」营销词唬住。

// ENGLISH GLOSSARY

Harness
包裹 LLM API 的运行时系统,负责 tool dispatch / loop / state / permission / recovery。本期主角。
Tool Registry
harness 中注册的工具集合(schema + handler)。少而正交优于多而花哨。
Permission Gate
tool 执行前的物理拦截层。Claude Code 的 allow/ask/deny 是典型实现。
Loop Controller
控制 agent 何时停止的逻辑:stop_reason、max_iters、token budget、人工 interrupt。
Hooks
harness 在事件边界(PreToolUse / PostToolUse / Stop 等)执行的钩子命令。Claude Code 暴露给用户的可编程面。
Subagent
独立 context window 的子代理。用于把可黑盒化的子任务隔离,避免污染主 context。
Compaction
history 超阈值时压缩历史的过程(Day 2 主题)。harness 的内置 memory 策略之一。
Workflow vs Agent
workflow 控制流在代码里,agent 控制流在模型里。Anthropic 推荐能用 workflow 就别用 agent。
Orchestrator-Workers
一个主 agent 切分任务、派给多个 worker 并行执行的 pattern。
Evaluator-Optimizer
一个 LLM 生成 + 另一个 LLM 评估,迭代逼近目标的 workflow pattern。

// 深入思考

Claude Code 的 plan mode 在执行前显示计划让用户审批。这是 UX 选择还是工程必需?自动执行会怎样?
是工程必需。1) LLM 「动手前先想」准确率高 30%+(ReAct 论文实验);2) 人工审批是最便宜的 verification,比 LLM-judge 还便宜;3) plan 记录让失败可调试。无 plan mode 的 agent 在 SWE-bench 上成功率比有的低 10-20pp。Harness 不强制 plan,agent 会 skip step 直接动手。
harness 的「OS」比喻是说它管理资源、调度、IPC——那 LLM 是 CPU 还是 process?
LLM 是 CPU(无状态计算单元),每次 inference 是一次 syscall。Harness 维护 process state(context、memory、tool registry),调度 syscall sequence,处理 syscall return(tool result)。这个 mental model 也帮你看懂为什么 multi-agent = multi-process(有 IPC 成本),为什么 prefix caching ≈ CPU cache。
100 行 Python 写 hello world harness 听起来诱人,但 production harness 通常膨胀到几千行。膨胀的部分通常是什么?
膨胀主要在:1) Error recovery(rate limit retry、tool 失败 fallback、partial result handling);2) Observability(每个 tool call 的 tracing/logging/cost tracking);3) Permission gates(每个 sensitive action 的审计与确认);4) Context management(truncation、compaction、tool result 大小限制);5) Prompt injection 防御。Hello world 100 行,production 1000+ 行。
Anthropic 'Building Effective Agents' 说 90% 需求该用 workflow 而不是 agent。怎么判断一个需求该用哪种?
三个判定:1) 流程是否预先确定?是 → workflow;2) 每步是否需要看上一步结果再决策?是 → agent;3) 失败成本如何?高 → workflow(更可控)。例:发邮件提醒 = workflow(流程固定);debug 用户错误 = agent(依赖动态发现)。误用代价:把 workflow 当 agent 跑会慢 5-10× + 不可靠。
Permission gate 是 harness 的关键。如果让 agent 自己决定哪些操作需要 confirm,会怎样?
Agent 会倾向「都不 confirm」(lazy),尤其在迭代任务里。正确做法:把 permission policy 写死在 harness 层,不交给 agent 决策。例如 Claude Code 把 write/exec/network 等「破坏性 action」默认 confirm。原因:LLM 不会评估自身错误概率,而 harness 可以根据 action class 静态判断风险等级。

// 延伸阅读