跳到主要内容

17 篇博文 含有标签「streaming」

查看所有标签

流式响应追踪模式鸿沟:为什么你的 APM 在 LLM 延迟上撒了谎

· 阅读需 12 分钟
Tian Pan
Software Engineer

凌晨 02:14,报警器响了:客户反馈助手在回答长问题时“话说到一半就卡住了”。你打开追踪(trace)。LLM 调用的 span 显示为 8.4 秒 —— 绿色,在 SLO 范围内,没有错误属性,结束原因(finish reason)为 stop。仪表板上该端点的 p95 延迟聚合组件显示为 9.1s,与过去一个月的情况完全一致。根据 APM 显示的所有信号,该请求都成功了。

用户看到前 200 毫秒表现完美,接下来的四秒钟生成了一个连贯的段落,然后眼睁睁看着同样的三句话片段在剩下的四秒钟里不断重复,直到连接结束。这种卡住的内容循环(stuck content loop)是真实的故障,但追踪系统对此一无所知 —— 因为追踪系统是为“返回即结束”的系统设计的,而不是为了这种行为表现为生成过程中产生的中间状态之墙的系统。

流式推理中的海勒姆定律:节奏、停顿和中间 Token 是未成文的契约

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个团队从前沿模型升级到了其更快的后继版本。评估套件(eval suite)全绿。最终答案一致。工具调用的 Schema 完全相同。结构化输出通过了与以往一样的 JSON Schema 验证。他们发布了。不到一天,支持票据就堆积如山:“助手感觉太匆忙了”,“它不再真正思考了”,“感觉不对劲”。产品经理调取了遥测数据,发现任务完成率没有变化。工程团队反复检查了评估和 Schema,没发现任何问题。投诉是真实的,但团队定义的契约——就如团队所定义的那样——依然完好无损。

改变的是流的纹理(texture)。旧模型在调用工具前会停顿 800 毫秒,发出一句“让我查一下……”的前导词,并以每秒约 35 个 Token 的速度输出,在子句边界处有自然的节奏。新模型以每秒 90 个 Token 的速度输出,从不停顿,且完全跳过了前导词。这些都没有出现在任何文档记录的契约中。但所有这些都是不可或缺的“承重”部分。

这就是海勒姆定律(Hyrum's Law),而流式传输(streaming)让它的表面积变得巨大。系统的任何可观察行为都会被某人所依赖——而流式 AI 界面暴露的可观察行为远比团队意识到的要多。

流式工具结果破坏了请求-响应式智能体规划器

· 阅读需 11 分钟
Tian Pan
Software Engineer

SQL 工具在数据从网络线路传出时即发送行。智能体调用它并期待得到结果。而一年前编写的运行环境(当时所有工具都是请求-响应式的)在调用模型之前,会尽职地将整个流缓冲成一个单一字符串。40 秒后,缓冲区达到了 200 KB,上下文窗口被消耗了一半,智能体正在对一个查询的第 47,000 行进行推理,而它本可以在第 30 行就停止。没有人故意设计这种失败——这仅仅是因为将“工具已返回”视为规划器唯一响应事件的结果。

向流式工具的转变正在规划器尚未察觉的情况下发生。SQL 引擎发出渐进式结果集。文档提取器生成分页。搜索 API 在相关性评分稳定后按批次返回命中结果。MCP 的 Streamable HTTP 传输协议(2025-03-26 规范中 HTTP+SSE 的替代方案)使增量响应成为一流的传输模式,而不再是一项稀有的功能。传输层已经准备就绪,但其上的规划器还没有。

流式结构化输出:为什么你的解析器会在第 47 个 Token 处卡住

· 阅读需 12 分钟
Tian Pan
Software Engineer

团队第一次构建带有结构化输出的流式 AI 功能时,遇到的 bug 总是如出一辙。模型生成正常,数据块(chunks)接收正常。但在第 47 个 token 左右,解析器挂掉了,UI 冻结了,或者更糟——一个半成型的枚举(enum)值被路由到了下游工具,导致其悄无声息地执行了错误操作。团队在 JSON.parse 周围加了一个 try/catch,觉得自己搞定了,然后发布。两周后,兄弟团队抱怨响应变长后流式 UI 感觉很卡。一个季度后,事故审查询问为什么在一个模型仍在描述为 "DeleteIfEmpty" 的记录上触发了 "Delete" 工具调用。

Bug 不在任何单个 token 中。Bug 在于 token 流式传输和结构化输出在架构上是冲突的,而大多数框架只是用“祈祷”来掩盖这种冲突。Schema 说“这是一个完整的对象”。Token 流说“这是一次一个字节的数据”。从定义上讲,这两个端点之间的每一个中间状态对于 Schema 来说都是无效的。团队的工作是决定在这些中间状态期间该做什么——而大多数团队并没有明确做出这个决定。

Token 间抖动:你的 p95 仪表盘看不见的流式传输 UX 失败

· 阅读需 13 分钟
Tian Pan
Software Engineer

你的延迟仪表盘显示一切正常。p95 的首字延迟(TTFT)低于 800ms 的目标。p99 的总生成时间也在 4 秒的预算之内。然而,一位资深 PM 转发了一个支持线程:“助手在回答中途卡住了大约三秒钟”,“它停顿了一下,然后突然吐出一整段文字”,“我以为它死机了”。本周有三位用户因为同样的投诉卸载了应用。团队中没人能在笔记本电脑上重现这个问题,而且你记录的每一项指标都显示系统运行健康。

能解释这个 Bug 的指标正是你没在测量的那个:连续 Token 之间时间间隔的分布。一个看起来很完美的 p95 总时长可能会掩盖这样一种流:其中 8% 的响应在生成中途包含一个 2.5 秒的停顿。对于一个看着字符实时出现的用户来说,这种停顿意味着系统出故障了,而不仅仅是慢。你的仪表盘测量的是电影的总时长,而你的用户正在观看电影。

投机采样(Speculative Decoding)是一项流式传输协议决策,而非推理优化

· 阅读需 14 分钟
Tian Pan
Software Engineer

每一篇关于投机解码(Speculative Decoding)的论文中提到的“等效输出”保证,其实是对 token 分布的保证,而不是对用户所见内容的保证。仔细阅读证明过程,你会发现一个纯粹的数学等效性:拒绝采样的接受标准旨在确保投机后的输出分布与目标模型(target model)独立生成的分布完全一致。这一保证约束的是离开推理引擎的字节流,而对于五百毫秒前已经到达用户屏幕、现在却必须收回的字节,它只字未提。

如果你在小模型生成草稿 token 的那一刻就将其流式传输给客户端,那么每当验证器拒绝某个后缀时,你实际上是在对自己的用户进行 A/B 测试。半个段落会自行重写。函数名在 IDE 已经完成语法高亮后发生改变。语音合成(TTS)可能已经读出了“答案很可能是否定的”,随后验证器却将其替换为“答案是肯定的,但有几点需要注意”。数学逻辑上,最终分布与慢速路径一致;但从用户体验来看,他们亲眼目睹了模型在公开场合“反悔”。

这是投机解码中未被计入加速倍数的部分。它也将所谓的“免费 3 倍吞吐量”变成了一个没人预料到的、长达一个半季度的流式协议开发工作。

流式 JSON 解析器:Token 与类型化对象之间的鸿沟

· 阅读需 13 分钟
Tian Pan
Software Engineer

模型正在逐个 Token 地输出 JSON。你的 UI 希望在字段出现的那一刻就进行渲染 —— 在冗长的回答正文之前显示置信度得分,或者在模型填充工具调用参数时实时显示它们。接着,有人尝试在每个数据块(chunk)上调用 JSON.parse,结果整个系统就崩溃了,因为 JSON.parse 是“全或无”的。它需要一个结构完整的文档才能返回任何结果。在模型输出闭合括号之前,你什么也显示不出来。

这不是一个可以通过 try/catch 解决的解析器问题。标准 JSON 解析器是针对内容长度已知的 HTTP 响应设计的。部分输入并不是它所建模的状态 —— 而是被视为“输入错误”。当你将 Token 流视为 HTTP 正文处理时,你继承了三十年来“文档要么完整,要么无效”的传统,而你的 UI 则为此付出了代价。

取消税:用户点击停止后的推理账单

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的停止按钮是个谎言。当用户点击它时,你的 UI 停止渲染 Token;但在大多数配置下,你的供应商仍在继续生成它们。这些字节从未到达浏览器,但却出现在你的发票上。用户看到的与你支付的之间的差距就是“取消税”(cancellation tax),它是 AI 成本仪表盘上被低估最严重的支出项。

取消税的存在是由结构性原因导致的。自回归推理是一个受 GPU 限制的流水线:当你的客户端关闭 TCP 连接时,模型已经排好队、完成了 KV 缓存,并正以每秒 30–200 个 Token 的速度输出。大多数推理服务栈在 Token 之间不会检查客户端的活跃状态。它们完成任务,记录用量,然后向你收费。客户端看到了 10 个 Token,而日志记录了 800 个。Langfuse、Datadog 以及所有其他观测平台都会忠实地报告这 800 个 Token,因为那是供应商 usage 数据块报告的内容。

输出承诺问题:为什么流式自我纠正比原始错误更损害用户信任

· 阅读需 12 分钟
Tian Pan
Software Engineer

用户向你的智能体提问。Token 开始流式输出。写到第三句时,模型写道“实际上,让我重新考虑一下——”并转向一个不同的答案。修改后的答案更出色。用户却关闭了标签页。

这就是输出承诺问题(Output Commitment Problem),它是已发布 AI 产品中被低估得最严重的 UX 失败案例之一。工程师思维将自我修正视为一项特性——模型注意到了自己的错误,这意味着系统正按预期运行。而用户感知思维则将其视为一场灾难——产品现场演示了其最初自信的断言是错误的。这两种解读都是正确的,且它们本身无法调和。

核心的不对称性在于,流式传输让思考过程变得清晰可见,而清晰的思考就是可审计的思考。一个静默地产生幻觉然后给出简洁最终答案的模型看起来很专业。而同一个模型,如果流式输出每一个不成熟的想法,看起来就像是在胡言乱语。答案的质量是相同的,但感知却截然不同。

首字延迟 (TTFT) 是你尚未监测的延迟 SLO

· 阅读需 12 分钟
Tian Pan
Software Engineer

调出过去一周的生产环境追踪记录,查看你的延迟仪表板。你几乎肯定在总请求延迟上设置了 p50 和 p99。你可能还有令牌吞吐量(token throughput)。你甚至可能有一张每秒令牌数(tokens-per-second)图表,因为某个供应商的基准测试说服你这么做了。但你几乎肯定没有的是按模型、按路由、按租户划分的**首字时间(time to first token, TTFT)**直方图 —— 这是决定你产品感知速度的核心指标。

这绝非一个小疏忽。对于任何流式界面 —— 聊天、代码补全、智能体侧边栏、语音 —— 用户感知的速度取决于在内容出现之前,他们盯着闪烁光标的时间。一旦第一个令牌(token)出现,用户就开始进入阅读状态;随后的令牌是在与他们的阅读速度竞争,而不是与他们的耐心竞争。总延迟(Total latency)对于吞吐量规划和成本预算很重要,而 TTFT 则决定了产品是否让人感觉“有生命力”。

这两个数字之间的差距正在拉大。推理模型(Reasoning models)产生的总延迟可能与其非推理兄弟模型完全相同,但却会将 TTFT 从 400 毫秒推高到 30 秒。一个“保持延迟持平”的路由更改,可能会悄无声息地将一个反应灵敏的助手变成一个卡死的窗口。如果你没有对 TTFT 进行图表化,你就是在发布连你自己都察觉不到的 UX 退化。

延迟感知差距:为什么3秒的流式响应比1秒的批量响应感觉更快

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的用户没有秒表,他们只有感觉。而这些感觉与时钟现实的偏差,对你构建AI界面的方式至关重要。一个逐字出现、持续三秒的响应,用户普遍感觉比一秒后突然全部出现的批量响应更快——尽管批量系统在客观上更快。这不是非理性的,也不是人类认知的缺陷,而是一种有据可查的感知现象。如果你在构建AI产品时没有考虑这一点,你就是在为错误的指标做优化。

本文将剖析延迟感知背后的心理学、真正预测用户满意度的指标、利用这些感知特性的前端模式,以及何时流式传输会带来比价值更多的复杂性。

LLM 应用中的 SSE vs WebSockets vs gRPC Streaming:那个稍后会让你头疼的协议抉择

· 阅读需 13 分钟
Tian Pan
Software Engineer

大多数构建 LLM 功能的团队选择流式协议的方式就像选择字体一样:快速、不加思索,然后忍受由此带来的后果多年。这种选择第一次让你踩坑通常是在生产环境中——比如 CloudFlare 524 超时导致你的 SSE 流损坏,WebSocket 服务器在持续负载下发生内存泄漏,或者 gRPC-Web 集成在单元测试中表现良好,但在客户端需要向上游发送消息时静默失败。协议决定了你的故障模式。基于基准吞吐量进行选择是一个错误的切入点。

每个主要的 LLM 提供商——OpenAI、Anthropic、Cohere、Hugging Face——都通过 Server-Sent Events (SSE) 流式传输 Token。这一事实是一个强有力的先验理由,但并不是因为 SSE 快。而是因为 SSE 是无状态的,能与 HTTP 基础设施轻松兼容,且其故障模式是可预测的。问题的关键在于你的应用是否有某些需求迫使你偏离这条路径。