Files
claude-code/docs/conversation/streaming.mdx
claude-code-best 66131a7b76 docs: 重写流式响应,移除 API 事件类型枚举和代码片段
从核心设计选择讲起(流式不只是打字机效果),
将错误处理重组为三层防护体系,
增加 Provider 适配策略对比表和设计考量分析。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 00:32:48 +08:00

112 lines
6.1 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "流式响应"
description: "为什么流式是 Claude Code 的核心设计选择?理解流式传输的设计考量、错误处理策略和多 Provider 适配。"
keywords: ["流式响应", "SSE", "streaming", "API streaming"]
---
## 为什么流式是核心设计
想象 AI 需要 30 秒才能生成完整回答——如果等 30 秒后才一次性显示,用户体验是灾难性的。
但流式不仅仅是为了"打字机效果"。在 Claude Code 中,流式是整个系统架构的基础假设:
- **工具并行执行**AI 在流式输出过程中就可能发出工具调用,系统可以立即开始执行,不必等整个响应结束。这使得"AI 边想边做"成为可能
- **实时反馈**:用户看到 AI 的思考方向后可以提前判断是否正确,必要时提前中断
- **可取消性**:流式架构天然支持用户中断——随时可以终止正在进行的流
- **工具执行反馈**:不仅是 AI 输出是流式的,工具执行(如 shell 命令)的输出也是流式的——用户实时看到命令输出
**代价**:流式架构比一次性响应复杂得多——需要处理连接中断、部分数据、乱序事件等边界情况。
## 流式响应的概念模型
一次流式响应不是"一个完整的回答",而是一系列按顺序到达的事件流:
```
消息开始
├── 内容块 1文本 "我来帮你修复这个 bug。"
├── 内容块 2工具调用 { name: "Read", input: "..." }
├── 内容块 3文本 "我看到了问题..."
└── ...
消息结束(包含停止原因和 token 用量)
```
关键设计点:
### 内容块的增量累加
每个内容块(文本、思考、工具调用)都是通过增量数据逐步构建的。文本逐字到达,工具调用的 JSON 参数逐段到达。系统需要在内存中持续累加这些片段,直到一个内容块完整后才传递给消费者。
### 多消息产出
因为一次 AI 响应可能包含多个内容块(文本和工具调用交替出现),每个完整的内容块都会触发一次消息传递。这意味着一个 API 响应会产生**多条消息**——文本消息和工具调用消息交替产出。
### 停止原因的回写
AI 最终是"回答完毕"还是"需要调用工具",这个信息要到最后才知道。所以停止原因是在消息结束时**回写**到最后一条消息上的——消费者在收到中间消息时还不知道整轮对话是否结束。
## 错误处理:三层防护
流式连接比一次性请求脆弱得多——网络波动、服务器过载、连接超时都可能导致中断。系统设计了三层防护:
### 第一层:被动停滞检测
系统记录每个事件到达的时间间隔。当间隔超过 30 秒时,记录为一次"停滞"并写入遥测。这是被动检测——只在下一个事件到达时才能发现之前的停滞,不会主动中断流。
**设计考量**:为什么不立即中断?因为 API 可能在做长时间的计算(如复杂推理),短暂的无响应不一定意味着故障。被动检测提供了观测能力,而不影响正常流程。
### 第二层:主动空闲超时
如果 90 秒内没有收到任何事件(可通过环境变量配置),系统主动终止流并进入重试流程。
**设计考量**:这是兜底机制。真正的故障不能靠被动检测发现——因为被动检测依赖于"下一个事件到达",而如果连接已经死了,下一个事件永远不会到达。
### 第三层:非流式降级
作为最后手段,系统可以回退到非流式请求——一次性获取完整响应。失去了实时性,但保证了功能可用性。
### 降级策略对比
| 策略 | 检测方式 | 响应时间 | 用户体验 |
|------|----------|----------|----------|
| 正常流式 | — | 最低延迟 | 逐字显示 |
| 被动停滞检测 | 下一个事件到达时 | 不变 | 无感知 |
| 主动超时中断 | 定时器触发 | 中断后重试延迟 | 短暂停顿后恢复 |
| 非流式降级 | 重试失败后 | 等待完整响应 | 等待后一次性显示 |
## Token 超限的两种场景
两种不同的 token 超限需要不同的处理策略:
| 场景 | 含义 | 处理方式 |
|------|------|----------|
| **输出超限** | AI 话说了一半被切断 | 提升输出上限重试,或提示 AI "接着说" |
| **上下文窗口超限** | 整个对话历史太长,塞不进 API | 触发自动压缩,用 AI 摘要替代原始对话 |
关键区别:输出超限是"AI 话太多",可以通过调整上限解决;上下文超限是"给 AI 看的东西太多",必须通过压缩或删除来减少。
## 工具执行的流式反馈
不仅是 API 响应是流式的,工具执行本身也是流式的。例如 BashTool 执行 shell 命令时,命令的标准输出会实时推送给 UI——用户不需要等命令完全结束才能看到结果。
长时间运行的命令还支持**自动后台化**如果命令执行超过一定时间系统自动将其移到后台AI 可以继续处理其他任务,命令完成后再回调结果。
## 多 Provider 适配
系统支持 7 种 API Provider每种有不同的流式协议和认证方式
| Provider 类别 | 流式方式 | 设计挑战 |
|---------------|----------|----------|
| Anthropic 直连 | 原生 SSE | 基准实现,其他 Provider 对齐它 |
| 云平台Bedrock/Vertex | SDK 封装的流式接口 | 需要适配认证、beta header、参数格式 |
| 第三方兼容OpenAI/Gemini/Grok | 各自的流式协议 | 需要转换为 Anthropic 内部格式 |
**设计策略**:所有 Provider 通过统一的流式抽象层屏蔽差异。上层代码(编排层、交互层)不需要关心底层用的是哪个 Provider——它们只看到统一的事件流。
这意味着切换 Provider 不需要修改任何业务逻辑,只需要在通信层适配新的协议。这也是为什么"通信层"是独立的一层。
## 接下来
- **多轮对话** — 理解跨迭代的上下文管理
- **上下文压缩** — 深入了解 token 超限时的自动压缩机制
- **工具系统** — 了解工具执行的并行策略