news 2026/9/7 3:49:19

Claude Agent SDK V2 预览解析:基于 send/receive 会话模式的多轮对话编程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Claude Agent SDK V2 预览解析:基于 send/receive 会话模式的多轮对话编程

Claude Agent SDK V2 预览解析:基于 send/receive 会话模式的多轮对话编程

【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode + More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem

本文围绕docs/context/agent-sdk-v2-preview.md预览文档展开,讲解 Anthropic Claude Agent SDK 的 V2 TypeScript 接口:它用createSession()/resumeSession()+send()/receive()取代了 V1 中需要手工协调的异步生成器,把多轮对话简化为三个核心概念。读完本文,你可以直接用 V2 预览接口编写单发提问、多轮会话、跨进程会话恢复(resume)等程序,并结合 claude-mem 仓库中真实使用 V1 SDK 的源码,理解两种模式在会话 ID 捕获与恢复机制上的同源性。

V2 接口定位:去掉异步生成器,保留会话语义

V2 是一个不稳定的预览接口:API 可能在稳定前根据反馈发生变化,部分功能(如会话分叉 session forking)目前仅 V1 SDK 可用。V2 的核心变化在于去掉了 async generator 与 yield 协调:

  • V1 中,输入和输出都流经同一个query()返回的异步生成器,多轮对话需要构造一个 async iterable 来"喂"消息,并自行协调何时 yield 下一条消息;
  • V2 中,每一轮对话都是独立的send()/receive()周期,API 表面收敛为三个概念:
    • createSession()/resumeSession():开始或继续一段对话;
    • session.send():发送一条消息;
    • session.receive():接收流式响应。

这种显式的收发分离让"在两轮之间插入自定义逻辑"(例如先处理上一轮响应再决定追问内容)变得自然,无需再为生成器状态做结构化改写。

安装

V2 接口包含在现有的 SDK 包中,无需单独安装:

npm install @anthropic-ai/claude-agent-sdk

claude-mem 仓库当前依赖的版本是^0.3.172(见 package.json 中的devDependencies说明:该包会被 esbuild 内联进 worker/server/npx 产物,是构建期依赖)。需要注意:本仓库自身运行在 V1 接口上,V2 预览是为 SDK 使用者提供的简化路径。

快速上手

1. 单发提问:unstable_v2_prompt()

对于不需要维持会话的简单单轮查询,使用unstable_v2_prompt()。它发送一个数学问题并打印答案:

import { unstable_v2_prompt } from '@anthropic-ai/claude-agent-sdk' const result = await unstable_v2_prompt('What is 2 + 2?', { model: 'claude-sonnet-4-6-20250929' }) console.log(result.result)

同样的操作在 V1 中需要消费一个异步生成器,并等待result类型的消息:

import { query } from '@anthropic-ai/claude-agent-sdk' const q = query({ prompt: 'What is 2 + 2?', options: { model: 'claude-sonnet-4-6-20250929' } }) for await (const msg of q) { if (msg.type === 'result') { console.log(msg.result) } }

2. 基础会话:send() 与 receive() 分离

对于超过单轮提示的交互,创建一个 session。V2 把"发送"与"接收"拆成两个显式步骤:send()派发你的消息,receive()流式返回响应。下例创建会话、向 Claude 发送 "Hello!" 并打印文本响应,使用await using(TypeScript 5.2+)在代码块退出时自动关闭会话,也可以手动调用session.close()

import { unstable_v2_createSession } from '@anthropic-ai/claude-agent-sdk' await using session = unstable_v2_createSession({ model: 'claude-sonnet-4-6-20250929' }) await session.send('Hello!') for await (const msg of session.receive()) { // 过滤 assistant 消息以获得人类可读输出 if (msg.type === 'assistant') { const text = msg.message.content .filter(block => block.type === 'text') .map(block => block.text) .join('') console.log(text) } }

V1 中同样的基础提示看起来类似(一个for await循环消费query()),但一旦要加多轮逻辑就必须重构为输入生成器。

3. 多轮对话:同一会话反复 send()

Session 跨多次交互保持上下文。要继续对话,只需在同一个 session 上再次调用send(),Claude 会记住之前的轮次。下例先问一个数学问题,再追问引用上一轮答案的内容:

import { unstable_v2_createSession } from '@anthropic-ai/claude-agent-sdk' await using session = unstable_v2_createSession({ model: 'claude-sonnet-4-6-20250929' }) // Turn 1 await session.send('What is 5 + 3?') for await (const msg of session.receive()) { if (msg.type === 'assistant') { const text = msg.message.content .filter(block => block.type === 'text') .map(block => block.text) .join('') console.log(text) } } // Turn 2 await session.send('Multiply that by 2') for await (const msg of session.receive()) { if (msg.type === 'assistant') { const text = msg.message.content .filter(block => block.type === 'text') .map(block => block.text) .join('') console.log(text) } }

作为对照,V1 完成同样多轮对话必须构造一个 async iterable 来逐条 yield 用户消息,并手工协调 yield 时机:

import { query } from '@anthropic-ai/claude-agent-sdk' // 必须创建 async iterable 来喂消息 async function* createInputStream() { yield { type: 'user', session_id: '', message: { role: 'user', content: [{ type: 'text', text: 'What is 5 + 3?' }] }, parent_tool_use_id: null } // 必须协调何时 yield 下一条消息 yield { type: 'user', session_id: '', message: { role: 'user', content: [{ type: 'text', text: 'Multiply by 2' }] }, parent_tool_use_id: null } } const q = query({ prompt: createInputStream(), options: { model: 'claude-sonnet-4-6-20250929' } }) for await (const msg of q) { if (msg.type === 'assistant') { const text = msg.message.content .filter(block => block.type === 'text') .map(block => block.text) .join('') console.log(text) } }

4. 会话恢复:session_id 捕获与 resume

如果你持有上次交互的 session ID,可以稍后恢复它。这对长时工作流或跨应用重启持久化对话很有用。下例创建会话、存储其 ID、关闭会话,然后恢复对话:

import { unstable_v2_createSession, unstable_v2_resumeSession, type SDKMessage } from '@anthropic-ai/claude-agent-sdk' // 从 assistant 消息中提取文本的辅助函数 function getAssistantText(msg: SDKMessage): string | null { if (msg.type !== 'assistant') return null return msg.message.content .filter(block => block.type === 'text') .map(block => block.text) .join('') } // 创建初始会话并对话 const session = unstable_v2_createSession({ model: 'claude-sonnet-4-6-20250929' }) await session.send('Remember this number: 42') // 从任意收到的消息中获取 session ID let sessionId: string | undefined for await (const msg of session.receive()) { sessionId = msg.session_id const text = getAssistantText(msg) if (text) console.log('Initial response:', text) } console.log('Session ID:', sessionId) session.close() // 之后:用存储的 ID 恢复会话 await using resumedSession = unstable_v2_resumeSession(sessionId!, { model: 'claude-sonnet-4-6-20250929' }) await resumedSession.send('What number did I ask you to remember?') for await (const msg of resumedSession.receive()) { const text = getAssistantText(msg) if (text) console.log('Resumed response:', text) }

V1 的等价做法是从消息中取session_id,再通过options.resume传回一个新的query()

import { query } from '@anthropic-ai/claude-agent-sdk' // 创建初始会话 const initialQuery = query({ prompt: 'Remember this number: 42', options: { model: 'claude-sonnet-4-6-20250929' } }) // 从任意消息中获取 session ID let sessionId: string | undefined for await (const msg of initialQuery) { sessionId = msg.session_id if (msg.type === 'assistant') { const text = msg.message.content .filter(block => block.type === 'text') .map(block => block.text) .join('') console.log('Initial response:', text) } } console.log('Session ID:', sessionId) // 之后:恢复会话 const resumedQuery = query({ prompt: 'What number did I ask you to remember?', options: { model: 'claude-sonnet-4-6-20250929', resume: sessionId } }) for await (const msg of resumedQuery) { if (msg.type === 'assistant') { const text = msg.message.content .filter(block => block.type === 'text') .map(block => block.text) .join('') console.log('Resumed response:', text) } }

资源清理:自动与手动两种写法

会话可以手动关闭,也可以使用 TypeScript 5.2+ 的await using特性自动清理资源;如果你使用旧版 TypeScript 或遇到兼容性问题,请改用手动清理。

自动清理(TypeScript 5.2+):

import { unstable_v2_createSession } from '@anthropic-ai/claude-agent-sdk' await using session = unstable_v2_createSession({ model: 'claude-sonnet-4-6-20250929' }) // 代码块退出时会话自动关闭

手动清理:

import { unstable_v2_createSession } from '@anthropic-ai/claude-agent-sdk' const session = unstable_v2_createSession({ model: 'claude-sonnet-4-6-20250929' }) // ... 使用会话 ... session.close()

API 参考

unstable_v2_createSession()

创建用于多轮对话的新会话:

function unstable_v2_createSession(options: { model: string; // 支持更多选项 }): Session

unstable_v2_resumeSession()

按 ID 恢复已有会话:

function unstable_v2_resumeSession( sessionId: string, options: { model: string; // 支持更多选项 } ): Session

unstable_v2_prompt()

单轮查询的一次性便捷函数:

function unstable_v2_prompt( prompt: string, options: { model: string; // 支持更多选项 } ): Promise<Result>

Session 接口

interface Session { send(message: string): Promise<void>; receive(): AsyncGenerator<SDKMessage>; close(): void; }

从接口签名看,receive()本身返回AsyncGenerator<SDKMessage>,即"接收侧"仍然以流的形式逐条产出消息,V2 消除的只是"发送侧"的生成器协调负担;消息类型(SDKMessage)与 V1 保持一致,session_id字段也继续随消息下发。

可运行的官方示例脚本

仓库的docs/context/目录下随预览文档附带了一份可直接运行的 V2 示例脚本 agent-sdk-v2-examples.ts,覆盖了文档中的全部四个场景,通过命令行参数切换:

npx tsx docs/context/agent-sdk-v2-examples.ts [basic|multi-turn|one-shot|resume]

脚本中有几处对预览文档的实操性补充:

  • 模型别名:示例统一使用model: 'sonnet'短别名,说明 V2 选项同样接受别名形式,而预览文档正文使用完整模型 IDclaude-sonnet-4-6-20250929,二者皆可;
  • 从 system/init 消息取 session_idresume场景中脚本从msg.type === 'system' && msg.subtype === 'init'的消息里读取msg.session_id并打印,这是对文档"从任意收到的消息中获取 session ID"的具体化——init 消息是最稳定的捕获点;
  • 单发结果的成本字段one-shot场景在result.subtype === 'success'时打印result.resultresult.total_cost_usd.toFixed(4),说明unstable_v2_prompt()Result对象携带subtyperesulttotal_cost_usd等字段,可据此做成功判定与成本核算;
  • 分块隔离的会话生命周期:resume 场景用两个独立{ }块分别包两个await using session,第一个块退出即关闭首个会话,模拟"时间流逝"后再恢复。

功能可用性边界:哪些仍需用 V1

并非所有 V1 功能在 V2 中可用。以下能力目前仍需使用 V1 SDK:

  • 会话分叉(forkSession选项);
  • 部分高级流式输入模式。

此外,unstable_前缀本身表明这是未稳定接口,生产代码应评估回退到 V1query()的成本。

V2 模式与 claude-mem 的 V1 用法对照

claude-mem 是一个"捕获 Agent 会话、AI 压缩、回注上下文"的持久记忆项目,其 worker 内部恰恰是 V1 SDK 的重度使用者,因此 V2 预览文档中的 V1 对照代码在仓库里都有真实的对应实现,可以印证两种模式的机制同源性:

  • 架构定位:docs/public/architecture/overview.mdx 在技术栈表将 AI SDK 一栏标注为@anthropic-ai/claude-agent-sdk (or Gemini / OpenRouter)
  • 多轮喂入与流式消费:src/services/worker/ClaudeProvider.ts 中startSession()query({ prompt: messageGenerator, options: ... })启动会话(约 L274),随后for await (const message of queryResult)逐条消费——这正是 V2 用send()/receive()简化掉的"输入生成器 + 输出消费"双生成器结构;
  • session_id 捕获与 resume:ClaudeProvider 在消费循环中检测message.session_id,与本地memorySessionId比对后更新并落库(ensureMemorySessionIdRegistered,约 L334-L356),下一轮再通过options.resume传回;这与预览文档 resume 章节"从消息捕获 ID、恢复时回传 ID"的流程完全一致。源码中的注释也点明了原因:每次query()启动一个全新的 SDK 进程,捕获的 ID 才能安全地喂回后续进程的resume
  • 知识库问答的 resume:src/services/worker/knowledge/KnowledgeAgent.ts 中query()调用携带resume: corpus.session_id,并在正则匹配到session|resume|expired|invalid.*session|not found类错误消息时判定会话已失效——这是对 resume 语义边界(会话可能过期/失效)的工程化处理。

这些实现细节说明:V2 的send()/receive()只是把 V1 中"输入生成器协调 + 输出流消费 + session_id 捕获/resume"这套机制内化到了 Session 对象里,会话上下文的持久化语义(跨进程靠 session_id 恢复)在两个版本中是同一套。对熟悉 claude-mem 源码的读者而言,迁移心智成本主要集中在"不再自己 yield 用户消息"这一点上。

适用前提与限制小结

  • V2 为不稳定预览:API 在稳定前可能变化,会话分叉等能力暂缺,需要这些功能时请使用 V1query()
  • await using自动清理要求 TypeScript 5.2+,旧版本请显式调用session.close()
  • model选项既接受完整模型 ID(如claude-sonnet-4-6-20250929),仓库示例中也使用sonnet别名;
  • 本仓库(claude-mem)的运行时路径构建在 V1 SDK(^0.3.172)之上,本文的 V2 内容基于仓库内预览文档与示例脚本,供 SDK 使用者参考,不代表仓库内部已切换 V2。

【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode + More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 3:46:27

从API 403到暂缓发布:AI安全如何重塑模型竞争逻辑

先从一个很具体的场景说起。这段时间在技术社区里&#xff0c;能看到不少开发者反馈 Anthropic API 连接异常&#xff0c;报错类似 "unable to connect to anthropic services: failed to connect to api.anthropic.com: status 403"。有人在排查自己的账号额度&…

作者头像 李华
网站建设 2026/9/7 3:46:20

内窥镜设备标准IEC 60601-2-18:2009解读与送检避坑指南

简介&#xff1a;IEC 60601-2-18:2009 是国际电工委员会发布的内窥镜设备专用安全标准&#xff0c;面向医疗设备研发、注册与检测人员&#xff0c;用于规范硬性、软性及纤维内窥镜的基本安全与主要性能要求。该标准在 IEC 60601-1 通用要求基础上&#xff0c;补充了电击防护、机…

作者头像 李华
网站建设 2026/9/7 3:41:05

Claude Code 插件实战:9款值得长期保留的高效工具与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 3:40:28

技术挑战项目实战指南:从基准测试到性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 3:40:09

Claude Code开源影响分析:代码生成工具的技术价值与生态挑战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华