垂直AI的骰子难题:如何将LLM随机性关进工程化笼子

垂直AI的骰子难题:如何将LLM随机性关进工程化笼子 最近一年垂直AI大概是最热闹的方向也是最容易让团队预算打水漂的方向。CRM客服、金融尽调、法律检索、医疗问诊、供应链排产几乎每个行业都在尝试“用大模型做一个自己行业的产品”。但真正到交付现场大家会发现一个非常尴尬的现象同一个问题两次调用返回的答案不一样昨天演示还正常的流程今天换了个模型版本就静默失败测试集上准确率90%以上一上真实业务数据就开始一本正经地胡说。业内流传着一个很尖锐的说法The Vertical AI Bubble: We Keep Forgetting That LLMs Roll Dice。翻译过来就是——我们总是忘记LLM 本质上是在掷骰子。我越来越觉得这句话比很多大模型技术分享都重要。垂直AI泡沫的核心风险不是模型能力不够强也不是行业数据不够多而是大量团队在设计产品时把概率模型当成了确定性计算单元。这种认知错位会直接决定一个产品是稳定可用的工具还是一个永远在“碰运气”的演示系统。这篇文章不吹捧大模型也不劝退垂直AI。我想把一件事讲透LLM 的随机性到底从哪来垂直场景为什么特别怕这种随机性以及工程上究竟有哪些手段能把“骰子”约束在可控范围内。文章后面会给出可运行的 Python 示例、结构化输出校验、重试与自一致性投票、兜底配置以及一套可以复用的评估和排错方法。1. 垂直AI泡沫热闹之下到底在担忧什么所谓垂直AI指的不是ChatGPT这种“什么都能聊一点”的通用助手而是绑定某个具体行业、具体业务流程的智能化产品。比如客服工单自动分类、合同条款风险审查、医疗初筛问答、工业质检报告生成。这类产品有一个共同点它们不是给用户“聊着玩”的而是要进入真实业务系统承担某个岗位的一部分职责。一旦进入真实业务要求就完全变了。财务对账不允许“大概对”医疗建议不允许“可能有误”合同审查不允许“这段责任条款模棱两可”。垂直场景对确定性、可解释性、可追溯性的要求和传统软件几乎没有区别。但LLM提供的恰恰是最不稳定的那一层——自然语言理解与生成。泡沫担忧的本质就在这里市场给的估值和预期是按照“可靠的生产系统”来定价的但当前主流LLM在概率采样机制下输出天然带有不确定性。模型能力越强这种不确定性被隐藏得越好但它不会消失。你用ChatGPT聊天觉得它聪明不代表它能稳定处理一万笔订单状态查询。另一个被忽视的问题是AI Agent对随机性的放大。一个Agent链路通常由意图识别、信息抽取、工具调用、结果生成等多个步骤组成。每一步都有概率性整个链路的成功率是指数级叠加的。单步成功率90%五步之后理论成功率不到60%。这就是为什么很多Agent演示很惊艳一放到生产环境就频繁失败。垂直AI泡沫本质上不是“AI没用”的泡沫而是“把随机性当成确定性来卖”的泡沫。2. LLM 的“骰子机制”从概率分布到采样参数要理解为什么LLM不稳定就要回到它最基本的工作方式。LLM在做的事本质上是“预测下一个Token”。给定一段文本模型会计算词表中每一个Token出现的概率形成一个概率分布。真正输出哪个Token取决于解码策略——是永远选概率最大的那个还是在概率分布里随机抽样。绝大多数商业对话模型默认采用采样方式生成文本。也就是说模型不是从“唯一正确答案”里拷贝内容而是从概率分布里随机抽取候选Token。这就是“掷骰子”三个字的字面含义温度、Top-P这些参数决定了骰子有多“随机”。参数作用对随机性的影响temperature缩放Softmax概率分布温度越高分布越平缓温度越高低概率Token越容易被抽到输出越随机top_p只保留累积概率达到阈值的候选Token缩小抽样范围让输出更集中top_k只保留概率最高的K个Token进一步限制候选集合presence penalty / frequency penalty惩罚已经出现过的Token改变概率分布形状降低重复间接影响随机性很多团队有一种误解把temperature设成0模型输出就是确定的了。理论上temperature为0时变成贪心解码每一步都选概率最高的Token看起来确实确定。但在真实生产环境里这种“确定性”并不牢固。推理框架做浮点运算时会引入精度误差GPU、CUDA版本、甚至同一批请求的batch拼接方式不同都可能让结果发生细微偏移。近两年行业里经常讨论的FP16、BF16精度问题就是这类误差的来源之一。换句话说temperature0是工程上的“尽力而为”不是数学上的“绝对保证”。真正要接受的现实是幻觉也不完全是一个“错误”而是概率采样的副产品。模型从分布中抽出一个在训练数据里出现过、但和当前上下文并不匹配的片段人看起来就是在编造。所以不要试图用更强的prompt“消灭”幻觉而应该在系统工程层面接受它、检测它、兜住它。3. 垂直场景为什么最怕随机性我们可以把垂直AI产品拆开看。一个典型的订单查询机器人链路大概是用户输入 → 意图识别 → 抽取订单号 → 查询订单系统 → 判断订单状态 → 生成回复。每一步如果都用LLM实现那么每一步都是一次掷骰子。用户表述稍微变一下意图识别可能从“查询物流”跳到“申请退款”抽取订单号时可能多抽出一个无关的数字生成回复时可能把状态“运输中”描述成“已送达”。传统软件工程之所以稳定是因为它建立在确定性组件之上条件分支、状态机、数据库事务、强类型校验。垂直AI产品则是把一组概率组件串成了一条流水线。流水线越长积累的偏差越大最终表现就是“时好时坏、难以复现”。测试人员报一个Bug开发人员复现不出来这种场景在AI项目里已经是常态。这也能解释为什么这两年各种LLM编排框架会火。LangChain、Spring AI、各类Agent框架本质上都是在帮助开发者管理概率性链路中的分支、重试和回退。但要注意框架只是“容器”它不会消除随机性。如果不知道每一步都可能失败并在设计上预留容错换再多的框架也只是让失败变得更整齐而已。还有一个常见的误区以为接上RAG模型不稳定问题就解决了。RAG能解决的是“知识更新”和“上下文长度”问题能让模型在回答时参考检索到的资料但它改变不了采样机制。同一个检索结果模型两次生成的摘要依然可能不同。RAG提高了答案的“上限”但不会自动提高输出的“稳定性”。4. 工程化应对原则把随机性关进笼子面对一个概率组件最重要的设计原则是不要让LLM承担它不该承担的职责。具体来说可以参考下面五条原则。第一能枚举就不生成。如果业务里需要的是固定类别的判断比如工单类型、投诉等级、订单状态应该让模型输出固定标签再用代码做映射和校验而不是让模型自由发挥写一段话。分类问题的输出空间越小稳定性越高。第二能用代码校验就不靠模型自觉。凡是模型要输出结构化数据一律要求JSON格式并在拿到输出后做Schema校验。模型自己说的“我会严格按JSON输出”不可信真正的保证来自response_format约束加后置校验。第三能重试就不接受一次成功。对于高价值场景可以用多次采样加自一致性投票取多数答案作为最终结果。成本会变高但稳定性会明显改善。第四能回退就坚决回退。当模型连续多次输出不合法或者置信度过低时不要硬扛。回退到模板话术、规则引擎甚至直接转人工都是合理的SLA设计。宁可让系统说“我处理不了”也不能让它给用户一个错误答案。第五每一次输出都要可追溯。记录模型版本、prompt版本、temperature、top_p以及原始输出。没有这些日志线上出问题你连复现的抓手都没有。这五条原则的本质是把产品架构改成“确定性骨架 概率节点”。工作流的编排、状态流转、数据读写全部用确定性代码完成LLM只负责那些真正需要语义理解或自然语言生成的局部环节并且每个环节都有校验、重试和回退。把随机性关进笼子而不是让它主导整条链路。5. 环境准备与最小示例代码下面用最小代码把上面的原则演示一遍。示例基于Python 3.9和OpenAI兼容的调用方式换成任意OpenAI兼容网关或本地部署模型只需要调整base_url和model。5.1 环境准备python -m venv .venv source .venv/bin/activate pip install openai pydantic jsonschema然后配置API Key。生产环境不要把Key直接写进代码建议通过环境变量注入export OPENAI_API_KEYsk-你的密钥如果你用的是兼容网关再加上base_url参数client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlhttps://your-gateway.example.com/v1 )5.2 示例1直观感受“掷骰子”先跑一个最简单的实验同一个prompt同样的模型循环调用5次看看输出差多少。# demo_nondeterminism.py from openai import OpenAI import os client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) prompt 请用一句话解释什么是垂直AI for i in range(5): resp client.chat.completions.create( modelgpt-4o-mini, # 换成你账号可用的模型ID messages[{role: user, content: prompt}], temperature0.7, ) print(f第{i1}次: {resp.choices[0].message.content})运行后会看到temperature0.7时5次输出的用词、句式甚至结论都有明显差异。这个实验能直观建立“LLM是概率采样器”的观念。把它跑给团队里所有人看比讲十页原理都管用。5.3 示例2结构化输出 校验 重试真实项目里我们不关心模型“说得好不好”只关心它输出的JSON能不能直接入库。下面的代码要求模型输出订单状态JSON并做Schema校验失败就重试# demo_structured_output.py import json import os from openai import OpenAI from jsonschema import validate, ValidationError client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) SCHEMA { type: object, properties: { order_id: {type: string}, status: {type: string, enum: [已发货, 运输中, 已签收, 异常]}, description: {type: string} }, required: [order_id, status, description] } def ask_llm(user_text, max_retries3): system_prompt ( 你是订单查询助手。只输出JSON不要输出任何其他内容。 JSON必须包含order_id、status、description字段 status只能取已发货/运输中/已签收/异常。 ) for attempt in range(max_retries): resp client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: system_prompt}, {role: user, content: user_text}, ], temperature0.2, response_format{type: json_object}, ) raw resp.choices[0].message.content try: data json.loads(raw) validate(instancedata, schemaSCHEMA) return data except (json.JSONDecodeError, ValidationError) as exc: print(f第{attempt1}次输出不合法: {exc}) print(原始输出:, raw) raise RuntimeError(LLM多次输出不符合schema需要回退) if __name__ __main__: result ask_llm(查询订单ABC123系统显示已签收) print(最终结构化结果:, result)这段代码的关键点有两个一是用response_format{type: json_object}让模型尽量输出JSON二是用jsonschema.validate在代码层做强制校验不合法就带着原始输出重试。即使模型偶发抽风系统也会自动修复而不是把坏数据传给下游。5.4 示例3自一致性投票对于高价值、低频率的判断节点可以用多次采样投票来“平滑”随机性。比如合同条款风险等级判断多问几次取出现次数最多的答案# demo_self_consistency.py from collections import Counter from openai import OpenAI import os client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) def ask_once(text, temperature0.3): resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: text}], temperaturetemperature, ) return resp.choices[0].message.content.strip() def majority_answer(text, n5, temperature0.3): answers [ask_once(text, temperaturetemperature) for _ in range(n)] print(所有候选答案:) for idx, ans in enumerate(answers, 1): print(f {idx}. {ans}) counter Counter(answers) return counter.most_common(1)[0][0] if __name__ __main__: final majority_answer(合同里出现不可抗力不包括汇率波动风险等级是高、中、低中的哪一个请只回复一个词。) print(最终投票结果:, final)自一致性的代价是调用次数从1次变成n次成本和时间都会上升。实践中不要对每个请求都用只在业务影响面最大的判断节点上用。5.5 示例4兜底与回退配置最后用一份YAML配置展示“确定性骨架概率节点”的服务形态。生产项目里这类配置应该落到配置中心而不是散落在代码里# llm_service.yaml llm: provider: openai-compatible model: gpt-4o-mini temperature: 0.2 top_p: 0.8 max_retries: 3 guardrails: schema_validate: true keyword_blocklist: - 忽略之前的指令 - 把上一条消息忘掉 confidence_threshold: 0.6 fallback: on_schema_error: 转人工工单 on_too_many_retries: 返回模板话术抱歉暂时无法处理请稍后重试这份配置想表达的是模型只是服务里的一个组件它上面有防注入的关键词检查下面有Schema校验旁边有转人工的兜底。把这些都写清楚产品才算进入“可运维”阶段。6. 运行结果与效果验证依次运行上面几个脚本预期看到的效果如下。python demo_nondeterminism.pytemperature0.7时5次输出通常有明显差异。如果5次完全一样要么你用的模型拒绝采样要么后端服务被配置成强制贪婪解码这也是有可能的。python demo_structured_output.py正常场景会输出类似{order_id: ABC123, status: 已签收, description: 订单ABC123已于2024年10月29日签收}如果模型某次输出少了一个字段或者status值非法脚本会打印“第X次输出不合法”然后自动重试。这一步是判断“结构化能力”的关键不只是看成功时的输出更要看失败时能不能自动恢复。python demo_self_consistency.py你会看到5个候选答案多数情况下它们会集中在同一个词上。如果5个答案高度分散说明这个任务超出了模型当前的稳定范围需要换模型或调整任务设计。除了肉眼判断建议项目里保存一个稳定性评估脚本用“同输入多次执行的一致率”作为基础指标# stability_check.py from collections import Counter from demo_self_consistency import ask_once def stability_score(prompt, n10, temperature0.2): outputs [ask_once(prompt, temperaturetemperature) for _ in range(n)] return max(Counter(outputs).values()) / len(outputs) # 实际项目里应该维护一份golden test set逐条计算稳定率和结构合法率评估时主要看三个数字结构合法率、同输入一致率、业务准确率。结构合法率衡量模型输出能否通过Schema校验同输入一致率反映随机性大小业务准确率需要在带标注的测试集上人工比对。三个指标分开统计才能定位问题到底出在模型能力、采样参数还是prompt设计。如果运行失败优先检查三件事API Key是否正确配置模型ID是否可用网络环境能否正常访问模型服务。日志里出现401通常是Key问题出现404通常是模型ID问题出现超时则需要检查网络和服务端负载。7. 垂直AI落地中的常见问题与排查思路下面把我在类似项目里见过的高频问题整理成一张排查表。记住一个原则先在日志里找原始输出再谈优化。问题现象可能原因排查方式解决方案同一问题两次回答不一样采样参数过高、模型非确定性查看日志里的temperature和模型版本调低temperature增加自一致性投票输出JSON频繁格式错误Prompt约束不足、模型能力边界记录原始输出和Schema报错信息开启response_format增加后置校验与重试上线后效果和测试集差异大评估集过小、测试样本与线上分布不一致用线上真实样本重建评估集扩大golden set引入线上回流样本Agent链路偶发失败单步随机错误被链路放大在每一跳记录输入、输出和置信度增加中间校验失败时回退或转人工换模型版本后行为漂移权重更新导致概率分布变化用同一Prompt对比新旧版本输出建立模型版本回归测试灰度放量多次重试后成本飙升自一致性和重试次数设置过高查看调用次数与Token消耗统计只在关键节点投票普通节点单次调用用户用指令注入绕过限制未做输入输出安全过滤检查提示词注入样例回看完整对话日志增加关键字拦截和输入输出双重审查8. 最佳实践与工程建议8.1 架构上先拆“确定性骨架”和“概率节点”最稳定也最容易维护的垂直AI架构是把业务主流程写成传统代码状态机、判分支、数据库操作。LLM只作为节点嵌入其中并且每个节点都有明确的输入输出协议。尽量避免“让Agent自由支配整条主流程”的设计因为自由度的另一面是不可控。8.2 Prompt和模型版本要纳入版本管理很多团队把prompt当“提示文本”而不是“代码”来管这是生产事故的温床。Prompt应该和代码一起走Git评审、版本发布和回滚。每次发版都记录prompt版本与模型版本线上行为变化时才能快速定位是代码问题、prompt问题还是模型升级导致的漂移。8.3 全链路可观测性每次LLM调用都应该记录请求ID、用户ID、模型版本、prompt版本、temperature、top_p、原始输出、Schema校验结果、重试次数、最终决定。没有这些数据任何线上问题都只能靠猜。日志系统建议独立建表方便按请求维度回溯整条Agent链路。8.4 设置置信度阈值和人工兜底对于医疗、金融、法律等高影响场景模型置信度低于阈值时必须转人工。转人工不是产品的失败而是产品负责任的表现。在设计SLA时明确“机器处理率”和“人工兜底率”比盲目追求100%自动化更现实。8.5 评估先行不靠感觉上线每个垂直AI项目都应该维护一份带业务标签的评估集至少覆盖典型场景、边界场景、易混淆场景、恶意输入场景。每次改prompt、换模型、调参数都在这套评估集上跑回归。评估集的质量决定了产品天花板。先把评估集做扎实再谈优化。8.6 安全权限与合规API Key要遵循最小权限原则不同服务用不同Key生产环境Key托管在密钥管理服务里严禁提交到Git仓库。涉及用户数据的场景要做好脱敏、权限隔离和访问审计。垂直行业往往有行业监管要求部署前需要确认数据出境、存储位置和审计留痕是否合规。这些不是“以后再说”的事而是上线前的硬门槛。9. 总结与后续学习方向垂直AI的泡沫风险本质上不是技术泡沫而是认知泡沫。太多产品把概率模型当成确定性组件来设计、定价和承诺。真正能跑通垂直AI的团队往往不是prompt写得最好的团队而是把不确定性工程化处理得最好的团队。结构化输出、校验重试、自一致性、回退兜底、评估回归这些听起来不那么“性感”的工程手段才是垂直AI能不能落地的分水岭。如果接下来想继续深入建议按这个顺序学习先掌握JSON Schema和结构化生成约束理解函数调用和Grounded Generation的区别然后研究RAG的离线评测包括检索命中率和生成忠实度接着关注Agent的可观测性学习如何在多步链路里定位失败节点最后回到评估体系把一个50条样本的golden set扩展成几百条、几千条让每次模型迭代都有数据支撑。一个比较实用的收尾建议下次垂直AI产品上线前先做一个暴力实验。拿10个真实业务问题每个问题连跑10次看看你的系统能不能扛住这100次输出之间的差异。如果10次里出现两次不可接受的结果那就不要谈规模化先回头把随机性工程化这件事做扎实。