1. 大模型为什么会“一本正经地胡说八道”
1.1 概率生成的AB面:聪明和胡话是同一个引擎
先说一个可能颠覆部分读者认知的事实:大模型的“聪明”和“胡说八道”不是两种模式,而是同一套机制的两面。现在主流的大模型,本质上是一个超大的条件概率系统。它做的事情,就是在给定上文的情况下,预测下一个词的概率分布,然后按概率采样选出最合适的词。
这个机制决定了模型说话的逻辑——它不是在“查询数据库”,不是在“回忆事实”,而是在“续写”。你问它“中国的首都是哪里”,它不是检索出“北京”这个事实条目,而是根据训练数据中学到的语言统计规律,一步步把“北京”这个词“续写”出来。这个过程中,模型对“正确答案”的依赖,其实是对“统计上合理的答案”的依赖。当训练数据里某个事实足够多、足够一致时,统计上的合理答案就是正确答案;但当数据稀疏、矛盾,或者问题偏离训练分布时,统计上的合理答案可能就是一个看着像样、实则虚构的东西——这就是所谓的“做梦”,业内叫幻觉(Hallucination)。
我见过很多刚接触大模型开发的人,会误以为幻觉是模型“笨”,或者“某个bug”。但实际上,幻觉是生成式模型的结构性副产品。你不可能通过“训练得更好一点”完全消除它,就像你不可能训练一个人类永远不会记错。真实世界的信息本来就存在噪声、矛盾和缺失,模型只是把它们学到的模式折射出来而已。理解这一点,是后面所有驾驭手段的前提。
1.2 幻觉的四个来源:数据、结构、对齐和采样
如果把幻觉按根源拆开看,基本可以分成四类:
第一类是训练数据本身的问题。数据里如果有错误信息、过时信息、互相矛盾的说法,模型就会“学歪”。比如某年的新闻数据里某个事件被误报,模型之后就一直“咬定”错误版本。这类幻觉在数据层面几乎无可避免,因为大模型的训练集是海量抓取的,没法逐条人工审核。
第二类是模型结构的固有局限。大模型的参数量是有限的,它的知识编码方式是“压缩”的。就像你用一个很小的抽屉塞进百科全书,必然有一些条目被折叠、模糊化。当模型从压缩表征中还原细节时,就会发生变形和错位——这会导致某些细节被张冠李戴。
第三类是对齐训练带来的偏差。为了让模型更有用、更“讨喜”,训练阶段会加入人类反馈强化学习(RLHF)。这个过程中模型学会了“即使不知道也要尽量回答”。你在产品里问它一个冷门问题,它宁可编一个流畅的答案,也不愿意说“我不知道”。这个倾向就是对齐训练直接塑造出来的。
第四类是采样过程的随机性。生成时温度参数(temperature)不为0,模型就会在概率分布中采样,留下一定的随机性。这个随机性如果赶上概率分布比较平坦——也就是多个候选词的概率相差不大——模型就可能随机选中一个偏离事实的方向。
实测下来,前两类幻觉最“硬”,靠提示词很难修正;后两类幻觉则是工程手段可以压制的重点。Harness Engineering的一大部分工作,就是在第四类和第三类上做文章,用成本和延迟换取可信度。
2. 所谓“驾驭工程”,到底在驾驭什么
2.1 Harness Engineering的概念边界:别和Prompt Engineering混为一谈
“Harness Engineering”(驾驭工程)这个词在中文社区里还没有特别统一的译法,但它描述的实践方向越来越清晰:它不是“问得更好”,而是“用工程手段把模型的不可控性约束在可控范围内”。
这个词里的“驾驭”很形象,就像驾驭一匹马——马本身有自己的脾气,有跑的冲动,你不能彻底把马变成机器,但你可以通过缰绳、马鞍、路径规划,让它的能量释放到你想要的方向。
对比一下几组概念,边界就清楚了。Prompt Engineering(提示工程)做的是“输入侧”的优化:怎么写指令、怎么给例子、怎么设计few-shot,目的是让模型更准确地理解需求。而Harness Engineering做的是“系统侧”的约束:在模型外面套一层工程框架,把模型的输出纳入校验、纠错、兜底、路由、分解的闭环里。你可以提示工程学得很好,但模型照样会“做梦”;而驾驭工程的目标,是让“做梦”不影响系统层面的正确性。
再对比微调(Fine-tuning)。微调是“改模型本身的权重”,投入大、周期长,而且训练完可能引入新的问题。驾驭工程是“不改模型,只改模型周围的世界”,相对轻量、迭代快,适合大多数应用团队。我个人的习惯是:模型能力不够或者特定领域风格差异太大时,才考虑微调;而不管是否微调,驾驭工程这一层框架永远不能省。
2.2 驾驭工程的六大支柱
把这几年在多个项目里沉淀的经验收拢一下,驾驭工程的完整体系大致包括六块:
结构化输出约束。这是最基础但也最容易被做崩的一层。让模型按照约定的JSON Schema、代码结构或固定模板输出,从机制上消除格式层面的“自由发挥”。具体实现上,工程上要么靠强提示词+示例,要么靠约束解码——直接在小模型套大模型的归一化层上做遮挡,只允许生成符合语法要求的token。后者的可靠性远高于前者。
信息检索增强(RAG)。把外部知识库挂在模型外面,让模型“开卷答题”。本质上不是让模型“记住”知识,而是让它在回答时参考给定的上下文片段。这样即使模型自身没有某个事实,它也能从检索到的内容里取用。RAG的工程核心在于检索质量、上下文拼接策略和引用追溯机制的结合。
工具调用与代码执行。把计算和精确逻辑从模型中剥离出去。需要算数就让模型写代码,让代码执行环境去算,结果直接拼回回答里;需要查天气就让模型发起API调用,用真实返回值来顶替编造。这一层把模型从“知道一切的说书人”降格为“调度员+发言人”,大幅度减少事实性幻觉的空间。
输出校验与纠错。对模型的输出做规则校验和事实核查。校验规则可以是行号引用是否真实存在、JSON是否可解析、数值是否落在合理范围;事实核查可以用交叉验证——拿另一份独立数据来对账。校验不过就重试、修正或拒绝回答。
置信度感知与路由。让模型在回答的同时给出置信度估计,或者通过多次采样检测一致性。低置信度的问题直接路由给人工,或者触发更保守的处理流程。这一步是对“模型不知道却硬答”这种对齐偏差的直接对冲。
会话管理与状态追踪。长对话中,模型经常会因为上下文混乱而“梦游”——比如把用户前面说过的信息记错,或者自己说过的话前后矛盾。驾驭工程要求系统层维护状态,把关键信息持久化、结构化,而不是让模型用自己的注意力去“记住”。
这六块互相配合,基本可以把“做梦”的影响从“产品级事故”压到“偶发瑕疵”。下面我会重点展开讲其中最常用的几条路线和实操细节。
3. 从“做梦”到“可控”:可落地的四步路线
3.1 第一层约束:输出格式必须“锁死”
我的习惯是,任何进入生产环境的大模型API调用,第一件事就是锁输出格式。如果你给模型一个完全开放的文本框让它随意发挥,那后面一切校验手段都是事后的、被动的。反过来,如果输出格式是结构化的,很多幻觉直接在格式层面就被拦截掉了。
具体操作上分三个台阶。第一个台阶是把JSON结构写死在提示词里,并且附上一个完整的示例。注意很多团队只贴了JSON结构的说明文字,没有贴示例输出,模型照样生成没法解析的垃圾。示例的价值不只是“展示格式”,而是在概率空间中给模型一个“锚点”,把生成路径往正确的格式分布上牵引。
第二个台阶是开启模型的JSON Mode或者约束解码。用开源模型做本地部署的同学,可以看一下推理框架是否支持GBNF语法约束或JSON Schema导出的正则约束——这类能力可以在解码阶段禁止模型生成不符合格式的token。举个具体例子:某个字段被定义为“数字”,约束解码时模型根本不可能在这个位置生成字母;某个字段被定义为“枚举值”,模型只能在给定的几个值里挑。这就相当于给模型戴了一个“模具”,它想胡来都没机会。
第三个台阶是在代码层做二次防御。哪怕模型已经约束输出,我还是会在调用后加一个JSON解析的try-catch,并且接一个“解析失败自动重试一次”的兜底。重试也没什么复杂的,就是把提示词不变原样再发一次,很多格式问题重试一次就好了。还有一个小技巧:在前端配合“流式解析”,边收token边校验结构完整性,有问题当场截断重来,体验上会好很多。
这三个台阶下来,格式层面的“做梦”基本绝迹。这套做法在几个项目上落地后,JSON解析失败率从早期的一两成降到了千分之几,而且耗时几乎没增加。
3.2 第二层约束:开卷比闭卷稳得多
“让模型做开卷考试”是压制事实性幻觉性价比最高的手段。这里说的开卷,不只是把资料塞进上下文,而是要把参考资料做成模型可以“引用”的结构化形式。
先看检索侧。我见过很多团队做RAG,检索回来的文本直接粗暴地拼进上下文,这其实效果很差。原因是:模型面对的是一大坨高密度的文字,它分不清哪些部分是回答当前问题需要的,哪些是附近段落里的干扰信息。正确的做法是——把资料切成语义块,块与块之间用显式的标记符隔开,并且为每个块分配一个可见的ID。然后在系统提示词里写一句:“引用资料时,请在回答中标注对应ID。”这样模型在生成时,会被引导到指定的块上取信息,而不是在整堆文本里盲目搜索。
再看上下文组织。我通常会把检索结果放在一个固定区域,标注“参考材料开始”和“参考材料结束”,并在提示词里写明:优先使用参考材料作答;参考材料未涵盖的内容,明确回答“不知道”。这两句话看着简单,但对幻觉的削弱作用非常明显——它把一个“必须给出答案”的指令,改成了“必须基于给定材料给出答案”。模型获得了一个“合法的拒绝通道”,就不会被前面说的RLHF“尽量回答”的偏向逼着编造了。
在拼接策略上还有两个细节值得说。第一,参考材料不要贪多。很多团队把Top-10甚至Top-20个片段全塞进去,结果相关的内容被淹没在噪声里,回答质量不升反降。我常用的做法是:从检索结果里取Top-3到Top-5个片段,同时按相似度从小到大排列,最相关的排在最前面。第二,片段长度要控制。理想情况下,单个片段不要超过模型上下文窗口的一个小尾数,比如窗口是8K时,每个片段控制在1K左右,宁可多切几个块,也不要每个块都又长又全。
这一套落下来,开放问答场景下的事实性错误能明显下降,而且有个很重要的副产物——引用可追溯。当回答标注了引用ID,给用户的体验会完全不同。他可以自己点开参考片段核对,就算模型说错了,用户也不会觉得是系统在瞎编,而会觉得是资料的问题。
3.3 第三层约束:把“想象”替换成“执行”
有一类幻觉特别容易让人忽略:模型做数学题、查时间、查地点这类“精确计算”任务时,表面看着很流畅,答案却完全不对。这种幻觉的本质是:模型用语言统计规律在“猜答案”,而不是在真正“计算”。应对思路也很直接——让模型不要猜,让它调用工具算。
拿数学题举例。你让模型口算“某年某月的第100个工作日是哪天”,它当然会一本正经地给出某个日期。但如果你提示词里写“先写出计算用的Python代码,在代码执行环境里算出结果,再基于执行结果回答”,模型把问题转成代码的能力本来就经过大量训练,而代码执行结果是精确的,这就把“猜测日期”变成了“执行确定性程序”。
工程上有两种接入方式。一种是函数调用模式:模型根据对话语义自行决定调用哪个函数,参数填好之后由系统去执行,执行结果回填给模型再组织最终回答。这个方案适合“模型知道自己需要工具”的场景,比如查天气、查汇率、做计算。另一种是代码解释器模式:模型生成一段代码,系统在沙箱里运行它,把stdout结果作为“标准答案”的一部分,模型在此基础上生成最终回复。这个方案适合“没有现成API,但问题可以转成代码执行”的场景。
我强烈建议在任何想接入外部数据的Agent项目里,尽早引入函数调用。举个例子,某个内部知识管理助手,早期只做RAG,遇到“A文档引用过哪些B文档”这类关系类问题经常胡编。后来给模型加了一个文档图谱查询的API,让它遇到这类问题先调接口,幻觉问题直接消失了——因为模型不再是“猜”引用关系,而是从真实数据里查。
当然,工具调用也有自己的坑。需要校验模型填的参数是否在合法范围内;需要给工具调用设置超时和异常分支;还需要让模型具备“工具调用失败时向用户如实说明”的能力。但总体而言,把推理交给代码、把数据交给接口之后,模型真正需要“发挥创造力”的空间只剩组织和表达——这两件事恰恰是它的强项。
3.4 第四层约束:回答之前,先思考三次
严格来说,第四层不是一个独立的工具,而是一套“行为策略”。核心是把“生成前、生成后、交付前”三个环节都加上主动的质量控制,而不是等结果出来了再去救火。
生成前做“任务分解”和“方案判定”。碰到复杂问题,先让模型输出一个简短的思路规划:这个问题需要哪些信息?哪些信息我可以直接给出?哪些我需要查资料?哪些需要调用工具?这个思维链不一定全部展示给用户,但要让它自己走一遍。经验是,强制模型“先想后答”能显著降低跳过关键推理步骤的幻觉,因为链式推理过程中,每一步都受上一步的约束,统计上的随机性被链条的依赖性压住了。
生成后做“自我核查”。让模型把刚才的回答重新读一遍,对照参考材料/工具结果逐条检查正确性,指出不确定的内容。这个做法看起来绕——多一次调用就多花一次钱——但对高价值场景非常值得。我习惯把这层自我核查做成“判断题”而不是“问答题”:给模型列出原回答的几组核心陈述,让它一对一判定“支持/不支持/无法判定”。判定逻辑更简单,模型说“哪些不确定”的能力比重建正确回答的能力要强得多。
交付前做“硬校验”。这一层就是纯规则了。校验引用ID是否都在参考材料里、校验JSON结构是否合法、校验数值是否在业务合理区间内、校验敏感词是否被拦截。规则引擎写死,不走模型判断。说实话,就算前三层全部失效,这一层兜住了,用户看到的也不会是“幻觉事故”,最多是“系统提示某条信息有待核实”而已。
四层约束全部落地的体验是:模型还是那个会做梦的模型,但在它周围织了一张网,梦里出错的东西碰不到用户。这正是Harness Engineering最核心的心法——不追求模型不犯错,追求系统不犯错。
4. 工具选型与参数调优实录
4.1 主流大模型的服务化选型对比
先说一句很多人不爱听的:不存在“零幻觉”的大模型,只有“幻觉分布特征不同”的模型。选型时应该关注的不是谁更“准确”,而是谁的错误模式更适合你的驾驭框架来兜底。
如果走云端API路线,过去我用得比较多的是两类,一类是带原生结构化输出能力的,另一类是带函数调用协议的。带原生JSON Schema支持的模型,在约束解码层面做得比较省心——你不需要自己在推理框架上做正则约束,直接在API参数里声明输出结构就行。函数调用方面,模型能否稳定输出正确的函数名和参数,直接决定了工具层方案搭建的难度。
如果走本地部署路线,目前在开源模型里比较热门的是两类:一类偏综合能力,指令跟随和推理都还行,适合做对话场景;一类偏代码/逻辑,工具调用尤其见长,适合Agent场景。本地部署时还要重点关注推理框架对结构化输出的支持程度,有些老牌框架支持GBNF或者JSON Schema,部署起来如虎添翼;不支持的话,你可能要在采样层自己写约束解码,那工程量就不是一两天的事了。
我给一个具体的选型看法:新手团队如果短期要上线,直接选云API加官方结构化输出,不要自己做太多底层约束;有一定工程能力、数据敏感、需要私有化部署的团队,再考虑开源模型加自定义约束解码。前者的优势是省心、迭代快,后者的优势是可控、成本可押注,但坑也多,后面小节细说。
4.2 关键参数:温度、Top-P与重复惩罚的联动调整
模型推理参数看着不起眼,但实际对幻觉的影响比很多提示词都大。这里把主要参数讲透,方便大家直接抄作业。
温度(temperature):控制采样的随机程度。温度越高,越可能选概率低的词,模型越“天马行空”;温度越低,越可能选概率最高的词,输出越保守。不少团队为压制幻觉直接“温度=0”,这个做法有点用力过猛——会让模型几乎没有多样性,稍微复杂一点的任务反而更容易陷入重复或卡壳。我的经验是:事实性问答、信息抽取这类场景,温度开到0.1~0.3;创意写作、头脑风暴这类场景,再放开到0.7~0.9。如果模型版本较新,对“温度=0”的支持较好,可以试但务必做回归测试。
Top-P(核采样):控制采样时只看概率累计到P的候选词集合。Top-P越小,候选池越窄,输出越稳定。实际调参时,我建议温度优先,Top-P其次。两者不要同时“拉满”,比如温度设为0.2,Top-P设为0.9,效果通常已经够稳。
重复惩罚(frequency penalty / presence penalty):惩罚重复token出现的概率。这个参数对压制“车轱辘话”有用,但别开大。开太大模型会刻意选择非常规词汇,反而更容易跳出事实边界。我的做法是,在需要长文本输出时设置轻微的重复惩罚,在事实问答里直接关掉。
最大生成长度(max_tokens):这算是被忽略的反幻觉利器。让模型一次性回答太长的内容,后半段极容易出现事实漂移和废话。合理的做法是:把大任务拆成多轮子任务,每轮限制在较短的生成范围内。比如一篇800字的产品分析,不要让它一口气生成,而是先让它列大纲,再分段生成,每段限制150~200字。这比一次性长输出稳定得多——原理也不复杂,生成链越短,累积误差越小。
4.3 RAG和工具调用的超参设置
RAG超参方面,最值得我们调的是检索数量(Top-K)和相似度阈值(Score Threshold)。Top-K控制在3~5之间,够用且不会引入太多噪声。相似度阈值用来砍掉“不高不低”的干扰片段,低于阈值的宁可放弃,也不要硬塞进上下文——模型看到一堆低相关片段,大概率会强行把它们当作论据使用,这本身就是幻觉的一个来源。
检索重排(Rerank)也值得多说一句。纯向量相似度检索在语义理解上还是不够,等召回Top-50之后再过一个重排模型,才取Top-3到Top-5。这个二次排序的成本很低,但对回答质量的提升非常显著。我测试过几组数据,加了重排之后,引用正确率大概能再涨一到两个档次。
工具调用超参方面,核心是超时和重试次数。给外部API一个明确超时(比如5秒),超时了直接让模型回复“当前暂时无法获取该信息”,而不是让它兜底猜一个答案。重试次数不要超过一次——第二次还超时,大概率是服务端不稳定,再多试只是浪费资源。
4.4 一个小型场景的完整参数配置参考
为了让大家有个具体参照,这里放一个之前做过的“文档问答机器人”侧写。场景是某公司的内部制度库问答,要求回答基于制度原文,事实不可编造。
- 模型参数:温度0.2,Top-P 0.9,不开启重复惩罚,输出长度上限500。
- 检索参数:向量检索召回Top-50,重排后取Top-4,相似度阈值0.6。
- 上下文拼接:每个片段控制在800~1000字,片段前加标签,系统提示词里写明“引用片段时标注片段ID”。
- 结构化输出:JSON Schema声明字段(结论、依据、引用ID列表、置信度),开启约束解码。
- 兜底策略:置信度低于0.7时,回答改为“我无法从给定制度原文中找到明确依据,建议咨询人工”。
- 校验规则:校验引用ID是否存在于本次检索结果中,不存在则触发重新生成,最多一次。
这个配置运行下来非常稳定,用户的整体满意度也很高。配置本身不算有多高深,但每个环节都在让模型的自由度收窄、可信度上升,组合起来效果才会体现出来。
5. 实战中的常见问题与排查手记
5.1 JSON格式反复崩溃:别只怪模型
JSON格式问题在项目上线初期最磨人。现象是:模型明明已经被要求“只输出JSON”,却偶尔在JSON里插入解释性语句、遗漏字段或用单引号代替双引号。
我的排查顺序是这样的:第一,确认你的提示词里是否给了“完整且唯一”的JSON示例——给了之后再看模型版本是否是较新的版本;第二,检查输出参数里是否有max_tokens限制得过紧的问题——截断是JSON解析失败的头号隐藏元凶,回答到一半被截断了,后面半个括号没了;第三,确认你是否开了结构化输出/约束解码——如果开了还崩,那大概率是你声明的Schema和实际提示词里的字段不一致,模型两边不知道听谁的。
如果排查完还是间歇性崩,我建议直接在代码里加“解析失败自动重试”的兜底。重试时的提示词可以加一句“你上一次的输出不符合JSON格式,请修复后只返回合法的JSON”,实测下来成功率很高,因为模型能意识到自己刚才做错了。
5.2 引用标注张冠李戴:RAG的经典翻车场景
RAG场景里最气人的问题:模型回答看着有理有据,但你点开引用ID一看,那个片段根本不涉及这个信息。这是典型的“引用幻觉”——模型知道你要它标注引用ID,但它不确定哪条片段支撑了结论,于是随便选了一个比较顺眼的标上去。
针对这个问题,我的经验是“片段要更小、更明确”。检索回来的片段如果是一大段复合内容,模型很难把结论定位到某一个具体块;反过来,如果你把原文切得足够细,每个片段只管一个点,模型才可能把“哪个结论对应哪个片段”一一对上。我建议把片段切到“单一主题块”,追求精确度而不是完整度——宁可检索回来10个小块,也不让它回来3个又大又全的块。
另一个对策是“引用ID校验通过后交给用户”,而不是完全信任模型。系统侧校验引用ID没有越界,是从根源上避免了“用户看到的是毫无关联的段落”。至于模型标注的ID是否是真正支撑结论的那一段,这个校验规则很难百分之百做到——那就退而求其次,让引用ID至少在“被用户点击验证”时不至于翻车。这个体验落差,比完全没有引用要大得多。
5.3 长对话越聊越“迷离”:状态管理的悖论
长对话场景下,模型越聊越迷糊的根源,几乎不在“推理能力”上,而在“上下文窗口被无关内容占满”上。如果你把整段历史全部塞给模型,它需要从一大坨对话里找到当前有效的状态信息——这对注意力机制来说负担很大。
我的处理方法是“状态外置”:把对话历史精简成“结构化记忆”,而不是原文堆叠。比如用户中途提过“我公司在杭州,人员规模约200人”,这个信息如果在后续对话中依然有效,就抽出来放到一个独立的“用户画像区”;等到回答需要参考时,再把这条记忆注回上下文。历史原文本身,只保留最近几轮。
这个方法落地后,长对话的话题漂移问题缓解得很明显。我建议任何对用户画像有依赖的应用场景,都考虑引入这种“外置记忆+关键信息注入”的模式。你不需要把每次对话都完整喂给模型,只需要让它知道最新的、最关键的上下文状态。
5.4 问题排查速查表
为了方便大家日常排查,我把上面这些常见问题整理成一张速查表:
| 现象 | 可能原因 | 优先排查项 | 兜底方案 |
|---|---|---|---|
| JSON频繁解析失败 | 输出被截断 / Schema与提示词不一致 | 检查max_tokens和Schema定义 | 自动重试一次,提示修复格式 |
| 引用ID与内容不匹配 | 检索片段过大过杂 | 缩小片段粒度,单点对应单结论 | 系统校验ID是否越界,越界则重生成 |
| 长对话中话题漂移 | 关键信息被历史杂讯淹没 | 状态外置,注入关键信息摘要 | 关键信息持久化,只回填最近摘要 |
| 事实性错误频发 | 检索质量差 / 阈值过低 | 调整Top-K和相似度阈值 | 增加重排后再取值 |
| 逼着回答的“硬答” | RLHF对齐导致模型不愿说不知道 | 在提示词中给出合法拒绝通道 | 置信度低时路由人工/模板回答 |
| 同一个问题答案飘 | 采样参数太高,随机性过大 | 降低温度到0.1~0.3 | 固定seed(如模型支持) |
5.5 一个很隐蔽的坑:系统提示词和模型权重打架
最后想单独提醒一个坑。很多人以为Harness Engineering就是把一堆约束堆在提示词里就完事了,但实际效果可能截然相反——因为模型在训练阶段学到了一些固定的行为模式,你如果在系统提示词里给出和它的训练偏好相矛盾的要求,模型会倾向于忽略你的提示,或者两边讨好、输出很别扭的内容。
举个例子:某些模型在训练时被重点强化了“保持对话友好、多给建议”的倾向。你要求它“只回答JSON,不要说别的”——它在服从指令和维持友好之间拉扯,偶尔会输出“好的,以下是JSON格式的回答”这种带解释的内容。这种问题不是提示词写得不好,是系统约束与模型权重发生了冲突。
面对这类问题,我的建议是不要试图靠提示词硬拧。优先换一个“指令遵循能力”更强的模型,或者把约束放在模型外部的解码和后处理环节,而不是放进提示词。Harness Engineering的本质,本来就是工程系统对模型行为的接管——你要是凡事都依赖提示词,那就还没走出Prompt Engineering的舒适区。
6. 驾驭工程的下一个台阶:失败规划与做人机边界
做了这么多约束、校验、兜底之后,真正让我觉得这套工程思路“成了”的,其实是一次思路上的反转:不再追求让模型少犯错,而是把失败当成一个预期内的路径,给系统设计好失败时的表现。
这个思路在Agent场景里特别关键。多步任务的执行链条中,模型不可避免会选错工具、猜错参数、编错中间结果。如果系统设计的目标是“每一步都要正确”,那这个目标本身就注定要落空。反过来,如果你把一个任务拆成多个子步骤,每个子步骤都接入校验,校验不过就回退到上一步重新规划,那整条链路的可靠性就被拉高了。链条上允许有单点失败,只要系统能感知到失败并能回退,最终结果仍然可控。
人机边界的划分也值得重新想。很多时候我们过度追求“全自动”,反而把系统搞得又贵又脆。更务实的做法是:让模型把不确定的分支明确标出来,把低置信度的决策交给人工;系统提供“自信模式”和“谨慎模式”,默认走谨慎模式,遇到简单任务再切自信模式。这样既保住了自动化率,也守住了业务底线。
我自己在实际项目中得到的一个经验是:驾驭工程做久了,你的关注点会从“模型说了什么”转向“模型什么地方不该相信”,再转向“系统如何优雅地承认不足、安全地降级”。这是一种和生成式AI打交道的成熟姿势——不要相信它,但也不要害怕它;给它套上缰绳,让它在前方跑,你在后面掌方向。
这套工程方法后续还可以继续扩展:比如引入更细粒度的置信度校准来做动态路由,比如用外部知识图谱做更严格的引用验证,比如在Agent场景里做“计划-执行-验证”的闭环。但无论怎么扩展,核心思路不会变:接受模型的“做梦机制”,然后用系统性工程把梦的边界画得清清楚楚。这才是真正跟大模型做同事的方式。