跳到主要内容

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

查看所有标签

那个‘总结’掉用户原始提问的压缩策略

· 阅读需 12 分钟
Tian Pan
Software Engineer

一位用户询问我们的支持智能体:“为什么发票 INV-2025-08-44719 在 4 月 3 日被扣了两次款?”45 分钟和 18 次工具调用之后,智能体自信地回复道:该账户在该季度没有任何重复计费的证据。用户理所当然地投诉了。当我们回放追踪记录(trace)时,答案变得显而易见。智能体在第九轮对话时对内容进行了压缩(compacted)。摘要显示用户“在询问 4 月初的一笔重复收费”。其中并不包含字符串 “INV-2025-08-44719”。随后的每一次工具调用——账本查询、退款 API 查询、审计日志扫描——都是针对转述后的意图发出的,而不是用户输入的字面意义上的发票编号。

Bug 不在工具里,也不在模型的推理中。问题在于我们的上下文管理器(context manager)与每个下游组件之间都有一个契约,但没有人把它写下来。契约写着:“我会保留语义。”而组件需要的是:“我会保留字符串。”

那些在评估集中遗漏、却在模型蒸馏中丢失的能力

· 阅读需 10 分钟
Tian Pan
Software Engineer

一个团队将一个 200B 的教师模型压缩成一个 7B 的学生模型,因为评估套件——包含五万个覆盖产品发布时所有功能的样本——显示学生模型仅落后教师模型不到两个点,且推理成本降低了一个数量级。迁移上线了。成本曲线下降。客户满意度曲线持平。三周后,客服开始看到一类团队无法在评估中复现的故障。

学生模型不再识别教师模型曾默默处理的边缘案例输入格式。它不再能从教师模型曾可靠消除歧义的特定模糊指令中恢复。它不再产生那种罕见但关键的“与其猜测不如询问澄清问题”的行为——因为评估集以这些提示词是“坏数据”为由,清除了其中的模糊提示词。

评估结果显示蒸馏是忠实的。评估对于“忠实性”的定义是错误的。

那个在你的代码冻结期间送达的模型弃用通知

· 阅读需 9 分钟
Tian Pan
Software Engineer

邮件是在周二发出的。你那两个最重要的功能所依赖的 Checkpoint 进入了 90 天的下线期。你的工程团队正处于为了另一个发布而进行的协同代码冻结(Freeze)的第二周。等到冻结解除时,你将只有不到三十天的时间来针对新模型重新验证两个生产环境的功能——这里的“重新验证”意味着重建评估集、运行影子流量、获得产品负责人签字,并在一个没人关注的 Feature Flag 之后发布,因为发布团队还在忙着处理代码冻结原本针对的那个项目。

这种冲突并不少见。主要供应商发布废弃周期的频率是以月为单位的,每个在托管模型上运行的团队现在都经历过至少一个周期。团队尚未吸收的教训是,供应商的废弃并不是像库升级那样的工程事件——它是一个运行在你无法控制的时钟上的排程事件,任何没有预留预算的路线图都会将这笔成本视为一场意外。

供应商配额在你的全球流量从未选中的时区重置

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的每月 Token 配额在 00:00 UTC 重置。你最大的客户在东京,他们在 21:00 UTC(即当地时间第二天早上 6:00)达到峰值负载。当重置时刻到来时,东京的工作日已经在配额耗尽的降级方案中消耗了该周期的最后六个小时。429 错误看起来只是“偶发”,因为你仪表板上的 UTC 日历轴将每日重置边界隐藏在了普通的时间戳之中。

这不是速率限制(rate limit)的 Bug。这是一个日历 Bug。供应商为了结算方便选择了一个重置时钟,而你流量的地理分布决定了哪些客户会分配到周期末尾的空窗期。那些将配额定价为统一资源的团队,正基于一个用户从未见过的日历来进行配额分配。

你的仪表盘以三种不同方式统计了那次重试

· 阅读需 13 分钟
Tian Pan
Software Engineer

一个 Agent 运行了。计划步骤(plan-step)崩溃了。工具调用(tool-call)步骤在经历了两次 500 错误重试后,在第四次尝试时成功了。用户得到了他们的答案。

那算是多少个事件?问产品,这是一个事件 —— 用户得到了有效结果,因此转化漏斗统计了一次转化。问 SRE,那是三个失败加上一个成功,底层步骤的错误率是 75%。问财务,那是四次计费推理,两次重试的工具调用,以及大约四倍于产品部门预测的单位成本。每个团队的仪表盘都是正确的。但它们也是不可调和的,一旦有人试图调和它们 —— 通常是在事故回顾期间 —— 他们会发现团队已经基于三个相互矛盾的可信度图景运行了数月之久。

你的后端基础设施并非为流式响应而设计

· 阅读需 13 分钟
Tian Pan
Software Engineer

流式传输(Streaming)是一项产品决策。设计团队的某个人看到竞争对手的聊天 UI 像打字机一样逐个吐出 Token,看到用户在第 200 毫秒看到第一个字符出现时肩膀放松了下来,而不是盯着 4 秒钟的空白屏幕发呆,于是决策就此达成:我们要做流式传输。这个拉取请求(PR)修改了 API 网关中的三个文件。现在,模型输出通过服务器发送事件(Server-Sent Events,SSE)增量刷新。功能在周二上线,周三的满意度评分就有了明显的提升。没人向基础设施团队提工单。

一个月后,值班工程师盯着三个互不一致的仪表盘发愁。自动扩缩容(Autoscaler)配置的 Pod 数量是 CPU 图表显示需求量的两倍。P99 延迟仪表盘坏了——不是出了故障,而是变得无法解读,因为直方图分桶(Histogram Buckets)止步于 5 秒,而现在大多数 Span 都落在溢出区间。上一季度定价时的容量模型显示,该服务每节点每秒可处理 1200 个请求。而值班人员面前的图表显示,它在处理 400 个请求时就已经难以为继。

输入分布与用户实际输入不匹配的合成训练样本

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个团队利用 80,000 个合成示例微调了一个客服模型。Teacher prompt 设定得很得体:“生成关于退货、退款和物流的真实客户问题。”Teacher 模型照办了。它生成了简洁、完整、拼写正确、每条消息只有一个意图、语气礼貌且语体一致的查询。在预留的合成验证集上的离线评估达到了 94%。于是团队发布了。

生产环境的表现差了 20 个百分点。团队花了一个 Sprint 的时间争论模型是否“不擅长客服”。事实并非如此。模型在客服方面表现良好。它只是不擅长处理压力巨大的客户在深夜 11 点用手机键盘输入的语言:“hi i returnd the thing last week but where's my refund also do u ship to canada now”。模型在训练过程中从未见过这种形式的输入,因为 Teacher 模型当时忙着生成它想象中的查询,而不是用户实际发送的查询。

逐渐腐化的工具描述:当你的 Agent 仍在盲目调用时

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的智能体已经悄悄出错六个月了,而你的错误率看起来却很正常。底层 API 发布了一个重命名的错误代码,将一个可选字段改为必填,并开始拒绝没有幂等性请求头(idempotency header)的调用。你智能体系统提示词(system prompt)中的工具描述 —— 那是去年第四季度从 Notion 页面粘贴过来的 —— 完全没有描述这些变化。智能体不断调用旧的参数结构,编排层不断捕获失败并使用同样错误的参数进行重试,而你遥测系统中的唯一信号只是略微升高的重试次数,且没有任何值班人员有足够的背景信息去调查它。

工具描述是接口契约。底层 API 发生变化的时刻,它们就开始老化。与强类型 SDK 不同,它们的失效是无声无息的 —— 模型只会发出更糟糕的调用。

语音代理 SLO 定义为首个音频时间,而你的服务商则以首个 Token 时间衡量

· 阅读需 11 分钟
Tian Pan
Software Engineer

产品规格说明书规定用户在说完话后的 600 毫秒内听到回复。LLM 供应商的仪表盘显示首个 Token 时间(TTFT)为 280 毫秒。你查看的每一张图表都在 SLO 范围内。但用户仍然抱怨智能体有延迟,当你亲自拨打电话时,确实能感觉到明显的停顿——每次都在 600 毫秒以上。仪表盘没有撒谎。它测量的是一个不包含 TTS 流水线、音频传输或接收端抖动缓冲(jitter buffer)的数值。流式传输的最后一个 Token 与第一帧音频之间存在的 350 毫秒差距是真实存在的,只是它没有出现在 LLM 团队的图表上。

Bug 不在模型中。Bug 出在 SLO 上。它被定义在了错误的堆栈层级。供应商的出站(egress)并不是用户的耳朵,任何忽视这一点的延迟契约都会在生产环境中显得数据健康,而产品体验却是一团糟。

你定义‘首个 Token’的位置决定了你的延迟 SLO 是否真实

· 阅读需 11 分钟
Tian Pan
Software Engineer

我上季度合作的一个团队在周二发布了推理层升级,周三就开始收到支持工单。用户反映助手感觉“坏了”、“冻结了”或“卡住了”。值班工程师查看了延迟仪表盘,没发现任何异常。p99 首字延迟(first-token latency)为 612 毫秒——远低于团队花了一个季度建立的 800 毫秒 SLO。仪表盘是一片绿色。电话却响个不停。

问题的根源在于 14 个月前做出的一个埋点决策,当时生产环境中还没有推理模型。标记为 “first token” 的指标测量的是供应商发出的第一个数据块(chunk)的时间戳。升级后,第一个 chunk 变成了推理 token——这对用户不可见,也从未渲染,但在 SLO 中却被计为“首个”。模型在流式传输第一个用户可见字符之前,会先发出 4 到 7 秒的内部思考过程。每个图表依然是绿色的。每个用户却在黑暗中等待。

这不是一个关于指标好坏的故事。指标对于它最初设计的模型来说是正确的。这是一个关于当你的埋点边界不再是用户的感知边界时会发生什么的故事——以及在不知不觉中发布这种偏差是多么容易且危险。

那些悄悄共享长期记忆的智能体 A/B 测试变体

· 阅读需 12 分钟
Tian Pan
Software Engineer

你运行了职业生涯中最干净的一次 A/B 测试。流量分配是 50/50,哈希函数看起来没问题,指标流水线没有造假,留存样本(holdout)得到了保留,经过三周的分析,得出了一个明确的胜者:变体 B 将任务完成率提升了 4 个点,统计团队对 p 值也没有异议。你将其 100% 发布了。发布两周后,你启动时所依据的核心指标却漂回了基准线,而且没人能解释原因。

这就是那个花了一些时间才看清的部分。两个变体都在向同一个长期记忆库写入并从中读取。变体 A 中的用户写入了一条记忆,比如 “该客户更喜欢直截了当的总结”,第二天,当同一个用户恰好处于变体 B 时,变体 B 的 Agent 加载了该记忆并将其读入其提示词(prompt)中。反向的情况也在另一个方向发生。实验并不是在比较提示词 A 与提示词 B。它是在比较 “读取提示词 B 形状记忆的提示词 A” 与 “读取提示词 A 形状记忆的提示词 B”。结果是受污染的联合分布的平均值,而发布则是回归到了同一曲面上的另一个点。

第四方风险:当供应商的供应商掌控了你客户的故障

· 阅读需 13 分钟
Tian Pan
Software Engineer

你与模型提供商签订了合同。你的运行手册(runbook)处理了该提供商降级的情况。当他们的仪表板变黄时,你的状态页订阅会向你发送告警。你觉得万无一失。然后,在某个周三下午,你提供商运行的基础云区域开始出现局部降级,你提供商的故障转移区域也受到了影响,因为他们为了控制单位经济效益而整合了容量。由于签署合同时上游两层的供应商决策,你的产品在 90 分钟内处于半瘫痪状态。

第二天早上,客户的事后分析(postmortem)请求出现在你的收件箱里。他们想要找到根本原因。根本原因存在于你的状态页无法看到的层级,也是你的合同无法约束的层级。这一层级正是所谓的第四方风险——它不是一个采购复选框,而是一个无形的依赖层,它会向上层传导故障,只会衰减而不会被吸收。