大语言模型本身是一个无状态函数:文本进、文本出。它无法打开文件、跑测试、记住昨天,或在看到错误后再尝试一次。一切让模型感觉像智能体(agent)的东西——读你的仓库、编辑文件、跑命令、从错误恢复——都活在包裹模型的软件里。那个软件是harness(运行外壳),把它做好是它自己的学科:harness 工程。随着前沿模型在原始能力上收敛,harness 越来越是编码工具胜负所在。本篇是 AI 编码智能体如何工作 的姊妹篇——那篇解释智能体;这篇关于工程化运行它的脚手架。
- 模型是引擎;harness 是车。模型推理;harness 给它感官(上下文)、手(工具)、一个循环、记忆、护栏,和检查自己工作的方式。
- 上下文组装是杠杆最高的活——决定什么进入有限上下文窗口比稍大的模型更决定输出质量。"上下文工程"已超越提示工程。
- 智能体循环是心脏——构建提示 → 调模型 → 跑它的工具调用 → 把结果喂回 → 重复直到完成或预算耗尽。
- 工具是你设计的 API——清晰 schema、好描述、合适粒度,和模型能从中恢复的错误消息。
- 验证闭合循环——跑测试/linter/构建并把失败喂回,是把一次性生成器变成迭代纠正的智能体的东西。
- harness 经验性调优——你需要 eval、轨迹分析和步数/成本预算,因为小的 harness 变化大幅摆动真实成功率。
- 能力 ≈ 模型 × harness。较小模型上的好 harness 常胜过差 harness 上的好模型。
harness 是模型周围所有工程:智能体循环、上下文组装、工具定义、记忆/压实、权限和沙箱,以及环境锚定的验证。模型供应推理;harness 供应让那个推理可行动且可靠的一切。因为模型在商品化,harness 质量——尤其它多好地组装上下文和验证工作——现在是 AI 编码工具间的主要差异。
什么是 Harness?
在任何 AI 编码工具的两部分间划一条硬线有帮助。模型是一个前沿 LLM(Claude、GPT、Gemini):它接收一块文本并产生一块文本,无记忆、无行动能力。harness 是它周围使它有用的程序——它组装输入、解释输出、执行模型请求的任何动作,并循环。Claude Code、Cursor、Devin、OpenHands 和 Aider 全是同一小撮模型上的 harness;它们的差异几乎全是 harness 差异。
| 模型做 | harness 做 |
|---|---|
| 推理与语言 | 组装被推理的上下文 |
| 决定调哪个工具 | 定义工具并实际执行它们 |
| 产生文本/diff | 应用编辑、跑命令、解析输出 |
| 无状态 | 持有记忆、历史和循环 |
| 无"安全"概念 | 强制权限、沙箱、护栏 |
实际后果:大部分工程努力——和产品大部分质量——在 harness,而非你调的模型。
智能体循环
核心上,harness 是一个循环。模型被调用,若它要求用一个工具,harness 跑那个工具、把结果追加到对话,并再次调模型——重复直到模型产生最终答案或 harness 撞上预算。这一个循环就是把一次性文本预测器转成探索、行动和迭代的东西。
state = [system_prompt, tools, user_goal]
steps = 0
while not done and steps < budget:
reply = model(state) # 唯一一次对 LLM 的调用
if reply.tool_calls:
for call in reply.tool_calls:
result = execute(call) # harness 行动: 读文件、跑命令、grep…
state.append(result) # 观测回到上下文
else:
done = True # 模型给了最终答案
state = compact(state) # 保持在上下文窗口内
steps += 1
注意多少是 harness 的责任:选预算、安全执行工具、决定什么算"完成",和压实状态。模型只见到 state 并发出下一步——循环及其中一切都是工程。
上下文组装:杠杆最高的活
模型只能推理它上下文窗口里的东西,而那是有限的。harness 最重要的单一工作是决定每轮什么进那个窗口——系统提示、工具定义、从仓库拉的正确文件、先前工具结果,和对话历史。把正确代码摆到模型面前,平庸模型也发光;搞错,最好的模型也幻觉。这就是为什么领域重心从提示工程移到上下文工程。
上下文窗口(比如 200K tokens)
├─ 系统提示 + 工具定义 ............ ~5K 固定开销
├─ 检索的文件 / RAG ............... ~40K ◀ 高杠杆切片
├─ 对话 + 工具结果 ................ 每轮增长
└─ 为输出保留的余量 ............... ~8K
填满时 → 总结 / 丢弃 / 压实较旧的轮次
| 提示工程 | 上下文工程 |
|---|---|
| 把指令措辞好 | 选择模型看到什么信息 |
| 一个提示,大体静态 | 每轮动态——检索、修剪、排序 |
| "我怎么问?" | "它面前需要什么才能回答?" |
具体地,harness 做检索(找相关文件/符号,按索引或让模型按需 grep)、排序(把最相关的放前面),和修剪(丢弃或总结不再要紧的)。当一个智能体"忽略"一个明显的文件,几乎总是上下文组装 bug,而非推理失败。
工具设计
工具是智能体的手,harness 定义它们——它们的名字、schema、描述和行为(见工具使用、函数调用与 MCP)。好的工具设计是真正的 API 设计,带个转折:消费者是模型,所以描述是接口的一部分——它是模型决定何时及如何调用工具的方式。
{
"name": "run_tests",
"description": "Run the project's test suite and return pass/fail"
" plus failing-test output. Use after editing code to"
" verify changes.",
"input_schema": {
"type": "object",
"properties": { "path": {"type": "string", "description": "dir to test"} }
}
}
使工具好用的原则:合适粒度(几个可组合工具胜过几十个超特定的)、清晰描述(模型只知道你告诉它的)、安全默认(破坏性工具应需显式确认),且关键地——可恢复错误:失败的工具应返回模型能读并行动的消息("文件未找到:你是指 X 吗?"),而非不透明的堆栈跟踪。错误文本是循环反馈的一部分。
记忆与状态
模型无状态,所以 harness 拥有记忆。有两层。短期记忆是对话/上下文窗口本身——目标、动作和观测的运行记录。长期记忆住在窗口外:一个 scratchpad 或笔记文件、一个 harness 每会话注入的项目指令文件,或一个智能体能查询的外部存储。难问题是短期层填满:一个长任务累积工具结果直到威胁窗口。harness 用压实(compaction)处理这个——总结或丢弃较旧轮次而保留目标和关键事实——使智能体能在比任何单个上下文窗口更长的任务上持续工作。
护栏、权限与安全
模型无"危险"概念。harness 是安全被强制的地方,因为 harness 是实际执行动作的东西。这层包括风险操作前的权限提示、命令的 allowlist/denylist、沙箱(sandboxing)(在受限环境跑工具调用,使智能体碰不到它不该碰的),和拦截动作以应用策略的 hook。一个工程良好的 harness 安全失败:当一个动作不可逆或对外时,它暂停确认而非信任模型的判断。这也是你阻止恶意文件的提示注入变成破坏性命令的地方。
安全不能住在模型——模型只发出一个行动请求。harness 决定是否兑现它。这就是为什么权限、沙箱和确认门是 harness 特性,以及为什么"模型不会那样做"从不是安全论证。
验证与自我纠正
最把真智能体与花哨自动补全分开的特性是验证:harness 跑代码、测试、linter 或构建,并把结果喂回循环。这把智能体锚定在环境而非它自己(可能错的)代码正确的信念。一个失败的测试成为模型推理并修复的观测——闭合一个模型独自无法闭合的循环。
模型编辑代码
│
▼
harness 跑测试 ──▶ 失败? ──是──▶ 把错误喂回 ──▶ 模型修复 ──┐
│ │
└────────────────────────◀──────────────────────── 循环 ┘
否
▼
完成 — 由环境验证,而非模型的一面之词
这个循环的质量取决于 harness 选择:跑什么、如何简洁地呈现失败(一个 5,000 行测试日志必须被修剪到要紧的部分),和何时停。带好验证的 harness 能让较弱模型磨到正确答案;没有它,即便强模型也自信地发布坏代码。
错误处理与恢复
智能体以一次性调用不会的方式失败,harness 必须预期它们:一个工具抛错、模型为一个工具调用发出畸形 JSON、一个命令挂起,或智能体卡在重复同一失败动作。健壮的 harness 加重试(带错误喂回使模型能调整)、工具执行的超时、循环检测(注意同一动作重复并跳出),和硬步数/成本预算使困惑的智能体不能烧无限 token。设计这些恢复路径是生产 harness 难的很大一部分原因。
评估与迭代
你无法仅凭直觉改进 harness,因为小变化——调过的工具描述、不同的压实策略、重排上下文——大幅摆动真实成功率。严肃的 harness 工作是经验性的:对基准任务跑一个 eval harness(如 SWE-bench 式的"修这个真实 bug"套件)、测量成功率、延迟和成本,并检查轨迹(trajectory)(完整步骤序列)以看运行在哪出错。然后改一件事并重测。harness 是一个你对照数据调优的系统,而非你写一次的提示。
子智能体与编排
随任务增长,单个智能体的上下文变杂乱。一个常见 harness 模式是子智能体(sub-agent):主智能体把一个限定范围的活(搜索代码库、评审一个 diff)委托给一个有自己干净上下文的全新智能体,它只返回一个结论。这让主上下文聚焦、支持并行工作,并约束任何一个上下文窗口必须持有多少。编排——决定如何拆分工作、并行跑智能体、组合结果——是大任务越来越重要的 harness 能力。
为什么 harness 与模型一样重要
harness 工程的论点:有效能力 ≈ 模型 × harness。同一模型,放进更好的 harness,正确完成更多任务——因为 harness 控制模型看到什么(上下文)、它能做什么(工具)、它是否抓住自己的错误(验证),和它是否保持安全和在预算内(护栏)。随前沿模型收敛,这些因素而非原始模型 IQ 越来越决定哪个编码工具真正完成活。这就是为什么"上下文组装胜过更大模型"是反复的教训,以及为什么 harness 工程已成为它自己的学科。
LLM 是一个聪明但盲、失忆、被绑手的推理者。harness 给它眼睛(上下文)、手(工具)、记忆、反射(验证和恢复)和良知(护栏)——并把它们接进一个循环。把那个脚手架做好,一个普通模型成为有能力的智能体;做差,世界上最好的模型也乱抓。harness 越来越就是产品。
什么是"harness"?使 LLM 成为智能体的、模型周围的软件:循环、上下文组装、工具执行、记忆、护栏和验证。Claude Code 和 Cursor 是同一些模型上的 harness。
为什么上下文工程胜过提示工程?窗口有限;把正确代码摆到模型面前比措辞或稍大模型更决定输出质量。
什么把一次性生成变成智能体?循环加环境锚定的验证——跑测试/构建并把失败喂回,使模型迭代到一个正确、已检查的答案。
安全住在哪?在 harness,不在模型——模型只请求动作;权限、沙箱和确认门决定是否执行它们。
模型 vs harness——哪个更重要?两者;能力 ≈ 模型 × harness。随模型收敛,harness 质量是主要差异。