Harness 工程:用 Best of N Sampling + LLM as Judge 构建自动化代码生成流水线

摘要

将LLM生成、评测、择优三阶段解耦为流水线,通过Best of N Sampling并行生成候选、LLM as Judge自动评分,实现prompt到最优代码的闭环筛选,解决LLM输出不稳定问题。


大语言模型的能力毋庸置疑,但它的输出质量却极不稳定。同一个 prompt 给 DeepSeek 或 GPT-4,跑 10 次可能得到 10 种质量参差不齐的结果------有时是完美的实现,有时是语法错误,有时是逻辑漏洞。这种随机性是 LLM 基于概率采样的本质决定的,无法从根本上消除。

Harness 工程提供了一个务实的解法:不试图让单次生成更稳定,而是批量生成、自动评测、择优输出。就像给烈马套上马具(harness),在结构化流程中驾驭 LLM 的随机性,让它稳定产出高质量结果。

Harness 的三层抽象

Harness 流水线将代码生成拆解为三个独立阶段,每个阶段各司其职:

csharp 复制代码
[Prompt] → 生成多个候选 → LLM 自动评分 → 择优输出最优代码
  • 生成阶段(Best of N Sampling):用同一个 prompt 并行调用 LLM 多次,每次获得不同的随机采样结果,覆盖更多可能性。
  • 评测阶段(LLM as Judge):用 LLM 充当自动化评分器,替代人工审查,对每个候选代码打分。
  • 择优阶段:按评分排序,输出最高分代码。

三个阶段各自独立、可替换、可扩展。评测器的 prompt 可以调整,生成数量 N 可以加大,评分维度可以扩展------每个环节都是流水线上的一个可替换模块。

Best of N Sampling:并行覆盖更多可能性

LLM 每次生成时,温度参数(temperature)控制着采样的随机性。温度越高,输出越多样化,但也越不稳定。Best of N Sampling 的策略是:不降低温度来换取稳定性,而是保留多样性,然后通过评测筛选质量

javascript 复制代码
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  baseURL: process.env.OPENAI_BASE_URL,
});

const askLLM = async (prompt) => {
  const res = await client.chat.completions.create({
    model: process.env.MODEL_NAME,
    messages: [{ role: 'user', content: prompt }],
  });
  return res.choices[0].message.content;
};

const generateCandidates = (prompt, n = 3) => {
  const tasks = Array.from({ length: n }, () => askLLM(prompt));
  return Promise.all(tasks);
};

generateCandidates 的核心是 Promise.all------N 个 LLM 调用完全并行执行,不等待前一个结果。Array.from({ length: n }, () => askLLM(prompt)) 创建 N 个独立的 Promise,每个 Promise 携带相同的 prompt 但获得不同的随机采样结果。N 取 3 是一个平衡点:足够覆盖多样性,又不会消耗过多 token。

LLM as Judge:让 AI 评审 AI

传统开发中,代码审查依赖人工。但在 Harness 流水线中,人工审查是瓶颈------你不可能让开发者实时评审每次生成的 N 个候选。LLM as Judge 将评审工作也交给 LLM,实现全自动闭环:

javascript 复制代码
async function judge(code) {
  const prompt = `
  你是一个严格的代码评审,请判断下面代码是否正确实现"数组去重函数"

  要求:
  1. 只返回一个数字评分(0-10)
  2. 不要解释

  代码:
  ${code}
  `;
  const res = await askLLM(prompt);
  const score = parseFloat(res);
  return isNaN(score) ? 0 : score;
}

judge 函数接收一段候选代码,构造一个评审 prompt,让 LLM 以严格评审的身份对代码打分。关键约束是"只返回数字,不解释"------这确保了输出格式的确定性,parseFloat 可以直接解析。isNaN 兜底处理极端情况下 LLM 返回非数字内容的情况。

评审 prompt 的设计是 LLM as Judge 的核心。一个好的评审 prompt 需要明确三点:评审角色(你是谁)、评审标准(评什么)、输出格式(怎么返回)。缺任何一项,LLM 的评分质量和一致性都会下降。

流水线编排:串联三个阶段的 harness 函数

三个阶段各自独立实现后,harness 函数将它们串联为完整的流水线:

javascript 复制代码
const evaluateAll = async (candidates) => {
  const results = [];
  for (const code of candidates) {
    const score = await judge(code);
    results.push({ code, score });
  }
  return results;
};

const pickBest = (results) => {
  return results.sort((a, b) => b.score - a.score)[0];
};

async function harness(prompt) {
  console.log('生成多个候选者...\n');

  const candidates = await generateCandidates(prompt, 3);
  candidates.forEach((c, i) => {
    console.log(`\n---- Candidate ${i + 1} ----\n${c}`);
  });

  console.log('\nEvaluating Candidates...\n');
  const evaluated = await evaluateAll(candidates);

  const best = pickBest(evaluated);
  return best.code;
}

const bestCode = await harness('请使用javascript实现一个数组去重函数');

evaluateAll 逐个评分候选代码,pickBest 按评分降序排列取第一名。harness 是编排层------它不关心具体怎么生成、怎么评分、怎么排序,只负责按正确顺序调用这三个阶段。

注意 evaluateAll 中使用 for...of 而非 Promise.all。这是因为评测阶段需要逐个调用 LLM------每个评分请求都需要前一个返回后才能发送,避免并发请求之间的速率限制冲突。如果 API 允许高并发,这里也可以改为 Promise.all 以加速评测。

完整执行流程

以"数组去重函数"为例,harness 的执行过程如下:

  1. generateCandidates 并行调用 LLM 3 次,获得 3 个候选实现------可能是 Set 去重、filter + indexOfreduce 去重等不同方案。
  2. evaluateAll 让 LLM 以评审身份逐个打分:正确性、简洁性、边界处理(如 NaN 去重)各占一定权重。
  3. pickBest 选出评分最高的代码作为最终输出。

整个过程不需要人工介入。从 prompt 到最优代码,全自动闭环。

为什么 Harness 比"多试几次选最好的"更工程化

"多试几次选最好的"听起来像常识,但手工操作和工程化流水线有天壤之别:

维度 手工操作 Harness 工程
生成 手动复制粘贴 prompt 多次 Promise.all 并行生成 N 个候选
评测 人眼阅读,凭感觉判断 LLM 按统一标准量化评分
择优 主观选择 sort + pickBest 确定性排序
可复现 每次结果不同,无法对比 固定流水线,可调参优化
可扩展 N 越大越累 改一个参数即可增加候选数

Harness 的工程化价值在于将人工判断转化为可复现的自动化流程。评测标准写死在 prompt 中,评分逻辑透明可审计,每次运行的结果可以对比分析------为后续优化(调整 N、改进评审 prompt、引入多维度评分)提供了可量化的基础。

从 Harness 到 Agent 的演进

Harness 流水线本身就是一个简化版的 Agent------它遵循 ReAct 框架的核心思想:生成(Act)→ 评测(Observe)→ 择优(Reason)。区别在于 Harness 的流程是固定的三段式,而 ReAct Agent 的流程是循环的------Agent 可以根据评测结果决定是否重新生成,形成"生成→评测→不满足→重新生成"的循环。

Harness 是 Agent 的前置模式。在走向全自动 Agent 之前,先用 Harness 这种固定流水线验证"生成+评测"模式的可行性,积累 prompt 工程经验,逐步过渡到更复杂的循环式 Agent 架构。

总结

Harness 工程用三层抽象解决了 LLM 输出不稳定的核心痛点:Best of N Sampling 覆盖多样性,LLM as Judge 实现自动化评测,流水线编排保证流程可复现。三个阶段的代码各自独立、职责清晰,加起来不过 70 行,却构成了一个完整的自动化代码生成流水线。

这套模式不限于代码生成------文本写作、翻译、摘要、SQL 生成等任何需要"高质量输出"的 LLM 场景,都可以套用"生成→评测→择优"的 Harness 流水线。你只需要调整评审 prompt 中的评分标准,其余基础设施完全复用。

相关推荐
张彦峰ZYF8 小时前
LangGraph 深入理解 ReAct:让 AI Agent 真正学会「边想边做」
人工智能·llm·agent·react·langgroup
想要成为糕糕手10 小时前
🚀 在浏览器里跑 DeepSeek-R1?WebGPU 端侧推理实战(六)—— 完结篇:消息渲染与流式收尾
react.js·typescript·llm
AndrewHZ13 小时前
【LLM技术全景】RAG 从原理到实战——检索增强生成完整指南
人工智能·深度学习·算法·llm·检索增强·生成式模型·rag
liulilittle17 小时前
DeepSeek Harness 自定义模型提供商配置
前端·javascript·人工智能·windows·llm·deepseek·harness
tachibana218 小时前
文件上传分布式限流如何做?
人工智能·ai·大模型·llm·prompt
To_OC1 天前
别死磕 Prompt 了!我用 Harness 流水线,让大模型自动产出高质量代码
人工智能·llm·agent
qpsj1 天前
让 LLM 控制 AutoCAD/ZWCAD:COM 自动化 + MCP 封装
python·llm