跳到主要内容

89 篇博文 含有标签「llm-ops」

查看所有标签

重跑反模式:为什么再次运行并不能发现 Bug

· 阅读需 11 分钟
Tian Pan
Software Engineer

当 AI 功能表现异常时,大多数工程师做的第一件事就是再次点击“运行(run)”。这种思路认为,模型是具有随机性的,所以这次运行可能只是运气不好。当第二次尝试产生看起来合理的结果时,工单就被关闭了。团队继续前进。而真正的 Bug——过期的工具响应、检索缺失、仅在包含特定 token 的输入时才触发的系统提示词冲突——仍然完好无损地留在生产环境中,等待下一个用户触发它。

这就是“重跑反模式(rerun antipattern)”,它是 AI 团队从聊天机器人时代继承下来的最昂贵的调试习惯。它看起来很严谨,因为模型确实是非确定性的。它看起来像是一种方差探测。但几乎没有人在重新运行之前写下假设,没有人预先决定多少次运行才算证据,也没有人考虑 token 的成本。正在发生的事情更接近于“老虎机式调试”:你不断拉动杠杆,直到红灯停止闪烁,然后你走开,并确信机器没问题。

快照评估衰减:当绿色的 CI 不再意味着你的产品仍然可用

· 阅读需 12 分钟
Tian Pan
Software Engineer

六个月的绿色 CI 掩盖了一个事实:大约 40% 的评估集(eval set)已不再代表用户在产品中的实际行为。测试套件仍在运行,裁判(judge)仍在打分,仪表盘依然闪烁着绿光。但这些案例是针对查询分布、语料库、工具界面和监管文本编写的,而这些内容早已发生了变化——现在的绿色运行意味着“昨天的产品在昨天的现实中依然有效”,而这并不是你付费让 CI 回答的问题。

这就是“快照评估退化”(snapshot eval decay),它是 AI 评估中最缓慢、最昂贵的失败模式。说它缓慢,是因为套件从未失败——陈旧性表现为无法区分模型优劣,而不是构建变红。说它昂贵,是因为当有人意识到评估通过的模型切换导致了生产环境回归时,团队已经建立了一年之久的“评估通过即发布”的肌肉记忆,而这一记忆是建立在一个早已悄然失效的资产之上的。

供应商 SLA 差距:为什么你的 LLM 提供商的运行时间忽略了导致产品崩溃的故障模式

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的 LLM 供应商声称 99.95% 的可用性。你的状态页显示绿色。你的延迟仪表盘在 SLO 范围内。但你的产品依然坏了 —— 助手在今天早晨开始拒绝常规请求,支撑下游解析器的 JSON 输出从紧凑变得啰嗦,而且你用模型分拣的支持工单中有三分之一返回了 “我无法提供帮助”。所有这些响应都在 800ms 内返回了 200 OK。它们都没有违反 SLA。这个 SLA 覆盖的是你实际上并没有遇到的故障模式。

这是采购谈判中没人预估到的差距。供应商出售的是 可用性(availability) —— 一种请求层面的承诺,即 API 及时响应了;而产品团队消费的是 能力(capability) —— 一种请求层面的承诺,即答案是可用的。这两者不是同一个指标,而混淆它们的团队离发现其中的区别只差一次静默的模型升级。

Agent 分支覆盖率:你的评测仅命中了 Happy Path,而非 Planner 的 If-Else 逻辑

· 阅读需 9 分钟
Tian Pan
Software Engineer

我上季度合作的一个团队针对其支持智能体(support agent)运行了一套包含 240 个案例的评估集。连续六个月,测试结果全线飘绿。接着,他们更换了规划器提示词(planner prompt)中的一个句子——仅仅是一次语气调整——结果第二天生产环境的人工接管请求激增了 3 倍。而评估分数却纹丝不动。人工接管分支仅仅是开始在以往能在线解决的临界案例上触发,而评估集中没有一个案例属于这种临界类型。该分支存在于提示词中,存在于生产环境中,唯独不存在于评估集中。

这就是我想命名的失败模式:智能体分支覆盖率 (agent branch coverage)。代码覆盖率工具在过去的 40 年里一直是调试的基础,但智能体系统具有运行时控制流——规划器分支会选择工具、限定回复条件、升级到人工、拒绝执行、或尝试不同的策略重试——而评估集仅触及团队想到的那些案例。规划器 80% 的决策分支从未在测试下执行,于是,一份“全绿”的评估报告就成了一场披着回归测试外衣的冒烟测试。

智能体内存驱逐:为什么 LRU 在模型升级中屹立不倒,而显著性评分却不行

· 阅读需 11 分钟
Tian Pan
Software Engineer

那些发布了带有显著性加权内存驱逐(salience-weighted memory eviction)功能智能体的团队,在不知不觉中,已经为每一次模型升级预订了一个内存迁移项目。驱逐策略表面上看起来是一个质量杠杆——选择最聪明的评分方法,获得最好的召回——但它实际上是一个隐秘的版本契约。当评分模型发生变化时,智能体实际上的“过去”也会随之改变。现有的围绕提示词和评估(evals)构建的工具链都无法捕捉到这一点,因为发生漂移的产物既不是提示词也不是评估,而是几个月前由一个已经不存在的模型做出的一系列关于“该忘记什么”的决定。

LRU(最近最少使用)和 LFU(最不经常使用)没有这个问题。它们是确定性的、与模型无关的,并且可以干净利落地在升级中幸存。但它们也会丢弃掉那些审慎的裁判模型会保留的信息。这是大多数团队在第一天——当 Demo 的召回率指标是衡量一切的唯一标准时——会做出的妥协。然而,这种妥协在智能体余下的生命周期里,每季度都会反噬你一次。

备用方案变成了默认方案:为什么你的分层配比需要 SLO

· 阅读需 12 分钟
Tian Pan
Software Engineer

仪表盘显示 0.5% 的请求触发了回退(fallback)。仪表盘这么显示已经持续六个月了。直到有人从头重新运行遥测数据(telemetry),发现次级模型正承载着 38% 的流量,而预设回复(canned-response)层级则处理了另外 9% 的流量。团队在路线图评审中一直讨论的尖端模型“主路径”,实际上已沦为少数派体验。没有人注意到这一点,因为没有任何警报被触发 —— 每次降级都是一个小规模的、理由充分的、局部正确的决定,而累积的偏差从未超过任何人事先设定的阈值。

这就是我想要定义的失效模式:成了默认项的回退机制。这不是故障,也不是单个组件的回归。它是产品表面的缓慢轮转,退而求其次的路径不再是安全网,而成了核心体验。团队的心理模型与生产现实渐行渐远,而这种差距是隐形的,因为现有的度量指标(meters)旨在检测失败,而非检测组合(mix)。

我要提出一个更强有力的观点:如果你的 AI 功能拥有两个以上的服务层级,那么你的层级组合(tier mix)本身就是一个 SLO。如果你没有测量它,你其实并不知道你发布了什么。

多维 Agent 二分查找:当回归出现在交互中时

· 阅读需 12 分钟
Tian Pan
Software Engineer

质量在一夜之间下降了。值班工程师打开仪表盘,追踪了几个异常会话,并开始进行显而易见的二分定位:模型提供商在 UTC 时间 02:00 切换到了新的快照,于是将模型回退到固定的旧别名。评估套件仍然显示红色。回滚昨天的提示词更改。仍然是红色。将检索索引固定回上周的版本。仍然是红色。每个负责团队都在孤立地回滚自己的维度,并报告“不是我们的问题”。三个小时过去了,没有人负责诊断,因为没有人负责回归真正存在的交互面(interaction surface)——新模型以一种旧模型绝不会采取的方式,解释了新的工具描述。

这就是单轴工具无法解决的失败模式。git bisect 之所以有效,是因为搜索空间是一维的:提交记录的线性序列。而 Agent 没有单一的时间线。它有四到五个并行运行的时间线——模型快照、系统提示词、工具目录、检索索引、采样配置——每个都有自己的负责人、自己的部署节奏,以及自己的“回滚”按钮,只能将其自身的轴恢复到已知状态。你正在追踪的回归通常是一个双因素交互作用,沿着任何单一轴进行二分都会返回假阴性结果,因为该 bug 仅在“新模型遇上新工具描述”的交叉乘积单元格中触发。

Prompt 卧推:对“快乐路径”之外的提示词进行压力测试

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个在你的评估集(eval set)上得分 92%,但在真实生产流量中得分 60% 的提示词(prompt),并不是一个有 bug 的提示词。它是一个评估集在结构上无法发现 bug 的提示词。这种差距并非噪声,而是针对那些与提示词设计意图共享语域(register)、长度分布、语言和礼貌程度的示例进行优化的结果——而这些示例正是由编写评估案例的同一个意图所创作的。

真实用户不会配合你的设计意图。他们会发送三个词的片段、十二个段落的文章、作为问题粘贴的代码块、省略冠词的非正式语域、添加敬语的正式语域,以及你的 few-shot 示例从未涉及的语言查询。这些都不是攻击性的,这只是输入分布(input distribution)。如果你的评估集是由编写提示词的同一个人策划的,那么它几乎肯定与这种分布毫无相似之处。

缩小这一差距的学科不是“更多评估”,而是一种不同类型的评估——一个压力矩阵(stress matrix),它刻意改变你策划的集合中保持不变的维度,并对退化曲线(degradation curves)进行评分,而不是单一的准确率数字。称之为提示词卧推(prompt bench press):你不是在测试提示词能否完成工作,而是在测试随着输入变得更难,它是如何失败的。

静默工具截断:你的智能体在不知情下进行推理的默认限制

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个工具调用返回了 142 KB 的 JSON 数据块。你的智能体框架丢弃了 8,192 字节之后的所有内容,将前缀交给模型,而模型根据一个它从未意识到是不完整片段的内容写出了一个自信的答案。三周后,一名客户升级了投诉。你翻看追踪记录(trace),看到“工具返回成功”,随后的复盘变成了寻找哪一步“忽略”了证据——然而没有哪一步忽略了它。证据在到达推理引擎之前就被裁剪掉了。

这并非假设。Codex 将工具输出截断硬编码为 10 KiB 或 256 行。Claude Code 的工具结果默认为 25,000 个 token,并且带有一个单独的显示层限制,曾在 2025 年短暂地将 MCP 响应裁剪到 700 个字符左右。OpenAI 的工具输出提交上限为 512 KB。每个框架都选择了一个看起来安全的数字,对于短工具调用确实如此。当单步输出越界时,故障模式就出现了——悄无声息地,没有异常,也没有模型可见的标记。

规范翻译税:当规范、提示词和评估发生漂移时

· 阅读需 12 分钟
Tian Pan
Software Engineer

一名 PM 用英文写了一份功能规范 (feature spec)。一名工程师将其翻译成带有惯用 LLM 模式的系统提示词 (system prompt) —— 思维链 (CoT) 脚手架、输出格式强制,以及一些涵盖规范中从未提到的失败模式的避险条款。一位评估 (eval) 作者打开同一份规范,冷读一遍,并根据自己的理解编写 JSON 测试用例。三周后,这三个产物各不相同,没人能说清楚一个回归到底是提示词的 bug、规范与实现的差异,还是从第一天就写错的评估。

这就是规范翻译税 (specification translation tax)。传统软件也有这种问题 —— PRD 与代码之间、代码与测试之间的差距 —— 但编译器和类型系统缩小了这种差距。AI 功能没有这种兜底保障。提示词是系统实际阅读的文档。评估是没人签署的合同。规范是没人执行的意图描述。每一项都是将同一意图翻译成不同的媒介,如果没有双向的一致性,行为就会通过那个最容易编辑的产物泄露进来。

工具行为漂移:Schema 没变,语义却变了

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的契约测试通过了。Schema 校验器显示正常。工具返回的数据结构与上个季度完全一致。然而,面向用户的回答已经悄无声息地错了六个星期。

这就是契约测试从未设计用来捕捉的故障模式。契约测试验证的是传输格式没有改变——比如 search() 是否仍然返回 { results: [{ id, title, score }] }create_event 是否仍然接受 ISO 8601 字符串,地理编码器是否仍然输出 { lat, lng }。它们无法捕捉到的是:搜索端点开始按新近度而非相关性排序的时刻;日历 API 在欧盟地区静默地将你 14:07 的开始时间吸附到 14:00;地理编码器在同一个模糊的多边形内选择了一个不同的点;或者作为工具的 LLM 分类器在稳定的端点后升级到了新模型,导致你的评估集从未采样过的某个类别中误报率上升了四个百分点。Schema 没变,但行为变了。你的智能体继续读取着代表通过的绿色勾选,并产生了没有任何错误日志捕捉到的退化答案。

智能体状态差异对比 (Agent State Diff):为什么肉眼对比两条追踪路径无法规模化

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个回归错误流入了生产环境。团队选取了导致失败的输入,针对上周的提示词进行回放,却得到了不同的输出。现在他们必须查明原因——而答案埋藏在 3 MB 大小的文本差异、分歧的工具调用序列以及被打乱的检索块中,人类根本无法有效地进行比对(diff)。于是,他们将两份记录粘贴到左右分栏的查看器中,滚动查看了二十分钟,得出结论“模型今天感觉不太一样”,然后发布了一个并没有解决根本原因的热修复,因为他们从未找到真正的原因。

这就是 Agent 状态差异问题,也是通用工程工具在处理 Agent 系统时失效的首要环节。传统的回归二分查找(bisect)针对的是确定性代码:相同的输入产生相同的输出,git bisect 遍历历史记录,直到你找到破坏代码的提交。但 Agent 的运行不是确定性的,输入也不仅仅是一个字符串,其“历史”是一个多轴的包(envelope)——模型快照、采样配置、检索到的上下文、工具目录、框架标志——其中任何一个变量都可以独立地改变行为。