Runtime OS · 运行时操作系统
Harness
Engineering
为会失忆、会自我感觉良好的模型,
工程化出一个能持续可靠工作的运行时。
Harness Engineering:
为会失忆的模型,造一个操作系统
它是什么 · 它怎么做 · 它的命运(协同进化)
深度研究 · 2026-08-11 · 基于 Li & Zhou 两篇综述 + Anthropic 三篇工程博客整理
Harness Engineering 的本质,是为「无状态、会失忆、会自我感觉良好」的语言模型,工程化出一个让它能持续可靠工作的运行时操作系统。它的核心动作不是「给模型加能力」,而是把「模型独自做不到可靠的事」外化成一层控制系统——而它的命运,是与模型进入一场永不停歇的协同进化(co-evolution)。
目录
- 01换班工程师的失忆症
- 02真正的转移:能力被搬到了哪里
- 03本质的难点:三组结构性约束
- 04Anthropic 的实践笔记:四类失败模式
- 05本质跃迁:harness 长成操作系统
- 06发展:模型与 harness 的协同进化
- 07结尾:三层收束与三判据
01先建立一个直觉:换班工程师的失忆症
在讲分类之前,先用 Anthropic 给的一个比喻建立直觉——这是理解「为什么需要 harness」最快的方式:
想象一个软件项目,由一群轮班的工程师完成,而每一个新到岗的工程师都对上一班发生的事毫无记忆。
这就是 long-running agent 的真实处境。上下文窗口有限,复杂任务无法在一个窗口内完成,于是 agent 必须在多个「会话(session)/上下文窗口」之间接力。每一次新的上下文窗口,都是一个带着失忆症上岗的新工程师。
一个反直觉的事实是:仅靠 compaction(压缩)不够。 Anthropic 的实验里,即便是 Opus 4.5 这种前沿编码模型,在 Claude Agent SDK 里循环跑多个上下文窗口,只给它一句「做一个 claude.ai 的克隆」,也做不出生产级应用。它会在两种模式下崩:
- 一次做完(one-shot):试图一口气把整个应用写完,结果在实现到一半时耗尽上下文,留下一堆半成品、且没文档说明,下一班只能靠猜,把大量时间花在「让基本功能重新跑起来」上。
- 提前宣布胜利:功能建了一些之后,后来的 agent 环顾四周,看到「已经有进展了」,就宣布整个项目完成。
这两个失败模式揭示的本质:模型不是「不够聪明」,而是缺少一种跨失忆班次的「工程纪律」。 Harness engineering 要工程化的,正是这种纪律。
02真正的转移:能力被搬到了哪里
harness 是闭环控制器 C_H
把模型当作一个无状态函数 f,harness 是包在它外面的控制器 C_H:
关键点:同一个模型 f,换了控制器 C_H,测出来的行为就变了。 改一条 context 策略、一个 tool schema、一个 verifier 或一条 recovery loop,改的都是 C_H。agent 性能是 (model, harness) 这个系统的属性,不是模型的属性。Harness engineering,就是把控制器 C_H 当作一阶工程对象来设计。
weights, context and harness
《Agent Harness Engineering: A Survey》把社区重心的迁移讲得很清楚,是一条向外走的三层路径:
| 层 | 把「能力」看作什么的属性 | 工程力气投在哪里 |
|---|---|---|
| Weights(权重层) | 模型参数 | 预训练 / SFT / RLHF |
| Context(上下文层) | 模型每步的输入信息 | prompt engineering / RAG / context engineering |
| Harness(脚手架层) | 模型所运行的基础设施 | 执行环境、工具接口、状态、编排、可观测、验证、治理 |
三层是叠加而非互斥——但每一层都改变了开发者把「可变智能」放在哪里。Harness engineering 就是这条路径走到最外层之后的产物。
硬数据背书(都是固定模型权重、只动 harness):
- Bölük (2026):在 15 个模型上只改「编辑工具格式 + 工具脚手架」,编码基准大幅提升,某模型直接约 10 倍。
- Trivedy (2026):固定 GPT-5.2-Codex,靠重写 system prompt + 注入 context middleware + 加 self-verification hooks,Terminal-Bench 2.0 从 52.8% → 66.5%。
- Meta-Harness (Lee et al., 2026):证明 harness 的一部分可以被自动优化,甚至超过手工脚手架。
这就是综述的核心主张——约束瓶颈命题(binding-constraint thesis):真实世界里,决定 agent 可靠性天花板的,是基础设施质量,而不仅是模型能力。
它工程化了什么?ETCLOVG 七层
综述提出的七层分类(ETCLOVG),前四层是「结构支柱」,后三层是「控制平面(control plane)」:
03本质的难点:三组结构性约束
如果只记住 harness 是什么,还没抓到本质。本质藏在它的三组结构性约束里(综述称为 cross-layer problems):
1. 能力-控制权衡(Capability–Control Tradeoff)
给 agent 越多能力,控制问题就越大。 工具菜单越宽 → 选择错误和 prompt injection 攻击面越大;持久记忆越强 → 溯源(provenance)/ 过时 / 隐私风险越多;sandbox 越宽松 → 自动执行越有用,但失准或被攻陷时的影响范围(blast radius)也越大。Harness engineering 不是「给能力」,而是「在给能力的同时等比例地给出控制」。
2. 成本-质量-速度三难(Cost–Quality–Speed Trilemma)
更强的验证、更严的治理、更丰富的可观测、更忠实的执行环境——都会增加成本和延迟。真正的工程问题不是「质量越高越好」,而是决定哪些检查必须同步、哪些可以异步、哪些失败才值得走昂贵恢复。
3. harness 耦合(Harness Coupling)—— 局部最优会脆弱
七层是耦合的:执行环境一改,评估结果跟着变;工具描述吃 context 预算又塑造模型行为;trace 只有在身份与权限同粒度记录时才成为治理证据。一个隔离测试里看起来有益的改动,组合进整条回路后可能让整体变差。 所以 harness 的改动必须当作系统改动来测试。
04Anthropic 的实践笔记:harness 在对抗的四类失败模式
理论讲完,下面是 Anthropic 三篇工程博客最有价值的部分——harness 具体在对抗什么、怎么对抗。这一节是综述里看不到的「现场手感」。对应到前面那个「失忆轮班工程师」的比喻,harness 工程化的是一支成熟工程团队的日常纪律。
失败模式 ①:一次做完(one-shotting) → 用「功能清单 + 增量」
让初始化 agent 把用户的高层 prompt 展开成一份结构化的功能清单(claude.ai 克隆那个例子是 200+ 条功能),每条都标记为 passes: false。后续每个编码 agent 只做一条功能。用 JSON 而不是 Markdown 存这份清单——模型更不容易去乱改/覆盖 JSON。再配一句强约束:「禁止删除或修改测试用例。」
失败模式 ②:提前宣布胜利 → 用「干净交接 + 自我验证 + 测试像真人」
每个会话结束必须留下可合并到 main 的干净状态(无重大 bug、有序、有文档):写带描述的 git commit + 进度摘要,让模型能用 git 回滚坏改动。绝不允许「没测就标完成」——必须用浏览器自动化(Puppeteer/Playwright MCP)像真人用户一样端到端测试。每个新会话开头先跑固定流程:pwd → 读 git log + 进度文件 → 读功能清单 → 选最高优先级的未完成项。
失败模式 ③:上下文焦虑(context anxiety) → compaction vs reset
模型在接近上下文上限时会提前收尾(context anxiety)。两种应对:
- compaction(压缩):原地把旧对话摘要,同一个 agent 继续——保连续性,但没有干净起点,焦虑仍在。
- reset(重置):清空上下文 + 结构化交接给下一个 agent——干净起点,代价是交接开销。
关键证据(这恰好是「承重假设会过时」的最干净例子):Claude Sonnet 4.5 的 context anxiety 强到 compaction 不够用,reset 是必须的;但同样的 harness 换到 Opus 4.5,这个行为基本消失了,reset 变成了 dead weight(死重),可以整个删掉。再后来 Opus 4.6 让原本必需的「sprint 分解」也能删掉。
失败模式 ④:自我评价虚高 → generator / evaluator 分离
让 agent 给自己的作品打分,它会自信地夸一通——即使人类一眼看出平庸。解决方案是借 GAN 的思路,把「生成者」和「评判者」拆成两个 agent:
把一个独立 evaluator 调教得「爱挑刺」,远比让 generator 对自己的工作变苛刻要可行得多;一旦存在外部反馈,generator 就有了具体的迭代目标。
这演化成三 agent 架构:planner(1-4 句话 → 完整产品 spec,鼓励野心、只管产品与技术大方向、不写死实现细节,否则错误会级联)+ generator(一次一个功能,自我评估)+ evaluator(用 Playwright 像用户一样点击跑通的应用,逐条打分,硬阈值不达标就打回)。两者在每个 sprint 前先协商 sprint contract:先就「这块做完长什么样」达成一致,再写代码。
Load-bearing assumption
三篇博客反复回到同一句话,我认为它是 harness engineering 的工程第一性原理:
harness 里的每一个组件,都是对「模型独自做不到可靠」的一条赌注(encodes an assumption about what the model can’t do on its own)。这些赌注值得反复压力测试——因为它们可能一开始就错,而且会随模型变强而迅速过时。
配套的方法论:「find the simplest solution possible, and only increase complexity when needed」(从最简方案开始,必要时才加复杂度);以及——简化时一次只删一个组件,观察影响(Anthropic 试过一次性激进砍掉,结果既复现不出性能、也搞不清哪块是承重的)。evaluator 不是固定的 yes/no:它只在任务「超出当前模型独自能可靠完成的边界」时才值得那个成本;模型越强,边界越外推,evaluator 对边界内的任务变成纯开销,但对边界处的任务仍有真实提升。
05本质跃迁:harness 正在长成 agent 的「操作系统」
这是我认为最高的一层「升华」。Anthropic 在 Managed Agents 那篇里,把 harness engineering 拔到了操作系统的高度:
几十年前,操作系统通过把硬件虚拟化成抽象——进程(process)、文件(file)——解决了「为尚未被想到的程序设计系统」这个老问题。抽象比硬件长寿:一个
read()调用,不关心底下是 1970 年的磁盘组还是现代 SSD。上层接口稳定,底层实现随便换。
Managed Agents 用同样的思路,把 agent 的组件虚拟化成三个长寿抽象:
| OS 的抽象 | Agent 的抽象 | 含义 |
|---|---|---|
| 进程 / 文件 / 磁盘 | session / harness / sandbox | session = 发生过一切的可追加日志;harness = 调用模型并路由工具的回路;sandbox = 跑代码/改文件的执行环境 |
| 上层稳定、底层可换 | 「对接口的形状有主见,对接口背后跑什么没主见」 | 三者各自可独立失败、独立替换 |
① 别养宠物(pets vs cattle)
一开始把 session/harness/sandbox 全塞进一个容器,就等于养了一只「宠物」——它一死,整个 session 就丢了;想 debug 还得进装着用户数据的容器里开 shell,等于没有 debug 能力。解法是把三者解耦:大脑(brain = 模型 + harness)从手(hands = sandbox + 工具)和 session 里分出来。harness 不再住在容器里,而是像调用任何工具一样调用容器 execute(name, input) → string——容器从宠物变成了牲口,死了就当一次工具调用错误抛回去,让模型决定是否重试。
② session ≠ 上下文窗口
长任务必然超过上下文窗口,而 compaction / trimming 都在做「不可逆的保留/丢弃决策」——但你很难知道未来的轮次会需要哪些 token。解法是把上下文当成一个活在上下文窗口之外的对象:session 就是这个对象,持久存在日志里;大脑用 getEvents() 按「位置切片」去盘问它(从上次读到的地方续读、回退几条看上下文、重读某个动作之前的状态)。关键的关注点分离:session 只保证「上下文可恢复且可盘问」;至于具体怎么做 context engineering,那是 harness 的事——因为我们无法预测未来的模型需要哪种 context engineering。
③ 安全靠结构性解耦,而不是寄望模型守规矩
耦合设计下,模型生成的不受信代码和凭证在同一个容器里——prompt injection 只要骗模型读自己的环境变量就拿到 token 了。结构性修复:token 永远不让 sandbox 触及。Git:用 token 在 sandbox 初始化时克隆、接好本地 remote,之后 push/pull 都不需要 agent 经手 token;自定义工具:OAuth token 存外部 vault,模型经一个专用代理调用,代理凭 session 关联去 vault 取凭证——harness 自始至终不知道任何凭证。
④ meta-harness:对接口有主见,对实现没主见
Managed Agents 是一个「meta-harness」——它不规定未来 Claude 要用哪个具体 harness(Claude Code 是个好 harness,特定任务的窄 harness 也各有专长),而是提供通用接口,让许多不同的 harness 都能跑。它只断言三件事:Claude 需要操纵状态(session)、执行计算(sandbox)、扩展到多脑多手。
把 brain 从 hands 解耦后,容器只在需要时才按需创建——p50 首字延迟(TTFT)降约 60%,p95 降逾 90%。一个「手」就是一个
execute(name, input) → string,harness 根本不知道那 sandbox 是容器、是手机、还是个 Pokémon 模拟器;脑与脑之间还能互相传递手。
这一节的意义:harness engineering 的终局,不是写出某个最好的脚手架,而是为自主智能造一套「操作系统」级的抽象——就像 read() 比 1970 年的磁盘长寿一样,session/harness/sandbox 这套抽象会比今天任何具体模型、具体 harness 都长寿。
06发展:模型与 harness 的协同进化(co-evolution)
回到那个最本质的追问——「模型够强之后,harness 会不会被淘汰?」
第六节那条「承重假设」原理已给出工程层面的回答:每条假设都会过时、被删。但综述给了更彻底的一层——harness engineering 的命运,是与模型进入一场永不停歇的协同进化(co-evolution)。这有四层含义,且每一层都有文献锚点。
1. 边界双向移动:能力不是单向外化,而是在模型内外之间震荡
外化综述 §8.1「The Expanding Frontier」给了最关键的洞察:模型内部与被外化之物的边界并不固定,而是随模型、任务、基础设施协同演化而移动——而且是双向的。
- 向内收回(internalize):模型改进可以把能力拉回内部。结构化输出更可靠了 → 原本必需的格式校验层可以删;有效上下文变大了 → 原本复杂的分层记忆架构可以简化。这正是 Anthropic 那两个例子的本质:Sonnet 4.5 需要的 context reset、Opus 4.5 需要的 sprint 分解,在更强模型上成了「已收回内部」的能力。
- 向外提出新要求(externalize anew):但更丰富的 harness 也对模型提新要求——尊重 schema、配合权限检查、协调分阶段的上下文注入——于是又催生新的外化组件。
所以边界不是单向收缩、harness 单调变小,而是双向震荡。核心工程挑战因此不是「外化得越多越好」,而是「知道何时进一步外化、何时收回」。这也催生了一个新的评估维度——transferability:保持 harness 不变、换底座模型,看它是否仍有效,借此直接测出能力在「基础设施 vs 权重」间各占多少(§8.6)。
2. harness 自己也会演化:自演化脚手架的三层与四路径
外化综述 §8.3「Toward Self-Evolving Harnesses」指出:一旦编排逻辑本身被外化,harness 就从「人工适配」变成「可程序化适配」的对象。自演化发生在三个层次:
| 层 | 适应对象 | 改变什么 |
|---|---|---|
| module level | 检索粒度、skill 排序启发式、协议路由规则 | 架构固定,内部策略随观测失败调整 |
| system level | 执行流水线本身 | 调度、执行顺序、资源分配在日志暴露瓶颈时改变 |
| boundary level | harness 的范围 | 随模型/任务扩张或收缩,加新组件、剪冗余(即第 1 条的边界动力学) |
技术路径按搜索空间不同分四类:RL(优化离散运行时策略:搜索深度 / 压缩比 / 重试)、program synthesis(把 harness 适配当代码修复——失败轨迹后提 patch、沙箱验证、部署)、evolutionary(搜索 harness 拓扑)、imitation(探索太贵时从专家/强模型蒸馏执行日志)。Meta-Harness、AutoHarness、RewardHarness(「self-evolving agentic post-training」)已是雏形——harness 正在被当作可优化的目标,而不是写死的脚手架。
3. harness evolution 就是 agentic regime 下的 test-time scaling
把上一条翻译成更锐利的语言:演化 harness,本质上是在不改进模型权重的前提下,把更多 test-time(推理时)算力组织进运行时——它产生的是 test-time-scale 量级的回报。
| 推理时改进类别 | 改什么 | 动权重? |
|---|---|---|
| Decoding Strategies | token / 序列级生成 | 否 |
| Reasoning-Based Improvement | 结构化中间思维(CoT、任务分解) | 否 |
| Agentic System-Based Improvement | 演化系统组件:prompt / tool / memory / workflow(即 harness) | 否 |
| Test-Time Training | 推理时临时改参数 | 是 |
综述明说,这一类是「不改编码动态,而演化系统本身「——harness 被视为一个「更广的计算基底」。而 test-time scaling(TTS)的经典实例——verifier 引导搜索、迭代精炼、agent 集成、best-of-N——正好就是 harness 的机制(evaluator 回环、generator/evaluator、多 agent、验证门)。于是得到一个干净的等式:
对 chat 模型,test-time scaling = 更长的推理链 / 更多采样;对 agent,test-time scaling = 更丰富的 harness。harness 就是 agentic regime 下 test-time 算力的载体。
Anthropic 那组数据正是这个论点的度量:完整 harness(9 / 20 min),本质上是经由 harness 多花了约 20 倍的 test-time 算力——权重没动,却把「核心功能根本跑不起来」变成「可用」。这是 test-time-scaling 量级的回报,完全由 harness engineering 交付。
这还给出一个漂亮的对称:RSI 综述的 sharpening 论题说,自改进的本质是「把推理时昂贵的搜索摊销(amortize)进权重」。它的镜像正是——harness engineering 是把 test-time 算力组织进一个持久、可检视、可演化的运行时结构,而不是塞进权重。同一份「test-time-scale 的增益」有两条「存」法:要么进权重(训练期 sharpening / RL),要么进 harness。于是模型↔harness 协同进化的一个核心决策,正是每一次 test-time-scale 的增益,该摊销进权重、还是外化进 harness——这正是第 1 条「边界双向移动」在 test-time-scaling 视角下的重述。
4. 最深一层:harness 与模型互为环境、互相提升
这是协同进化的真正闭环。harness 不只是模型跑任务的脚手架,它产生的 trace、失败、评估信号正在反过来成为改进模型的数据——harness 同时是「评估器」和「训练环境」。于是形成回路:
harness 评估模型 → 产生奖励 / 失败信号 → 改进模型(post-training)→ 模型变强又重写 harness → harness 攻更难的题 → 产生更强的评估信号 ⋯⋯
这正是 self-improving agents 综述里 「The Red Queen Gödel Machine: Co-Evolving agents and their evaluators」 的精神——agent 和它的评估器协同进化,像红皇后赛跑,谁都不能停下;也呼应「多智能体协同共演化」——专门的智能体通过共享回归测试、代码补丁、改进的工具封装来共演化(co-evolve)。Anthropic 那句「设计空间不缩小、只会向前移动」并非孤立的乐观,而是这个回路的工程观测:模型每变强一次,旧 harness 死重被删,腾出的空间立刻被用来攻克原本做不到的题——所以 harness 不是变少,而是变深、变抽象。
协同进化的终点不是「最好的 harness」,而是「更好组织的认知系统」
关键问题不再只是「如何造更强模型」,而是如何在模型与基础设施间分配能力、如何评估外化系统的贡献、如何治理 agent 越来越依赖的共享工件。agent 的进步将来自模型与外部基础设施的协同进化——更好的 agent 不仅是更好的推理者,而是更好组织的认知系统。
07结尾:三层收束与三判据
把全文收成三层:
| 层次 | 本质 |
|---|---|
| 是什么 | 给无状态、会失忆、会自我感觉良好的模型,造一个运行时操作系统——把模型输出变成受控动作、把环境反馈变成回路、决定何时继续/停止/上报。 |
| 怎么做 | 把「模型独自做不到可靠的事」外化成持久、可检视、可消融的控制结构;每一个组件都是一条「承重假设」,要随模型进化不断重测、重写。 |
| 命运(协同进化) | 它不是临时脚手架,而是与模型协同进化(co-evolution)的认知组织层:模型变强 → 能力被拉回内部 → 旧组件变死重被删 → 腾出空间攻更难的题 → 更丰富的 harness 又对模型提新要求、并把 trace/评估信号反喂给模型训练。终点不是「最好的 harness」,而是「更好组织的认知系统」。 |
三个可操作的判据:
- 范式判据:能力定位从 weights → context → harness 向外迁移;改环境的边际收益正在超过改模型。
- 闭环判据:同一模型换控制器 C_H,行为就变;agent 分数无法干净归因到模型。
- 协同进化判据:成熟的标志是这套运行时变得显式、可消融(ablatable)、可归因、可自我重写——每个组件都能被关掉、被测因果贡献、在新模型到来时被重新估计,并能反过来把 trace / 评估信号喂回模型训练,进入 model↔harness 的协同进化回路。
一个最朴素的判据:如果你在评估一个 agent,却说不清它的分数里有多少来自模型、多少来自 harness——那你还没有在做 harness engineering。
参考来源
综述:
- Junjie Li et al., Agent Harness Engineering: A Survey, 2026(ETCLOVG 七层、约束瓶颈命题、三组 cross-layer 约束、§12.5「Keeping Harnesses Useful as Models Improve」、170+ 项目映射)。
- Chenyu Zhou et al., Externalization in LLM Agents, 2026(Norman 认知人造物;§8.1 边界双向移动、§8.3 自演化 harness 三层×四路径、§9「模型与外部基础设施协同进化 / 更好的 agent 是更好组织的认知系统」)。
- Self-Improving Agents 综述,2026(§9 多智能体协同共演化;「The Red Queen Gödel Machine: Co-Evolving agents and their evaluators」)。
- RSI-LLM: A Technical Overview and Future Directions, 2026(§5 推理时改进四类;§5.5 sharpening 论题)。
Anthropic 工程博客:
- Effective harnesses for long-running agents(Justin Young)—— initializer/coding agent、功能清单、干净交接、「换班失忆」问题。
- Harness design for long-running application development(Prithvi Rajasekaran)—— generator/evaluator 分离、context anxiety、承重假设原理。
- Scaling Managed Agents: Decoupling the brain from the hands(Lance Martin 等)—— OS 类比、session/harness/sandbox、pets vs cattle、结构性安全。
关键工程证据: Bölük (2026) 工具格式 ~10×;Trivedy (2026) Terminal-Bench 2.0 52.8%→66.5%;Lee et al. (2026) Meta-Harness 自动优化;Anthropic 单 agent 200(20×);解耦 brain/hands 后 p50 TTFT -60%、p95 -90%。
Self-Improving Agent · 两篇综述文章 · Harness Engineering for Self-Improvement(译文 from Lilian Weng)
Made with beautiful-article · tufte theme · 自包含单文件 · 断网可开 · 信息保留 100% // 轻量目录滚动高亮(无依赖、离线可用) (function(){ var links = Array.prototype.slice.call(document.querySelectorAll(‘.toc a’)); var sections = links.map(function(a){ return document.querySelector(a.getAttribute(‘href’)); }); if(!(‘IntersectionObserver’ in window) || !sections.length) return; var obs = new IntersectionObserver(function(entries){ entries.forEach(function(e){ if(e.isIntersecting){ var id = ‘#’ + e.target.id; links.forEach(function(l){ l.style.color = (l.getAttribute(‘href’)===id) ? ‘var(–ra-color-accent)’ : ”; }); } }); }, {rootMargin:’-10% 0px -80% 0px’, threshold:0}); sections.forEach(function(s){ if(s) obs.observe(s); }); })();