大语言模型本质上只是一个"文本进、文本出"的函数:给它一段文字,它返回一段文字,仅此而已。它不能读文件、不能执行命令、甚至记不住上一轮对话。可它今天能改代码、查资料、连续完成几十步任务,靠的不是模型本身,而是模型外面套着的一整套系统——这套系统叫 harness。这个词的本义是马具:马有力量,但光有马拉不了车,得有一套把马力牵引到车上的挽具。
模型是马,harness 就是挽具。本文先讲清 harness 的定义与部件构成,再按对话循环、工具系统、上下文管理、缓存稳定性、权限安全的顺序逐个拆开看,最后给出四条建造路径的选型建议。
1、Harness 定义与词源
harness 在软件工程里最早指 test harness(测试挽具):围绕被测代码搭的脚手架——测试数据、运行环境、结果比对逻辑,负责驱动被测代码并收集产出。AI 领域借用了这个比喻,agent harness 指包裹在模型外面、把模型变成"能用"的那整套系统。纯模型与套上 harness 后的差别如下:
| 能力 | 纯模型 | 套上 harness |
|---|---|---|
| 记住上文 | 每次请求独立 | 打包历史全量重发 |
| 读文件 | 不能 | 文件工具代读,结果回传 |
| 执行命令 | 不能 | 命令工具代跑,回传输出 |
| 连续做事 | 一次回答即结束 | 循环驱动直到任务完成 |
正因为这些能力都来自 harness 而不是模型,同一个模型才会长出完全不同的产品:套上 Claude Code 是编程助手,套上工单流程就是客服机器人。业内对此有个流行公式:
Agent 能力 = 模型 × harness。模型决定聪明程度,harness 决定能干什么,两边都得强。
把一套典型 harness 拆开,部件大致如下:
agent-harness/
├── loop.py # 对话循环:打包历史、收发、判断停止
├── tools/ # 工具注册、校验与执行
├── context.py # 压缩、清除、外置记忆
├── cache.py # 前缀稳定性与缓存断点
├── permissions.py # 审批门与拦截钩子
└── ui.py # 流式呈现与人机交互
目录里的部件就是后文的展开顺序:循环是心跳,工具是手脚,上下文管理是记忆,缓存是钱包,权限是缰绳。
2、核心循环与工具系统
2.1、对话循环
对话式 API 是无状态的:服务端不保存任何对话。所谓"连续对话",是 harness 每轮把全部历史消息重新打包发给模型,这个机制叫全量重发。好处是历史完全握在 harness 手里,可以自由裁剪与改写;代价是输入 token 随轮次线性增长,成本问题留到第 4 节。循环的骨架代码如下:
messages = [{"role": "user", "content": task}]
while True:
resp = client.messages.create(
model=MODEL, messages=messages, tools=TOOLS)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use": # 不再调用工具,任务结束
break
for block in resp.content: # 逐个执行模型要的工具
if block.type == "tool_use":
result = run_tool(block.name, block.input)
messages.append(tool_result_message(block.id, result))
十来行就是 agent 的心跳:判断停止原因,执行工具,把结果拼回历史,再来一轮。模型只负责决定"想调用什么",harness 负责"真的去做"。整个循环的时序如下:
图中模型只出现两次,其余箭头全是 harness 在跑腿。一次真实任务里这个循环可能转几十轮,每轮都是一次完整的 API 请求。
2.2、工具系统
模型调用工具,靠的是 harness 预先声明的工具定义(见工具使用官方文档),一个最小示例如下:
{
"name": "read_file",
"description": "读取指定路径的文本文件内容",
"input_schema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"}
},
"required": ["path"]
}
}
schema 就是合同:模型读 description 决定何时调用、按 schema 填参数,harness 校验参数后代为执行。两条实战经验:description 要写清"什么时候该用",写得含糊模型就会乱调;工具宜精不宜多,几十个工具会稀释模型注意力,可以按需加载、用到再取。另外模型一次可能并行发起多个调用,harness 应并发执行、把结果合并回同一条消息。
3、上下文管理三板斧
上下文窗口是输入与输出共享的 token 预算,而全量重发让历史逐轮膨胀,长会话必然撞墙。成熟 harness 靠三种机制续命,分工如下:
| 机制 | 做法 | 适合 | 代价 |
|---|---|---|---|
| 压缩 | 接近上限时自动摘要早期对话 | 长会话整体保留语境 | 细节有损 |
| 清除 | 直接清掉旧的工具结果与思考块 | 工具产物巨大且不再需要 | 失去回看能力 |
| 外置记忆 | 要点写进外部文件,需要时读回 | 跨会话、跨任务复用 | 依赖检索时机 |
三种机制分别对应官方的 compaction、context editing 与 memory 工具;编码工具里"压缩历史"的提示,就是 compaction 在工作。三者不互斥,长跑 agent 通常是组合使用。
4、缓存稳定性设计
全量重发决定了 agent 输入的大头,是每轮字节级相同的前缀:系统提示、工具定义、全部历史。这正是 prompt caching 的用武之地,它的规则是前缀匹配:前缀中任何位置变一个字节,其后所有缓存全部失效。缓存价格约为写入 1.25 倍、读取 0.1 倍,布局稳定的 harness 能把长会话成本压掉一个数量级。高频肇事模式如下:
| 模式 | 后果 |
|---|---|
| 系统提示拼时间戳或用户名 | 每个请求都是新前缀 |
| JSON 序列化不排序 | 字段顺序漂移,字节不一致 |
| 中途增删工具或换模型 | 缓存整体失效 |
缓存是 harness 的隐形职责:用户完全感知不到它,它却决定一个 agent 在经济上是否成立。排查也不难——响应 usage 里的缓存读取字段为零,就说明前缀在某个位置被改写了。
5、权限与安全边界
模型会犯错,也会被骗,harness 是唯一能拦住它的地方。第一道防线是审批门:写文件、执行命令、发网络请求这类高危操作先经人工确认,Claude Code 的权限模式(GitHub 仓库)就是典型实现。第二道防线在工具设计:一把梭的 bash 工具最灵活也最危险,只能读固定目录的专用工具天然带边界,生产环境优先专用工具、最小权限。第三道防线是拦截钩子:工具调用前后插入自定义检查,拦危险命令、做脱敏。
注意:模型处理的网页与文档里可能藏着诱导它调用工具的注入指令。harness 必须假设模型会被骗,把校验放在执行侧,而不是指望提示词约束。
6、四条建造路径
从零造轮子到全托管,四条路径对比如下:
| 路径 | 自己写什么 | 适合场景 |
|---|---|---|
| 手写循环 | 循环、工具、上下文全部 | 学习原理、极致定制 |
| SDK 循环助手 | 工具与提示词 | 快速落地自定义 agent |
| 成品 harness 库 | 配置与扩展 | 编码、研究类产品化 |
| 托管平台 | 工具结果回调 | 不想管运行时的团队 |
选型先问一个问题:要不要拥有循环本身。多数业务在 SDK 层就能解决,Anthropic 的 Building Effective Agents 一文也主张从最简单的结构起步——两步工作流能解决的事,不必上 agent。当单 agent 跑稳、任务量超过单窗口承载,再考虑主 agent 派工子 agent 的多 agent 架构,Anthropic 工程团队的多 agent 研究系统实践有完整复盘。
7、总结
回顾全文:harness 是把"文本进出函数"变成"能做事系统"的全部外围——对话循环是心跳,工具系统是手脚,上下文管理是记忆,缓存设计是钱包,权限安全是缰绳。马还是那匹马,挽具决定它拉车、耕地还是上场参赛;模型竞争拼智力,harness 工程拼的是把这些部件做稳、做省、做安全。对于准备落地 agent 的团队,建议先手写一个最小循环吃透原理,再迁移到 SDK 或成品 harness;无论走哪条路,都把前缀稳定性与权限边界当作第一等设计目标——这两个看不见的部件,恰恰决定 agent 能不能便宜且安全地跑起来。


