这几年做大模型工程化,我见过太多团队卡在同一个地方:Demo阶段跑得飞起,一到生产环境就天天救火。问题五花八门,但根子都指向同一件事——大模型本身的不确定性。同一个Prompt,上午回答和下午回答不一样;同一个问题,换一个微调版本结果直接翻车;同一个Agent流程,多绕一个工具调用就开始胡说。
这个标题“工程化收敛体系:把大模型的不确定性,转化为确定性交付”,其实讲的就是一套我这两年一直在打磨的方法论。它不是什么玄学框架,而是一套包括评测基线、运行时约束、Agent编排控制、数据回流在内的工程闭环。文章会把核心思路、关键步骤和踩过的坑都摊开来讲,适合正在把大模型应用从原型推向生产,或者已经在生产环境里被不确定性折腾得够呛的团队参考。
1. “不确定性”到底是什么:先别急着骂模型,把问题拆开看
大多数人一说到大模型不稳定,第一反应就是“模型不行,换一个”。但真到了工程层面,这个说法太笼统了。不确定性不是一个单一问题,而是从输入、模型到环境一整条链路里多个变量叠加的结果。不把它们拆清楚,后面所有收敛手段都是瞎使劲。
1.1 输入侧的不确定:用户的嘴从来不是说明书
用户在对话框里敲的那句话,和你的系统能处理的那句话,往往不是同一种语言。同一个需求“帮我查一下上周的订单”,用户可能说成“我上次买的东西啥时候发的货”“我要查快递”“上周那单走到哪了”,甚至一句话里同时包含查询、退换、投诉三个意图。
哪怕你把Prompt写得再完美,只要入口是自由文本,输入侧的方差就已经存在。更麻烦的是,很多系统还会把历史对话上下文拼接进去,上下文越长,模型就越容易在冗余信息里跑偏。采购一个意图识别模块,或者在入口做一次输入改写/意图归一化,往往比在后面堆一万条Prompt都管用。
1.2 模型侧的不确定:同样的Prompt,两次输出差在哪
模型侧的不确定性是最容易感知的,也是大家吐槽最多的地方。这里得区分两种不同性质的随机:第一种是采样策略带来的随机,也就是temperature、top_p这些采样参数导致的候选词概率波动;第二种是模型版本迭代带来的行为漂移,同一个Prompt在v1.0和v1.1上可能给出完全不同风格的答案。
很多人为了“降低随机性”,把temperature直接调到0。这个操作有效,但代价是牺牲多样性,遇到需要创造性回答的场景质量会明显下降。我个人的做法是:区分任务性质来设置采样参数。事实性问答、信息抽取、JSON填充这类任务用低温;文案生成、摘要改写、头脑风暴这类任务才用高温。后面聊运行时约束的时候还会展开讲。
1.3 环境侧的不确定:版本、上下文和工具链的连锁反应
还有一类不确定性藏在你不容易注意到的地方:依赖环境。同一个模型跑了两个版本,向量库里的数据更新了,检索回来的top-k文档变了,输出自然就变了。更隐蔽的是工具链的变化,比如你改了某个函数的部分逻辑,模型的工具调用结果不一样,最终回答跟着一起变。
这类问题有一个很形象的类比:大模型应用像一台精密仪器,任何一个螺丝钉微调,都会在末端输出上放大。所以环境侧的不确定性,本质上要靠“可复现”来解决——模型版本、Prompt版本、知识库版本、工具版本全部纳入版本管理,才能追溯哪些变化造成了行为漂移。
1.4 把不确定性分类:哪些要消除,哪些只能管理
接触了足够多项目之后,我得出了一个判断:不确定性不能全消除,也不需要全消除。真正要做的是分类处理。
可以消除的,比如输入文本里的格式混乱、上下文过长、工具返回的结构化数据不规整,这些用规则代码就能做好。需要管理的,比如模型输出的风格偏移、复杂推理链条的偶尔断裂,这些只能靠评测基线、兜底逻辑和人工抽检去控制。还有一个分类是必须理解并接受的,比如大模型对开放域问题的创造性回答天然有方差,你把这类场景强行标准化,反而会损失能力。
把不确定性的来源分类看清楚,才算读懂题。后面所有的手段,都是围绕这张分类表来设计的。
2. 建立质量基线:没有评测体系的收敛都是耍流氓
我见过太多团队做“效果调优”是这么干的:找三个同事,翻来覆去看几条case,觉得“感觉好多了”,就上线了。这种模式在小规模Demo阶段没什么问题,但一旦进入长期迭代,就是你所有痛苦的源头。因为你根本不知道哪一次改动让系统变得更好了还是更坏了,你只是“感觉”。
要收敛不确定性,第一步不是写更好的Prompt,而是建一套能回答“到底好不好”的评测体系。
2.1 评测集怎么建:不要用感觉评估大模型应用
评测集的质量直接决定了收敛工作的上限。网上很多人说评测集要“够大、够多样”,这个方向没错,但不落地。结合我的实操经验,评测集建设要抓住三个点。
第一,来源必须是真实请求。别自己编测试题,编出来的题覆盖不到真实用户的表达习惯。从线上日志里抽真实case,哪怕丑、哪怕乱,都比你编的精美例句有价值。
第二,必须分层维护。我建议至少分成三层:核心冒烟集,大概50条左右,覆盖最关键的功能路径,每次发布都跑一遍;回归测试集,300到1000条,覆盖各业务分支和常见边界场景,版本迭代和Prompt修改后跑;灰度评估集,从新产生的线上流量里持续抽样,用来发现增量问题。
第三,标注标准要具体到可以执行。光说“回答好”没意义。要给标注同学明确的分值定义。比如信息准确性,按事实级别打分:完全正确记2分,部分正确且无关键错误记1分,存在虚构或关键信息错误记0分。风格合规、步骤完整性、引用可验证性,也都要有类似的明文规则。
2.2 评测维度设计:准确率只是底线,稳定性和风格同样重要
很多团队建评测集只盯着“回答内容对不对”,这是一个常见的盲区。我吃过大亏:某个知识问答应用,准确率从85%拉到95%,但用户投诉反而变多了。后来一查,因为新版本的回答语气生硬、格式混乱,用户觉得“AI味太重”,信任度下降。
所以我把评测维度设计成四层,缺一不可:
| 维度 | 考察内容 | 典型问题示例 |
|---|---|---|
| 事实准确性 | 回答内容是否与知识库、真实数据一致 | “2024年销售额”表述是否与数据源完全一致 |
| 逻辑完整性 | 回答是否覆盖用户所有问题点,步骤是否闭环 | 查询订单后是否附带物流状态与预计时间 |
| 风格合规性 | 语气、格式、长度是否符合产品定位 | 专业咨询场景是否保持了克制与严谨的语气 |
| 稳定性 | 同一问题短期内多次回答是否一致 | 同样的退换货政策在不同对话轮次是否表述统一 |
单项维度都过线,才算“可发布”。这一步做完,你手里才第一次有了“基线的概念”,也才有了后续所有调优决策的依据。
2.3 从评测到回归:把不确定性变成可追踪的数字
评测集建好后,工作还没完。你要让评测跑起来,而且要跑得足够频繁,跑完的结果能自动汇总到你的迭代流程里。
我的建议是,评测尽量接入到CI流水线里。Prompt模板或模型版本的每一次变更,都触发一次评测任务,把结果反馈到群里。这时候你就能看到一些有意思的现象:某个Prompt改动把A类case准确率拉高了,但B类case掉分了。没有这套自动化回归,这种“此消彼长”的问题根本发现不了。
评测结果建议按维度、按领域分片统计,而不是只给一个总分。总分是会骗人的——它会把几个维度的优劣互相抵消。分片看,才能定位到具体哪个场景的哪类能力在退化。
2.4 我踩过的坑:评测集的污染问题
最后提醒一个大家很少谈但真实存在的坑:评测集污染。如果你的评测集固定不变跑三个月,模型和Prompt都可能过拟合到评测集上。回答模板、句式风格都越来越像评测集的“标准答案”,而真实用户的问题却越来越接不住。
我们的解决办法是定期做评测集更新:每周从新增的线上badcase中筛选题目替换掉旧题目,保持评测集的“新鲜度”。同时保留一份不对外公开、不参与常规调优的“盲测集”,每隔几周拿来突击测试一次,专门用来揪出过拟合。
没有评测体系之前,你调Prompt是开盲盒;有了评测体系之后,调优才变成可控的实验。
3. 运行时约束:在推理路径上给不确定性装上护栏
评测解决了“你怎么知道好不好”的问题,接下来的是另一个更棘手的问题:上线之后,模型在真实流量里还是会犯浑。你不能等它错了再补救(虽然补救也必须有),你得在推理路径的每个环节上给不确定性装上护栏。
所谓运行时约束,就是在模型生成前后,用工程手段把输出框在一个可控的范围里。这套思路和传统软件开发里的防御性编程异曲同工。
3.1 输入侧兜底:把自己能控的变量控死
你控制不了用户说什么,但你控制得了用户的话进入模型之前经历什么。
第一件事是输入清洗与改写。全角半角、大小写、错别字、口语缩写,这些噪音如果不处理,模型偶尔会被带偏。我们线上做了一层轻量清洗+意图改写,把口语化的表达转成更规范的任务描述,效果比直接怼原始文本好很多。
第二件事是上下文长度管理。上下文不是越长越好,越长注意力越分散。该截断的截断,该摘要的摘要,历史对话里和当前任务无关的语句直接丢。我们做过实验:把上下文从3000字压到800字,同一批测试case的准确率平均提升了约4个百分点,生成时间还缩了。
3.2 输出结构化:让模型学会“说人话”且“说标准话”
模型生成的自然语言,对你来说是没法直接用的——你要对接的往往是一个函数、一个数据库、一个API。让模型直接吐一段JSON给你,远比你事后去解析一整段自然语言要稳。
这里推荐的做法是:给模型明确的输出Schema,并开启受约束的JSON模式。比如你要模型从一段用户反馈里提取订单号和情绪,Prompt里直接给出:
请从用户的反馈文本中提取以下字段,并严格按照JSON格式返回: {"order_id": "字符串或null", "sentiment": "no_issue|critical|general_complaint", "category": "shipping|quality|refund|other"}
然后配合模型接口里的response_format参数(各家都有类似能力),让解码阶段强制走合法JSON路径。这样模型再怎么自由发挥,结构是锁死的,你后续的解析逻辑也完全不用容错。
但这里有个技术要点:JSON模式不能保证字段值一定正确,只能保证格式一定合法。模型可能把order_id提取成null,也可能把sentiment归错类。所以结构化输出之后,字段级的规则校验照做不误——再在层规则校验后面接兜底逻辑,几乎可以消灭格式错误问题,剩下的只是语义层面的误差。
3.3 模型路由与降级:别让一个模型承担所有风险
模型侧的不确定性里有一块经常被忽略:你只部署了一个模型,它就是这个系统唯一的“咽喉”。线上问题一旦出在模型上,你只能硬着头皮扛。
我的建议是给系统部署多级模型路由策略。基础判断是把确定性高的任务交给小模型或本地模型,把复杂任务交给大模型。成本是次要考虑,主要是风险隔离。
更实用的是设计确定性降级链路:当模型服务超时、返回异常、内容校验不通过时,不要直接报错给用户,先尝试切换到备选模型;如果备选模型还是不行,再落到模板化兜底。比如一个客服场景,模型突然不可用时,至少给用户返回“您好,系统正在升级中,请稍后重试或拨打人工客服”,而不是白屏或者一个500错误。
从工程化角度来说,模型路由层存在的意义就是让某个模型的行为异常不再等于整个系统不可用。
3.4 自校验与重试:一次生成不行,就给它二次机会
模型的一次性输出,即使是高概率采样,也可能出错。这怎么办?答案不是放弃,而是给模型一个“自我检查再修正”的机制。
最简单有效的是规则校验+重生成循环。比如你要求模型生成一段SQL查询,那就先把生成的SQL在测试环境跑一遍——语法对了没有、字段存在不存在、有没有缺WHERE条件。如果执行失败,把报错信息反馈给模型,让它自己改。加粗讲这个方法的精髓:把外部环境的反馈变成Prompt的一部分,模型是完全有能力完成自我修正的。
自校验还有一个增强玩法:用另一个更小、更快的模型当“评审员”,对主模型的输出做二次检查。主模型生成答案,评审模型负责找出逻辑瑕疵、未覆盖的用户意图点、潜在的事实风险,再把评审结果回灌给主模型做修正。这个模式在需要生成较长回答的场景下效果非常显著。
当然,重试也要设置上限。最怕的是模型在同一个坑里反复横跳。我的建议是重试两到三轮,再不行就走降级链路,别让用户等太久。这里顺便提一句,自校验不是万能的——如果模型本身缺乏特定领域的知识,让它自己再查十遍也查不出错,这时候要靠工具调用去外部验证,而不是闭门自检。
4. Agent编排里的不确定性:比单模型调用更棘手的连锁反应
如果说单模型调用的不确定性是“一颗不定时炸弹”,那Agent编排里的不确定性就是“一整片雷区”。每个节点都有不确定性,节点和节点之间的依赖还把这些不确定性串联、放大。这也是为什么很多团队单模型玩得溜,一到Agent就翻车。
4.1 Agent为什么会失控:工具调用链上的每一步都是放大器
Agent的本质是让模型自主决策:用哪个工具、传什么参数、下一步做什么。听起来优雅,但在工程上,这是把不确定性从“回答内容”扩展到了“行动路径”。
模型可能选错工具,可能把参数写错格式,可能一个简单的查询任务非要绕三个工具调用,可能在遇到错误输出之后陷入死循环。一个环节出错,后续所有环节都会连锁出错,比单次生成错误难排查得多。
而且Agent还有一层舆论干扰:非结构化工具结果混进上下文,模型被错误信息带跑,又基于错误信息做下一步决策。这种错误是“层层嵌套”的,你从最后结果根本看不出最初错在哪。
4.2 收敛Agent行为的方法:工具定义、边界提示词与超时熔断
Agent的不确定性不能靠“模型更聪明”来解决,要靠收敛自由度来解决。我整理了三个最核心的抓手。
第一个是工具定义的“窄而清晰”。工具描述别写太泛,每个工具就要做好一件事。拿订单查询举例,不要说“查询订单信息”,而要写清楚这个工具体现了什么样的查询场景:用什么参数查、返回什么结构、查不到时返回什么状态码。工具描述越具体,模型选错工具的概率越小。
第二个是给Agent加“行为宪法”。在System Prompt里写明边界:只允许使用给定的工具列表处理业务;当工具返回明确错误时,禁止自行脑补数据,必须如实告知用户;涉及金额、地址、个人信息等敏感字段的修改操作,必须有用户二次确认。这些边界条件用大白话写进系统Prompt,能让Agent的行为在一开始就被框住。
第三个也是最重要的:超时、最大步数和熔断机制。Agent绝对不能无限循环下去。设定最大工具调用步数(我常用6到8步),设定单步超时时间,再设定一个“总预算”:比如Agent在限定步数内没有拿到可验证的结果,直接终止并进入模板化兜底。这个逻辑很像金融系统的熔断器——评估不了风险的环节,宁可停下来,也不能继续往外走。
4.3 可观测性:看不见的Agent才是最危险的
单模型调用返个错你还能猜个七八分,Agent流程一旦出问题,你不加日志根本查不出来。我在早期做Agent的时候有过一次惨痛教训:线上一个“查请假余额”的机器人突然开始胡言乱语,排查了整整半天,最后发现模型在某个特殊输入下反复调用同一个“查日历”工具,把日历返回的数据当成请假数据填回去了。
从那之后,我把Agent的可观测性当成了硬性指标。每一步工具调用都要记录:调用了哪个工具、传入的参数是什么、工具返回了什么、模型基于这个结果做了什么判断。配套一个简单的tracing面板,把整条链路可视化出来。排查Agent问题,链路的完整日志比什么都重要。
4.4 一个实际的案例复盘:订单查询Agent的收敛过程
抽象讲一堆还不如来一个具体复盘。我们曾经做过一个订单查询Agent,最初版本上线后问题不断,典型表现是:用户问“退款到哪了”,Agent居然去调用“创建售后单”工具,直接给用户提交了退款申请。
收敛的过程分了四步。第一,给工具定义加上了明确的“读取类/写入类”标签,并规定只有用户明确表达办理意图时才允许调用写入类工具。第二,在System Prompt里增加了任务边界描述:“本助手为查询助手,不负责执行退款、改地址、取消订单等操作;如用户提出此类需求,请明确告知需要联系人工客服。”第三,把线上每一次该类问题都拉出来复跑,形成专项回归case,只有全部通过才允许上线。第四,写了一个轻量规则层:凡是检测到意图为查询但模型要调用修改类工具的,直接拦截。
这套组合拳打完之后,这个Agent的异常调用率从早期的约8%降到了0.5%以下。所以说,Agent不是不能做,而是你必须把它的自由度约束在你能接受的范围里,还要有能观测它行为的眼睛。
5. 数据飞轮与组织协作:让收敛成为一种持续能力
前面讲的方法,本质上都是在和“当前版本的不确定性”做斗争。但大模型应用迭代很快,模型版本、Prompt、知识库、工具定义都在变,昨天收敛好的行为,明天可能因为一个Prompt改动全部回到解放前。
要把“收敛”从一次性动作变成一种持续能力,就得靠数据飞轮和组织层面的协作机制。
5.1 线上真实case回流闭环
整个收敛体系的燃料,是真实线上数据。Badcase不回流,你的评测集就会过时,你的Prompt优化就没有方向,你所有的收敛手段都像是在打一场没有侦察的仗。
我推荐搭一个最简单的回流管道:线上badcase自动打标入库,能自动的环节不让人工碰。比如当用户对回答点了“踩”、当用户连续追问同一个问题超过三次、当规则校验模块拦下了模型的可疑输出,这些信号都会把case自动丢进待分析队列。
接着是定期人工分析和打标,标注出badcase的类型(知识错误、逻辑断裂、风格不当、工具误用等),把分析结果变成评测集的新增case,再推动一次Prompt或Agent配置的迭代。这就是一个最小的数据飞轮,跑起来之后,你会发现系统的表现是稳步上升的,而不是靠运气时好时坏。
5.2 提示词是代码:版本管理、评审、灰度
我见过很多团队的Prompt,散落在开发人员本地的txt文件里,没有任何版本管理。问一个人“你上一版Prompt是怎么写的”,对方只能翻聊天记录,这简直是灾难。
把Prompt当代码来管理,至少要落实三件事。第一,进Git。每个Prompt模板、System Prompt的每一次修改,都要有提交记录和变更说明。第二,做评审。Prompt改动不能一个人拍脑袋改完就直接上,至少要有另一名有经验的同事做一次代码评审,重点看边界条件是否完整、提示词里有没有自相矛盾的地方。第三,走灰度发布。Prompt不能全量直接切,先在5%到10%的流量上跑一跑,对比评测核心指标后,再慢慢放量到全量。这个灰度策略和发微服务是一样的逻辑,只是很多人忘了它同样适用于Prompt。
5.3 团队协作的分工与节奏
最后聊一聊组织结构。我见过不少项目,模型训练、Prompt调优、应用开发、内容运营各干各的,出了问题互相推锅,这完全是工程化收敛体系的“反模式”。
比较有效的分工是:产品/业务同学负责定义评测集的标注规则和基准答案;算法工程师负责模型选型和微调,以及评测集的更新维护;应用工程师负责运行时约束、工具设计和Agent编排;测试同学负责把badcase回流变成标准流程。关键是大家共用同一套评测基线,任何环节的改动都先跑一遍全量回归再谈优化。
节奏上,尽量保持小步快跑:每周一次评测集更新和badcase评审,每两周一次Prompt/Agent配置的灰度迭代,每个月一次对整体指标的复盘。把“收敛”变成例行公事,而不是某一次发布前的临时突击。
在我自己的实践中,这套工程化收敛体系的建立大概花了三个月,前一个月是在补评测基线和可观测性的功课,后两个月是靠数据飞轮一点点把线上关键指标从“可用”打磨到“稳定”。有几个经验想再强调一下:第一,评测基线一定优先于一切的Prompt优化,没有尺子之前别乱量;第二,运行时约束宁可多给模型加几道箍,也不要赌模型的自觉性;第三,最隐蔽的不确定性往往藏在Agent的工具链路里,日志和tracing不是可选项而是必需品。做工程化就是这样,把能消除的变量消除掉,把管不住的变量用流程框住,剩下的方差,再靠飞轮一点点碾平。