别死磕 Prompt 了!我用 Harness 流水线,让大模型自动产出高质量代码

前阵子用大模型批量写工具函数,踩了个巨恶心的坑。就拿最简单的数组去重来说,同一句提示词丢进去,三次生成的结果里总有一两个藏着暗坑 ------ 要么没处理 NaN,要么对象引用去重逻辑写错,更气人的是有时候表面能跑,边界情况直接拉胯。我得挨个复制出来跑测试、改 bug,一下午净干重复活了。

本来想着能不能写个脚本自动跑测试用例,结果翻资料的时候撞见个有意思的概念:Harness 工程。说白了就是把「生成 - 评测 - 择优」整个流程串成自动化流水线,让大模型既当选手又当评委,自动筛出最优结果。折腾了两天跑通了最小版本,今天顺着我踩坑的路子,跟大家唠唠这玩意儿到底是怎么回事。

先唠明白:Harness 到底是个啥

我第一次见这个词的时候,查了下直译是 "马具、挽具",当时还觉得挺抽象。后来跑通了才反应过来,这比喻还真挺形象。你就把大模型当成一匹不受控的野马,每次跑出来的结果时好时坏,你不知道哪次能跑出满意的。而 Harness 就是给这匹马套上的整套马具 + 赛道 + 裁判系统:让好几匹马同时在赛道上跑,终点有裁判挨个打分,最后把跑最快、得分最高的那匹牵出来。

换成技术话说,就是把大模型的单次调用,拆成了三段式的流水线。第一段是生成:同一句提示词并行生成 N 个候选结果,靠随机性覆盖更多可能性,行话叫 Best of N Sampling。第二段是评测:不用人一个个看,再调用一次大模型当评委,按统一标准给每个候选打分,也就是常说的 LLM as Judge。第三段是择优:按分数排序,直接返回最高分的结果。这三段拼起来,就是一个最基础的 Harness 流水线。说穿了不是什么黑科技,就是用工程化的思路,把本来要人工重复做的事,变成自动化的闭环。

说起来这逻辑其实和 ReAct Agent 的思考路子有点像 ------ 先生成几个方案,再评估方案好坏,最后选最优的执行。只不过 Harness 是把这个思考逻辑,固化成了可复用的工程框架,不用每次都让大模型自己走思维链,直接靠流程保证结果质量。

整条流水线跑起来,到底是怎么流转的

我画了个最简单的流程示意图,一眼就能看明白:

说个更接地气的类比,你就当开奶茶店测新品:生成阶段就是让三个研发师各做一杯同款奶茶,配方各有差异,对应生成 N 个候选答案;评测阶段就是找专业评委按甜度、茶香、口感几个维度打分,对应 LLM 当评委统一评分;择优阶段就是直接选得分最高的那款上架,对应排序取最高分结果。

以前我们用大模型,相当于只找一个研发师做一杯,好不好喝全凭运气;加了 Harness 之后,相当于搞了个小型盲测赛,质量下限直接拉高了。说实话我一开始觉得这玩意儿会不会很浪费 token?后来算了笔账,生成 3 个候选加 3 次评测,总共 6 次调用,换来的是结果正确率大幅提升,比起你反复调 prompt、反复人工审核,其实效率高多了。

跑通最小 Demo:手把手搭一遍流水线

光说概念太虚,我把自己跑通的最小版本掏出来,基于 OpenAI SDK 接阿里云的通义千问兼容接口,复制过去改下密钥就能跑。

先搭基础环境

就两个依赖,openai 的 SDK 加 dotenv 读环境变量:

bash 复制代码
npm init -y
pnpm i openai dotenv

然后根目录建个 .env 文件,填你的 DashScope 配置:

env 复制代码
    DASHSCOPE_API_KEY=sk-你的密钥
    DASHSCOPE_API_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
    DASHSCOPE_API_MODEL=qwen-plus

注意这里的 baseURL 一定要填到 compatible-mode/v1 这层,我一开始漏了 compatible-mode 路径,直接报 404,对着报错愣了二十多分钟,重置了两次密钥都没用,最后翻文档才发现是路径错了,属实是低级错误。

核心代码实现

直接上完整代码,注释里标了我当时踩过的坑:

javascript 复制代码
    import OpenAI from 'openai';
    import { config } from 'dotenv';
    config();

    // 初始化客户端,走DashScope兼容模式
    const client = new OpenAI({
        apiKey: process.env.DASHSCOPE_API_KEY,
        baseURL: process.env.DASHSCOPE_API_BASE_URL,
    })

    // 封装最基础的大模型单轮调用
    const askLLM = async (prompt) => {
        const res = await client.chat.completions.create({
            model: process.env.DASHSCOPE_API_MODEL,
            messages: [
                {
                    role: 'user', 
                    content: prompt
                }
            ],
        })
        return res.choices[0].message.content;
    }

    // 批量生成候选答案
    const generateCandidates = async (prompt, n = 3) => {
        // 别写for循环串行调用,慢死!直接Promise.all并行
        // 别问我为什么知道,一开始写的循环,等得我咖啡都凉了
        const tasks = Array.from({length: n}, () => askLLM(prompt));
        return Promise.all(tasks);
    }

    // 评委函数:给单段代码打0-10分
    const judge = async (code) => {
        const prompt = `
        你是一个严格的代码评审,请判断以下代码是否正确实现"数组去重函数"
        要求:
        - 只返回一个数字评分(0-10)
        - 不要任何解释,不要加任何文字
        代码:
        ${code}
        `
        const res = await askLLM(prompt);
        // 这里一定要做容错!大模型偶尔会不听话,返回"8分"或者带解释
        // 不处理的话parseFloat直接NaN,整条流水线直接崩
        const score = parseFloat(res);
        return isNaN(score) ? 0 : score;
    }

    // 批量评测所有候选代码
    const evaluateAll = async (candidates) => {
        const results = [];
        for (const code of candidates) {
            const score = await judge(code);
            results.push({code, score});
        }
        return results;
    }

    // 选出得分最高的候选
    const pickBest = (evaluated) => {
        return evaluated.sort((a, b) => b.score - a.score)[0];
    }

    // 完整的Harness流水线入口
    const harness = async (prompt) => {
        console.log('生成多个候选者......\n');
        const candidates = await generateCandidates(prompt, 3);
        console.log('候选结果:');
        candidates.forEach((c, i) => {
            console.log(`\n------Candidate ${i + 1}------\n ${c}`);
        });

        console.log('\n开始评测候选...\n');
        const evaluated = await evaluateAll(candidates);
        console.log('打分结果:');
        evaluated.forEach((c, i) => {
            console.log(`\n------Candidate ${i + 1}------\n 得分:${c.score}`);
        });

        console.log('\n选出最优结果...\n');
        const best = pickBest(evaluated);
        return best.code;
    }

    // 跑个测试:生成数组去重函数
    const bestCode = await harness("请使用 javascript 实现一个数组去重函数");
    console.log('最终最优代码:\n', bestCode);

跑起来的实际效果

我本地跑了一次,控制台输出大概是这样的流程:先生成 3 个候选版本,有的用 Set 实现,有的用 filter+indexOf,有的手写了遍历去重;然后挨个打分,比如没处理 NaN 的给 6 分,只支持基本类型的给 7 分,考虑了 NaN 和多种边界的给 9 分;最后直接返回得分最高的那段代码。当时看着控制台自动走完整个流程,还挺有成就感的 ------ 以前要手动复制粘贴半小时的活,现在敲个命令等十几秒就出结果了。

往深了唠:为什么要拆成这三段?

可能有人会说,这不就是多调用几次大模型吗,至于搞个框架的名头?我一开始也这么想,直到我后来想换评测逻辑 ------ 本来用大模型打分,后来想换成跑单元测试用例来算分,要是写在一坨代码里,改起来得动整个流程。

这就是 Harness 最核心的设计思路:把生成、评测、择优三个环节完全解耦。生成层只管按提示词生成结果,你可以换不同的模型,也可以加参数调温度,甚至可以混合多个模型生成候选,都不影响后面的环节。评测层是整条流水线的核心。你可以用大模型当评委,也可以用自动化测试、语法检查、性能跑分当评委,只要最后返回一个统一的分数就行。择优层最简单的就是按分数排序取第一,也可以搞加权,或者按不同维度综合打分,逻辑独立,随便改。

说白了,Harness 就是个流水线架子,你往里面塞不同的节点,就能解决不同的问题。今天可以用来选最优代码,明天改改评测逻辑,就能用来选最优文案、最优测试用例,甚至最优方案设计。这也是为什么说它是工程化手段 ------ 不是靠单次 prompt 技巧提升质量,而是靠流程和架构,把不稳定的大模型输出,变成可控、可预期的结果。

我踩过的几个坑,你们别再踩了

这套东西写起来不难,但细节里全是坑,我踩了好几个,给你们列出来避避。

坑 1:接口地址写错,半天连不上

最蠢但最容易踩的坑。用 OpenAI SDK 接 DashScope 的时候,baseURL 必须带 compatible-mode 路径,我一开始直接写了 https://dashscope.aliyuncs.com/v1,要么报 404,要么报鉴权失败,折腾了好久才反应过来。错误写法:

plaintext 复制代码
    https://dashscope.aliyuncs.com/v1

正确写法:

plaintext 复制代码
    https://dashscope.aliyuncs.com/compatible-mode/v1

坑 2:评委输出不规范,直接把流水线搞崩

大模型不是程序,你让它只返回数字,它偶尔就会给你加个 "分" 字,或者带一句 "这段代码整体不错,给 8 分"。我一开始没做容错,直接 parseFloat,结果某次大模型返回了个 "8 分",转出来是 NaN,后面排序直接乱了,找了半天 bug 才定位到。正确做法就是像代码里写的那样,加个 isNaN 判断,解析失败就给 0 分兜底,至少不会让整条流水线挂掉。

优化思路如果想更稳,可以加个正则提取数字,比如 /\d+/ 匹配返回结果里的数字,容错率更高。

坑 3:生成候选串行调用,慢到离谱

一开始图省事,生成 N 个候选写了个 for 循环,一个个调用,3 个候选要等十几秒。后来改成 Promise.all 并行调用,同样 3 个,四五秒就完事了,速度提升了两三倍。当然也别太贪,N 别开太大,5 个以内差不多,开太多一方面费 token,另一方面容易触发接口限流,反而更慢。

坑 4:生成和评测用同一个模型,会不会 "互相包庇"?

这是我一开始最担心的问题:同一个模型生成的代码,同一个模型来打分,会不会王婆卖瓜自卖自夸?实际测了几次,发现只要评测的 prompt 写得足够严格、标准明确,基本不会有太大问题。但如果是对准确性要求极高的场景,建议用更强的模型当评委,比如生成用 qwen-plus,评测用 qwen-max,结果会更客观。

最后说几句掏心窝子的

折腾完这一套,我最大的感受是,别总把大模型当 "一次性问答工具"。很多人总纠结怎么写一句完美的 prompt,让大模型一次就输出满意的结果,但实际上大模型的随机性是天生的,你再怎么调 prompt,也不可能保证 100% 稳定。

而 Harness 这套思路,本质上是换了个打法。接受大模型的不稳定性,靠多生成几个候选来覆盖可能性,比死磕单条 prompt 效率高得多;把大模型拆成流水线里的不同节点,既能当生成器,也能当评测器,不同节点干不同的活,组合起来能解决很多单调用解决不了的问题;工程化的核心是解耦,把流程拆成独立的阶段,以后不管是换模型、换评测标准,还是加新的环节,都不用推翻重来。

当然这玩意儿也不是万能的。如果是很复杂的需求,比如写一整个项目的代码,评测标准很难量化,大模型评委也打不准,这时候还是得人工上。还有如果你的评测 prompt 写得烂,相当于评委不专业,选出来的结果自然也好不了。

总的来说,这是个投入产出比很高的工程化思路,尤其是批量生成代码、文案这类可以量化评分的场景,能省超多重复劳动。如果你也试过用大模型干活踩过幻觉的坑,不妨照着代码跑一遍试试。跑通了或者有别的改法,记得回来留个言,我也想看看你们的玩法。

相关推荐
恒拓高科WorkPlus44 分钟前
企业级即时通讯与数字协同平台:BeeWorks如何连接沟通、文档、业务与AI
人工智能
XGeFei1 小时前
【AI应用/Agent智能体搭建平台2】Dify——自部署
人工智能·笔记·学习
hans汉斯1 小时前
《软件工程与应用》期刊推荐&10月版面征稿中
图像处理·人工智能·深度学习·算法·音视频·软件工程
前端开发江鸟1 小时前
为了实现“小说自由”,我搭了一套 AI 小说工作流,结果却不如回家种田
人工智能
叠层归一研究院1 小时前
AGI 系统(八):符号范畴嵌入函子 — SymCat ↪ C_M107 严格化
c语言·开发语言·人工智能·算法·transformer·agi
2601_964840271 小时前
4G云门禁普惠化技术实践:基于边缘计算破解成本、隐私、部署三大行业瓶颈
大数据·人工智能·边缘计算
明朝百晓生2 小时前
Deep RL learning[2026/8]
开发语言·javascript·人工智能
具身智能进化论2 小时前
国产协作机器人品牌如何选择,企业长期使用更看重什么
大数据·人工智能·机器人·工厂方法模式
AKAMAI2 小时前
2026年Gartner Peer Insights 边缘分布平台客户之声将Akamai评为客户之选
人工智能·云计算