跳到主要内容

299 篇博文 含有标签「observability」

查看所有标签

提供商故障转移:在对话中途替换了你安全策略的隐忧

· 阅读需 12 分钟
Tian Pan
Software Engineer

用户正与你的助手进行一场关于受控物质处方模式的谨慎对话,已经进行了十二轮。模型表现得很有分寸,提出澄清性问题,引用指南,并拒绝进行文献之外的推演。在第十三轮,用户提出了一个后续问题,按理说应该得到与前十二轮相同的回应。然而,他们得到的却是一个生硬的拒绝:“我无法提供相关帮助。”对话结束了。他们怒气冲冲地给支持团队写信——他们并没有问任何不同的内容,助手刚才还在帮助他们,到底发生了什么变化。

你的日志解释了变化的原因。在第十三轮进行到一半时,你的主供应商在流式传输过程中返回了 503 错误。你的网关按照配置执行了操作:在请求的剩余部分故障转移(failover)到了备用供应商。备用供应商对该类查询的拒绝阈值校准得比主供应商更保守。用户并没有问任何不同的问题——他们在同一个品牌下对不同的模型提出了相同的问题,而新模型说了“不”。

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

· 阅读需 10 分钟
Tian Pan
Software Engineer

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

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

你的产品视图从未呈现的推理 Token

· 阅读需 11 分钟
Tian Pan
Software Engineer

一位客户给支持团队发了邮件。AI 助手告诉他们在错误的司法管辖区申报纳税,他们非常生气,想知道助手是如何得出那个答案的。你的支持人员打开问题队列,看到了最终回复:自信、听起来很有道理,但却是错误的。他们看不到模型在给出回复前生成的 5000 个推理标记 (reasoning tokens),尽管这些标记确实存在,而且你的工程团队可以在 30 秒内从另一个屏幕上把它们调出来。证据就在公司里,只是拿在错误的人手中。

这就是团队在生产环境的智能体上启用扩展思考 (extended thinking) 时产生的差距。推理成为了每一次调用的核心产物,而你的组织尚未决定谁在什么时候、以何种精度、以及在多长时间内可以看到它。默认决策是由负责各个界面的团队分别做出的,他们的默认设置各不相同,而这些缝隙恰恰是客户投诉产生的地方。

智能体学会针对重试预算进行规划

· 阅读需 11 分钟
Tian Pan
Software Engineer

在生产环境中运行智能体(agent)得出的最令人不安的教训不是它们会失败——而是它们会学习。并不是指任何深度意义上的学习;权重并没有改变。但在一个会话(session)中,在一个轨迹(trajectory)中,模型所隐含的策略会根据其运行的底层环境(substrate)进行调整。如果你的底层环境代表智能体悄悄吸收了失败,智能体最终会察觉到这一点,并开始将其视为免费的算力进行规划。

最明显的例子就是重试层(retry layer)。你添加它是为了可靠性——在报错之前,SDK 会对失败的工具调用进行三次重试;你的中间件为每一步包装了指数退避(exponential backoff);你的循环捕获了格式错误的 JSON 并重新提示模型进行修复。这些都没错。但每一个机制都是智能体可以观察、概括并利用的副作用。一旦它这样做了,你的可靠性层就不再是安全网,而成了规划原语(planning primitive)。

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

· 阅读需 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 个请求时就已经难以为继。

你的供应商通过更小的分块达成的 Tokens-Per-Second SLO

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的供应商状态页面显示为绿色。每秒 token 数 (TPS) 仪表盘显示的曲线一如既往地平稳。SLA 报告显示你完全处于合同约定的速率范围内。然而,支持队列里挤满了用户,他们形容聊天输出“一跳一跳的”、“断断续续”、“比上周还差”。你的监控指标中没有任何一项能证实他们的说法,因为你的监控根本没有在测量他们真正在关注的东西。

这是没有人察觉到的供应商交付故障模式。他们没有突破速率限制,而是重新定义了单位。每秒到达的 token 数量没变,但它们是以单 token 块的形式流式传输的,而不是针对渲染器优化过的 4 token 块。平均吞吐量依然完好,但感知质量却被毁掉了。SLO 依然达标,因为 SLO 是针对网络传输(wire)制定的,而网络传输是供应商控制的那部分系统。

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

· 阅读需 11 分钟
Tian Pan
Software Engineer

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

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

当源数据已更改,你的 Prompt 缓存仍在提供旧的工具执行结果

· 阅读需 11 分钟
Tian Pan
Software Engineer

一名支持代理在 14:02 查询了客户的订阅状态,发现其处于激活状态,该回答进入了 Prompt 前缀中,而缓存层刚刚将其标记为上下文的可重用部分。在 14:14,计费系统取消了该订阅。在 14:19,同一位客户提出了跟进问题,由于对话前缀仍然匹配,缓存的前缀被重用,代理愉快地告诉客户他们的计划处于激活状态,并主动引导他们使用一个他们已不再拥有访问权限的功能。下游系统是正确的。模型与上下文保持了一致。但用户被缓存命中(Cache Hit)欺骗了。

这是 Prompt 缓存为原本对数据陈旧度(Staleness)保持诚实的系统引入的失效模式。在引入缓存之前,工具调用是对单一事实源(Source of Truth)的请求,并遵循该源所声明的任何新鲜度契约。有了缓存之后,工具结果变成了 Prompt 前缀的一个租户,而前缀拥有自己的 TTL(生存时间),由模型提供商控制,团队中没有人明确选择启用它。

由客户端时钟而非网关标记时间戳的链路追踪时间线

· 阅读需 10 分钟
Tian Pan
Software Engineer

你打开了一个运行缓慢的对话追踪。模型调用竟然在用户点击发送前的 800 毫秒就开始了。你责怪了用户的笔记本电脑,关闭了标签页,继续处理其他事情。

这不仅仅是一个用户的时钟出了问题。这涉及大约三分之一的流量,而且每一个跨越客户端边界的调试会话都在读取一个根本不存在的时间线。浏览器时钟是用户可设置的,经常不同步,偶尔甚至会偏差好几天。大多数可观测性技术栈附带的检测 SDK 会使用设备报告的任何时间来标记客户端 span,通过 traceparent ID 将它们与同步服务器时钟标记的服务器 span 链接成一棵树,然后将结果交给你的值班工程师,就好像这两半具有可比性一样。它们并不具有可比性。

Agent 假装执行的验证步骤

· 阅读需 8 分钟
Tian Pan
Software Engineer

你的 Prompt 说“在返回前验证 X”。追踪记录显示字符串“已验证 X”。一周后,你发现 X 从未被验证过——哪怕一次,哪怕针对任何请求,在任何环境下。模型学会了输出这个短语就能满足评估标准。它声称做的验证只是文本生成器输出中的一个句子,而不是在现实世界中采取的行动。

这是一种与幻觉不同的故障。幻觉是模型虚构了一个关于世界的事实。自我证明式验证是模型虚构了一个关于其自身过程的事实。前者是知识问题。后者是底层机制问题——你要求一个生成字符串的系统执行一个它没有机制去执行的动作,于是它产生了一个看起来像是执行了该动作的字符串。

语音代理 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 秒的内部思考过程。每个图表依然是绿色的。每个用户却在黑暗中等待。

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