ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

大模型答错先别怪模型?数据-提示词-参数-模型四步定位法

大模型答错先别怪模型?数据-提示词-参数-模型四步定位法

"答案错了,先别怪模型。"这句话并不是给模型开脱,而是我这些年被现实教育出来的习惯。每次有同事气冲冲跑来跟我说"这个模型答错了""这模型太蠢了",我通常不会第一时间打开模型配置,而是先问三个问题:它到底看到了什么输入?你给它的指令说了什么?这次运行的参数是什么?原因很简单——大模型是一个概率系统,它输出的每一个字都建立在你喂给它的上下文之上。你以为它"应该知道",它其实只能根据输入去猜。这篇文章就专门聊聊,当模型给出一个错误答案时,该怎么一步步定位真正的病灶,并顺手把排查思路沉淀成一套能复用的方法。无论你是在调本地部署的开源模型,还是接商用API,亦或是在训练LightGBM回归模型,这套思路都适用:数据先行,提示词次之,参数第三,最后才轮到模型本身。

1. 那次“模型智障”时刻,问题其实出在我的配置上

1.1 一个被我冤枉的本地模型

先讲个我自己的真实案例。有一阵子我在本地部署了一个开源对话模型做合同关键字段抽取,显卡显存不大,只能跑4bit量化,模型参数在可接受范围内。任务很简单:从一段合同文本里提取签约日期。结果模型返回了一个明显不存在的日期,年份都对不上。

我当时的第一个念头就是:这模型不行,得换更大的。但我强迫自己先别急着换,去看了一眼实际发给模型的输入,发现了一个很尴尬的事实:合同原文被切片成了两块,签约日期恰好被切在上一块的末尾,而检索召回只返回了下一块。模型看到的文本里,根本没有任何日期信息。

这种情况下,大模型会默认"脑补"一个最像日期的内容填进去——这是它的默认行为,不是玄学。所以错的不是模型,是我的切片策略和检索策略。这件事之后我养成了一个习惯:每次模型答错,先看它到底"看到了什么",而不是先看它"是什么模型"。

1.2 先给错误答案做个“分诊”

很多人一遇到模型输出不对,就陷入"重新生成一次看看"的循环。但更高效的做法是先给错误答案做个分诊,根据不同症状决定优先排查的方向。我把自己常遇到的几类问题整理成了一个表:

错误答案的样子优先排查方向常见根因
内容看着合理,但关键事实不对上下文与检索文本被截断、切片切断关键信息、召回片段不完整
反复输出同一句话或模板话采样参数temperature过低、重复惩罚设置不当
输出格式乱七八糟输出约束没给明确的JSON Schema或格式示例
答非所问、回答过于简短提示词指令不明确、缺少角色设定和示例
同一问题多次结果差异大采样参数/提示词随机性太高、提示词对细节过于敏感

这个表不是标准答案,但它能帮你快速缩小排查范围。更重要的是一种心态:错误答案只是一个"症状",模型权重只是众多嫌疑人之中的一个,而且往往不是真正的凶手。接下来我按排查优先级逐一展开。

2. 排查第一站:数据与上下文,而不是模型权重

2.1 模型的输入比你想象的更“残缺”

大模型本质上是一个"输入决定输出"的函数。你给它什么文本,它就只能基于这个文本作答。所以在RAG(检索增强生成)场景里,我从来不先怀疑生成模型,而是先怀疑检索链路。

常见的坑有这么几个。第一是切块策略不对:文本切片太短,把一个完整的实体或关键数字切成两半;或者切片之间没有重叠,导致关键信息落在缝隙里。第二是embedding模型选择不合适,有些通用embedding模型在特定领域(比如法律合同、医疗文本)的语义召回能力明显偏弱。第三是相似度阈值设得过高,明明有相关片段却被过滤掉了,模型只能靠"记忆"胡编。

我个人的做法是:切片长度控制在256到512个字符左右,保留10%到20%的重叠区域,确保日期、合同编号、金额这类关键信息尽量完整地落在同一个切片里。选择embedding模型时,先用一小批领域内真实query做召回评测,看Top5命中率,别只看排行榜上的综合分数。

2.2 不只有大模型:传统模型同样适用“数据先行”

这个道理不限于大模型。我在调LightGBM回归模型的时候,也踩过类似的坑:验证集上误差突然变得离谱,第一反应总以为是模型参数没调好,但排查之后发现是训练集和验证集的某个特征分布不一致——训练集里某个字段的缺失值用了均值填充,验证集却用了0填充。还有一次是做LSTM序列预测,滑窗长度设置太短,导致模型根本没有足够的历史信息去预测下一个点,结果当然好不了。

所以"数据先行"是一条跨模型类型的铁律。不管你是用Transformer架构做文本生成,还是用滑动窗口滤波模型做信号平滑,只要输出和预期不符,先把输入数据的采集、清洗、切分流程完整过一遍。数据侧的bug,成本最低,影响却最大。

2.3 排查时的日志与留档习惯

既然要看模型"看到了什么",就必须把每次请求的输入完整记录下来。很多同学只在调试时打印一次prompt,上线后就什么都不留了,等到线上出问题再反推,根本无从下手。

我会在调用链路上加一层日志,记录四样东西:完整的系统提示词、用户输入的上下文片段、检索召回的具体内容、最终发给模型的参数(temperature、top_p、max_tokens等)。这样一旦出现"模型答错"的反馈,我可以直接复现当时的输入条件,而不是靠猜。这一层日志花费不多,但在排查时能省掉你几天的命。

3. 提示词是真正的“隐藏代码”:把命令写成需求文档

3.1 大多数人把提示词当聊天,但它是需求文档

我见过太多人把提示词写得像在跟朋友聊天:"帮我看看这个合同有没有问题""给我提取一下日期"。这种写法不是不行,但完全依赖模型自己去揣摩你的意图。大模型不是读心术大师,它只会严格按照字面指令执行。

更好的做法是把提示词当成需求文档来写,包含五个要素:角色、任务、约束、输出格式、示例。我常用的提取类任务模板长这样:

你是一名合同审核助手。请从下面的合同文本中提取字段并输出JSON: {"签约日期": "YYYY-MM-DD", "合同金额": 数字, "甲方": "..."} 要求: 1. 如果文本中没有明确信息,输出null,不要推测。 2. 金额只保留数字和币种,不要写"约"字。 3. 不要输出除了JSON之外的任何内容。 合同文本: {context}

这个模板看起来朴素,但它把模型的自由发挥空间压缩到了最小。你会发现,很多时候模型"答错"不是因为它蠢,而是因为你在需求文档里留了太多模糊地带。

3.2 正向指令优先,反面教材用示例堵住

提示词里有一个反直觉的规律:负面指令往往不如正面指令有效。你写"不要编造信息",模型可能还是编;但如果你写"如果文本中没有明确信息,输出null,不要推测",模型就更容易遵守。原因是模型在训练时见过太多"不要xx"后面跟着一堆例子的文本,它很难把"不要"后面的内容当成真正的约束。

更有效的做法是给一两个正确示例。但这里有个我踩过的深坑:few-shot示例本身必须是完全正确的、且和真实任务同分布的。我曾经在一组示例里放了一个错误的输出格式,模型在正式任务里"忠实地"复刻了这个错误格式,我排查了很久才意识到是示例害的。所以每次改提示词,先检查示例,别让坏例子成为隐藏的误导源。

3.3 拆分任务:让模型先找证据再下结论

很多复杂任务,直接让模型一步到位很容易错。比如"判断这份合同里甲方是否违约",模型可能直接给你一个理所当然的结论。更稳的做法是让模型先提取证据,再基于证据下结论。

我会在提示词里加一句:"先引用合同原文中与甲方义务相关的条款,再基于这些条款给出判断。"这相当于让模型先做一步信息定位,再做一步推理。实测下来,这种"先证据后结论"的方式,能明显降低多步推理类任务的错误率。它的原理也很好理解:把一个大任务拆成两个小任务,每一步的搜索空间都变小了,模型自然更容易走对。

4. 采样参数与幻觉边界:概率的游戏里没有100%

4.1 温度、top_p和max_tokens怎么背锅

很多人不知道,大模型的每次输出都是一次概率采样,而不是在数据库里查答案。它预测下一个词的概率分布,然后从中抽一个词。所以同一个问题问两次,结果可能不一样,这不是bug,是设计如此。

这里就牵扯到几个参数。temperature控制分布的"尖锐程度",越低越倾向于选概率最大的词,越高越随机;top_p控制候选词的范围,相当于只从累计概率前p的词汇里采样;max_tokens决定最多生成多长。我见过一次"错误答案",其实是max_tokens设得太小,模型在关键结论还没写完时就被截断了,看起来像答非所问。

所以排查的时候,先看一眼参数配置。表格化一下我的经验:

参数影响适用场景
temperature=0~0.3输出稳定、保守信息抽取、代码生成、结构化输出
temperature=0.7~1.0输出多样、有创意文案创作、头脑风暴
top_p配合使用控制候选范围与temperature二选一微调即可
max_tokens防止截断按任务最长输出预留空间

对于"我们只想要正确答案"的任务,我一般直接temperature设为0,并固定随机种子。先把随机性这个变量干掉,再去谈模型能力问题。

4.2 低显存与量化模型的“能力折扣”

如果你是在本地低显存环境跑模型,用了4bit或8bit量化,那有一个你必须接受的事实:量化会带来一定的推理能力损耗,尤其是在复杂推理和长文本理解上。这时候出现的"错误答案",本质上是你用算力换来的妥协,不是模型原本的能力上限。

我并不是说低显存不能干活,而是说要合理预期。小型量化模型适合做分类、抽取、关键词生成这类"窄而浅"的任务,不适合做复杂推理。如果你非要用8G显存跑一个7B量化模型去解决数学竞赛题,那出错是正常的,这不是模型的错,是任务和资源不匹配。这个时候要么换更强的API,要么把任务降维拆解成更适合小模型的形式。

4.3 幻觉不可怕,可怕的是系统设计没留“拒答”通道

大模型的幻觉是抹不掉的本性——它天生就会一本正经地胡说八道。所以任何一个正经的AI应用,都应该给模型留一条"拒答"通道:当上下文信息不足时,允许它输出"信息不足,无法判断",而不是强迫它硬挤出一个答案。

这一条特别重要。我在合同抽取任务里加上了"如果文本中没有明确信息,输出null,不要推测"之后,错误率下降非常明显。表面上看起来模型"答得少了",但真正有用的准确率反而上去了。你要的是一个可靠的系统,不是一个自信的骗子。与其事后跟模型较劲,不如在设计阶段就给不确定性留一个出口。

5. 怎么确认“这次真的得怪模型”:三组对照实验

5.1 实验一:换提示词,不换模型

如果错误答案出现了,第一个对照实验是:保持模型和数据不变,把提示词换成最清晰、最简单、甚至官方推荐的版本。如果换了提示词之后答案变对了,那说明问题出在提示词,而不是模型。

这个实验的成本最低,也因为太简单反而被很多人忽略。一个真实的案例是:团队反馈某个模型"不会做表格提取",我用一个角色更明确、加了JSON Schema的提示词跑了同一份表格,一次就对了。相同模型、相同数据、相同参数,唯一变量是提示词。这足以说明问题。

5.2 实验二:换强模型,不换输入

如果换提示词没用,再做第二个实验:保持输入不变,换一个明显更强的模型来跑——比如从本地量化模型换到商用API模型,或者从7B级别换到70B级别。用模型A和模型B跑同一份输入。

如果强模型能答对,说明输入本身没问题,是原模型的能力边界不够。如果强模型也答错,那就要重新审视任务的定义、数据的完整性,或者问题本身是否矛盾。我做这个实验时还有一个习惯:把强模型的输出也存下来,当作后续优化的小型标注参考。

5.3 实验三:重复测试,消除随机性

第三个实验是用来区分"随机错误"和"稳定错误"的。把temperature设为0,固定随机种子,同一个问题连续跑10次。如果10次结果不一致,说明你的应用对随机性太敏感,问题出在采样参数;如果10次结果一致地错,那才是模型或提示词层面的稳定问题。

我遇到过不少"时好时坏"的case,用这个实验一测就明白了:不是模型突然变笨了,而是应用层用了偏高的temperature,导致同样的输入有时对有时错。生产环境里,该稳定的时候就得稳定,创意和随机的需求另说。

5.4 一台“毛坯房”换一台“精装房”的成本账

做完三组实验,你基本能把责任划分清楚。但还有一笔账要算:就算真的是模型能力不足,你得评估"换模型"的代价。商用API能力强,但会带来持续的token成本,也就是"模型API费用"会肉眼可见地涨。本地开源模型便宜,但需要显存、电费、运维精力。

我的建议是:先用提示词工程和检索优化把现有模型的潜力榨干,再谈升级模型。大多数业务场景的问题根本轮不到换模型——换个说法就能解决的事,没必要花十倍的钱。升级模型是最后一张牌,不是你遇到第一个错误答案时就要打出去的牌。

6. 把一次错误变成长期资产:bad case 管理流程

6.1 留档、归类、回归测试三步走

普通用户遇到错误答案是吐槽,工程师遇到错误答案应该把它当成资产。我每一轮迭代都会维护一个bad case清单,流程就三步:留档、归类、回归。

留档是指把出错时的完整输入、上下文、参数、错误输出都存下来。归类是指给每个case打一个根因标签,比如"数据缺失""检索失败""提示词歧义""参数不当""能力不足"。回归是指定期把历史bad case重新跑一遍,确认修复没有引入新的问题。

这一步看似繁琐,但它能让你从"天天救火"变成"系统迭代"。每次改完提示词或检索策略,我不会只盯着一两个新case的效果,而是把过去一周积累的bad case全部重跑一遍,看整体准确率是升还是降。只有这样,你才知道你的修改是真正变好了,还是只是把问题从A挪到了B。

6.2 微调是最后手段,不是第一选项

很多团队一遇到模型答不好就想微调,这在我看来是本末倒置。微调成本高、周期长,而且很容易踩过拟合的坑。数据、提示词、检索、参数都优化到极限之后,如果还有一类稳定的bad case无法解决,那时候才轮到微调出场。

而且微调的目标也不是"让模型学会某一个case的正确答案",而是"让模型学会某一类问题的通用解决模式"。这意味着你的训练集至少要覆盖几十个同类型样本,并且要和验证集做好区分,否则模型只是把样本背了下来,遇到稍微变化一点的提问照样错。传统模型也是一样——LightGBM回归模型拟合不好时,先看特征工程和数据质量,而不是一上来就调树深度或者动正则项。

6.3 我的个人体会:别把单个case直接喂给训练集

最后分享一个我自己踩出来的原则:单个bad case,绝不直接丢进训练集。原因很简单,大模型和小模型都非常容易过拟合单一样本。你喂进去一个case,它可能确实学会了这个case的答案,但代价是损失了一部分泛化能力,而且这种损失很难提前察觉。

我现在的流程是:一个bad case先走规则修复和提示词修复,解决不了就攒着。攒到同一类型有足够多的样本,再合成一个小数据集,针对性地做增量训练或微调,然后立刻跑回归测试。这套流程走下来,模型不是一个case一个case地"补丁化",而是一类问题一类问题地"升级"。我也说不清这是不是最优解,但至少比"改一次错一个"的状态好太多了。如果你也在被各种离谱的模型答案折磨,不妨先试试换个心态:答案错了,先别怪模型。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进