1. 从跑通Demo到扛住生产流量,中间隔着什么
很多团队第一次把大模型能力接进业务时,都会经历一个极其相似的阶段:某个下午,某位开发者用几十行代码调通了一个接口,模型返回的结果看起来像模像样,演示给相关方看的时候大家都很兴奋,觉得这件事成了。然后进入排期、准备上线,问题开始一个接一个冒出来——响应时间忽高忽低、同样的输入两次结果不一样、某些请求莫名其妙超时、成本账单比预期高出一大截、出了badcase却不知道从哪查起。
这个落差不是某个团队的问题,而是AI应用落地过程中普遍存在的结构性断层。传统软件的行为是确定的,输入A必然得到B,测试用例写好了就能回归验证。但AI应用的核心是一个概率系统,同样的输入可能得到语义相近但表述不同的输出,甚至偶尔给出完全偏离预期的结果。把这样一个系统放进生产环境,需要补的课远不止“把接口调通”那么简单。
这篇笔记想聊的就是这个补课过程——AI应用的工程化治理。所谓治理,不是写几份文档、开几次评审会,而是围绕一个概率型系统建立一整套可观测、可控制、可迭代的工程机制。它覆盖的范围包括但不限于:提示词怎么管、模型输出怎么校验、成本怎么控制、效果怎么评估、线上异常怎么定位、版本怎么回滚。适合正在把AI能力往生产环境推的工程团队参考,也适合刚接触这块、想提前了解坑在哪里的开发者。
我自己的体会是,Demo阶段拼的是“能不能跑通”,生产阶段拼的是“跑不通的时候你能不能快速知道、快速定位、快速恢复”。这两件事需要的能力模型完全不同。下面按我实际踩过的顺序,把几个核心环节拆开讲。
2. 提示词不是字符串,是要纳入版本管理的工程资产
2.1 把提示词散落在代码里,是第一个要改的习惯
最开始做AI应用的时候,提示词往往就是代码里的一个字符串常量,或者配置文件里的一段文本。改一改、测一测、上线,看起来没什么问题。但当业务迭代到一定阶段,你会发现提示词的数量和复杂度都在膨胀:不同场景用不同的提示词、同一个场景针对不同用户群体有变体、为了修复某个badcase临时加了一段约束、为了适配新模型版本又调了一版措辞。
这时候如果提示词还是散落在各个代码文件里,会出大问题。最典型的场景是:线上效果变差了,你想回滚到上一个版本,结果发现根本不知道上一个版本的提示词长什么样,因为它是跟着代码一起提交的,而代码可能已经合并了好几个其他改动。你没法单独把提示词回滚回去。
我的做法是把提示词从代码中彻底抽离出来,作为独立的资产来管理。具体来说,每个提示词有唯一的标识符,有版本号,有变更记录,有对应的负责人。存储上可以用配置中心、可以用数据库、也可以先用文件加版本控制,关键是它必须能独立于代码进行发布和回滚。
2.2 提示词版本管理的最小可用方案
不一定一上来就搞得很重,但有几个要素是必须的。下面这张表是我认为提示词管理的最小要素集:
| 要素 | 作用 | 缺失后的典型问题 |
|---|---|---|
| 唯一标识 | 定位具体是哪个提示词 | 出问题时不知道改哪个 |
| 版本号 | 区分不同迭代 | 无法回滚,无法对比 |
| 变更说明 | 记录改了什么、为什么改 | 过两周自己都忘了 |
| 关联评估结果 | 知道这个版本效果如何 | 凭感觉判断好坏 |
| 生效范围 | 控制灰度还是全量 | 一改全量,风险不可控 |
我见过比较务实的做法是:用一个简单的表来存提示词,字段包括标识、版本、内容、状态(草稿/灰度/全量/下线)、创建时间、备注。每次修改生成新版本,不覆盖旧版本。线上请求根据标识和当前生效版本去取。这样回滚就是改一个状态字段的事。
注意:提示词版本和代码版本要解耦。不要用代码的发布流程来管理提示词,否则每次改一句话都要走一次完整的代码发布,迭代速度会被拖死。
2.3 提示词变更必须过评估,不能凭感觉
这是很多团队容易忽略的一点。改提示词的成本太低了,低到让人产生一种“随便改改没关系”的错觉。但实际上,提示词的一个措辞变化,可能导致模型在某个子场景下的表现大幅波动。你修复了A场景的问题,可能同时把B场景搞坏了。
所以提示词变更应该有一个轻量的评估环节。不需要像模型训练那样搞大规模评测,但至少要有一个固定的评估集,覆盖核心场景和已知的边界情况。每次变更后跑一遍评估集,对比新旧版本的关键指标。指标可以是准确率、可以是人工评分、可以是某个业务维度的通过率,具体看场景。
评估集本身也是需要维护的资产。线上发现的badcase应该定期补充进去,让评估集越来越贴近真实分布。我一般建议评估集至少覆盖三类样本:典型正常样本、已知边界样本、历史badcase样本。三类样本的比例根据业务特点调整,但badcase样本一定要有,否则评估集就是自嗨。
3. 模型输出的不确定性,必须用工程手段兜住
3.1 为什么不能直接信任模型返回的结果
模型输出是概率性的,这意味着它可能返回格式不对的内容、可能返回语义偏离的内容、可能返回包含不该出现信息的内容。如果你直接把模型输出透传给下游系统或者展示给用户,出问题是迟早的事。
我遇到过最典型的情况是:要求模型返回JSON格式,大部分时候没问题,但偶尔会多一句解释性文字,导致下游解析直接报错。还有的情况是模型在某个边界输入下返回了空内容,下游没做空值处理,直接崩了。这些都不是模型“坏”,而是概率系统的正常表现,工程上必须兜住。
3.2 输出校验的分层策略
我的经验是把校验分成几层,从轻到重依次是:
第一层是格式校验。如果约定返回JSON,就严格解析JSON,解析失败直接判定为异常。这一层成本最低,能拦掉大部分低级问题。
第二层是结构校验。JSON解析成功了,但字段是否齐全、类型是否正确、枚举值是否在允许范围内,这些都要检查。比如约定返回的某个字段只能是三个固定值之一,那就必须校验。
第三层是业务规则校验。这一层跟具体场景相关,比如返回的内容长度是否在合理范围、是否包含敏感词、数值是否在业务允许区间内。这一层需要结合业务知识来设计规则。
第四层是语义校验。这一层最难,通常需要另一个模型或者人工来判断输出是否真的符合预期。成本高,一般只用在关键路径上,或者作为抽样检查。
实际落地时,前两层是必须的,第三层根据业务风险决定,第四层看投入产出比。校验不通过时的处理策略也要提前想好:是重试、是降级到兜底回复、还是直接报错让上游处理。不同策略对应不同的用户体验和系统复杂度。
3.3 重试不是万能药,要有退避和上限
模型输出不合格时,一个自然的想法是重试。但重试有几个坑要注意。首先是重试次数要有上限,不能无限重试,否则一个坏请求可能拖垮整个链路。其次是重试要有退避,连续快速重试往往得到同样不好的结果,稍微等一等再试成功率会高一些。最后是重试要考虑幂等性,如果这个请求已经产生了副作用,重试可能造成重复操作。
我的习惯是:格式和结构校验失败,可以重试一到两次;业务规则校验失败,看情况,有些适合重试有些不适合;语义校验失败,一般不自动重试,而是记录下来人工介入或者走兜底逻辑。重试的间隔用指数退避,比如第一次等200毫秒,第二次等500毫秒,避免给下游造成瞬时压力。
4. 成本失控往往不是模型贵,而是调用方式粗放
4.1 先把成本拆开看,才知道钱花在哪
很多团队第一次看到AI调用账单的时候会吓一跳,觉得怎么这么贵。但如果你把成本拆开看,会发现大部分钱花在了不该花的地方。成本的大头通常来自三个地方:输入token过多、输出token过多、无效调用过多。
输入token过多,常见原因是把一大堆无关的上下文塞进了提示词。比如做问答的时候,把整个知识库的相关文档全量拼进去,其实真正有用的可能就其中一两段。输出token过多,常见原因是没限制输出长度,模型洋洋洒洒写了一大段,但业务只需要一个简短结论。无效调用过多,常见原因是没做缓存,同样的请求反复调用,或者用户误操作触发了大量重复请求。
4.2 几个立竿见影的成本优化手段
第一个手段是缓存。对于相同或相似的输入,如果之前已经调用过并且结果可用,直接返回缓存结果。缓存可以按输入内容的哈希来做,也可以按语义相似度来做。语义缓存复杂一些,但命中率更高。我一般建议先做精确缓存,简单有效,能省掉相当一部分重复调用。
第二个手段是控制上下文长度。在拼装提示词的时候,对上下文做筛选和截断。筛选可以用关键词匹配、可以用向量检索、可以用规则,目的是只把最相关的内容放进去。截断要有策略,优先保留开头和结尾,中间部分按重要性取舍。
第三个手段是限制输出长度。在调用参数里设置最大输出token数,同时在提示词里明确要求简洁。很多模型对“请简洁回答”这类指令是敏感的,加上之后输出长度会明显下降。
第四个手段是分级调用。不是所有请求都需要用最强的模型。简单任务用小模型,复杂任务用大模型。可以先用小模型试,置信度不够再升级到大模型。这样整体成本会降不少。
| 优化手段 | 实施难度 | 预期节省 | 注意事项 |
|---|---|---|---|
| 精确缓存 | 低 | 中高 | 注意缓存失效策略 |
| 上下文筛选 | 中 | 高 | 筛选逻辑要评估 |
| 输出长度限制 | 低 | 中 | 别把有用信息截掉 |
| 分级调用 | 中高 | 高 | 需要置信度判断机制 |
4.3 成本监控要细化到场景和调用方
只知道总账单是不够的,必须能拆到具体场景、具体调用方、具体时间段。这样才能定位到是哪个功能在烧钱,是哪个用户或哪个上游系统在异常调用。我一般会记录每次调用的场景标识、输入输出token数、耗时、是否命中缓存,然后按维度聚合。有了这些数据,成本优化才有方向,而不是盲目地砍功能。
5. 效果评估不能靠感觉,要建立可复现的评测机制
5.1 没有评测集,所有优化都是盲人摸象
这是我最想强调的一点。AI应用的迭代如果缺少评测机制,就会退化成一种玄学:改了一版提示词,感觉好像好了一点,但又说不上哪里好;换了一个模型,好像快了一些,但效果是不是降了不确定。这种状态下,团队会陷入无休止的争论,谁也说服不了谁。
评测集的作用是提供一个相对客观的标尺。它不需要完美,但需要稳定、可复现、覆盖核心场景。每次变更后跑一遍,看指标是升是降,决策就有了依据。评测集的建设是一个持续过程,从零开始也没关系,先覆盖最重要的几个场景,然后逐步补充。
5.2 自动评测和人工评测怎么配合
自动评测适合大规模、高频次的回归验证。可以用规则匹配、可以用另一个模型来打分、可以用向量相似度。优点是快、便宜、可重复。缺点是对于语义层面的细微差别判断不准,容易漏掉一些只有人才能发现的问题。
人工评测适合小规模、高价值的深度验证。比如新版本上线前的验收、重大变更后的效果确认。优点是判断准确、能发现意外问题。缺点是慢、贵、主观性强。
我的做法是:日常迭代用自动评测做快速回归,发现指标异常再拉人工复核。重大版本上线前,自动评测加人工抽检双重确认。人工评测的样本要随机抽取,避免只挑容易判断的样本。
5.3 评测指标要跟业务目标对齐
技术指标好看不等于业务效果好。比如模型输出的BLEU分数很高,但用户就是不满意,这种情况并不少见。所以评测指标的设计要尽量贴近业务目标。如果业务目标是提升用户问题解决率,那评测就应该看问题是否被真正解决,而不是看输出和标准答案的相似度。
实际操作中,可以把业务指标拆解成可评测的子指标。比如问题解决率可以拆成:意图识别是否正确、关键信息是否完整、是否有误导性内容、用户是否需要追问。每个子指标设计对应的评测方法,综合起来就能比较全面地反映业务效果。
6. 线上出问题不可怕,可怕的是不知道从哪查起
6.1 AI应用的可观测性要补哪些课
传统应用的可观测性主要看日志、指标、链路追踪。AI应用同样需要这些,但还需要补充一些特有的维度。最核心的是要能还原每一次调用的完整上下文:输入是什么、提示词是哪个版本、模型是哪个版本、参数是什么、输出是什么、耗时多少、token消耗多少、走了哪条处理链路。
这些信息如果不在调用时就记录下来,事后是补不回来的。所以日志设计要在开发阶段就考虑好,不能等出了问题再临时加。我一般会把每次调用的关键信息结构化记录下来,存到可以快速检索的地方。出问题时,根据用户反馈的时间点或者请求标识,能快速定位到具体调用记录。
6.2 一个真实的排查链路长什么样
假设线上收到反馈:某个功能最近回答质量下降。排查链路大致是这样的:
第一步,确认问题范围。是个别用户还是普遍现象?是特定输入还是所有输入?是最近开始的还是一直存在?这一步通过日志聚合和用户反馈交叉验证。
第二步,定位时间点。如果确定是最近开始的,对比问题出现前后的调用记录,看有什么变化。常见的变化包括:提示词版本变了、模型版本变了、上游数据变了、流量特征变了。
第三步,复现问题。拿问题出现时的输入和当时的配置,在测试环境复现。如果能复现,就可以逐步排查是哪个环节导致的。如果不能复现,可能是环境差异或者数据差异,需要进一步对比。
第四步,验证修复。找到原因后,修复并验证。如果是提示词问题,回滚或修改提示词;如果是模型问题,切换版本或调整参数;如果是数据问题,修复数据链路。修复后要在评测集上验证,确认没有引入新的问题。
这个链路看起来简单,但实际排查时最耗时的是第一步和第二步,因为需要足够细粒度的日志和足够灵活的检索能力。如果日志记录不全或者检索很慢,排查就会变成大海捞针。
6.3 告警要设,但要设得聪明
AI应用的告警不能只设技术指标,比如响应时间、错误率。这些当然要设,但还不够。还需要设效果相关的告警,比如:输出校验失败率突增、缓存命中率骤降、某个场景的调用量异常波动、token消耗异常增长。这些告警能帮你更早发现效果层面的问题,而不是等用户投诉才知道。
告警阈值不要设得太敏感,否则会被大量误报淹没,最后大家都不看了。我一般会先观察一段时间的正常波动范围,然后阈值设在正常范围之外一点。告警触发后要有明确的处理流程,谁来看、看了之后做什么,这些要提前定好。
7. 版本迭代和回滚,要像呼吸一样自然
7.1 AI应用的版本维度比传统应用多
传统应用的版本主要是代码版本。AI应用除了代码版本,还有提示词版本、模型版本、参数配置版本、评估集版本。这些版本之间有关联关系,任何一个变了都可能影响最终效果。如果版本管理混乱,出了问题根本说不清楚是哪个版本导致的。
我的做法是给每次线上变更打一个组合版本号,记录这次变更涉及的代码版本、提示词版本、模型版本、参数配置。这样回滚的时候,回滚的是整个组合,而不是单独回滚某一个维度。组合版本号可以简单用时间戳加序号,也可以用更语义化的命名,关键是能唯一标识一次变更。
7.2 灰度发布是降低风险的关键手段
AI应用的效果波动比传统应用更难预测,所以灰度发布尤其重要。新版本先放小流量,观察核心指标没有异常再逐步放大。灰度期间要重点看效果指标,不能只看技术指标。技术指标正常但效果指标下降的情况很常见,如果只看技术指标就全量,很容易出事故。
灰度的粒度可以按用户、按流量比例、按场景。按场景灰度比较适合AI应用,因为不同场景的风险不一样,可以先在低风险场景验证,再推到高风险场景。灰度期间要有快速回滚的能力,发现问题能立即切回旧版本。
7.3 回滚不是失败,是正常的工程操作
有些团队对回滚有心理负担,觉得回滚意味着这次迭代失败了。这种心态会导致该回滚的时候犹豫,结果问题扩大。实际上回滚是工程成熟度的体现,说明你有能力控制风险。AI应用的效果受很多因素影响,有些问题在测试环境发现不了,只有线上才能暴露。回滚是正常的风险控制手段,不应该被污名化。
回滚要快,最好是一键操作。回滚后要保留现场,把出问题的版本和相关的调用记录保存下来,事后分析原因。分析清楚了再决定是修复后重新发布,还是放弃这个方向。
8. 团队协作里那些没人明说但很重要的规矩
8.1 提示词变更要有评审,哪怕很轻量
提示词是AI应用的核心逻辑之一,它的变更应该像代码变更一样被review。但很多团队没有这个习惯,谁都可以改提示词,改完直接上线。这会导致两个问题:一是变更质量参差不齐,二是出了问题找不到责任人。
轻量级的评审就可以,不需要很重的流程。比如在群里说一下改了什么、为什么改、评估结果如何,有一个人确认一下就行。关键是要有记录、有确认,而不是完全靠自觉。
8.2 评估集是公共资产,不是某个人的私货
评估集如果只掌握在一个人手里,其他人不知道里面有什么、不知道怎么用,那它就发挥不了应有的作用。评估集应该是团队公共资产,谁都可以跑、谁都可以补充样本、谁都可以看结果。当然,补充样本要有一定的审核,避免引入低质量样本影响评估准确性。
我见过比较好的做法是:评估集放在代码仓库里,跟代码一起版本管理。每次补充样本走正常的代码提交流程,有人review。跑评估的脚本也放在仓库里,任何人可以一键执行。这样评估就变成了团队共同维护的基础设施,而不是某个人的负担。
8.3 线上问题的复盘要聚焦机制,不聚焦个人
AI应用出线上问题很正常,复盘的时候要聚焦机制层面的改进,而不是追究个人责任。比如输出校验失败导致下游报错,复盘的重点应该是:为什么校验规则没覆盖这种情况、为什么告警没提前发现、为什么回滚不够快。而不是“谁写的代码出了问题”。
聚焦机制的好处是,每次问题都能转化为系统能力的提升。校验规则补上了、告警加上了、回滚流程优化了,下次同类问题就不会再造成同样大的影响。如果聚焦个人,大家会倾向于隐瞒问题,反而让系统更脆弱。
9. 我踩过的几个印象深刻的坑
第一个坑是提示词里写死了模型名称。当时觉得反正就用这个模型,写死省事。后来想换模型对比效果,发现要改的地方散落在各处,漏改了一处导致线上行为不一致。教训是:模型名称、版本、参数这些都应该作为配置项,跟提示词内容分开管理。
第二个坑是评估集长期不更新。上线初期建的评估集,用了大半年没动过。后来发现评估集里的样本跟线上真实分布已经差很远了,评估结果好看但线上效果不行。教训是:评估集要定期补充线上badcase,保持跟真实分布同步。
第三个坑是缓存没有失效机制。做了精确缓存之后,命中率很高,成本降了不少。但有一次更新了提示词,缓存里的旧结果还在返回,导致新提示词的效果被旧缓存掩盖了。教训是:缓存要跟版本关联,版本变了缓存要失效。
第四个坑是日志记录太粗。只记了请求时间和耗时,没记输入输出内容。出问题的时候完全不知道当时模型收到了什么、返回了什么,排查无从下手。教训是:关键调用要记录完整上下文,但要注意脱敏和存储成本。
第五个坑是灰度期间只看技术指标。有一次灰度新版本,响应时间和错误率都正常,就全量了。结果全量后用户反馈回答质量下降。回头看灰度期间的效果指标,其实已经有下降趋势,只是没关注。教训是:灰度期间效果指标和技术指标要一起看。
10. 这套治理思路的边界和适用性
需要说明的是,上面聊的这些不是放之四海而皆准的模板。不同团队、不同业务场景、不同规模,需要的治理力度是不一样的。一个内部工具类的AI应用,可能不需要那么严格的版本管理和灰度流程;一个面向海量用户的核心功能,治理就必须做扎实。
我的建议是:先识别你的AI应用属于哪种风险等级。如果出错的影响可控、用户可以容忍一定的波动,那治理可以轻一些,优先保证迭代速度。如果出错的影响很大、用户对质量很敏感,那治理就必须跟上,宁可慢一点也要稳。
另外,治理机制本身也是有成本的。版本管理、评估集维护、日志记录、灰度流程,这些都需要投入人力和时间。如果业务规模还没到那个程度,过度治理反而会拖累迭代。找到适合当前阶段的平衡点,比照搬一套完美方案更重要。
我自己的经验是:治理机制应该随着业务规模的增长逐步加码,而不是一开始就搞得很重。先解决最痛的问题,比如提示词版本管理、输出校验、基础日志,这些投入产出比最高。等业务量上来了,再补评估体系、灰度流程、成本优化。一步一步来,比一步到位更现实。