Agent SDK 给你两个接口。选哪个归结为一个问题:下一次交互需不需要知道上一次发生了什么?
query():发完即忘
query() 把一个 prompt 作为独立操作处理。不需要建立会话、不需要清理、没有残留状态。你调用它,它跑完整的 agentic 循环(包括工具调用),然后返回结果。下一次 query() 调用对上一次做了什么一无所知。
这适合自包含的任务:分类一个文档、从发票里提取数据、回答一个独立的问题。每个操作本身就是完整的。
ClaudeSDKClient:跨轮次的上下文
ClaudeSDKClient 在一个会话内的多次 query() 调用之间维持对话状态。你说”看看 auth.ts”,然后接着问”这个函数在哪里被调用了”,client 知道你说的是哪个函数——它保留了第一次交换的上下文。
它作为 async context manager 使用(async with ClaudeSDKClient(...) as client),自动管理会话生命周期。状态在会话期间存在于内存中。进程死了,状态就没了——没有自动的磁盘持久化。要扛过重启需要开发者自己实现持久化。
两个都支持工具
一个关键误解:“query() 不能用工具。“错了。query() 在单次调用内跑完整的 agentic 循环。如果任务需要调 Read,再调 Grep,再调 Edit 才能完成,这些全在一次 query() 调用内发生。工具使用不限于 ClaudeSDKClient。
区别在于跨调用的状态,不是调用内的能力。两个接口都支持工具,都接受 ClaudeAgentOptions 配置,都用同一个模型。
选对接口
- 100 个独立文档要处理 → 100 次
query()调用。每个自包含。用 ClaudeSDKClient 会累积无关的跨文档上下文。 - 客服对话有后续问题 →
ClaudeSDKClient。后续问题引用之前的上下文。 - 一次性工单分类 →
query()。分类完就走。 - 调试会话(“读这个文件,再查那个函数”)→
ClaudeSDKClient。每一步都基于上一步。
不要为了”一致性”在简单任务上用 ClaudeSDKClient——它多了不必要的会话生命周期复杂性。也不要用 query() 手动拼接历史记录来做多轮——那正是 ClaudeSDKClient 自动化的事。
一句话总结: 独立的单次任务用 query(),需要跨调用上下文的多轮交互用 ClaudeSDKClient——两者都支持工具和完整的 agentic 循环。