跳到主要内容

639 篇博文 含有标签「llm」

查看所有标签

何时跳过实时 LLM 推理:异步批处理流水线的生产实践

· 阅读需 10 分钟
Tian Pan
Software Engineer

某个团队此刻正看着他们的 LLM 月支出以 10 倍速度增长,而 p99 延迟徘徊在四秒左右。工程师们增加了更多重试。重试触发了速率限制。速率限制触发了回退。回退也是 LLM 调用。没有人停下来问:这个功能真的需要实时响应吗?

大多数 AI 产品团队都为"幸福路径"设计架构——用户发消息,模型响应,用户看到结果。同步调用模式是 API SDK 在第一个代码示例中演示的内容,因此它就这样上线了。但生产 LLM 工作负载中有相当大一部分与用户坐在键盘前等待毫无关系。它们是文档增强任务、内容分类流水线、向量嵌入生成、夜间摘要生成和后台质量评分。对于这些工作负载,实时推理是错误的工具——而坚持使用它所付出的代价是真实的金钱、级联故障,以及你要花费数月才能理清的运营复杂性。

当你的模型具有随机性时,快照测试在撒谎

· 阅读需 12 分钟
Tian Pan
Software Engineer

当你团队中的初级工程师第一次输入 --update-snapshots 并推送到 main 分支时,你的测试套件就不再是测试套件了,它变成了一份记录稿。虽然 Diff 依然显示为红绿颜色,CI 徽章依然会变为通过,但信号已经悄然反转:测试套件不再告诉你代码是否正确,而是告诉你是否有人费心看过输出。对于确定性的代码,这种风险尚在可接受范围内,因为大多数 Diff 确实是符合预期的。但当网络调用的另一端是一个随机模型时,同样的流程会让每一个 PR 变成一场硬币投掷,让每一位评审者变成一个橡皮图章。

快照测试曾是确定性世界里的一个美妙构想。你记录下上周二 render(<Button />) 的生成结果,断言本周二它会生成相同的字符串。从定义上讲,任何 Diff 都是值得人工核查的行为变更。这种模式在 Jest、Vitest、Pytest、整个 React 生态系统以及一代又一代的 UI 快照扩展中得以幸存,是因为底层契约依然成立:相同的输入加上相同的代码等于相同的输出。但这个契约对 LLM 调用并不奏效。相同的输入、相同的代码加上相同的提示词(Prompt),却会产生不同的字符串——而且这种差异并非 Bug,而是产品按设计正常运行的结果。

你的 LLM 网关缺少的长尾容错重试策略

· 阅读需 14 分钟
Tian Pan
Software Engineer

查看你的网关重试配置。三次尝试。带有抖动的指数退避。在 5xx 错误和超时时重试。最大延迟限制在几秒钟。这看起来很合理,而且是某人两年前从微服务运行手册中复制过来的。但这正是你的 P99 是 P50 两倍、在服务商发生故障期间 Token 费用激增,以及相当一部分用户在默默流失前盯着 30 秒加载圈的最主要原因。

专为 50 毫秒 RPC 设计的重试策略在面对 8 秒的 LLM 调用时注定会失效。失败的形式不同,每次尝试的成本不同,用户感知到的时间维度也不同。默认设置并不安全,只是让人感到熟悉。大多数团队通过同样的方式发现这一点:在一次事后回顾中,网关日志显示响应成功,而客户的截图却显示 UI 已经卡死。

工具 Schema 设计即是你的爆炸半径:当函数定义成为安全边界

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的 Agent 代码库中最危险的文件是你一直当作 API 文档来编写的那个。工具注册表(Tool Registry)——即告诉模型存在哪些函数以及它们接受哪些参数的 JSON 或 Pydantic schema —— 不再仅仅是一个 docstring。它是你的授权层(authorization layer)。如果你像大多数团队那样设计它,你就是把万能钥匙交给了大模型(LLM),并称之为优秀的工程设计。

考虑一个典型的工具初步尝试:query_database(sql: string)。初衷是合理的 —— 让模型根据用户的问题制定正确的 SQL。现实情况是,模型现在成了一个不受信任的客户端,拥有连接字符串所指向的任何数据库的无限 DDL 和 DML 权限。系统提示词说“仅在 orders 表上运行 SELECT” 只是一个建议,而不是控制手段。当一个受到提示注入(prompt-injected)的工具结果 —— 比如邮件正文、网页或 PDF —— 告诉模型运行 DROP TABLE users 时,你的授权模型就变成了对模型指令遵循能力的纪律要求。那不是授权。那是祈祷。

Abandon 原语:为什么你的智能体循环需要一个一等公民的方式来终止计划

· 阅读需 12 分钟
Tian Pan
Software Engineer

看看大多数智能体框架提供的循环原语:continuereturnretry 以及一个硬性终止运行的步数预算。注意缺失了什么。这里有一条表示“工作成功”的路径,一条表示“模型想继续”的路径,还有一条表示“我们耗尽了资金或耐心,于是强行终止循环”的路径。唯独没有一条头等路径来表达“我正在执行的计划是错误的,我想将其丢弃并开始一个全新的计划”。放弃原语(abandon primitive)——即一种让规划器显式、结构化地宣布当前轨迹无望的方式——是智能体循环语法中缺失的动词。它的缺失导致了一类通常被误诊为“模型需要更多推理”的失败。

一个在注定失败的分支上执行了三步的规划器,会不断优化同一个错误的计划,因为循环唯一的出口是成功、重试上一步或耗尽预算。这些路径都不包含“放弃该策略并尝试另一个”。因此,智能体执行了循环所允许的操作:它就地修改计划、再调用一个工具、再请求一次澄清,并在步数预算耗尽前不断收敛到一个非解决方案。当最终撞墙时,用户看到的是一条礼貌的失败消息,而不是问题的答案。这些浪费的步骤所带来的成本是真实的——生产数据表明,智能体系统 5-10% 的 Token 支出都花在了无法产生任何可用结果的重试上,而这一数字主要由长期的“注定失败分支”占据,而非孤立的工具错误。

为什么你的 AI 路线图不应该有 12 个月的计划

· 阅读需 10 分钟
Tian Pan
Software Engineer

我上个季度合作的一个团队花了六周时间构建了一个“智能文档分类器”——微调模型、评估框架、自定义 UI,以及整个生产流水线。它在周二上线。接下来的周一,一个全新的通用模型发布了,在同样的评估中,它以零样本 (zero-shot) 的方式击败了他们的微调模型,且无需任何基础设施投入。他们整个第二季度的 OKR 变成了一个仅包含一行 API 调用的包装器。路线图在 12 个月前承诺要“掌控分类技术栈”。而这项承诺在墨迹未干之前就已经错了。

这并非孤例。行业追踪器记录显示,仅在 2026 年第一季度,各大实验室就发布了 255 个模型,到 3 月份为止,平均每周约有三次意义重大的前沿模型发布。成本已经崩溃:API 定价自 GPT-3 以来下降了 97%,顶级供应商之间的差距在大多数基准测试中已缩小到统计噪声范围内。当底层技术变化如此之快时,一份为期 12 个月的特性路线图就不再是计划——而是一份你无法重新审视的赌注清单,这些赌注是根据在你交付第二个项目之前就会过时的信息做出的。

物理隔离 LLM 蓝图:无出站流量部署的真正需求

· 阅读需 12 分钟
Tian Pan
Software Engineer

云端 AI 的策略通常建立在一个没有人明确写下来的前提之上:出站 HTTPS (outbound HTTPS)。厂商 API、托管评测器、遥测流水线、模型注册表、向量存储、仪表板 SaaS、密钥管理器——其中的每一个都静默地解析到公网上的一个域名。一旦拔掉这根电缆,整个技术栈并不会优雅降级,而是会直接崩溃。

大多数团队直到那一刻才会发现,他们的架构中存在从未考虑过的出站依赖。一个“微小”的提示词更新可能需要调用托管分类器;评估套件需要通过网络访问 LLM 评测器;可观测性代理会向后端发送数据;模型注册表从 CDN 拉取权重。这些都不是恶意的,也并不罕见。当你忽视了那根电缆时,云原生技术栈本就是这个样子的。

澄清预算:你的智能体何时应该询问而非猜测

· 阅读需 12 分钟
Tian Pan
Software Engineer

智能体最糟糕的两种失败模式看起来截然相反,但它们其实源于同一种失效的策略(Policy)。第一种智能体在执行任何操作前都会先问四个后续问题,这让它的用户因为繁琐而最终放弃使用。第二种智能体从不提问,它自信地生成用户不得不推倒重做的输出,这让它的用户对其产生不信任感。同样的策略,只是一个缺失参数的不同设置:即提问的成本相对于错误答案成本的比例。

大多数智能体根本没有任何策略。模型只是被要求“提供帮助”,然后被留下来独自应对模糊性。因为下一个 Token 预测(next-token prediction)机制奖励对答案的确定性,所以智能体倾向于猜测。又因为 RLHF 奖励礼貌,智能体偶尔会为了安全而过度纠偏并提出问题。其结果就是一种毫无原则的行为,这种行为在不同会话之间波动不定,团队层面也无法直观地判断智能体何时会暂停、何时会盲目推进。

澄清预算(Clarification budget)正是那个缺失的参数。它是针对每个任务制定的、允许智能体施加摩擦力的配额,并配有一套判断何时值得花费预算去提问的决策规则。你可以把它看作是对话领域的“延迟预算(latency budget)”——每个产品都有一个,即使没人把它写下来;而那些把它写下来的团队,就能停止交付那种让人困惑的智能体。

将 Eval 作为 Pull Request 评论而非任务:在代码审查中嵌入 LLM 质量门禁

· 阅读需 12 分钟
Tian Pan
Software Engineer

许多自称“有评估(evals)”的团队,其实际情况是:有一个仪表板,某人每周运行一次测试套件,然后将数据粘贴到没人看的 Slack 频道。评审人员批准提示词(prompt)更改时,甚至根本没看过它是否影响了测试套件,而回归问题(regression)两周后才在客户反馈单中显现。评估确实存在,但评估并未进入开发循环。

解决办法在于结构,而非意愿。只有当评估存在于变更发生的地方——即 Pull Request(PR)评论中,紧挨着代码差异(diff),并带有单个 PR 的增量变化和评审员无法忽视的回归提醒时,评估才能真正起到质量把关的作用。在其他任何地方,它们都只是表演性的产物:投入了大量精力构建,却什么也拦截不到。

分层内存压缩:你的智能体内存缺失的四个层级

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数智能体内存系统将一个四层的问题压缩成两层,然后在出现破绽时表现得大吃一惊。一个是当溢出上下文窗口时会被截断的会话缓冲区(conversation buffer),另一个是旧内容堆放其中的“长期记忆”向量数据库。那不是内存架构。那只是一个队列和一个杂物抽屉。

如果一个智能体连续三个周一向老用户询问同一个新手引导问题,这并不是因为模型不好,而是因为系统中没有一个地方能保存“该用户跨会话告知我的事情”,并且其生命周期不同于“所有用户告知我的关于产品如何运作的事情”。这是不同的记忆。它们有不同的访问模式、不同的隐私契约以及不同的遗忘规则。将它们混为一谈是架构上的错误——而且这是可以修复的。

工具调用顺序是偏序,而非集合

· 阅读需 12 分钟
Tian Pan
Software Engineer

“先创建后通知”的序列在开发阶段运行良好。而“先通知后创建”的序列则会为一个尚不存在的实体触发 webhook,导致消费者返回 404,接着你的团队会花上一周时间来调试这个看起来像是不稳定的集成测试。这种不稳定并非随机。它是确定性的,源于你的工具集拥有而你的规划器(planner)却不知晓的隐藏排序不变性。

这就是生产环境中 Agent 工具调用排序 bug 的常见形态:工具集在底层以偏序(partial order)方式组合——某些操作必须先于其他操作执行,而另一些则可以按任意顺序运行——但在规划器看来,它们只是一个无序的能力集合。模型选择了一个昨天行之有效的顺序。而明天,一次提示词修改、模型升级,甚至只是不同的 temperature 采样,都会选出另一个顺序。对于阅读追踪记录(trace)的人来说,这两种顺序看起来都很合理。但其中只有一个是正确的。

如果不声明顺序,团队交付的就是一个最终会被模型的提示词敏感性(prompt sensitivity)触发的 bug 隐患。

弃权作为一种路由决策:为什么“我不知道”应该属于路由层,而不是提示词

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数团队通过在系统提示词中加入一句话来处理弃权(abstention)问题:“如果你不确定,就说你不知道。”模型偶尔会遵守,但经常不遵守,而且这种失败模式是不对称的。一个自信的错误答案会以全速发布——它直接落入用户手中,在 Slack 讨论串中被引用,在下游摘要中被采纳。而一个诚实的弃权则会触发客户成功(CS)升级,因为用户期望智能体处理请求,而现在必须有人解释为什么它没有处理。六个月后,团队已经了解到哪种失败的发布成本更低,而那个名义上控制弃权的系统提示词修改,已经被悄悄地调整为倾向于顺从,而非诚实。

解决这个问题的准则不是寻找更好的措辞。而是要认识到,弃权是一个路由决策,而不是一种提示词模式。它理应拥有一个一等公民级别的输出通道、自己的 SLO、自己的评估套件,以及在系统拓扑中的独立位置——位于提示词之外,可以被测试、维护和扩展。