跳到主要内容

678 篇博文 含有标签「ai-engineering」

查看所有标签

拒绝延迟税:为什么分层护栏会侵蚀你的 p95 延迟预算

· 阅读需 11 分钟
Tian Pan
Software Engineer

我最近交流的一个团队为他们的 AI 助手构建了一个所谓的“深度防御”(defense in depth)流水线。一个输入分类器检查提示词注入;一个越狱过滤器扫描对抗性模式;模型生成回复;一个输出审核环节扫描结果;一个拒绝检测器检查模型是否回避了问题,如果是,则通过重新表述步骤,用更委婉的框架再次提问。评估套件显示该提示词在 1.4 秒内生成了答案,但真实用户的等待时间中值是 3.8 秒,p95 则超过了 9 秒。

每一个安全层都是一次往返。每一次往返都包含网络跳数、排队时间、模型加载和解码。当你将它们串行地堆叠在生成调用前后时,你为产品设定的延迟预算就会灰飞烟灭——而几乎没人在设计评审时考虑到这一点。更糟糕的是:流水线中最慢、最昂贵的路径往往是那些触发了安全边缘提示词的路径,而这恰恰是你的安全机制存在所要处理的长尾场景。你正在默默地用普通用户的账单来补贴这些长尾流量。

停用 AI 功能是一次信任事件,而非简单的功能弃用

· 阅读需 14 分钟
Tian Pan
Software Engineer

数据指标告诉你该砍掉它了。3% 的月活跃用户。评估(Eval)刷新已经推迟了两个周期。Prompt 中还有一个一年前留下的 // TODO: 在脱离旧版工单架构时重新审视。你的资深 AI 工程师每月要花整整一周的时间来照看这个功能 —— 模型升级、标签偏移,还有一个只要上游 API 更改日期格式就会挂掉的工具集成。每次季度评审,总有人会问为什么这个助手仍然存在,而每个季度的回答都是“我们还没顾上处理它”。

于是你写了一份弃用备忘录。你参考了平台团队完善的 API 停用手册:提前 6 个月的公告、迁移指南、产品内横幅提示、面向合作伙伴的 Webhook,以及常见的 Sunset: HTTP 标头。你在周二发布了它。到了周四下午,你的客户成功经理(CSM)转发来的邮件听起来完全不像是对 API 弃用的投诉。它们听起来更像是分手信。

在那一刻,大多数团队才意识到他们将一个类别错误带到了生产环境。你要弃用的不是一个 API,而是用户与一个能够回应他们的实体建立的关系。

检索膨胀:当“加个 RAG 就行”变成架构上的干扰

· 阅读需 12 分钟
Tian Pan
Software Engineer

这种模式太熟悉了,以至于被视而不见。模型幻觉出了一个事实,于是团队增加了一个检索步骤。三周后,模型从不断增加的工具库中选错了工具,于是他们在工具目录上增加了一个检索步骤。模型的回答感觉太笼统,于是他们在过去的高质量回答上增加了一个检索步骤。一个季度过去了,系统现在变成了一堆检索器拼接在一起的提示词,而本质上,最初的问题依然存在。

改变的不是失败率 —— 而是失败模式的名称。“模型出错了”变成了“检索未命中”,这听起来更易处理,但事实并非如此。评估套件的分数更高了,因为从构造上讲,检索到的上下文对于测试集来说是分布内(in-distribution)的。生产环境的情况则截然不同,但到那时,架构已经有了三个检索层,每一层都有自己的嵌入模型、索引刷新频率和值班轮换,而且没有人想成为那个提议拆除它们的工程师。

这就是检索膨胀(retrieval sprawl)。这是一种架构上的分心:一种将难题(提示词设计、模型能力、模糊的规范)转移到更舒适的问题(信息检索工程)上,而实际上没有解决任何问题的方式。

你的审核队列是自主权承诺消亡之地

· 阅读需 10 分钟
Tian Pan
Software Engineer

发布的 AI 功能带有一个完美的“安全方案”。任何置信度高于阈值的请求都会自动执行。任何低于阈值的请求都会进入人工审核队列。刚发布时,每天下午 5 点队列就会被清空。市场部门在幻灯片上写下“人工参与(human-in-the-loop)”。合规部门签字批准。大家打道回府。

六个月后,该功能的使用量增长了 10 倍,但审核团队并没有。队列里堆积了 72 小时的待办任务。一个需要“人工审核”的项目在未读状态下躺了三天,然后被一名疲惫的审核员批准——他平均处理一个决策只需 11 秒,因为只有这样才能保证队列不会在夜里翻倍。产品依然宣称“每项操作都经过审核”。现实情况是,“人工参与”已经退化成了“人工最终会在队列里看到”——这在功能上其实就是带有文书延迟的自主运行。

安全方案并不是因为 Bug 而失效,而是因为一个没人负责的人力资源计划而崩溃。

下午 3 点和凌晨 3 点的同一个 Prompt 并不是同一个 Prompt:LLM 评估中的昼夜漂移

· 阅读需 13 分钟
Tian Pan
Software Engineer

评估套件在凌晨 2 点运行。流量很低。缓存是冷的,但队列是空的。供应商的连续批处理程序有空闲插槽,并将以接近其 TTFT(首 Token 延迟)底线的水平处理每个请求。延迟分布很紧凑,评测模型分数稳定,仪表盘显示一片绿色。团队发布上线。

六个小时后,太平洋时间上午 8 点,同样的 Prompt 在美国早高峰期间进入生产环境。p95 延迟是评估报告的 2.4 倍。相当一部分请求从一个供应商那里收到了 529 错误,并回退到另一个供应商的较小路由层级。流式传输的节奏更加断断续续。评测模型(当天晚上对生产环境追踪样本进行重新运行)给出的中位数得分比凌晨 2 点给出的相同 Prompt 的得分低了半分。代码库没有变化。Prompt 没有变化。只是挂钟时间变了。

必须意识到的架构真相是:LLM 调用不是其输入 Token 的纯函数。它是一个随机分布式系统调用,其输入包括挂钟时间、供应商集群的负载、Prompt 缓存的状态、当前解码批次的大小,以及供应商负载均衡器在你的请求到达的那一毫秒所做出的路由决策。在凌晨 2 点运行评估的团队,是在一种用户永远无法体验到的条件下校准仪器。

结构化输出重试循环:你被忽视的算力浪费

· 阅读需 13 分钟
Tian Pan
Software Engineer

打开你的结构化输出仪表盘。它自豪地显示着类似 “98.4% 的 Schema 合规率” 这样的数字。这就是成功率——即第一次尝试就生成有效 JSON 对象的请求比例。团队为剩下的 1.6% 构建了一个重试封装器(retry wrapper),发布上线,然后就没再管了。两个季度后,推理费用增长了 15%,而请求量仅增长了 4%。首席财务官(CFO)想要个解释。工程师们给不出解释,因为跟踪结构化输出成功率的仪表盘并不跟踪结构化输出的成本。

仪表盘隐藏的部分在于:失败路径并非只有一次重试。第一次重新提示(re-prompt)修复了缺失的 enum 字段,但引入了一个格式错误的嵌套数组。第二次重新提示修复了数组,但丢掉了一个必填键。第三次尝试终于通过了验证,但到那时,该请求已经消耗了四次完整的推理调用加上最初的生成过程,而你的单次请求 Token 计数器显示的是 总和,而不是循环过程。从计数器的角度来看,这是一个昂贵的请求。从成本线的角度来看,这是一个你从未定价的随机循环。

这篇文章将探讨该循环究竟对你的算力预算产生了什么影响,为什么你现有的观测能力(observability)无法察觉到它,以及哪些规范可以使其变得可见且可控。

Token-Per-Watt:你的仪表盘无法计算的 AI 可持续性指标

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的可持续发展仪表盘报告显示:“本季度 AI 能耗:2.3 GWh,同比下降 4%”,这张幻灯片在 ESG 评审中得到了礼貌性的认可。六个月后,CFO 走出分析师电话会议,向平台负责人提出了一个听起来很简单的问题:“我们的每瓦特 Token 数(token-per-watt)是多少?与竞争对手相比如何?”仪表盘无法回答。这并不是因为数据缺失——仪表盘里堆满了数据——而是因为它将推理视为单一的条目,将任务视为产品概念,而 AI 可持续性唯一真实的单位存在于这两者的交汇点。

这种错位并不是报告中的 Bug。这是一个分类错误,现有的碳核算指南(为基于 CPU 小时和每台虚拟机 kWh 的云工作负载而完善)无法独自解决。推理并不是一种具有稳定能量特征的工作负载。每 Token 的瓦特数会根据响应请求的模型层级(model tier)产生 30 倍的变化,根据调用时的批处理大小(batch size)产生 4 倍的变化,并根据前缀缓存(prefix cache)是否命中而产生另一个数量级的差异。将这些汇总成一个单一的 GWh 数字,就像在包含踏板车、轿车和 18 轮大卡车的车队中报告“平均汽车燃油效率”一样——在最无用的层面上,它是准确的。

你的工具结果缓存是一份你从未签署过的过期数据契约

· 阅读需 12 分钟
Tian Pan
Software Engineer

追踪记录看起来很干净。Agent 调用了 get_inventory_status,工具返回了 {"available": 142, "warehouse": "SEA-3"},模型将这些信息编织成了一个自信的回答。客户下单了。仓库却说该商品自上午 9 点以来一直缺货。缓存的行数据是四小时前的。团队中没人决定过四小时是可接受的 —— 这只是平台团队连接包装器(wrapper)时,缓存框架默认的设置。

这种失效模式经常被误归类为幻觉。模型并没有在胡编乱造;它是在忠实地根据一个过期的工具结果进行推理,而没人费心将该结果标记为过期。追踪记录显示的是一次干净的调用和干净的响应,评估集(eval set)从未见过过期缓存的情况,而这种退化在每一个撞上相同 TTL 窗口的客户身上悄无声息地累积。

翻译并非本地化:多语言 AI 正面临的文化校准债务违约

· 阅读需 13 分钟
Tian Pan
Software Engineer

一个多语言发布版本,如果只是将英文提示词翻译成 N 种语言,并将英文评估集也翻译成同样的 N 种语言,那它并没有发布一个真正的多语言产品。它只是将同一个产品发布了 N 次,并让所有的失败模式在它自己的仪表盘上变得不可见。该系统虽然表达流畅,但在文化层面上显得格格不入,而团队优化的指标——翻译质量——并不是衡量用户反应的正确维度。

发布当天的明显缺陷通常很小。一位日本用户收到的回复虽然语法正确,但显得生硬无礼。一位印度尼西亚用户发现助手以一种欢快直接的语体说话,听起来却很不礼貌。一位韩国用户收到的建议是围绕个人选择展开的,而提示词其实是关于家庭决策的。这些都不是翻译错误。它们是文化语体(cultural-register)错误,翻译无法修复,且经过翻译的评估也无法检测出来。

12 个月的 AI 功能悬崖:为什么你的生产模型在无人标记的日历上悄然衰减

· 阅读需 13 分钟
Tian Pan
Software Engineer

一个功能发布时通过率为 92%。发布演示稿(Launch deck)为此庆祝。12 个月后,同样的功能通过率降到了 78% —— 没有事件报告,没有部署失败,没有任何单一的变更可以追责,仅仅是无人负责监控的缓慢侵蚀。团队将其归咎于“幻觉”或“用户行为转变”,选了一名初级工程师去调查,并设定了一个“提高质量”的季度 OKR。OKR 没能达成。该功能上线了一个道歉对话框,告诉用户 AI 有时会犯错。六个月后,它被弃用,取而代之的是一个发布时通过率为 91% 的新版本,循环再次开始。

这并非运气不好。这是 AI 功能运行的“第二时钟”,一个在发布时没有人会在日程表上标注的时钟。传统软件也有功能衰减 —— 依赖漂移、代码库腐化、缓慢积累的半成品重构 —— 但这些衰减是发生在工程团队已经理解并预留预算的时钟上。AI 功能具备上述所有问题,此外还有一系列传统摊销假设无法建模的并行衰减源:模型弃用、厂商权重轮换、用户输入的分布偏移、不断叠加的 Prompt 补丁、评估器(Judge)校准偏移,以及不再能代表生产流量现状的评估集的悄然老化。

在下一个 AI 功能发布之前,而不是之后,必须落地的架构认知是:AI 功能具有非零的基础维护成本。功能在发布时并没有“完成”。它已经进入了一个无法逃避的维护周期,而那些没有为这个周期预留预算的团队,终将通过惨痛的方式发现这一点。

双语问题:为什么类型安全会在提示词边界失效

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的代码库中有两种语言,但只有一种拥有编译器。一种是你的团队编写的严格类型的代码 —— 开启了 strict: true 的 TypeScript、CI 中运行 mypy 的 Python、强制返回值的 Go —— 另一种则是 Prompt:一个模板化的字符串,经过拼接后发送给远程模型,并返回另一个运行时希望能成功解析的字符串。在这两个区域之间,类型系统就成了瞎子。IDE 不会高亮任何内容。编译器不会发出任何报警。而那些凭借“但它通过了类型检查”就发布功能的团队,实际上将核心契约放在了契约检查器看不见的地方。

这个接缝伪装得很好。从外部看,它就像一个函数调用:generate(input: UserQuery): Promise<AgentResponse>。函数签名诚实地反映了输入和输出。而虚假的部分发生在调用点和响应之间:输入被插值到通过字符串引用字段名的 Prompt 模板中;模型被要求生成一个符合该 Prompt 内部以自然语言描述的 Schema 的 JSON 对象;响应作为一个字符串返回并交给解析器;最后解析器返回类型系统终于能再次看到的内容。两端的每一个类型化表达式都在对中间一个完全没有静态保证的区域做出断言。

这并非理论上的担忧。各团队报告称,在生产环境中,朴素的结构化输出基准 Schema 失败率为 10–20%,而且失败往往集中在那些你最无法承受无声丢弃的输入上 —— 长上下文、深层工具链、边缘情况用户。类型系统提供了一种虚假的正确感,直到格式错误的 JSON 返回且运行时将其吞下为止。

Agent 飞行记录仪:在第一次事故发生前必须捕获的字段

· 阅读需 14 分钟
Tian Pan
Software Engineer

当 agent 在生产环境中第一次失控时——它删错了行,给错误的客户发了邮件,在单个任务上烧掉了 400 美元的推理费用,或者对受监管的用户说了法律风险极高的话——团队打开日志,却发现他们实际上拥有的是:一串参数被截断的 CloudWatch 工具调用名,一个只捕获了最新一轮对话的“用户提示词”字段,而且没有记录实际运行的是哪个模型版本。供应商在两周前滚动更新了别名。系统提示词存在于一个没有快照的配置服务中。由于框架默认值是 0.7 且“人尽皆知”,因此没有记录温度。触发错误操作的工具结果超过了日志行大小限制,并被截断为“...”。

你无法重现决策过程。你只能猜测。六个月后,你堆积了一堆无解的“它为什么这么做”的报告,团队开始像对待天气一样对待 agent——把它当作一种发生在你身上的事情,而不是你可以调试的东西。

飞行记录仪准则(Flight recorder discipline)是你为了防止这种情况所能交付的最廉价的东西,但如果你等到第一次事故发生才开始,它也将是你交付的最昂贵的东西。以下字段是最低要求,存储形式不容商量,采样和隐私边界必须同步设计,而不是事后修补。