大模型像一匹精力旺盛但脾气难测的烈马------单次输出可能惊艳,也可能翻车。Harness(马具)工程的核心思想,就是用结构化的"缰绳"把 LLM 套住:不让它裸奔一次定生死,而是让它并行产出多个候选,由一个自动裁判打分,再把最优解挑选出来。整个过程无需微调权重,纯粹在推理期通过"选择压力"提升质量。
本文从一段最小可运行 Demo 出发,将其还原为一个可落地的工程化框架,帮助读者理解 Harness 的设计理念与实践要点。
🎯 一句话理解 Harness
Best-of-N Sampling(并行生成)+ LLM as Judge(自动评测)+ 择优筛选 = 闭环流水线
这三件事被解耦成独立的阶段,像工厂流水线一样串联:生成工人只管生产,评测工人只管打分,调度器只管挑最好的那个。这就是"Harness"------让不可控的 LLM 行为,收敛为可交付的工程结果。
代码走读:三阶段流水线
下面是一段简短的 Harness 示例代码,虽然只有几十行,但已经包含了完整的骨架。
阶段一:Best-of-N 并行生成
ini
const generateCandidates = (prompt, n = 3) => {
const tasks = Array.from({ length: n }, () => askLLM(prompt));
return Promise.all(tasks);
};
这里 n=3意味着同一个 prompt 被并发请求 3 次。为什么要多生成? 数学上,如果单次生成正确的概率是 p,那么 N 次独立生成至少有一次正确的概率是 1 - (1-p)^N。举例:当 p=0.3时,N=5能让成功率飙升到约 83%。
💡 工程启示 :生成阶段是"算力换质量"的过程。
Promise.all保证了 N 次请求并行,墙钟时间只相当于 1 次请求------这是 Best-of-N 在工程上可行的前提。
阶段二:LLM as Judge 自动评测
javascript
async function judge(code) {
const prompt = `
你是一个严格的代码评审,请判断下面的代码是否正确实现"数组去重函数"
要求:
- 只返回一个数字评分(0-10)
- 不要解释
代码:${code}
`;
const res = await askLLM(prompt);
const score = parseFloat(res);
return isNaN(score) ? 0 : score;
}
这一步是整个 Harness 的"大脑"。它用 LLM 代替人工评审,实现了闭环自动化。
但这里隐藏着一个大坑 ⚠️:LLM 裁判远不是中立的仲裁者,它有四类系统性偏差:
- 位置偏差:A/B 对比时偏向排在前面的答案
- 冗长偏差:答案越长,分数越高(即便长不代表好)
- 自我偏好:裁判偏爱和自己风格相似的输出
- 风格偏差:容易被表面的文采、自信的措辞带偏
实测数据显示,同一批用例,脚本验证通过率 72%,LLM Judge 通过率却高达 89%------那 17% 的差异就是 Judge "放水"的部分。
阶段三:择优筛选
css
function pickBest(evaluated) {
return evaluated.sort((a, b) => b.score - a.score)[0];
}
简单粗暴取最高分。香港城市大学与微软研究院的 RHO 系统在采用类似策略时,加入了一条保守原则:即便得分最高的方案,也必须严格高于零分(即真正优于原版)才会被采纳,打平视为不合格------这是为了防止 AI 因自我评判的随机误差而误判。
🏗️ 从 Demo 到生产:必须补齐的工程化能力
上面的代码是一个完美的"概念验证",但要放到生产环境,还需要补齐以下能力。
1. 规则校验先行,Judge 辅助排序
纯靠 LLM Judge 排序是有风险的。业内最佳实践是把评测拆成三层:
| 评测层 | 适用场景 | 特点 |
|---|---|---|
| 规则/代码校验 | 格式、字段、数值、工具调用 | 稳定、便宜、可复现 |
| LLM as Judge | 语义相关性、完整性、策略合理性 | 可扩展,但有偏差 |
| 人工抽检 | 高风险、争议样本 | 最接近业务共识 |
一个更稳健的模式是:先用验证器(单元测试、类型检查、Schema 校验)过滤掉明显错误的候选,让 Judge 只在少量"已通过验证"的候选中做精细排序。这样 Judge 的偏差被限制在极小的候选池里,影响可控。
2. 温度参数与多样性
Best-of-N 的质量高度依赖 N 个候选的差异性。如果 temperature=0,N 次生成的结果几乎一模一样,等于浪费算力。一般建议:
- 代码生成:
temperature0.7 左右 +top_p0.9~0.95 - 典型部署 N 取 4~64,研究场景可推到数百
3. 评分解析的保护
parseFloat(res)过于脆弱------Judge 模型偶尔会输出 "8分"或 "我认为这段代码得 8 分"而非纯数字。生产代码必须:
- 用正则提取数字:
res.match(/-?\d+(.\d+)?/)?.[0] - 设定分数边界裁剪(clamp 到 0-10)
- 解析失败时降级处理,而非直接判 0 分
4. 超时、重试与失败兜底
Promise.all是"全有或全无"------一个请求超时,整个生成阶段就挂了。生产环境应改为:
- 单个候选超时/失败 → 用剩余候选继续
- 全部失败 → 返回降级方案(如最简单的基础实现)
- Judge 调用失败 → 该候选记 0 分或剔除
5. 多裁判集成(Multi-Judge Ensemble)
单一 Judge 的偏差是确定的。用 2-3 个不同模型家族的 Judge 分别打分再平均,可以显著降低偏差。代价是 Judge 调用成本上升,但远低于把 N 翻倍------因为"评判比生成便宜"。
6. 评测日志与可追溯性
每一次 Harness 运行都应记录:
- 输入 prompt
- N 个候选的完整输出
- 每个候选的 Judge 评分与理由
- 最终选中哪个、为什么
这一份"评测轨迹"是后续做 Badcase 聚类、Judge 校准、提示词迭代的命脉。
🔧 生产级 Harness 的伪代码骨架
ini
async function productionHarness(prompt, {
n = 8,
temperature = 0.7,
verifier = null, // 单元测试 / Schema 校验
judges = [], // 多个 LLM Judge
timeoutMs = 30000,
} = {}) {
// 1. 并行生成 N 个候选(带超时与降级)
const candidates = await generateWithTimeout(prompt, n, temperature, timeoutMs);
// 2. 规则验证器先过滤(如有)
let filtered = candidates;
if (verifier) {
filtered = candidates.filter(c => verifier(c.code));
if (filtered.length === 0) filtered = candidates; // 全挂则回退
}
// 3. 多 Judge 集成打分
const evaluated = await Promise.all(
filtered.map(async (c) => {
const scores = await Promise.all(judges.map(j => j.judge(c.code)));
const avg = scores.reduce((a, b) => a + b, 0) / scores.length;
return { ...c, score: avg, scoreBreakdown: scores };
})
);
// 4. 择优(保守原则:必须高于基线才采纳)
const best = evaluated.sort((a, b) => b.score - a.score)[0];
return best;
}
⚖️ Harness 工程的边界与权衡
Harness 不是银弹,它在以下几个维度上有明确的取舍。
✅ 适合的场景
- 代码生成(可用单元测试做验证器)
- 数学推理(答案可验证)
- 有明确定义"对错"的任务
⚠️ 必须谨慎的场景
- 开放式创意写作(Judge 偏差放大)
- 高风险决策(医疗、法律、金融)
- 需要严格稳定性的生产系统------用户不会接受"多试几次总有一次成功",他们要求每次都稳定
📉 N 的边际收益递减
随着 N 增大,输出分布会逐渐偏离基础模型的原始分布。更关键的是,Judge 本身不可靠时,N 越大越容易选出"骗高分"的答案而非真正好的答案。经验法则:
- 有验证器的场景(代码、数学):N 可以推到 32-64+
- 纯 Judge 排序的场景:N 控制在个位数,把预算花在多 Judge 集成上
💰 成本不对称
Best-of-N 是"每次请求都付 N 倍成本"。如果发现某个 N 值持续表现优异,说明这批优选数据值得拿去做拒绝采样微调(Rejection Sampling Fine-tuning) ,把选择压力固化到权重里,从而以 1 倍推理成本保持行为。
📌 写在最后
上面这段只有几十行的代码,却浓缩了 LLM 应用从 Demo 走向生产的精髓:承认单次生成的不可靠性,用并行采样覆盖可能性,用自动评测替代人工,用结构化流水线把"碰运气"变成"工程化交付" 。
Harness 工程的真正价值不在于某一次输出有多惊艳,而在于------它让 LLM 的能力变得可重复、可度量、可迭代。每一次运行都留下评测轨迹,每一个 Badcase 都能反哺 prompt 和 Judge 的优化,这才是 AI 应用从玩具走向生产系统的根本路径。
💡 如果读者正在构建 Agent 或 AI 编程工具,不妨从这三件事开始:把生成改成并行 N 次、给 Judge 加上规则验证器兜底、把每次运行的完整轨迹落到日志。这三步做完,就已经站在 Harness 工程的门口了。