跳到主要内容

702 篇博文 含有标签「llm」

查看所有标签

确定性种子:为什么供应商将其视为“提示”而非“契约”

· 阅读需 12 分钟
Tian Pan
Software Engineer

CI 测试只有一个断言:相同的模型、相同的温度、相同的提示词、相同的种子(seed)、相同的输出字符串。它在每个开发者的笔记本电脑上都通过了,在前一百次 CI 运行中也通过了,但在三周内每五十次运行就会出现一次随机失败(flake),直到最后有人承认这种模式是真实存在的。第一个假设是显而易见的——测试工具中某处存在非确定性依赖——三天的调查却一无所获。实际原因隐藏在供应商 API 参考文档的一个脚注中:“seed 提供尽力而为的确定性。”团队读到了参数名称,并将其视为一种契约。而供应商记录的只是一个提示。

这是托管推理的一种特定失败模式,它困扰着那些围绕单一心智模型设计测试基础设施的团队:模型是其输入的纯函数,而 seed 是使函数具有可重现性的关键。在生产环境中,这个模型的这两个部分都是错误的。API 表面与底层物理原理之间的差距如此之大,以至于团队在供应商明确否认的假设之上,构建了整个评估和回归测试栈。

你的 Token 预测从未考虑过的重尾效应

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的 AI 功能成本预测是基于一个 50 人的试点项目建模的。那些用户输入了三句话的提示词,因为那是人们在被要求评估测试版时通常会输入的内容。产品上线了,你突破了一万名用户,财务团队指出你的模型账单是计划书中人均成本的三倍。你去寻找 Bug。但根本没有 Bug。你的试点项目是从一个分布中采样,而生产环境是从另一个分布中采样,两者的区别在于一个长尾用户群——他们是在 Twitter 上了解到你的产品,并粘贴了从推文中截取的 30 KB 非结构化上下文。

这是每家消费级互联网公司在 2010 年代都吸取过的同样财务教训,现在被移植到了 LLM 经济学中。试点项目的中位数用户并非生产环境中的 p99.5,而一个使用平均值作为预测输入的 Token 成本模型,在面对账单时注定会一败涂地。

本地化系统提示词:模型表现为何比英文原版更差

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的英文系统提示词(system prompt)花了六周的时间进行调优。一位资深工程师先后四次重写了约束列表,评估套件终于在留存任务集(held-out task set)上跑出了 94% 的通过率,发布检查清单也为生产环境亮了绿灯。随后,国际化(i18n)团队接手,将其放入处理按钮标签和工具提示的相同翻译流水线中,并在下个迭代周期交付了日语、德语、印地语和阿拉伯语版本。针对非英语市场的发布仪表盘显示了相同的任务量、相同的用户转化漏斗,而且——直到六个月后收到东京客户的一张工单——始终保持着代表正常的绿色状态。

东京客户投诉称,智能体忽略了英文提示词中明确禁止的一项指令。你重新阅读了日语提示词,发现从语义上看,两者的意思完全相同。你针对英文变体重新运行了英文评估套件,通过了。但日语变体没有评估套件。从来都没有。

擦除模型仍在读取的上下文:数据保留策略带来的隐患

· 阅读需 13 分钟
Tian Pan
Software Engineer

一个每晚运行的数据留存 worker(retention worker)会删除任何超过 30 天的用户消息。一个从 3 月初开始的长周期企业支持会话,到 5 月底仍然处于活跃状态。在第 41 轮对话请求进来时,你的 Prompt 组装器(prompt assembler)从同一个消息表中读取数据,而那个留存 worker 一直在悄悄地清理这个表。第 1 到 28 轮已经消失了。模型接收到的对话是从第 29 轮开始的,没有任何信号表明之前的对话曾经存在过。用户问道:“我们之前商定的 SLA 是什么?”模型自信地编造了一个数字,因为真正的答案在第 4 轮——而留存 worker 在前一天晚上把它删除了。

这不是模型故障。模型完全按照其应有的方式运行:从交给它的上下文中生成一个看似合理的答案。故障发生在更上游,处于两个团队之间的鸿沟中——每个团队都认为自己拥有消息表。

与验证器共享盲点的自我修正循环

· 阅读需 10 分钟
Tian Pan
Software Engineer

在智能体复盘中流传的截图每次看起来都一模一样。一段长长的追踪记录。一个单一的任务。十二次迭代。智能体生成了草稿,进行了评估,发现了一个小瑕疵,生成了修订版,进行了评估,发现了一个略有不同的微小瑕疵,接着又生成了另一个修订版。验证器返回的分数一直徘徊在 0.78 到 0.84 之间。它从未跨越门槛。智能体从未上报异常。三小时后,任务因超时而终止,产生了一笔足以支付一名高级工程师四分之一日薪的 Token 账单。

团队将其称为“自我修正”问题,因为架构图上就是这么标注的。实际的失败是结构性的。验证器其实就是换了一个提示词的生成器。收敛准则是模型自己的意见。重试预算是隐式的,受限于智能体的超时时间,而不是由智能体本身推理决定的。这三个失败孤立来看都不像是 Bug,这正是团队会将其上线的原因。

两个模型对同一结构化输出 Schema 的不同理解

· 阅读需 10 分钟
Tian Pan
Software Engineer

当你的备用路由(fallback route)第一次在生产环境中触发时,绝不是发现两个供应商对你的 schema 定义存在分歧的好时机。在两个客户端配置中,JSON Schema 看起来完全一样。验证器对两个输出都通过了。下游代码按名称读取字段并获取一个值。接着,账单总额以数字字符串而非整数的形式出现,或者长度为一的列表以纯对象而非单元素数组的形式到达,一段已经正常运行了六个月的代码路径会静默地返回错误答案。

结构化输出引人入胜之处在于它消除了一类错误——无法解析的 JSON、幻觉字段、缺失的键——因此让人感觉它彻底解决了解析问题。实际上它所做的是将解析问题向上移动了一层,从词法分析器(lexer)移到了类型系统,在那里问题变得更难被察觉。两个供应商可以都遵循 JSON Schema,但仍然产生不可互换的输出,因为在这个生态系统的角落里,“遵循”至少有四种不同的含义,而你的 schema 并没有指明你想要哪一种。

误将假期低谷视为新基线的 Token 预测

· 阅读需 10 分钟
Tian Pan
Software Engineer

一位容量规划师带着基于干净的过去四周回溯窗口构建的 Token 预测走进了季度预算审查会议。这四周中有三周恰好跨越了一个地区性假期。在此期间,日活跃会话下降了 40%。预测结果比 Q+1 的实际消耗低了 35%,限流仪表盘在新季度的第一天就全线飘红,而复盘发现模型的表现完全符合预期——它计算了最近四周需求的平均值并进行了向前预测。模型没有错。错的是窗口。

这不是一个关于蹩脚预测者的故事。这是一个关于将 LLM Token 支出视为与它共用成本中心的 EC2 账单相同形态的故事。EC2 账单受你控制的基础设施决策支配:配置的实例、预留容量以及响应负载的扩展策略。而 Token 账单则受决定休长假的用户的支配。前者是工程输出,后者是消费者需求。如果规划者混淆了两者,就会不断地在日历确保是非平稳的窗口上构建预测。

你的代码从未检查过的 Finish Reason

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的处理器(handler)做对了一切。HTTP 状态码是 200。Body 解析成功。文本字段里有内容。你增加了 responses_succeeded 的计数,将消息追加到对话中,把 JSON 返回给客户端,然后继续下一步。用户得到的是一个在句中戛然而止的句子,一个伪装成正常答案的经过编辑的回复,或者是一个被措辞为补全的礼貌拒绝。你的仪表盘对这一切一无所知。供应商已经告诉了你,但你没有读取那个字段。

每个主流的推理 API 都会在文本之外返回一个停止信号:OpenAI 称之为 finish_reason,Anthropic 称之为 stop_reason,Gemini 称之为 finishReason。这个字段很小,每个响应对应一个枚举值。它也是模型用来告诉你刚才发送的响应是一个完整答案还是一个碎片的唯一带外(out-of-band)通道。将其视为无关紧要的装饰,与忽略 HTTP 状态码属于同一种类型的 Bug —— 不同之处在于,你的监控系统在十年前就能捕捉到 HTTP 错误,但对这个错误却无动于衷。

你的编程 Agent 记错的库版本

· 阅读需 11 分钟
Tian Pan
Software Engineer

Diff 看起来很干净。Agent 导入了正确的模块,调用了看起来正确的函数,TypeScript 也没有报错。PR 描述甚至引用了文档。随后 CI 中的构建开始运行,调用却由于 TypeError: x is not a function 而崩溃 —— 这是因为该函数在八个月前的一次小版本更新中被拆分成了两个,而 Agent 是根据其训练数据中存在的库版本生成的代码,而不是你 package.json 中安装的版本。

这并不是“LLM 会产生幻觉”这一框架能让你做好准备的那种故障。模型并不是在发明一个从未存在的 API。它是在记忆一个曾经存在但现在已不存在的 API。Agent 进行推理的心智模型是一个冻结在训练时的快照。世界在向前发展。代码库在向前发展。而 Agent 却一无所知,因为没人告诉它。

被你的个性化层悄悄杀掉的 Prompt 缓存

· 阅读需 12 分钟
Tian Pan
Software Engineer

产品团队发布了个性化功能。智能体(Agent)现在会直呼用户姓名,根据用户的偏好调整回答长度,了解用户在医疗行业工作,并尊重用户在提及日期时所处的时区。用户满意度的提升是真实且可衡量的——A/B 测试显示点赞率提升了四个百分点,随后功能全量上线。三周后,财务部门指出推理成本大约翻了三倍,而 AI 团队中没人能立即解释原因。

解释就在系统提示词(System Prompt)构建器中一行被埋没的代码修改里。每个用户的上下文——姓名、偏好的回答长度、行业、时区——都被添加到了系统提示词的开头,以便模型在每一轮对话中都能看到。这使得从第一个 Token 开始,每个用户的 Prompt 都是独一无二的。你的供应商提供的 Prompt 缓存——原本能以标准价格的十分之一服务大约 90% 的输入 Token——失效了。延迟几乎没有波动,所以性能仪表盘(Performance Dashboard)依然显示绿色。直到月底,计费仪表盘才反映出这一情况。

自相矛盾的流式响应

· 阅读需 9 分钟
Tian Pan
Software Engineer

模型在第一句说“答案是肯定的”。到了第三段,它又改口说“实际上,经过反思,不——原因如下”。最终状态是正确的。但用户已经离开了。他们读了第一段,将其视为答案,并在模型完成修正之前就付诸行动了。你的评估认为该回答是正确的。但你的用户得到的却是错误的。

这是流式传输 UX 所隐藏的失败模式。逐字渲染(Token-by-token rendering)将每个区块都视为既定事实,但模型并没有“提交”(commit)的概念。在模棱两可的话语和结论之间没有边界,也没有信号表明“接下来的两段将推翻我刚才说的话”。界面将中间状态作为最终状态发布,且回答越长,这种差距就越严重。

那个直到触发时你才察觉的 Token 预算

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的团队与推理提供商协商了月度 Token 配额。合同规定了上限。提供商门户的仪表板显示昨天的使用情况,存在一天的延迟。API 本身返回每分钟速率限制头——anthropic-ratelimit-tokens-remainingx-ratelimit-remaining-requests——而对于你实际需要规划的月度配额桶却只字未提。你的智能体集群没有机制在预算耗尽时减速,因为实时到达的唯一信号是 429 错误——而这个信号在预算已经用完后才出现,且伪装成重试逻辑通常会忽略的瞬时错误。

这是一个与速率限制(rate limiting)性质不同的问题。速率限制是一个快速波动的节流阀,消费者必须在几秒钟内做出反应;响应头告诉你桶里还剩一千个 Token,并在 40 秒内补满,一个编写良好的客户端会退避并重试。月度配额则是一个缓慢变化的预算,消费者必须以周为单位进行规划。这两者之所以容易混淆,是因为它们共享错误代码,有时甚至共享同一个仪表板,但它们需要不同的控制手段——而提供商公开的信息与消费者需求之间的差距,正是本月最严重事故的导火索。