流式与单次两种模式怎么选:Claude Agent SDK 把差异划在哪

2026-08-18

先说清楚这篇要解决的困惑。假设你要给自己的程序接上 Claude Agent SDK,希望输出「边生成边往外吐字」而不是等一整段生成完再一次性打印,去翻文档,会翻出两页:一页叫 Streaming Input(code.claude.com/docs/en/agent-sdk/streaming-vs-single-mode),一页叫 Stream responses in real-time(code.claude.com/docs/en/agent-sdk/streaming-output)。名字都带 stream,标题都带流式。

这两页讲的不是同一件事,而且官方文档自己在第二页顶部就提示了这一点:那一页写的是 output streaming(实时接收 token),输入模式(你怎么把消息发进去)请看另一页。这个岔口很容易走错——按文档口径,把输入模式选成流式,并不等于输出就会以增量形式给你。这是两个互相独立的选择。

一、第一层选择:输入怎么进去

streaming-vs-single-mode 这一页的写法,Claude Agent SDK 支持两种输入模式,就两种:

  • Streaming Input Mode:一个持续存在的交互式会话(a persistent, interactive session)
  • Single Message Input:一次性查询,靠会话状态与 resuming 来续接(one-shot queries that use session state and resuming)

文档把 Streaming Input Mode 标为 preferred(首选),紧接着的一句描述是:它让 agent 作为一个长生命周期的进程运行,能接收用户输入、处理中断、把权限请求呈现出来、并管理会话。文档只写到这里,没有再解释为什么这样设计,我也不替它补。

流式输入模式下,文档列了五项能力:图片上传(直接把图片附在消息上)、消息排队(发多条消息按顺序处理,且可以打断)、工具集成(会话期间可完整访问所有工具与自定义 MCP 服务器)、实时反馈(看到响应生成过程而不只是最终结果)、上下文持续(多轮之间自然保持对话上下文)。

单次输入这一侧,文档给的适用条件同样很具体,三条:

  1. 你只需要一次性的响应
  2. 你不需要图片附件,也不需要会话中途的控制方法(mid-session control methods)
  3. 你需要在无状态环境里运行,例如 lambda 函数

这三条里第三条是硬约束——它不是「哪个更好」的问题,是你的运行环境根本不给你一个长活进程。这也是我认为选型时应该最先问自己的一句:我这段代码有没有资格持有一个活着的会话? 没有,后面的讨论就都不用展开了。

二、单次模式明说不支持的四项

这是本篇的落点之一:不可兼得项在文档里是白纸黑字列出来的,不需要猜。 streaming-vs-single-mode 页面用一个 Warning 框写明,单次输入模式支持这四项:

  • 消息中直接附带图片(Direct image attachments in messages)
  • 动态消息排队(Dynamic message queueing)
  • 实时打断(Real-time interruption)
  • 自然的多轮对话(Natural multi-turn conversations)

注意最后一条的措辞。单次模式并不是完全不能续接——文档同一页的示例里就用了会话续接:TypeScript 侧是 options 里给 continue: true,Python 侧是 ClaudeAgentOptions(continue_conversation=True, ...)。所以准确的读法是:续接是有的,但它是靠会话状态和 resuming 拼出来的,不等于流式模式里那种「自然的多轮」。 文档把 single message input 概括为 “simpler but more limited”,这个 more limited 具体就落在上面四条上。

反过来讲,如果你的活儿是「跑一次、拿个结果、进程就退出」,比如 CI 里跑一遍检查、或者一个云函数被触发一次,那这四项你一项也用不上,选单次就是对的。

三、第二层选择:输出要不要增量

到这里才轮到最开始那个「边生成边吐字」的诉求。streaming-output 这一页开头写明:默认情况下,Agent SDK 在 Claude 生成完每条响应之后才 yield 完整的 AssistantMessage 对象。要拿到增量更新,得单独开一个开关:

  • Python:include_partial_messages=True
  • TypeScript:includePartialMessages: true

打开之后,SDK 会在原有的 AssistantMessageResultMessage 之外,额外 yield StreamEvent 消息,里面装的是原始 API 事件。文档明确写了你的代码需要做三步:

  1. 检查每条消息的 type,把 StreamEvent 和别的消息类型区分开
  2. StreamEvent,取出 event 字段,检查它的 type
  3. content_block_delta 事件里 delta.typetext_delta 的那些,那才是真正的文本块

官方文档的 TypeScript 示例原样是这样:

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "List the files in my project",
  options: {
    includePartialMessages: true,
    allowedTools: ["Bash", "Read"]
  }
})) {
  if (message.type === "stream_event") {
    const event = message.event;
    if (event.type === "content_block_delta") {
      if (event.delta.type === "text_delta") {
        process.stdout.write(event.delta.text);
      }
    }
  }
}

三层嵌套判断看着啰嗦,但它反映了一件事:StreamEvent 里装的是原始事件,不是累积好的文本。 文档在 StreamEvent reference 一节里把这句话说得很直白——两个 SDK 里的这个类型(Python 是从 claude_agent_sdk.types 导入的 StreamEvent,TypeScript 是 type: 'stream_event'SDKPartialAssistantMessage)装的都是 raw Claude API events,文本增量需要你自己提取并累加

event 字段里常见的事件类型,文档列了一张表,六种:message_startcontent_block_startcontent_block_deltacontent_block_stopmessage_deltamessage_stop。工具调用同样是走这套增量事件的,文档给的是三个事件点的组合:content_block_start 表示工具开始,content_block_deltadelta.typeinput_json_delta 时用 partial_json 一块块拼参数,content_block_stop 表示这次工具调用的参数收齐了。

四、开了输出流式,仍然拿不到的东西

这一节是我觉得最值得单独拎出来的部分,因为它不写在「限制」标题下面,散落在正文里,很容易漏读。

第一,subagent 的 token 级增量拿不到。 文档写明:stream events 只为主会话发出,subagent 的 token 级 deltas 不会被转发。如果你要把输出归属到某个 subagent 上,得用完整消息——完整消息带 parent_tool_use_id。而 StreamEvent 这个结构里的 parent_tool_use_id 字段,Python 侧恒为 None、TypeScript 侧恒为 null。也就是说,「实时逐字显示」和「知道这段字是哪个 subagent 说的」在当前文档口径下拿不到同一份数据,你得两种消息都收。

第二,structured output 没有增量。 这一条在 Known limitations 里:JSON 结果只出现在最终的 ResultMessage.structured_output 里,不会以流式 delta 的形式给你。所以「一边生成一边把 JSON 字段填进界面」这种做法,按这一页的说法不成立。

第三,TypeScript 侧有个只在特定事件上出现的字段值得留意SDKPartialAssistantMessage 类型里有 ttft_ms?,文档注释写的是 time to first token(毫秒),并注明只在 message_start 事件上存在。别在别的事件上去读它。

顺带一提,不开 partial messages 时你收到的消息类型,文档也点了名:SystemMessage(会话初始化)、AssistantMessage(完整响应)、ResultMessage(最终结果),以及一条表示对话历史被压缩的 compact boundary 消息——TypeScript 里叫 SDKCompactBoundaryMessage,Python 里是 subtype 为 "compact_boundary"SystemMessage。同一件事在两个 SDK 里叫法不同,这种地方最容易在跨语言移植时踩空。

五、出错的形态也不一样

选模式的时候没人会想到这一层,但真出问题时它比功能差异更难定位。文档里这两段值得单独抄下来贴在自己项目的注释里。

流式输入模式下:TypeScript SDK 里,如果你的消息生成器抛异常(例如它要读的文件不存在),流会以一个错误结束,而错误文本是 Claude Code process aborted by user不是原始错误。文档因此建议:看到这句话时先去查你生成器里面的代码。而且这个错误前面可能还跟着一长行压缩过的 SDK 源码,要读到输出末尾才能看到真正的错误文本。Python SDK 里更隐蔽——生成器异常只在 debug 级别记日志,会话就那么卡住、不抛异常;所以流式会话挂着没输出时,开 debug 日志去查你的生成器。

单次模式下:如果一次查询以错误结果收场(文档举的例子是 error_max_turns),query() 会在 yield 出最终 result 消息之后抛出一个包含失败文本的错误。所以文档的建议是,如果你的代码需要在这之后继续跑,就把循环包在 try 里。Python 侧文档还补了一句提示:SDK 对错误结果抛的是一个普通的 Exception,所以捕获时就按 Exception 匹配。

还有一个只在 Python 流式侧出现的坑:receive_response() 这个循环会在第一条 result 消息处结束。文档举的例子里,生成器 yield 了两条消息,但 Python 版本只会打印出第一条的分析结果;要两条都读到,得按文档说的,每条消息各配一对 query()receive_response()

六、平台差异这一层,文档没有讲

这两页里没有任何针对 Windows 与 Linux/macOS 的分别说明,安装方式、路径写法、shell 差异一概没提。官方文档没有说明这一点,所以这里不编。

唯一沾边的是流式输入示例里读图片那一句——TypeScript 是 await readFile("diagram.png", "base64"),Python 是 with open("diagram.png", "rb") as f:。文档在示例前特意加了一句说明:这些示例读的是工作目录下名为 diagram.png 的图片,你得先在那里放一张,或者把文件名改成你自己的图片。这是相对路径,Windows 上同样以进程的当前工作目录为准——但这属于语言运行时的通用行为,属于通用做法、不是这两页文档的内容,标出来只是提醒你:同一份脚本在不同工作目录下启动,diagram.png 能不能被找到是不一样的,把它挪进 <你的项目目录> 时要一并确认启动目录。

七、把选择收拢成一条路径

把上面这些文档依据串起来,选择顺序可以这么排:

先问运行环境。在 lambda 这类无状态环境里——文档点名的场景——只能走单次输入,那么图片附件、消息排队、实时打断、自然多轮这四项就直接放弃,别在架构里给它们留位置。

能持有长活进程,再问要不要那四项里的任意一项。要图片、要中途打断、要排队,就是流式输入模式;文档也把它标为 preferred。

然后单独决定输出。想要逐字显示,就把 include_partial_messages / includePartialMessages 打开,并接受随之而来的三件事:文本要自己累加、subagent 的 token 增量收不到、structured output 只在最终 ResultMessage.structured_output 里。不想处理这些嵌套判断,就别开,默认拿完整的 AssistantMessage 反而省事。

最后把错误路径按模式对号入座写好:单次模式包 try、流式模式把生成器内部的异常自己兜住(TypeScript 会被改写成那句 aborted 文本,Python 会静默卡死)。

以上的模式组合是按官方文档中的字段语义与适用条件整理的示例路径,未经实测;代码片段原样引自官方文档,以官方文档最新内容为准。这个产品迭代频繁,字段名、默认行为与限制随版本变动,别把这篇当作参数手册用——它只是帮你知道该去文档的哪一页翻哪一段。


本文依据 Claude Code 官方文档(code.claude.com/docs)于 2026-08-17 的公开内容整理。 该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。 该产品迭代频繁,文中涉及的命令、配置项与默认值随版本变动,请以官方文档最新内容为准。 本文不涉及价格、额度与限流的具体数值,相关信息请以官方定价与用量说明页为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。