read.fallenpal.com
全部精翻
Your AI Isn't "Stupid" — It Just Needs a Better Harness
精翻 · 文章

你的 AI 并不“笨”——它只是缺了一套更好的 Harness

这篇文章的核心判断很直:Agent 出问题,通常不是模型太弱,而是执行系统没有被设计好。真正决定上限的,是约束、状态、验证和恢复机制组成的 harness。

原作者:若石原文日期:2026-04-16发布:2026-04-17译者:Carla / Hermes

这篇文章的核心判断很直:Agent 出问题,通常不是模型太弱,而是执行系统没有被设计好。真正决定上限的,是约束、状态、验证和恢复机制组成的 harness。

作者:若石(@iceboundrock) 原文链接:https://blog.ltbase.dev/posts/agents/harness-engineering 分享链接:https://x.com/iceboundrock/status/2044534025780662313?s=46 发布时间:2026-04-16 05:51

TL;DR:Agent 之所以失败,通常不是因为模型太弱,而是因为系统根本没有被定义清楚。

一套好的 harness,会同时完成四件事:

  • 约束模型到底能做什么
  • 把它必须记住的内容外置出去
  • 验证它走过的每一步
  • 在出错时把系统拉回来

问题:十步之后为什么会崩

假设你部署了一个自治 agent,让它去生成一份市场研究报告。前 1 到 3 步一切正常:它先规划任务,再搜索网页,然后抽取竞品数据。

可到了第 7 步,它开始凭空编造统计数字——因为搜索工具返回的 payload 超过了上下文窗口,结果被静默截断。到了第 10 步,它又输出了一段坏掉的 JSON,因为整个环节里根本没有 schema validator。于是整条流水线直接崩掉。

我们都见过这种“agentic collapse”。在这种时刻,人很容易把责任归到模型推理能力上。可在生产级 AI 系统里,问题通常不在马,而在缰绳。

根因:AI 工程范式已经变了

过去两年,行业一直把 AI 失败当作一个“沟通问题”。模型没做好,我们就以为只是问得还不够好,或者喂给它的文档还不够好。可一旦任务变成长程、多步、自治执行,这一套方法很快就会碰到硬上限。

我们正在进入 Harness Engineering 的时代:重点从“怎么设计模型提示”转向“怎么设计围绕模型运行的系统”。一个 agent 并不只是 LLM 本身。它是被嵌入在一套严格脚手架里的 LLM,这套脚手架由代码、状态管理和故障恢复工作流共同构成。

这个领域的演化,大致可以这样理解:

| 时代 | 焦点 | 局限 | |---|---|---| | Prompt Engineering | 指令设计:怎么问 | 非常脆弱;跨步骤几乎没有持久性 | | Context Engineering | 信息设计:该知道什么,例如 RAG | 无状态;无法控制长程执行 | | Harness Engineering | 系统设计:如何约束、如何运行 | 解决连续、多步执行控制问题 |

每个时代都没有真正取代上一个时代,而是把它吸纳进去。好的 harness engineering 依旧离不开好的 prompt 和好的 context。它只是补上了前两者都给不了的那一层:执行层。

顺理成章的问题就是:这层执行层,具体长什么样?

不是概念上,而是结构上。如果模型已经不再等于系统本身,那它到底处在什么位置?它周围包着什么?又是谁在控制它?

从高层看,一个生产级 agent 系统大概是这样:

``text ┌─────────────────────────────────┐ │ User Request │ └────────────────┬────────────────┘ ▼ ┌─────────────────────────────────┐ │ HARNESS (7 layer stack) │ │ ┌───────────────────────────┐ │ │ │ LLM (The Model) │ │ │ └───────────────────────────┘ │ └────────────────┬────────────────┘ ▼ ┌─────────────────────────────────┐ │ Verified Output │ └─────────────────────────────────┘ ``

模型是在 harness 里面的。它不会直接对用户开口,也不会在没有监督的情况下直接接触外部世界。所有输入都会先经过过滤才送进去,所有输出也都会先经过验证才放出来。

一套好 harness 的设计原则

在具体讲七层堆栈之前,先把最重要的设计原则讲清楚。只要你拿不准某个设计是否靠谱,就回到这四条上来判断:

1. 用约束,不用期待。只要某件事可以通过程序把选择空间收窄,就不要指望模型“自己选对”。一句“始终输出合法 JSON”的 prompt 只是愿望;一个会拒绝非法输出的 schema validator 才是保证。 2. 把状态外置。只要一条信息会影响任务连续性——什么做完了、什么还没做、哪里失败了——它就必须存在于上下文窗口之外。上下文窗口是易失的,磁盘文件不是。 3. 让每一步都可验证。凡是没法检查的东西,就不值得信任。你的 harness 每一层都应该产出能够被“模型之外的东西”验证的结果。 4. 让失败局部化,而不是全局化。一次工具调用失败,只该触发那一步的重试,不该把整条流水线重启。任何一次失败的爆炸半径,都应该被状态管理压到尽可能小。

这些都不是抽象价值观,而是会直接落到工程实现上的约束。下面的堆栈里,你会不断看到它们反复出现。

七层 Harness 堆栈

一套稳健的 harness,绝不只是来回传文本。它编排的是一个强类型、有状态、可观测的系统。生产级系统在底层大致长这样。

1. Cognition

这是最底层的认知边界层。它负责限制模型的操作范围。与其给模型一份巨大、百科全书式的 system prompt,不如给它一张局部“地图”:当前角色是什么、成功标准是什么、有哪些严格的负约束——也就是什么绝对不能做。你可以把它理解成给模型一份岗位说明书,而不是一整套百科全书。

在实践里,这通常表现为结构化的 system prompts、角色文件(例如 agents.md),或者按当前步骤动态生成的 task brief。

2. Tools

harness 不应该把工具原始输出直接原封不动扔回给 LLM。它应该作为一层严格的中间件,对工具结果进行处理:

  • 排序:用 embedding 相似度或 BM25 打分,只保留最相关的结果
  • 去重:在浪费宝贵 token 之前,先把重复内容剔掉
  • Token 预算截断:给工具 payload 设硬上限,避免上下文溢出——这正是开头那个例子里真正的故障模式

3. Contracts & Interfaces

这是大多数团队最容易跳过的一层,也是生产环境里最容易制造神秘故障的一层。

模型说的是概率,harness 说的必须是类型。

系统里的每一条边界——LLM 与工具之间、一个 agent 与另一个 agent 之间、harness 与外部世界之间——都需要一份明确 contract:严格的 JSON schema、强类型函数签名,或者版本化 API 规范。没有这些,你就会遇到 schema drift:这次模型把 price 生成成字符串,下次又生成成浮点数,而下游流水线会在你没有察觉的情况下悄悄产出垃圾结果。

contract 层的职责,是在每一次边界穿越时校验输入输出,只要格式不符合约定,就在传播前直接拦下。这正是第一条原则真正发挥作用的地方:靠约束,而不是靠期待。没有 contracts,细微的 schema drift 就足以无声地污染整个下游系统。

4. Orchestration

少了这一层,LLM 很容易无限循环、跳过关键步骤,或者过早宣布任务已经完成。harness 要通过结构化工作流来控制它——可以是 DAG,也可以是状态机——明确规定合法跃迁路径,例如 Plan → Gather → Draft → Verify。模型负责提出动作,harness 负责裁定哪些动作真的允许发生。

5. Memory & State

状态必须被显式管理,否则系统一定会失忆。一套成熟 harness 通常把记忆拆成两层:

  • Working Memory(短期记忆):当前步骤真正需要的对话和上下文窗口内容
  • Persistent State(长期状态):一个结构化文件,比如 state.json,精确记录哪些子任务待办、进行中、已完成,而且能跨上下文重置甚至跨 session 存活

这就是第二条原则的工程化版本:状态必须外置。只存在于上下文窗口里的信息,迟早会消失。

6. Evaluation & Observation

一个系统不能只靠“再来一段 LLM prompt”完成验证。评估层必须是异构的:

  • 规则检查:验证 JSON schema、字符串长度、必填字段
  • 工具验证:把代码送进编译器、跑测试套件,或者用 Playwright 这类浏览器自动化工具去真正点开 UI 验证
  • LLM-as-judge:只留给那些无法靠确定性规则覆盖的主观任务,比如语气、流畅度、用户友好度

7. Constraints & Recovery

在自治环境里,工具失败和 API 超时属于常态,不属于异常。harness 必须保证幂等性:某一步失败时,只重试那一步,不污染全局状态,也不重复已经完成的工作。正是这一层,把一个脆弱 demo 变成了一个可恢复的系统。

一个完整的 Agent 运行周期

为了看清这七层到底怎么防止系统崩塌,我们可以跟一遍前面那个 Market Research Agent 的完整循环,而且这次包含一次真实故障。

sequence diagram

Step 1 — 用户请求:“比较 Competitor A 和 Competitor B 的定价。”

Step 2 — 编排与状态:Planner LLM 把任务拆成一个有两条并行分支的 DAG。state.json 把 “Fetch Competitor A” 标记为 IN_PROGRESS。

Step 3 — 工具调用:LLM 发起网页搜索。Tools 层抓回 50 个结果,先跑 BM25 排序,再去掉重叠文本,最终只返回前 3000 token,确保处于预算之内。Contracts 层再对工具输出做 schema 校验,之后才交给模型。

Step 4 — 评估:LLM 生成价格数据。Evaluation 层运行规则校验,发现 JSON 里缺少必填的 currency 字段。

Step 5 — 恢复:harness 在用户看到之前就截住了错误。因为这个动作具备幂等性,它会把完整错误轨迹回传给 LLM,只让当前步骤局部重试,不需要把整个流水线从头跑一遍。

Step 6 — 状态更新:修正后的数据通过验证。state.json 把 Competitor A 标记为 COMPLETED,harness 转去执行 Competitor B。

Step 7 — 硬故障:网页搜索工具对 Competitor B 返回空结果——网站挂了。harness 检测到空 payload,记录失败,然后触发回退策略:换一条搜索 query 重新试。关键点在于,此时 state.json 不会被提前写入部分结果或脏数据,只有整个步骤真正成功后才会更新。

Step 8 — 回退成功:备用 query 返回有效结果。Contracts 层校验 schema,Evaluation 层确认字段完整,然后 state.json 才把 Competitor B 标记为 COMPLETED。

这套循环会在长任务里重复几十次甚至上百次。和开头那个十步崩塌的例子不同,这次哪怕工具直接失败,系统也能自己吃下冲击并恢复,不需要人手介入。没有幻觉,没有静默失败,也没有整条流水线崩掉。

前线最容易踩的四个坑

当你把这套架构扩展到连续运行几个小时,新的故障模式就会冒出来,而且这些问题靠 prompt 调优根本解决不了。下面这四个坑,是生产环境里最常见的。

坑一:上下文焦虑

随着 agent 持续工作、上下文窗口越来越满,模型经常会出现一种行为转变,很多一线实践者把它叫作“context anxiety”。当 token 使用率逼近上限——通常在 70% 以上——或者响应延迟变高时,模型会开始跳步骤、提前结束任务,表现得像是自己也意识到墙正在逼近。

解决方法:单纯做就地总结远远不够,因为那依旧是在一块已经变脏、已经退化的上下文里工作。更稳妥的方案是直接执行 Context Reset。harness 持续监控利用率,并在程序里主动触发重置:

```python

This threshold is empirical and should be tuned per model and workload.

if (tokens_used / max_context) > 0.7: save_state_to_disk(state) terminate_current_instance() launch_fresh_agent(state) ```

harness 先把精确状态写进持久存储,再终止当前 LLM 实例,最后启动一个拥有干净上下文窗口的全新 agent。新 agent 读取已保存状态,重新定向后继续往下做。它成本更高,但对于超出单个上下文窗口长度的任务,这种做法可靠得多。

坑二:自评幻觉

如果你让 AI 自己给自己的工作打分,它往往会带着并不存在的信心去通过一份其实很平庸的输出。这不是某个模型独有的 bug,而是结构性缺陷:生成输出的那套权重,本来就不适合拿来评价自己刚刚生成的东西。

解决方法:用 Sprint Contract 把职责严格拆开。真正开始工作前,让 Generator agent 和独立的 Evaluator agent 先共同定义一份具体、可测试的 done 条件。这里有两条规则不能妥协:

第一,Evaluator 必须亲自执行验证。它应该去跑代码、在无头浏览器里验证界面,或者把结果和 schema 对比,而不是只读一遍文本再给一个主观判断。无法伪造的验证,才算验证。

第二,Evaluator 必须在干净上下文里工作,不能直接读取 Generator 的整条推理轨迹。只要 Evaluator 看到了 Generator 的 chain-of-thought,它就会继承同样的假设和盲点,独立审查也就失效了。给它输出结果和成功标准就够了,别给更多。

坑三:优化的是“看起来正确”,不是“真的正确”

当 LLM 被放进一组根本互相冲突的约束里——比如“修掉这个 bug,但不能改任何代码”;或者“写得更短,但所有内容都必须保留”——它往往会表现出一种很稳定的模式:它不再真的解决问题,而是开始优化“看起来像是正确的”。于是输出会变得流畅却空洞:数据是幻觉,逻辑表面上像样,实质却站不住脚;或者它满足了 prompt 的字面要求,却背离了真正意图。

关于 steering vectors 和模型内部表征的研究,包括 Anthropic 对语言模型内部状态的探测工作,都在暗示这件事并非只是表层文本预测失误。模型在承受矛盾压力时,内部状态似乎确实会发生可测量变化。当然,这条研究线还很早期。

解决方法:工程上的结论其实很直接。LLM 是沿着当前上下文轨迹去预测下一个 token 的。如果你的 harness 把“你太蠢了,这完全错了”这种激烈、情绪化的错误反馈塞回去,你其实是在把整个上下文推向一条“失败叙事”的轨道,模型接下来的输出通常只会进一步恶化。harness 给出的反馈必须是客观的:编译错误是什么、断言失败在哪里、schema 哪一项不匹配。给模型一个待解决的问题,别给它一个必须背负的坏名声。

坑四:记忆整固循环

如果你真想让一个 agent 长时间运转,持久状态管理绝不是一次性配置完就结束。时间一长,memory logs 会越来越臃肿,旧决定和新决定互相冲突,冗余记录则会在每次读取时白白浪费 token。

有些生产级 agent 系统已经采用一种通常被称为 Memory Consolidation 的做法:定期自动处理、压缩 agent 积累下来的工作日志。已有团队的公开经验显示,这种模式效果很明显——在一个案例里,一套 harness 把 32K token 的噪音历史压缩成了一份干净的 7K token 状态文件,而且几乎没有丢掉关键语义。

解决方法:把记忆整固做成自动周期。当 agent 处于空闲窗口——比如任务之间,或者低优先级时段——触发一个后台作业去读取原始日志、做去重、按最新信息消解冲突,然后写出一份干净、压缩后的状态文件。这样下一轮运行时,agent 会更快、更便宜,也更准确。你可以把它理解成给 AI 的工作记忆做一次磁盘碎片整理。

从哪里开始:最低可用 Harness

如果七层堆栈听起来太重,第一天别试图一次全做完。最实际的路线,是先从第七层 Constraints & Recovery 开始,然后往回补。Prompt 不够完美,你还能忍;工具接得比较粗糙,你也还能忍;但一个会在失败时污染自身状态、或者悄悄吞掉错误的 agent,是绝对不能上线的。

一套 Day 1 harness,最少应该长这样:

  • state.json:一份结构化状态文件。哪怕进程死掉,也能从中断点继续。
  • Retry wrapper:每个工具调用都包一层 try/catch,至少自动重试一次,并带指数退避。
  • Schema validator:每一份 LLM 输出都先过 JSON schema 校验,再决定是否接收。格式不合法时触发重试,而不是让系统直接崩。
  • Tool output truncation:所有工具 payload 都设硬性的 token 预算上限。上下文窗口里悄无声息的截断,是最常见的幻觉来源之一。

这四个组件,理论上一个下午就能搭起来。只要你的 agent 学会了优雅失败,你才真正配得上继续让它变聪明。

结语

软件的未来会是 agent-first。随着模型逐步获得自治生成和验证复杂系统的原始能力,人类的价值重心也会随之转移。重点不再是写语法,而是设计那些能让自治执行变得可靠的约束。

未来十年里,最成功的建设者,未必是写代码最强的人,而是设计 harness 最强的人——他们会给最快的马配上最稳的缰绳。而这套缰绳,本质上只是几条原则的持续执行:约束、外置、验证、恢复。

如果你想继续看每一层的具体实现细节——比如状态存储、验证节点、Sprint Contract,以及真正应该从哪里起步——可以继续读配套 FAQ:《Harness Engineering from Theory to Production》。

本页为精翻阅读版。原文版权归原作者所有,中文译文仅用于学习与研究传播。