Files
obsidian-notes/3Resources/AI/AutoHarness-论文解读.md
2026-05-07 11:14:29 +08:00

223 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
原文发布链接: https://zhuanlan.zhihu.com/p/2016839356833341880
tags:
- AI
- 论文解读
- LLM
- Agent
- ProgramSynthesis
- GRPO强化学习
- harness
---
## 一句话总结
用小模型Gemini-2.5-Flash自动写一段"规则检查代码"包裹在 LLM 外面,让它不再犯"非法操作"的低级错误,结果小模型+代码 > 大模型裸跑。
---
## 这篇论文到底在解决什么问题?
### LLM 做 Agent 时的尴尬现实
你可能已经知道,现在很多人在用 LLM 做 agent——让模型去完成一些需要"行动"的任务,而不是简单地回答问题。比如让 LLM 下棋、玩游戏、操控机器人等。
**问题来了LLM 经常做出"非法操作"。**
论文举了一个非常生动的例子:在 Kaggle GameArena 的国际象棋比赛中Gemini-2.5-Flash 78% 的失败不是因为下棋策略差,而是因为走了不符合规则的棋。比如让马走直线、让兵倒着走之类的。
这就好比你请了一个非常聪明的人来帮你下棋,他对棋局分析得头头是道,但就是经常把棋子摆到不合法的位置上。
### 为什么会这样?
LLM 本质上是一个文本生成模型。它"知道"国际象棋的规则(因为训练数据里有大量棋谱),但它没有一个硬编码的规则引擎来确保输出的每一步都合法。它的"知道"是概率性的、模糊的,不是精确的。
### 现有解决方案及其问题
| 方案 | 怎么做 | 问题 |
|------|--------|------|
| Fine-tuning | 用大量合法游戏轨迹去微调模型 | 成本极高;可能降低模型在其他任务上的能力 |
| 手写 Harness | 人工为每个游戏写一个规则检查器 | 费时费力,每换一个游戏就要重写 |
**这篇论文的创新点是:让 LLM 自己写这个规则检查器。**
---
## 核心概念:什么是 "Harness"
### Harness 的直觉理解
"Harness" 这个词在英文里是"挽具/马具"的意思用来控制和约束马的行为方向。在这篇论文里Harness 就是包裹在 LLM 外面的一层"安全壳",确保 LLM 输出的动作是合法的。
用工程的话说,这就是一个 wrapper / middleware / interceptor
```
传统做法:
用户请求 → LLM → 输出动作(可能非法)→ 环境报错
加了 Harness 之后:
用户请求 → LLM → Harness检查 → 合法? → 执行
↓ 不合法
告诉LLM"这步不行" → LLM重新生成 → 再检查...
```
### 三种 Harness 变体
论文提出了三种不同"约束力度"的 harness
#### ① Harness-as-Action-Verifier动作验证器— 论文主要聚焦的方案
```python
while True:
action = LLM.generate(observation) # LLM 提出一个动作
if code_harness.is_legal_action(obs, action): # 代码检查是否合法
break # 合法就执行
else:
# 不合法,告诉 LLM 这步不行,让它重新想
observation += f"\n警告:{action} 是非法操作,请重新选择"
return action
```
类比:就像你写代码时 IDE 的实时语法检查——你写了不合法的代码,红线提示你改。
#### ② Harness-as-Action-Filter动作过滤器
```python
legal_actions = code_harness.propose_action(obs) # 代码先列出所有合法动作
best_action = LLM.rank(legal_actions) # LLM 从中选最好的
return best_action
```
类比:就像下拉菜单——用户只能从合法选项中选,不可能输入非法值。
#### ③ Harness-as-Policy代码即策略— 最激进的方案
```python
action = code_harness.propose_action(obs) # 完全由代码决定动作
return action # 根本不需要 LLM
```
类比:你直接写了一个规则引擎/算法来玩游戏LLM 只在"开发阶段"用来写这个算法。运行时零 LLM 调用,零成本。
---
## 核心方法:怎么让 LLM 自动写出 Harness
这是论文最核心的技术贡献。如果你熟悉 GRPO你可以把这个过程理解为用环境反馈来优化代码而不是优化模型权重。
### 整体流程
```
1. 初始化LLM 写一版 harness 代码propose_action + is_legal_action
2. 测试:用这个代码在游戏环境里跑 rollout
3. 评估:记录哪些动作被判为非法,收集错误信息
4. 反馈:把错误信息喂给 LLMCritic 模块整理错误)
5. 优化LLM 基于错误反馈生成改进版代码Refiner 模块)
6. 重复 2-5直到合法率达到 100% 或超时
```
### 和 GRPO 的对比
| 维度 | GRPO | AutoHarness |
|------|------|-------------|
| 优化的对象 | 模型权重 θ | 代码文本(程序) |
| 搜索空间 | 参数空间(连续) | 程序空间(离散) |
| 反馈信号 | reward标量 | 执行反馈(错误日志+reward |
| 优化方法 | 梯度下降 | LLM 当"突变算子"改代码 |
| 探索策略 | 采样多个 response 对比 | Thompson sampling 做 tree search |
| 类比 | 调参让模型更好 | 让 AI 写更好的代码 |
**关键区别**GRPO 改的是模型内部的权重AutoHarness 改的是模型外部的代码。一个改"大脑",一个改"工具"。
### Tree Search + Thompson Sampling
#### 为什么要用 Tree Search
简单的迭代优化(写代码 → 测试 → 改代码 → 测试…)有个问题:容易陷入局部最优。比如 LLM 沿着一个思路改了 5 版代码,发现这个方向走不通了,但已经回不去了。
Tree search 的思路是:同时维护多个版本的代码,像一棵树一样分叉发展。
```
初始代码 v0
/ \
v1a v1b ← 两个不同的改进方向
/ \ |
v2a v2b v2c ← 继续分叉
|
v3a ← 这个方向成功了合法率100%
```
#### Thompson Sampling 是什么?
你面对这棵树上的多个节点,每次迭代应该选哪个节点来继续优化?这就是经典的 exploration-exploitation探索-利用)问题:
- **利用Exploitation**:选当前表现最好的代码版本继续改进
- **探索Exploration**:试试那些还没被充分优化的代码版本,也许潜力更大
Thompson sampling 是一种概率性的选择策略:
```
对每个节点:
1. 根据它历史的"合法率"数据建一个概率分布Beta 分布)
2. 从这个分布中随机采样一个值
3. 选采样值最高的节点来优化
效果:表现好的节点被选中的概率更高(利用),
但表现差的节点也有机会被选中(探索)
```
工程类比:这和你做 A/B testing 时的 Multi-Armed Bandit 问题几乎一模一样。Thompson sampling 就是一种 bandit 算法。
### Critic 和 Refiner 的分工
**Critic批评者**
- 输入rollout 中失败的步骤(最多 5 个)
- 工作:整理和归纳各种错误类型
- 输出:结构化的错误摘要
- 类比Code Review 时给你提 bug 的同事
**Refiner优化者**
- 输入:当前代码 + Critic 的错误摘要
- 工作:基于反馈生成改进版代码
- 输出:新版本的 harness 代码
- 类比:你根据 code review 意见改代码
一个关键细节:如果 `is_legal_action()` 返回 True 但环境说动作非法(漏判),则两个函数都要改;如果 `is_legal_action()` 返回 False 且动作确实非法(检查器工作正常,只是 `propose_action` 提出了错误动作),则只改 `propose_action()`。这个区分很重要,避免了"改了不该改的代码"。
---
## 实验结果解读
### 训练效率:多快能学会?
- 平均 **14.5 次迭代**就能学会(即 LLM 改代码 14.5 次)
- **19/32 个游戏**不到 10 次就搞定了
- 最难学的游戏GermanWhist43次、Chess64次、Othello62次
直觉理解:简单游戏(如猜数字、骰子)规则简单,几次就能写对检查器;复杂游戏(如国际象棋)规则多样(王车易位、吃过路兵等),需要更多轮迭代。
最终结果:**全部 145 个游戏都达到了 100% 合法动作率。**
### 双人游戏:小模型+Harness vs 大模型
| 对阵 | 我们的方法胜率 | 对手胜率 |
|------|--------------|----------|
| Flash+Harness vs Gemini-2.5-Pro | **56.3%** | 38.2% |
| Flash+Harness vs Flash原始 | **64.8%** | — |
这意味着什么?**一个小模型Flash配上自动生成的规则检查代码可以打败一个大几倍的模型Pro。**
---
## 核心启示
1. **"代码即策略"可能是 LLM Agent 的终局形态** — 让 LLM 写代码,然后运行时零 LLM 调用
2. **小模型+好代码 > 大模型裸跑** — 这打破了"模型越大越好"的迷信
3. **Program Synthesis + RL 的结合** — 这可能是下一代 AI 系统的核心范式
---
## 标签
#论文解读 #AutoHarness #LLM #Agent #ProgramSynthesis #GRPO #强化学习