Claude Agent SDK 把「如何把消息送进 Agent」拆成了两种截然不同的输入模式:流式输入(Streaming Input)把 Agent 当作长期运行的进程来交互,单消息输入(Single Message Input)则退化为一次性的「一问一答」。两者的会话生命周期、上下文持久化能力与中断控制权差异巨大,混用会导致功能缺失或资源浪费。
一、两种输入模式的总体差异
| 维度 | 流式输入模式(默认推荐) | 单消息输入模式 |
|---|---|---|
| 会话性质 | 长期、持久、交互式进程 | 一次性、无状态查询 |
| 客户端入口 | ClaudeSDKClient(Python)/ query({ prompt: AsyncGenerator })(TS) |
单次 query() / claude_code.query() |
| 上下文持久 | 自然保持多轮上下文 | 通过 continue: true 或 session 管理间接续接 |
| 图像附件 | 支持 | 不支持 |
| 消息排队 | 支持 | 不支持 |
| 实时中断 | 支持 | 不支持 |
| Hooks / 权限回调 | 全面支持 | 不支持 |
| 适合场景 | 富交互、长任务、Agent 化应用 | 一次性问答、Lambda 等无状态环境 |
把这两类模式想成「打电话」和「发电报」的差别,会比读文档更直接。
二、生命周期:从连接到关闭
2.1 流式输入:常驻进程
流式输入模式下,应用通过 connect 启动一个长连接,Agent 进程随之常驻。客户端可以反复调用 query() 发送新消息,每条消息都会被排入队、按序处理;处理过程中,应用通过 receive_messages() 异步迭代器实时拿回部分结果。整个生命周期由应用显式控制——disconnect() 显式结束,interrupt() 在中途取消正在进行的任务。
import asyncio
from claude_agent_sdk import ClaudeSDKClient
async def main():
async with ClaudeSDKClient() as client:
await client.query("Analyze this codebase for security issues")
async for message in client.receive_response():
# 实时处理部分响应
if message.get("type") == "text":
print(message["text"], end="")
# 第二轮:上下文自动保持
await client.query("Now focus on the auth module only")
asyncio.run(main())
2.2 单消息输入:一问一答
单消息输入模式下,每次调用 query() 都是一次独立的请求。即使通过 continue: true 复用 session,应用也无法在请求中途插入新消息、无法动态排队、也无法中断已经发出的请求——它从结构上就是为了无状态环境设计的。
from claude_agent_sdk import query
# 一次性查询
async for message in query({
"prompt": "Explain the authentication flow",
"options": {"max_turns": 1}
}):
if message["type"] == "result":
print(message["result"])
两段代码的差别仅在表面:背后跑的是两种完全不同的进程模型。
三、上下文持久化:显式 vs 隐式
流式输入的上下文是隐式持续的:客户端只持有同一份会话状态,每条新消息自动追加到历史里,模型不需要任何「我之前说过什么」的提示。单消息输入的上下文则是显式续接:每次调用要么独立(无历史),要么依赖 continue: true 串接到指定的 session id,应用必须自己管 session 生命周期。
工程上的取舍:流式输入省心但要求常驻进程;单消息输入灵活、易水平扩展,但不能在请求中途追加新上下文。
四、中断处理:主动权在谁手里
只有流式输入模式支持真正的实时中断。client.interrupt() 会发送一个中断信号,让 Agent 立即停下当前轮次(工具调用、模型推理、文件写入都会被中止),之后你可以发新指令接续。这种能力在「用户改主意」或「触发了不该继续的操作」时尤为关键。
单消息输入模式没有等价的接口。一旦请求发出,只能等它跑完或依赖超时,无法在请求中途插入新方向。
五、能力对比:Hooks、图像、消息队列
| 能力 | 流式输入 | 单消息输入 |
|---|---|---|
| 在消息里附 base64 图像 | ✅ | ❌ |
| 排队多条消息并按序处理 | ✅ | ❌ |
| 实时中断 | ✅ | ❌ |
| 生命周期 Hooks(PreToolUse、PostToolUse 等) | ✅ | ❌ |
| 多轮对话 | 自然保持 | 需手动续接 |
| 自定义工具(@tool 装饰器) | ✅ | 受限 |
图像上传是很多视觉场景绕不开的能力;Hooks 是做安全审计与权限拦截的关键。两者都只在流式输入模式下可用——这也是主流文档把流式输入作为默认入口的原因。
六、落地决策:选哪一种
6.1 三步决策
- 应用是否需要「人-机持续交互」?是 → 流式输入;否 → 单消息输入;
- 是否需要图像附件、Hooks、消息排队、中断?任一是 → 流式输入;
- 运行环境是否是无状态函数(Lambda、Cloud Run 任务)?是 → 单消息输入(流式会因进程被冻结而中断)。
6.2 常见场景对照
| 场景 | 推荐模式 |
|---|---|
| 构建 IDE 插件,编辑器内持续对话 | 流式输入 |
| Slack Bot 中被 @ 后一次性回答 | 单消息输入 |
| 自动化脚本里跑一次安全审查 | 单消息输入 |
| 桌面应用里允许用户中途打断 Agent | 流式输入 |
| 一次性 PR 自动评论机器人 | 单消息输入 |
七、混合使用:单消息也能借力流式特性
虽然「中途插入消息」只能在流式输入里做,但单消息输入可以通过外部编排把多条消息串起来:先用 query() 拿到 session id,再用 continue: true 接续。这能模拟一定的多轮效果,但排队、并发中断这类能力仍然拿不到。
到这里,两种输入模式的边界就很清晰了:流式输入是「把 Agent 当成进程」,单消息输入是「把 Agent 当成函数」。理解这一点,比记任何 API 细节都重要。
常见问题(FAQ)
Q1:流式输入一定比单消息输入好吗?
不一定,无状态环境或一次性任务用单消息更省事,避免长连接带来的资源占用。
Q2:图像上传只能用流式输入吗?
是,单消息输入模式不支持在消息里附 base64 图像。
Q3:能不能在单消息输入中途插入新消息?
不能,若需中途干预必须切换到流式输入并使用 interrupt()。