摘要
将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 的执行过程如下:
generateCandidates并行调用 LLM 3 次,获得 3 个候选实现------可能是Set去重、filter+indexOf、reduce去重等不同方案。evaluateAll让 LLM 以评审身份逐个打分:正确性、简洁性、边界处理(如 NaN 去重)各占一定权重。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 中的评分标准,其余基础设施完全复用。