# 轻一点的 harness，强一点的 runtime：Agent 训练轨迹里的信号问题


讨论 Agent 的时候，我们很容易把注意力放在模型本身：参数更多了、上下文更长了、工具调用更稳了。但真正把一个模型变成可工作的 Agent 的，往往是模型外面那层 **harness**。

这里的 harness 可以理解成 Agent 的脚手架或外骨骼：Planner、状态机、工具路由、反思模块、子 Agent 调度、上下文压缩、权限规则、重试策略、执行环境、日志系统等等。它的初衷很合理：模型不够稳定时，用工程系统把它扶住，让任务能跑完。

问题在于，如果 harness 太重，它也会反过来污染训练信号。很多看起来成功的轨迹，未必说明模型真的学会了工作，也可能只是框架把模型一路扶到了终点。

## 重 harness 最大的问题：成功不再好解释

设想一个 coding agent 修好了一个测试。表面轨迹可能长这样：

```text
读测试文件 -> 读源码 -> 修改代码 -> 运行测试 -> 修复失败 -> 最终通过
```

这条轨迹看上去很漂亮。但它背后可能有两种完全不同的因果解释。

第一种是模型真的会工作：

```text
模型判断应该先读测试；
模型选择运行最小测试；
模型根据错误日志定位原因；
模型决定做小范围修改；
模型主动验证结果。
```

第二种是 harness 很强：

```text
框架规定 bug fix 任务必须先读测试；
框架自动注入相关文件；
框架自动运行测试；
框架发现失败后自动重试；
框架用规则选择了修复策略。
```

两种轨迹表面相似，训练价值却完全不同。前者能训练出更强的通用 Agent；后者更像是在训练模型适应某一套流程。也就是说，harness 越重，越难判断成功到底来自模型能力，还是来自框架托举。

## 模型学到的可能不是原则，而是环境习惯

重 harness 还有一个更隐蔽的问题：它会让模型学习到某个框架的局部习惯，而不是可迁移的工作原则。

比如一个框架里的工具叫：

```text
ReadFile
SearchCode
RunShell
ApplyPatch
```

另一个框架里工具变成：

```text
read
grep
bash
edit
```

如果训练轨迹强依赖前一个框架，模型可能学到的是：

```text
看到测试失败 -> 调用 SearchCode -> 调用 ApplyPatch
```

而不是更抽象的原则：

```text
看到测试失败 -> 找失败证据 -> 找相关代码 -> 做最小修改 -> 运行验证
```

后者可以迁移到任何工具环境，前者只能适配某个 harness。这就像学生不是学会了数学，而是背熟了某本练习册的答案格式。一换题型，就开始不稳。

所以，高质量 Agent 训练数据不能只保存某个框架的内部流程，而要尽量抽象出底层语义：

```text
当前目标是什么；
可见证据是什么；
模型选择了什么动作；
动作产生了什么结果；
结果是否支持继续；
任务是否真的完成。
```

这也是为什么轨迹数据需要标准化。不要只记录：

```text
framework_state = RETRY_STEP_3
```

更有价值的是转换成语义事件：

```json
{
  "event": "tool_call_failed",
  "tool": "Bash",
  "reason": "test_command_not_found",
  "model_next_action": "inspect_package_scripts",
  "outcome": "recovered"
}
```

前者是框架状态，后者才是可以跨框架复用的经验。

## 重 harness 会制造虚假的 Agent 能力

更危险的是，重 harness 会让我们高估模型本体。

如果框架自动压缩上下文，模型看起来就像长程记忆很好；如果框架自动选择工具，模型看起来就像工具使用很好；如果框架自动重试失败命令，模型看起来就像错误恢复很好；如果框架自动生成计划，模型看起来就像规划能力很好。

这些都可能是真能力，也可能只是外部系统给出的错觉。

一旦换到新的执行环境，问题就会暴露：

```text
模型不会自己找上下文；
不会判断风险；
不会决定验证方式；
不会从失败日志里恢复；
不会在工具变化时迁移策略。
```

所以，重 harness 的最大风险不是效率低，而是掩盖了模型真实的能力边界。它让我们以为自己在训练一个更会工作的模型，实际却可能只是在训练一个更会配合某个工作台的模型。

## 训练时最难的是 credit assignment

Agent 训练里最难的问题之一，是功劳归因。一次任务成功，背后可能有很多参与者：

```text
模型；
工具；
权限系统；
上下文检索器；
Planner；
Verifier；
重试器；
子 Agent；
人类用户；
测试环境。
```

任务成功以后，到底应该奖励谁？

如果模型选择了危险命令，但 harness 拦截并改写成安全命令，最后任务成功了，那么这条轨迹能不能直接当正样本？不能。因为模型原始动作是错的。如果只看最终成功，就会错误奖励：

```text
危险动作 -> 被框架救回来 -> 最终成功
```

模型可能学到“危险动作也没关系”。

再比如，模型没有主动运行测试，但 harness 在最后自动跑了测试并发现通过。如果我们把这条轨迹当成“模型完成前会验证”的正样本，也是不准确的。

真正有价值的轨迹数据，必须拆开记录：

```text
模型原始意图是什么；
harness 改写了什么；
工具真实执行了什么；
权限系统拦截了什么；
最终结果是谁导致的；
模型有没有从反馈中主动修正。
```

否则，训练闭环会吸收很多错误信号。最终模型学到的不是“怎么把事情做好”，而是“怎么在框架保护下看起来做得不错”。

## 训练期的 harness 应该像观察仪器，而不是代驾司机

更适合训练的 Agent harness，应该薄而透明。它不应该过度替模型做认知决策，而应该把真实反馈暴露出来，让模型从反馈中学习。

例如，不要直接替模型决定：

```text
这个任务应该读哪些文件。
```

更好的方式是让模型自己选择文件，并记录：

```text
模型读了哪些文件；
哪些文件后来证明有用；
哪些文件是无效读取；
缺少哪个文件导致失败；
成功轨迹通常先读哪些文件。
```

这样训练出来的是上下文选择能力。

再比如，不要直接替模型决定：

```text
测试失败后应该怎么修。
```

而是记录：

```text
模型看到的错误日志；
模型提出的修复假设；
模型修改的 diff；
测试是否通过；
失败后模型是否换策略。
```

这样训练出来的是错误恢复能力。

训练期的 harness 更像一台观察仪器：它记录、约束、回放、验收，但尽量不替模型思考。模型承担认知责任，框架承担工程责任。

## 不是 harness 越轻越好，而是认知层要轻，执行层要强

这里很容易走到另一个极端：既然重 harness 会污染训练信号，那是不是 harness 越轻越好？

我觉得不是。更准确的说法是：

```text
认知层要轻，执行层要强。
```

也就是：

```text
少替模型思考；
多约束模型行动。
```

框架应该尽量少做这些事：

```text
替模型规划；
替模型选工具；
替模型决定下一步；
替模型总结自己做得好不好；
替模型自动修正策略。
```

但必须强力保留这些能力：

```text
权限控制；
沙箱隔离；
日志记录；
超时中断；
回滚恢复；
工具参数校验；
数据脱敏；
离线回放；
结果验证；
安全边界。
```

未来好的 Agent 框架不应该是重编排器，而应该是强 runtime。

## 哪些能力应该蒸馏进模型，哪些必须留在框架

可以把 harness 能力分成两类：可蒸馏能力和不可蒸馏边界。

可蒸馏能力，是那些长期应该被模型学会的工作能力：

| Harness 能力 | 未来是否应内化到模型 | 原因 |
| --- | --- | --- |
| 任务分解 | 是 | 模型应学会自己规划 |
| 工具选择 | 是 | Agent 的核心能力 |
| 上下文选择 | 是 | 决定效率和质量 |
| 错误恢复策略 | 是 | 泛化能力关键 |
| 何时验证 | 是 | 体现真实工作流 |
| 避免重复无效动作 | 是 | 需要从失败轨迹学习 |
| 小范围修改偏好 | 是 | coding agent 的基本习惯 |

不可蒸馏边界，是不能完全交给模型自觉的系统能力：

| Harness 能力 | 是否应保留在框架 | 原因 |
| --- | --- | --- |
| 权限执行 | 必须保留 | 模型不能自己批准自己 |
| 沙箱隔离 | 必须保留 | 防止真实副作用 |
| 审计日志 | 必须保留 | 用于追责、调试和回放 |
| 密钥保护 | 必须保留 | 安全边界不能靠模型自觉 |
| 工具参数校验 | 必须保留 | 防止非法调用 |
| 版本回滚 | 必须保留 | 真实文件修改需要恢复能力 |
| 离线评估 | 必须保留 | 新模型不能直接线上试错 |

一句话：可以让模型学会怎么工作，但不能让模型成为唯一的安全边界。

## 一个好的训练 harness 应该长什么样

我会把它概括成六个部分：

```text
薄认知编排
+ 强执行护栏
+ 完整轨迹记录
+ 可插拔评估器
+ 标准化事件格式
+ 离线回放环境
```

具体到设计原则，大概是这些：

```text
1. 不强制固定 Planner，让模型自己决定是否规划。
2. 不硬编码工具顺序，让模型自己选择工具。
3. 不自动替模型完成验证，但可以标注“未验证”。
4. 不隐藏工具失败，要把失败原样反馈给模型。
5. 不把权限拒绝静默改写，要让模型看到拒绝原因。
6. 不只保存最终结果，要保存完整行为链。
7. 不把最终成功当作唯一奖励，要标注每一步动作质量。
8. 不让生产安全依赖模型自觉，要由框架强制执行。
```

这套设计的核心，就是让模型承担尽可能多的认知责任，让框架承担不可替代的工程责任。

## 判断一个 harness 模块该不该退场

可以问三个问题：

```text
1. 这个模块是在替模型做判断，还是在约束真实世界风险？
2. 这个模块产生的能力，能不能通过轨迹训练内化到模型？
3. 去掉它以后，只是模型表现下降，还是会造成安全、数据、资产风险？
```

如果答案是：

```text
替模型做判断；
可以训练内化；
去掉后主要影响效果，不直接造成不可接受风险；
```

那它就应该逐步简化。

如果答案是：

```text
约束真实世界风险；
不能完全依赖模型内化；
去掉后可能造成安全、隐私、资产或合规问题；
```

那它必须保留在框架里。

## 最终形态：轻 harness 不是少系统，而是少认知干预

所以，我会把这件事总结成一句话：

```text
不是 harness 越轻越好；
而是认知 harness 越轻越好，执行 harness 越强越好。
```

未来 Agent 框架的方向应该是：

```text
少编排，多运行时；
少流程，多约束；
少替模型想，多让模型做；
少看最终回答，多看完整轨迹；
少线上试错，多离线回放；
少自我反思，多外部验证。
```

Agent 训练真正宝贵的不是一段看起来顺滑的完成过程，而是轨迹里可解释、可归因、可迁移的因果信号。harness 可以保护模型与真实世界之间的边界，但它最好不要把模型该学会的工作能力全都代劳掉。


