Harness工程的概念,以及简单的代码示例

大模型像一匹精力旺盛但脾气难测的烈马------单次输出可能惊艳,也可能翻车。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 工程的门口了。

相关推荐
Dr.kangder1 小时前
嵌入式面试总结(一)——嵌入式系统实时性
面试·职场和发展·架构·嵌入式·虚拟化
AI_paid_community1 小时前
如何使用 Claude 在 AI 时代快速入局新的行业?(经验贴)
前端·javascript·后端
sunly_1 小时前
React Suspense 用法详解
前端·javascript·react.js
渣波1 小时前
TS 必考题深度解析:type 与 interface 的终极对决
前端·javascript
LONGZETECH2 小时前
无人机实训高成本痛点解法:虚拟仿真实现 70% 耗材损耗下降
大数据·算法·unity·架构·无人机
ai_coder_ai3 小时前
事件驱动架构(EDA)在分布式业务系统中的应用
分布式·架构
必须会一定会3 小时前
用纯 HTML/JS 做一个 AI 需求澄清器:把模糊想法转换成可执行任务书
开发语言·前端·javascript·人工智能·html·ai编程
MindUp3 小时前
企业网盘选型的技术评估维度与主流产品架构简析
人工智能·安全·架构
小帅不太帅3 小时前
1.5M 参数的 OCR 模型,我把它跑进了浏览器
前端·javascript·ai编程