
最近一段时间我一直在折腾一个挺典型的落地问题怎么让大模型在真实业务里稳定输出而不是靠拍脑袋调提示词碰运气。试了很多套路之后我越来越确信一件事——想要提示词效果好必须让测试集说了算想要成本降下来必须把大模型的能力“搬”到小模型上。这条链路我愿称之为“测试集驱动的大小模型协同优化”。今天就把整个思路、操作细节、踩过的坑完整写一遍希望能帮你少走点弯路。这篇文章适合谁如果你正在做 LLM 应用开发每天都在调 prompt 但总被线上 badcase 打脸如果你已经把大模型接进了业务但觉得调用成本太高、延迟太大又或者你只是听说过“蒸馏”“小模型微调”但不知道从哪下手——那这篇文章就是写给你的。1. 先理清思路测试集、提示词、大小模型到底怎么配合1.1 为什么提示词要靠测试集说话很多刚接触提示词工程的人有个错觉prompt 写出来读着通顺、看起来专业就觉得它“应该能用”。但 prompt 这东西说白了一个自然语言写的指令它的质量高低根本不是靠肉眼判断的而是靠“在足够多的真实样本上表现如何”来判断的。我见过太多类似的场景工程师花一下午写了个提示词自己测了五六个例子觉得挺满意结果一上线各种幺蛾子全冒出来了——输出的 JSON 格式不稳定、该抽的实体没抽出来、不该出现的编造内容频繁出现。为什么因为那几个手测的例子里你潜意识里已经把 prompt 要什么结果写在脑子里了你是顺着 prompt 的逻辑去挑例子的这跟真实线上分布的差距可能非常大。测试集就是干这个的。它是一组固定不变的输入样本附带了明确期望的输出标准。提示词好不好不要问感觉直接一套评测跑下来看指标。这就好比考驾照不能靠“我觉得我开得挺稳”得去科目二考场按标线跑一圈压线就是压线没过就是没过。所以我在做任何 prompt 工程之前第一件事永远是先攒测试集。1.2 大小模型协同的完整链路很多文章只讲“怎么调 prompt”也有文章只讲“怎么微调小模型”但实际工作中最值钱的其实是把它们串起来的那条完整链路。我目前最常走的工作流是这样的先用一个大模型比如 GPT 系列、Claude或者是能力比较强的开源模型配合测试集反复调优提示词把任务能力做到一个满意的上限。然后用这套“大模型 优化好的提示词”去批量生成高质量的训练数据。最后拿这些数据去微调一个小模型比如 7B 或更小把小模型部署上线承担生产流量。为什么要这么绕一圈原因很实在大模型推理贵、延迟高不适合大规模线上调用小模型便宜、快、还能私有化部署但直接拿小模型做任务往往能力不够。这条链路本质上是用提示词工程把大模型变成“数据工厂”再通过微调把能力转移到小模型里。相当于让老师傅把所有经验写成了操作手册再让徒弟照着手册练上手练完之后徒弟独立干活。1.3 这条路适合哪些场景不是所有任务都值得走这条路。根据我的经验凡是“输入输出都是结构化、判断标准相对明确”的任务非常适合这条链路。典型的就是信息抽取、文本分类、意图识别、情感判断、JSON 字段提取、质检规则判定、客服对话标签化等等。这类任务有两个共同特点一是可以通过少量样本就能说清楚“什么是对的”二是大模型做起来很稳小模型经过微调也能学到八九成。但如果是开放式创作、长文写稿、需要大量外部世界知识做推理的场景这条路就不太好走。因为小模型容量有限你把大模型的推理能力压进去会打很多折扣用户体感会明显下降。这种场景不如老老实实用大模型 API 或者云端服务别惦记蒸馏。2. 测试集整个优化过程的地基2.1 测试集样本从哪来构建测试集是第一步也是最容易偷懒但其实最不能偷懒的一步。样本来源我一般用三个渠道混合。第一个渠道是历史标注数据。如果业务之前做过人工标注或者有线上系统的日志那直接拿过来用。这些数据分布贴近线上真实情况是最宝贵的“金矿”。没有现成标注的话可以让业务同学帮忙标几百条成本不高但价值极高。第二个渠道是线上日志采样。特别是如果你已经有了一版线上运行的规则或模型可以把它们处理过的真实数据随机抽样再人工确认一下标签。这个做法的好处是能暴露真实输入的多样性——你会发现用户的语言远比你想象的更“脏”有错别字、有口语化表达、有半句话、有中英混排。这些恰恰是测试集里最该有的样本。第三个渠道是人工构造对抗样本。这是最容易被忽略的。什么叫对抗样本就是专门挑 prompt 容易翻车的边角料比如抽取订单号时给一个“订单号疑似为 12345 或 67890 不确定”的句子分类时给一个“看似 A 类但有明显线索是 B 类”的模糊例子。这类样本数量不用多每类任务造十来个就能把 prompt 的鲁棒性逼出来。2.2 样本怎么分类、难度怎么分层测试集不是把样本堆在一起就完事的必须要分层、分类组织不然优化时无从下手。我的习惯是按照两个维度来切任务维度和难度维度。任务维度是指业务里天然有多个子任务比如“先判断对话属于咨询还是投诉再抽取订单号再输出对用户情绪的判定”。这时候我会把每个子任务单独切一个 slice分别统计指标。好处很明显总体准确率从 80% 涨到 85%你根本不知道是哪个子任务贡献的但如果每个子任务指标单独看就能精准定位是“情绪判断”这个环节太弱还是“订单号抽取”格式老出错。难度维度我习惯按 easy / medium / hard 三档分比例大约 5:3:2。easy 是大多数正常输入medium 是有点干扰项或表达绕的输入hard 是各种极端格式、模糊表达、边界情况。这种分层在评估时非常有用——你可以一眼看出优化到底是在“吃 easy 红利”还是真的把 hard 能力也提上去了。2.3 评估指标怎么设计测试集有了评估指标也得定好否则“好不好”永远是一笔糊涂账。这里我强烈建议至少分为三层自动规则校验、模型打分、人工抽检。自动规则校验是底线。如果你的任务有严格结构要求比如 JSON、XML、固定枚举值那一定要让程序去检查输出格式是否合法、必填字段是否缺失、枚举值是否在预定范围内。这个指标叫 parse_rate解析通过率或 format_pass_rate不过这一关后面的语义准确率谈都不用谈。模型打分是主力。现在比较主流的方式是 LLM-as-a-judge让一个大模型当裁判把模型输出和标准答案一起丢给它让它给出 1-5 分或者直接判对错。这种方式比纯规则灵活能覆盖很多语义层面的判断。但要注意裁判模型也会有自己的倾向性我一般会让裁判输出判断理由再定期人工抽检裁判的准确率避免裁判自己“带偏”了评估。人工抽检是压舱石。每轮评测完成后我会随机抽 30-50 条让业务同学盲评。为什么必须人工因为很多业务细节是模型看不到的——比如某些“看起来说错了但其实业务上可以接受”的答案只有懂业务的人才能判断。人工抽检不追求数量追求稳定它存在的意义是防止自动指标和真实业务需求之间产生系统性偏差。2.4 测试集版本与回归管理测试集不是一次性建完就固定的它需要持续维护和演进。我现在每次做回归评测都坚持用同一套“测试集版本”同时把新增的 badcase 不断沉淀进去。具体做法是为测试集维护一个 manifest 文件JSON 或 CSV 都行每一条样本都带 ID、输入、标注输出、任务类型、难度等级、期望的严格程度、来源线上日志/人工构造/历史标注这些字段。这样做最大的好处是可以做回溯对比。这轮 prompt 改完我把整个测试集重新跑一遍不仅能看出总分涨还是跌还能按任务和难度拆分看到底是哪里退了。很多时候一个改动会“按下一个葫芦浮起一个瓢”你要是不做回归很容易发现不了。有一件事要特别注意测试集会过拟合。如果你的 prompt 反复照着同一套测试集调慢慢就会“背答案”表面指标越来越高但换一批新样本立刻打回原形。避免的方法是定期从线上抽样新增样本并且隔一段时间就把旧样本里已经“太简单”的换掉一部分始终保持测试集对真实分布的敏感性。3. 提示词迭代一次完整的实战过程3.1 第一步先做一个可复现的 baseline拿我自己最近做的一个客服对话抽取任务来举例。业务要求是从一段客服和用户的对话里抽取三个字段用户订单号、问题类型枚举值物流/售后/价格/其他、用户情绪正面/中性/负面。我先把一段最朴素的基础提示词写上确保它能跑出一个“及格线”的结果然后才谈优化。基础提示词大概是这样的保留核心逻辑业务细节做了简化你是客服对话分析助手。请从下面的对话中抽取信息并输出 JSON {订单号: 这里填订单号或null, 问题类型: 物流/售后/价格/其他, 用户情绪: 正面/中性/负面} 对话 ...第一次评测的结果我记得很清楚JSON parse_rate 只有 78%语义准确率 62%。也就是说100 条里二十多条是直接输出格式崩了的剩下的里面还有很多字段抽错。这就是个很典型的 unoptimized baseline但没关系有了它我们就有了一个可以对比一切的“原点坐标”。3.2 第二步把错误归类找到真正的瓶颈Baseline 跑完不要急着改 prompt先做错误分析。很多人一上来就加更多措辞改得更花哨这完全是本末倒置。正确姿势是把评测的失败样本全部拉出来按失败模式归类。我那次分析下来问题集中在三大类。第一类格式问题模型有时候在 JSON 前面夹解释文字有时候字段名自己改比如把“订单号”写成“订单ID”。这是小模型或者 prompt 约束不足时特别常见的问题解决方案是强化输出格式约束比如让模型先输出一个特定的起始标记或者给一个完整的 JSON 模板。第二类漏抽取用户明明在对话里报了订单号但模型没抽出来。原因多半是当时上下文比较长订单号被淹没在大量文本里基础 prompt 里没有强调“只要对话中出现订单号就必须抽取”。解决方案是要么加规则指示要么给个 few-shot 示例。第三类语义理解偏差特别典型的例子是用户说“你们这破物流怎么回事”同时又说“行了赶紧把订单给我退了”。模型会把这个判断成“物流”类但业务上用户真正想解决的是售后退货。这种语义混淆靠加大模型参数解决不了必须靠任务拆解和业务规则定义。3.3 第三步逐条优化提示词的手段针对上面三类问题我分别用了几种不同的优化手段这里一个个拆开说。第一个是输出结构约束。与其在 prompt 里说“只输出 JSON”不如直接给出 JSON Schema 和“不要输出任何解释”。更硬核的做法是要求模型把结构化内容放在特殊标记之间然后程序只取标记之间的部分去解析。这一步把 parse_rate 从 78% 提到了 93%收益非常直观。经验是别指望模型自觉遵守格式你要在 prompt 里用最短的句子把规则说死。第二个是 few-shot 示例。不要塞十几个示例挑两个最有代表性的就够了。我选的一个是“订单号藏在对话中段且夹杂了口语”的例子另一个是“问题类型容易混淆”的例子。注意 few-shot 不是给模型“参考”而是给它“边界”——告诉它哪种情况算 A、哪种情况算 B。这比在 prompt 里写一百字抽象描述有用得多。第三个是任务拆解和 CoT 的结合。我们把一个复杂的抽取任务拆成两步第一步让模型先判断问题类型并给出理由第二步再抽取订单号和情绪。这一步实质上是让模型“先想后说”把推理过程外显。但对于这种短文本抽取任务CoT 的收益不如对长文本复杂推理那么明显反而增加了一点延迟和 token 消耗。所以我不建议所有任务都无脑上 CoT要看你任务里到底有多少“需要多步推理”的成分。我还加了一条负面约束“如果对话中没有出现订单号订单号字段输出 null不要编造。”这条千万别小看大模型天生有补全倾向不明确禁止它就敢给你编一个看起来像订单号的字符串。这类“不要做什么”的约束往往比“要做什么”更管用。经过三轮迭代评测结果变成了 parse_rate 97%语义准确率 86%。hard 分层从最初的 40% 提到了 65%。整体可以接受但还没到敢上线生产的程度。3.4 回归测试与版本记录每改一版 prompt我都会记录这个版本的变更点和评测结果绝不靠“上一版改了几句话”这种模糊记忆。具体做法是在项目目录里为每一版 prompt 建一个文件命名带版本号存放 prompt 全文、变更说明和评测结果。这个习惯帮我避免过两个大坑一是改了半天发现效果反而倒退回滚时却找不到上一版原文二是同一套链路用了不同版本的 prompt 跑数据导致训练数据质量波动后面排查起来非常痛苦。频繁修改 prompt 的人强烈建议你把 prompt 版本管起来。不需要上特别复杂的工具就用 Git 仓库管理 markdown 文件就够了提交信息里写明评测指标的变化。这个习惯的回报周期很短用不了几天你就会感谢自己。4. 进阶把能力从大模型迁移到小模型4.1 为什么要做这一步很多人会问大模型都调得不错了直接用不行吗行但成本可能让你肉疼。我给大家算一笔账假设你的业务每天要跑 100 万次调用每次输入输出加起来算 800 token用主流大模型 API 的话一天单是推理成本就可能到几千块一个月下来小十万。就算调到足够便宜的模型一秒并发几十上百次时延迟和限流也够你喝一壶的。而换成小模型呢一个 7B 开源模型用 vLLM 部署在两卡 A 系列显卡上吞吐可以做到每秒几百甚至上千 token单次调用边际成本几乎可以忽略还能内网私有化部署数据不出域。这种差异带来的不只是省钱还有做产品时完全不同的自由度——你甚至可以跑一些“高风险”的调用因为不用担心账单。所以我的经验是大模型调 prompt本质上是付费买方案小模型微调本质上是把方案学成自己的本事。两条腿走路才能既保证效果又控住成本。4.2 数据生产让优化好的大模型当“标注员”小模型的微调第一关是数据。数据最关键的一点是务必用最终版的“大模型 提示词”组合来生成而不是用中间半成品。因为提示词的每一次微小变化都会影响输出分布而数据里的“杂音”会直接限制小模型的上限。你在 prompt 上花的功夫会原封不动地折进训练数据里。生成的时候我会做几件事。第一准备一个足够大、分布合理的原始输入池可以来自历史日志、爬取的公开数据、人工构造输入等确保覆盖 easy/medium/hard 三类场景。第二用最终训好的提示词批量跑推理把输出存下来。第三做一轮清洗和过滤格式不合格的丢弃、去重用 embedding 相似度或者 simhash、与某些安全规则冲突的排除。这里有个小技巧生成数据的时候可以适当把 temperature 设在 0.3 左右让输出稳定中带一点多样性避免训出来的小模型只会一种“标准话术”。还有一个经验对每一条数据保留大模型输出的“中间推理”如果 prompt 里有 CoT 或两步推理的话这些推理文本可以作为小模型微调时的监督信号提升它的泛化能力。但也要清醒小模型是学不会大模型深层推理的。如果你生成的数据里有太多复杂的多步推理小模型训练时会很大概率“学歪”——要么输出表层套话要么干脆崩掉。所以我的建议是数据里尽量保持“输入-输出”的映射关系清晰简单推理过程只作为辅助信号不要让小模型承担超出容量的任务。4.3 微调小模型的方法数据准备完后面就是微调阶段了。目前最主流也最容易上手的是用 LoRA / QLoRA 做参数高效微调基座模型一般选开源 7B 级别的也有场景适合 1B-3B 的小模型。选择基座时要考虑三件事一是模型对中文的支持程度二是模型的指令跟随能力三是你本地的推理部署资源。训练数据格式上要跟基座模型的对话模板保持一致。如果是 Chat 类模型就把数据组织成 system user assistant 的形式system 里放你要部署后的 prompt 指令user 里放业务输入assistant 里放大模型生成的输出。训练时把 system 和 user 部分 mask 掉只学习 assistant 部分的生成。我自己常用的工具是 LLaMA-Factory 这一套或者直接写 HuggingFace Trainer 脚本。数据量不需要特别夸张一般 5000-20000 条高质量样本就够跑一个很不错的领域小模型了。训练参数上LoRA rank 我习惯用 16 或 32学习率 2e-4 到 5e-4epoch 数 2-3 轮同时保留一个验证集看全局 loss 防止过拟合。微调完之后部署方面用 vLLM 做生产服务非常方便openai 兼容的接口可以直接复用你之前调大模型 API 的代码。如果只是本机体验Ollama 也是不错的选择把微调后的 LoRA 合并导出成 GGUF 格式丢进去就能跑。4.4 小模型的验证与迭代闭环小模型训练完千万不要只看训练 loss 就完事必须拿它跑同一套测试集。在我那个客服对话任务里7B 模型微调之后 parse_rate 是 96%语义准确率 81%比大模型低几个点但在可接受范围以内。最让我惊喜的是hard 分层的准确率提升到了 58%——虽然还是比大模型的 65% 低但对于一个推理成本几乎为 0 的模型来说这笔账太划算了。如果小模型评测分数不达标怎么办我的建议不是立刻去调整模型训练参数而是回到数据源头把测试集里小模型做错的样本找出来扔回“大模型 提示词”的组合里去生成更多类似样本补进训练集再训一轮。这是一个典型的闭环测试集发现问题 → 大模型生产数据 → 微调补强小模型 → 再评测。我靠着这个闭环把那个 7B 模型的准确率从 76% 一路提回了 81%。5. 常见问题与排查实录5.1 测试集评测过关但线上效果崩盘这是最让人崩溃的问题之一。出现这种情况九成原因是测试集和线上真实分布不一致。比如测试集里数据都是清洗过的整齐文本但线上来的用户输入夹杂一堆表情、错字、无意义填充词模型自然就懵了。排查方法把线上近期真实数据抽 100 条回来人工打标后临时加入测试集跑一版评测看分数掉多少。如果掉得多说明是 distribution shift 问题接下来要做的是往测试集里持续补充线上日志样本而不是死磕 prompt。5.2 提示词越写越长效果反而下降很多人调 prompt 有个惯性一看到 badcase 就加一句“千万不要……”结果 prompt 越来越长上下文被堆满模型反而无所适从。我见过有人把提示词写到 3000 字效果比 500 字的版本差了十万八千里。一个比较科学的指标是“关键指令密度”——prompt 里有多少句是必不可少的规则而不是为了安慰自己加的废话。如果你的 prompt 里出现超过十个“不要”建议停下来重新写一版。尽量用正面指令去表达要求负面约束只保留最高频、损失最大的那几条比如“不要编造字段”“不要输出多余解释”。5.3 小模型和大模型的失败模式不一样沿用“大模型没问题的场景”小模型可能直接不按格式来或者对某些同义表达没有泛化。最典型的情况是训练数据里“物流”出现得多换了个说法叫“快递到哪了”小模型就不认识了但大模型根本没这个问题。解决方案有两个方向。一是数据增强把训练数据里关键表达做同义词替换/句式改写增加多样性。二是降低任务难度把复杂任务拆成更简单的子任务分别训练专门的小模型。后者虽然费一些工程功夫但往往更彻底。如果条件允许也可以考虑在 1-3B 的模型上做同样实验有的任务小模型效果并不差选一个性价比最高的容量点。5.4 评估判不准指标和真实体验互相打架这种情况经常出现在 LLM-as-a-judge 方案里。裁判模型有时候会偏好格式更好看的输出哪怕语义有一点偏差有时候会漏判某些微妙的错误。我的处理方式是给裁判模型设计一个非常严格的评测 prompt并要求它先引用证据再给分。如果还是不稳就退回人工抽检一部分样本用人工结果校准自动指标。5.5 测试集过拟合指标虚高这个前面提过一点。测 zzz 试集过拟合的典型症状是同一个测试集上指标不断涨但换一个新季度抽样的样本集指标掉得厉害。根本原因是测试集被“调教”得太久模型已经记住了这些样本的“正确答案模式”。对策是给测试集设定“保质期”每月或每季度主动替换一部分样本让测试集始终比模型“领先半步”。6. 实操总结与个人心得6.1 几个好用的工具和流程建议整套流程做下来我沉淀了一个固定动作清单先建测试集 manifest → 写一个批量评测脚本 → 跑 baseline → 错误分类 → 逐条优化 prompt → 全量回归 → 数据生成与清洗 → 微调小模型 → 部署并持续回流 badcase。每个环节都有对应的工具可以辅助不一定一步到位但可以从最简单的脚本开始慢慢搭建。评测脚本我现在一般用 Python 写流程是读 manifest、并行调模型、落盘结果、跑规则校验、再调裁判模型打分、最后输出一个结构化的报告。具体实现不复杂核心代码大概就一两百行但省下来的时间非常可观。如果你不想从零写promptfoo 这类开源工具也支持批量回归和对比关键是让你能快速重复评测而不是靠“手感”。6.2 微调数据里加一点“坏样本”效果可能更好这个经验可能跟很多人的直觉不一样。我第一次微调的时候只保留了大模型成功输出的数据结果小模型上线后遇到边缘情况很容易“膨胀”给一些明显不合理的答案。后来我尝试在训练集里故意保留少量失败样本把对应的正确输出改成“该情况无法判断请转人工”的兜底回答模型的鲁棒性反而提升了。原因是小模型需要学习“什么时候该承认不会”而不只是“拿到输入就开始编”。大模型天生有一点这种边界意识但小模型不教它是学不会的。这类“拒绝回答”的数据不用多几十条就够能显著减少线上幻觉问题。6.3 最后分享一点我的长期体会如果让我总结这条链路里最核心的一句话那就是让测试集成为唯一的裁判。没有测试集你调提示词是在猜有了测试集调提示词才变成了工程。优化好大模型之后小模型的微调就有了可靠的数据源整个流程像一条“知识流水线”每一环都有明确的输入、输出和质量标准。老实说这条链路不是一天能搭完的。我第一次完整跑通大概花了两三周其中有将近一半时间都耗在测试集整理和评测脚本调试上。但搭完之后后面每次迭代新业务需求都能复用这套基础设施效率提升是十倍级别。如果你现在正处于“调 prompt 全靠灵感、上线全靠运气”的阶段我的建议就是赶紧从第一个测试集建起别等完美才开始。