一行模型标识末尾多出日期后缀,旁边两份结构相同的输出只有字段取值不同,被放在核对表上逐项比对

一、名称没变但物换了:这是一次没有变更单的环境改动

某模型服务在 10 月 17 日宣布新版本更新,网页端、应用与接口上的模型标识同步切到了新模型;另据公开报道,同月有厂商的接口后台短暂出现过未经公告的内部代号模型。两条消息合在一起的工程读法只有一句:调用名保持不变的前提下,服务端实际返回的可能是另一个模型。这里引用的公开说法只用来说明现象存在,不是我们对效果作判断的依据,我们既没有对比过两版的好坏,也不打算在本文给优劣结论。

按服务治理的老口径,这属于典型的不可复现外部依赖。要治理它,第一步不是加评测集,而是让每一次调用都留下事后能归因的三处标识。

第一处是请求里我们自己填写的模型名,也就是当时以为在用的那一个;第二处是响应回显的实际构建标识,能拿到就原样落盘,拿不到就把响应头与返回体里所有可能承载版本的字段一并落下来;第三处是我们自己的缓存与日志里那层配对关系:哪个请求名对应哪个实际构建。三处缺任一处,事后就无法回答”这条线上输出到底是谁生成的”。

落库字段建议固定成五列,一次写入,不要在日志里以自由文本记录:requested_model、resolved_build、observed_at、prompt_hash、response_parse_ok。这五列是所有后续动作的地基:没有它们,下面的五步回归只能在事后补跑,而且补出来的结果无法和当时的线上状态对齐。

二、五步回归顺序与每步的通过判据

排序原则是便宜的先跑、能定位的先跑、不需要人工的先跑。下面这张表是我们内部的执行顺序,也是给客户做版本核对时的交付项。

步 回归项 测什么 通过判据 判据不过时先怀疑什么
一 结构与约束遵循 固定提示下的结构化输出:必填项齐全率、枚举取值合法性、长度与条数约束、结束标记是否完整、是否多写了一段解释文字 同一提示重复执行后,字段齐全率与约束满足率不低于既有基线,且不出现新的失败形态 服务端的包装层与解码设置,而不是模型能力
二 工具调用与函数签名 工具名与参数类型、必传项、多工具串行调用、工具返回失败后的续跑路径、非法参数是否被拒 出现的工具集合与参数清单和既有清单完全一致,无新增、无静默改名、无参数类型漂移 版本切换后工具描述的呈现方式与顺序变了
三 业务金标集抽样 按业务线、输入形态、历史返工类型分层抽样,人工按现行验收标准打分 抽样接受率不明显低于旧版基线,且退化能归到某一层 该层提示模板对格式假设过强
四 拒答与安全边界行为 边界提问的应答分档、拒答是否被写进结构化字段、是否新增过度承诺 分档结果与策略文档一致,无新增承诺 系统提示的优先级与注入位置
五 时延与单位消耗 首字时延、总时延分布形态、单请求计量字的分布 分布形态可解释,超出预算时能按业务线归因 服务端换掉的可能是同系列不同尺寸的模型

第一、二步完全不需要人工,可以挂在持续集成里每次标识变更自动跑;第三、四步需要人,成本高,所以要在前两步通过之后才启动;第五步只读日志,随时可看。

为什么把格式稳定性放在第一位:结构化输出是下游系统的接口契约,它的判定是二元的、可重复的、几乎不花钱的。线上真正引发故障的,多数不是”答案质量降了一档”,而是”少了一个字段””末尾多了一句客气话””本该是数组的返回成了字符串”。格式这一步跑干净,后面四步的解释空间会大幅收窄;反过来先跑金标集,你会把解析层的问题误读成能力问题,然后去做一件完全无关的提示词优化。

三、漂移监控的最小指标集

评测是主动打,监控是被动接。四个指标够用,缺一个就会有盲区。

  1. 输出被解析失败率:按小时滚动,按 requested_model 与 resolved_build 分组。它是最灵敏的一列,通常在版本切换后第一个窗口就跳。
  2. 关键字段缺失率:比解析失败更细,针对”能解析但字段为空或取值跑偏”的情况,取每个业务的核心字段各一到两个即可,别把所有字段都做成指标。
  3. 人工抽检接受率的变动:滞后指标,但它才对应”客户感知的质量”。抽检样本量要固定,否则曲线变化里混着抽样量变化。
  4. 缓存命中率的变动:模板与调用量都没动、命中率却掉了一个台阶,往往就是服务侧的实际构建换了,前缀复用不再成立。

告警口径上有三条纪律:分组维度必须是版本配对而不是全局,否则一条平缓的均线会盖住某条业务线的塌陷;判定用”分布形态改变”而不是”超过某个绝对值”,绝对阈值在不同业务之间不可复用;解析失败与请求错误不要放在同一个面板上,一个是契约问题,一个是可用性问题,处置人不同。

四、缓存键里必须带模型标识,重算要分批

三类缓存的作废规则不一样,混在一起清一次会把自己打穿。

提示词缓存(前缀复用那类)与模型构建强绑定,实际版本一变就必须作废。缓存键的最小构成是:实际构建标识、提示模板版本、解码参数、租户或调用方标识。只写请求名是不够的,因为请求名恰好是不变的那一个。

检索结果缓存要分两层看:召回得到的片段本身与生成模型无关,可以留;片段拼装进上下文时的截断策略、条数与顺序,和模型的格式假设耦合,这一层要随版本重算。把两层混作一个键,等于要么白白重算召回,要么带着旧的拼装假设去打新模型。

生成产物缓存(摘要、改写、抽取结果)按用途分:下游要靠结构消费的,随版本作废;只给人读、且已归档的,可以不重算,但记录里要写明产出时的实际构建,否则后续复核没有依据。

还有一处最容易漏:由某个模型产出的向量表示和已建好的索引,属于”模型产物”而不是”数据”。换模型时不能默认新旧向量在同一语义空间里可比,也不能默认维度一致。迁移顺序是先确认新模型的产物要不要重建索引,再决定读写切换点,中间要有一段时间两套索引并存。

重算的执行方式上,建议双写加分批预热:灰度期内两套键同时存在,按业务线分批把流量导到新键,禁止一次性清空全部缓存——那会把上游检索与推理链路同时打到峰值,故障表现会看起来像版本问题,实际上是缓存雪崩。

五、合同与 SLA 里要写的五条,以及灰度回退

版本静默替换之所以难处理,是因为责任链上没有纸。以下五条建议逐项落到书面材料里。

  1. 版本保留期:某一版本在被标注停用后还能被调用多久,锁定的版本是否有独立的保留承诺。
  2. 变更提前通知:提前多久、通过什么渠道、通知粒度是否到具体接口。只有控制台公告的渠道不能算。
  3. 能否锁定版本:锁定方式是什么、锁定期内出问题谁负责,以及锁定的代价是什么。
  4. 未公告版本的处理:如果线上出现未经公告的内部代号,服务方是否有义务告知与回滚。
  5. 可观测条款:响应里能否回显实际构建标识。这一条决定了上面所有指标的分组能不能自己做。

至于该锁版还是该跟着换,我们不给统一结论,因为答案取决于两个变量:该业务线单次错误的代价,以及代码对输出格式的耦合强度。格式耦合越强、错误代价越高的链路越适合锁版并单独排期升级;探索性、有人复核、错了可以重来一次的场景,跟着走反而是更省的做法。这两个变量要按业务线各填一次,不是全局一个答案。

灰度与回退要提前准备三件事:同一套评测在两版并行跑一段时间,两版都可用时才有对照基线;按业务线切流而不是按比例随机切,随机切会让一次退化分摊到所有线上,看不出源头;回退的判定阈值要写成”哪一步的哪个指标越过哪条线就切回”,而不是出故障之后临时决定。回退的可行性还有一个常被忽略的前提:你必须留着当时那版产生的样本与配对标识,否则回退之后无法证明”变好了”。

六、把这套动作固定下来

洹拓科技内部现在把版本相关的记录拆成两类结论:一类是”可版本归因”的(结构遵循、工具签名、时延与单位消耗,都能在版本配对上对齐),一类是”不可版本归因”的(一次金标跑分不能断定是版本引起的,除非两版并行跑过)。写结论时必须标出自己属于哪一类,这一条规则消灭了不少事后互相打脸的会议纪要。

给客户交付时,我们固定带三份东西:版本标识的落库字段清单、上面那张五步回归表的可执行脚本入口、以及缓存键规范与分批重算的灰度方案。缺任何一项,就在交付说明里写清缺的原因与补齐时间点。

这类事情最坏的应对方式,是把版本替换当成”服务方自己的事”。上游没有变更单,不等于下游不需要变更流程;把回归顺序、监控分组和缓存键规则提前写好,静默替换发生时你要做的就只是按下那个已经评审过的开关。