🏠 总目录📚 本教程 12 · Harness 骨架 ← →
📑 本页目录(点开跳转)

12 · Harness:长时任务的骨架

⏱ 28 分钟 | ⭐⭐ 本教程最重要的一节


🔗 可运行实践: 持久化审批与故障恢复 。

🎯 一句话

Harness = 把原始模型智能变成可用 Agent 的那一整套东西(循环 + 工具 + 上下文管理 + 护栏)。 而这一节最重要的一句话是:

模型决定「做什么」① 循环控制什么时候继续、什么时候停② 状态管理上下文裁剪 + 进度持久化③ 错误处理失败了要不要重试、几次④ 终止条件步数 / 成本 / 无进展检测⑤ 可观测性每步都能记录和回放Harness = 包在模型外面的那层工程结构⭐ 模型决定「做什么」,Harness 决定「怎么把它跑稳」⚠️ 最容易漏的是④ —— 很多实现只设了「成功就停」,没设无进展检测,结果在死循环里烧 token
模型决定「做什么」,Harness 决定「怎么把它跑稳」。⚠️ 最容易漏的是④ —— 很多实现只设了「成功就停」,没设无进展检测,结果 Agent 在死循环里反复尝试同样的失败操作,把 token 烧光。

「Harness 的每个组件都编码了一个『模型自己做不到某事』的假设—— 而这些假设会随模型进步而过时。」


🧠 一、长任务为什么会失败

让 Agent 连跑几小时到几天(做完整 App、迁移代码库),朴素做法必撞的五头怪:

失败模式 表现 根因
过度野心 想在一个上下文窗口干完全部,留下一堆半成品和破损状态 没有分解机制
过早宣告完成 ⭐ 下个会话看到部分功能能跑,就宣布"项目完成!" 没有客观完成标准
上下文焦虑 感知到接近 token 上限,开始草草收尾、偷工减料 模型的自我保护行为
目标漂移 跑几十轮后,做的事和最初目标越偏越远 目标没有被反复重申
状态丢失 会话重启后不知道做到哪,重复劳动或互相破坏 状态只存在上下文里

所有 harness 设计都是在对付这五头怪。 记住它们,你就知道每个组件为什么存在。


🏗️ 二、Harness 的四个组成

① 循环 Loop
  • 什么时候继续、什么时候停、怎么恢复
② 工具 Tools
  • 它能碰到什么(第 7 章)
③ 上下文管理 Context
  • 压缩、外置状态、子 Agent(第 4 章)
④ 护栏 Guardrails
  • max_steps、超时、预算、权限(第 15 章)

💡 模型生成文本,harness 决定这些文本能触碰什么。


📉 三、Harness 减法的三种模式

核心心态:harness 设计不只是「我还该加什么」,更重要的是「我可以停止做什么」。

模式一:依靠模型,而非 harness

对照

用 Claude 已深度掌握的【通用工具】,而不是发明专用工具

📌 Claude 3.5 Sonnet 仅用 bash + 文本编辑器

就在 SWE-bench Verified 上达到 49%

—— Claude Code 本身也主要靠这两个工具

💡 为什么通用工具更好:模型在训练中见过海量 bash/文件操作的例子, 而你发明的 my_custom_edit_tool 它一次都没见过。熟悉度本身就是能力。

模式二:给 harness 做减法

让 Claude 自己做什么 怎么做 实测效果
编排自己的动作 给代码执行工具,让它自行过滤/管道化输出 BrowseComp 45.3% → 61.6%
管理自己的上下文 Skills 渐进式披露 + context editing 子 Agent 再加 2.8%
持久化自己的上下文 压缩 + 记忆文件夹 BrowseComp-Plus 60.4% → 67.2%

模式三:谨慎设置边界

边界 说明
缓存友好的上下文设计 稳定内容在前、动态在后;不要中途换模型(缓存是模型专属的)
声明式工具服务于安全与可观测 bash 杠杆大但可观测性差;专用工具的类型化参数便于拦截和记录
可逆性准则 ⭐ 难以撤销的动作值得用户确认

🧬 四、长时运行架构的三代演进

这是本章最有价值的部分——它展示了 harness 是怎么随模型进步而变化的。

第一代:初始化 Agent + 编码 Agent

要解决:过度野心 + 过早宣告完成

关键信息

两个关键细节:

细节 为什么
用 JSON 而非 Markdown ⭐ 模型更不容易随意修改或覆盖 JSON 文件(格式刚性提供保护)
像人一样测试 给浏览器自动化工具,让 Agent 像真人用户一样点击验证——能发现纯代码审查发现不了的 bug

第二代:GAN 式生成-评估分离

核心洞察:

⭐ 模型评估自己的工作时有系统性正面偏差,主观任务上尤其明显。 把生成与评估分离,远比让生成者自我批判有效。

关键信息

【规划 Agent】把 1–4 句需求扩展成详细产品规格
【生成 Agent】按 Sprint 迭代实现,交 QA 前先自检
【评估 Agent】通过浏览器自动化测试运行中的应用
↑ 实现前先协商「Sprint 契约」定义成功标准

⭐ 让主观评估可行的关键技巧:

对照

❌ 「这个设计美吗?」 ← 无法回答,评估会流于形式

✅ 「这个设计是否符合以下四项标准?」

· 设计质量 · 原创性 · 工艺 · 功能性

💡 把主观判断【转化成可回答的具体条目】——这是所有 LLM 评估的通用技巧

成本对比(复古游戏制作器案例):

方案 耗时 成本 结果
单 Agent 20 分钟 $9 玩法损坏、流程僵硬、输入不可用
完整 harness 6 小时 $200 10 个 Sprint 覆盖 16 项功能、UI 精致、游戏可玩

💡 20 倍成本买的是「能用 vs 不能用」,不是「好 vs 更好」。

⚠️ 但要注意演进:对某一代模型必不可少的 Sprint 级分解, 到下一代反而成了多余开销。

🔑 「有用的 harness 设计空间不会随模型进步而缩小,而是会移动。」

第三代:模型自己写 harness

Claude 可以为当前任务现场写出专属的多 Agent 系统(JavaScript 文件 + 派生子 Agent 的函数)。

要对抗的三种失败:

失败 说明
Agent 惰性 提前收工
自我偏好偏差 偏袒自己的结果
目标漂移 多轮后原始目标失真

六种常用模式:分类后执行、扇出后综合、对抗式验证、生成后过滤、锦标赛、循环到完成。

⚠️ 什么时候别用: - 没有真正并行需求的常规编码 - 不值得多 Agent 协调开销的任务 - 一个上下文窗口就能解决的问题


🧰 五、可以照抄的最小 harness

# 一个能对付"五头怪"的最小骨架
class MinimalHarness:
    def __init__(self, model, tools, goal):
        self.goal = goal
        self.state_file = "progress.json"      # ⭐ 对付"状态丢失"
        self.max_steps = 50                     # ⭐ 护栏
        self.token_budget = 100_000

    def run_session(self):
        state = self.load_state()               # 从文件恢复,不靠上下文
        # ⭐ 对付"目标漂移":每个会话开头重申目标 + 当前状态
        system = f"""目标:{self.goal}

当前进度:{state['done']} / {state['total']} 项已完成
下一项:{state['next_task']}

规则:
1. 一次只做【一个】功能项,做完立即更新 progress.json 并提交
2. 宣布完成前必须运行验证脚本并通过     ← 对付"过早宣告完成"
3. 不要开始新功能,直到当前这个通过验证  ← 对付"过度野心"
"""
        for step in range(self.max_steps):
            if self.tokens_used > self.token_budget * 0.75:
                self.compact()                  # ⭐ 对付"上下文焦虑"
            ...
        self.save_state(state)                  # 会话结束前落盘

这个骨架里每一行都对应一头怪——这就是 harness 设计的思维方式。

⭐ progress.json 有个正式名字:工作记忆(working memory)。 它是04b · Agent 记忆四层结构里的一层——只服务于当前这个任务,任务做完就该整个丢掉。 知道它是「哪一层」很有用,因为四层的写入和淘汰规则完全不同,混在一起就会烧钱或者出错:

这里的东西 是哪一层 任务结束后
progress.json 里的 next_task / done 工作记忆 ⭐ 整个丢掉,留着只会污染下一个任务
「这个项目用 pnpm 不用 npm」 长期记忆 留下,下次开工直接生效
「上次改这个文件把 CI 跑挂了」 情景记忆 留下,是下次的避坑依据

⚠️ 最常见的错法是把三者全塞进同一个 progress.json——文件越滚越大, 每个会话开头都要整个读回上下文,Token 成本随任务时长线性上涨,而其中大部分早就没用了。 04b 讲的就是怎么分层、怎么判断一条该不该写、以及过期的怎么忘掉。


🔁 六、Harness 的定期体检

每次模型升级后,问这五个问题:

流程图

① 哪些提示词是为了绕过旧模型的缺陷写的?→试着删掉,跑评测
② 哪些专用工具可以换成通用工具?→模式一
③ 哪些编排逻辑可以交给模型自己做?→模式二
④ 哪些分解粒度可以放粗?→Sprint 级→功能级
⑤ 哪些护栏还有必要?→但安全护栏别乱删 ⚠️

⚠️ 注意第 ⑤ 条:功能性的变通可以删,安全边界不能删(第 15 章)。 「模型现在更聪明了所以不用沙箱了」是错误的推论—— 安全护栏防的是外部攻击,不是模型能力不足。


🕳️ 七、六个常见坑

坑 症状 修
状态只存在上下文里 ⭐ 会话一断全丢 外置到文件(JSON)
没有客观完成标准 Agent 说完成了其实没有 功能清单 + 验证脚本
目标只在第一条消息里说过 跑几十轮后跑偏 每个会话开头重申
harness 从不做减法 越堆越复杂,模型升级后成累赘 定期体检五问
让生成者自评 永远说自己做得好 生成-评估分离
主观标准无法执行 「做得好看点」→ 评估流于形式 转成可回答的具体条目

📚 延伸阅读

👉 这一节对应的框架:LangGraph 主攻的正是这里最难的半截 —— 状态怎么外置、断了怎么接着跑。对照关系在 16b · 框架这层皮。


✅ 检查点

  1. Harness 是哪四部分组成的?
  2. 长任务失败的五头怪是什么?
  3. 本章最重要的那句话是什么?它的实践含义是什么?
  4. 减法的三种模式分别是什么?
  5. 为什么长时任务的状态文件推荐 JSON?
  6. 为什么要把生成和评估分离?
  7. 「这个设计美吗」该怎么改写才能让评估可行?
  8. 模型升级后哪些 harness 组件可以删,哪些不能?
👀 答案
  1. 循环、工具、上下文管理、护栏。
  2. 过度野心、过早宣告完成、上下文焦虑、目标漂移、状态丢失。
  3. 「harness 的每个组件都编码了一个『模型做不到某事』的假设,而假设会过时」。含义:要定期问「我可以停止做什么」,而不只是「还该加什么」。
  4. ①依靠模型而非 harness(用通用工具如 bash)②给 harness 做减法(让模型自己编排/管理/持久化上下文)③谨慎设置边界(缓存友好、声明式工具、可逆性准则)。
  5. 模型更不容易随手修改或覆盖 JSON——格式刚性提供了保护。Markdown 太"软"容易被整体重写。
  6. 模型自评有系统性正面偏差,主观任务尤其明显。分离比让生成者自我批判有效得多。
  7. 转成可回答的具体条目:「是否符合以下四项标准:设计质量/原创性/工艺/功能性」。
  8. 功能性的变通可以删(为旧模型缺陷写的提示、过细的分解);安全边界不能删——它防的是外部攻击,不是模型能力不足。

🛑 可以停在这里

⚡ 走神救援

先记住这几件事

下一节 👉 13-多Agent协作.md

打卡记录保存在你的浏览器里,首页能看到总进度