跳到主要内容

创建日期:2026-09-20 | 最近更新:2026-09-20 证据分级:

  • 【实测】=本机真跑(ai@7.0.107、openai@7.20.0、@anthropic-ai/sdk@0.127.0,Node v24.14.1):把官方 SDK 指向本地假服务端,抓它们真实发出的请求与消费流的方式,无需 API Key;脚本在 source/aisdk-lab/sdks.mjs。
  • **【文档】**=官方文档/包内说明。
  • **【二手】**=第三方横评(口径可能不一致,已标注)。 本篇是 Vercel AI SDK 入门 的横向篇,承接 Agent 流式传输选型 的协议结论。

AI SDK 横向调研:官方 SDK、统一接入层、Agent 框架

「用哪个 AI SDK」这个问题问不清楚,通常是因为三类东西被放在一张表里比。它们的职责根本不同:

  1. 厂商 SDK(OpenAI / Anthropic / Google):只服务自家模型,但保真度最高;
  2. 统一接入层(Vercel AI SDK、LangChain):抹平多家 provider,换模型不改业务代码;
  3. Agent 框架(AI SDK 的 ToolLoopAgent、OpenAI Agents SDK、Mastra、Google ADK):管循环、状态、多 agent。

这三层可以叠着用,不是互斥选一个。 下面先给实测证据(第 2、3 节),再谈生态位(第 4 节)。

1. 先把三类分开​

层代表解决的问题换掉它的代价
厂商 SDKopenai、@anthropic-ai/sdk、@google/genai「怎么跟这一家的 API 说话」低(但业务要重写)
统一接入层Vercel AI SDK、LangChain「换模型/换厂商不改业务」中(抽象泄漏时要绕)
Agent 框架ToolLoopAgent、OpenAI Agents SDK、Mastra、Google ADK「循环、状态、多 agent、审批」高(循环语义与状态都在它手里)

一个常见的误区是把 Vercel AI SDK 和 LangChain 当同类。它们重叠的部分只有「统一接入」,再往上的取向完全不同:LangChain 给方法论抽象(Runnable/状态图/Reducer),AI SDK 给流式协议与前后端绑定。本站另有一篇专门比过这组哲学差异:LangChain 还是 pi-agent?。

2. 实测:官方 SDK 的流式「形状」差异​

把两个官方 SDK 指向本地假服务端(返回各自格式的 SSE),抓真实行为:

openai@7.20.0@anthropic-ai/sdk@0.127.0
消费方式for await (const chunk of stream) 异步迭代器stream.on('text', cb) 事件回调
增量在哪chunk.choices[0].delta.content(要层层剥)回调直接给字符串
拿完整结果自己拼接await stream.finalMessage() 拿到组装好的消息(含 content 块、stop_reason)
实测分块到达2ms / 13ms / 33ms28ms / 49ms / 69ms
请求头 acceptapplication/jsonapplication/json
流式怎么表达body 里 "stream": truebody 里 "stream": true

两个可迁移的观察:

  1. 两家的 Accept 都不是 text/event-stream —— 流式是靠 body 的 stream: true 表达的,响应才切到 SSE。这和 MCP 的 Streamable HTTP 正好相反(后者要求 Accept: application/json, text/event-stream)。所以别用 Accept 头判断对方要不要流式。
  2. Anthropic SDK 的易用性明显更高一档:finalMessage() 直接给你组装好的完整消息,不用自己拼增量。这属于「SDK 帮你做了多少」的差异,跟协议无关——协议一样是 SSE,封装层可以差很多。

3. 三家线格式:都是 SSE,但事件结构不同​

把三家的原始字节并排放(Vercel AI SDK 的数据来自 入门篇 的实测):

OpenAIAnthropicVercel AI SDK
content-typetext/event-streamtext/event-streamtext/event-stream
用 event: 具名字段吗❌ 只用 data:✅ 用(event: message_start 等)❌ 只用 data:
类型信息在哪隐式(靠结构)SSE 事件名JSON 里的 type 字段
结束标志data: [DONE]event: message_stop 事件data: [DONE]
版本协商——x-vercel-ai-ui-message-stream: v1
防缓冲——x-accel-buffering: no

原始字节对比(均为实测抓取):

OpenAI : data: {"choices":[{"delta":{"content":"杭州"},"index":0}]}\n\n … data: [DONE]\n\n
Anthropic: event: message_start\ndata: {"type":"message_start","message":{…}}\n\n
event: content_block_delta\ndata: {"type":"content_block_delta","delta":{"type":"text_delta","text":"杭州"}}\n\n
Vercel : data: {"type":"text-delta","id":"t1","delta":"你好"}\n\n … data: [DONE]\n\n

这张表最实用的价值:你要写一个跨家通用的流式读取器,其实只差三件事——切帧都一样(\n\n)、都要处理 data: 前缀、但「怎么判断结束」和「类型怎么读」各不相同。Anthropic 用事件名,另两家用哨兵 + JSON 内字段。

4. 生态位【二手 + 文档,口径已标注】​

以下定位来自第三方横评与官方文档,不同来源口径不一致(例如有来源说 ToolLoopAgent 是 AI SDK 6 引入的,而我实测装在 ai@7.0.107 里)。版本相关结论请以自己 package.json 里装的为准。

框架语言定位强项短板
Vercel AI SDKTS给 Web 产品加 Agent 能力流式 UI 绑定最好、25+ provider、MCP 支持、部署无关、依赖轻内置记忆/可观测相对薄(v7 已有 memory 文档,但不如专门框架成体系)
MastraTS做 Agent 产品工作流 + 记忆(working/semantic)+ RAG + 评估一体较新
LangChain / LangGraphTS/Py复杂编排与状态机图、条件边、checkpointer、生态最大抽象厚,绕框架成本高(见 InkOS 那篇)
OpenAI Agents SDKPy 为主(TS 部分)OpenAI 原生 Agent交接/多 agent 语义干净、内置 tracing、自动 Session 记忆新特性常先在 Python 侧;OpenAI 优先
Google ADKPy/Java/Go/TSGCP 原生多模态、A2A 协议、Vertex 托管可观测出 GCP 较难;文档被评「两套框架易混」

一条被反复提到的工程事实(来源一致,可信度较高):框架只解决生产需求的约三成,其余是测试、可观测性、记忆、提示词管理等基础设施——这部分无论选谁都要自己补。

5. 选型建议​

只用一家模型、要最高保真度 ──────────────► 厂商 SDK(openai / @anthropic-ai/sdk)
要多模型可切换 + 流式 UI ───────────────► Vercel AI SDK(+ 厂商 SDK 作为 provider)
要做 Agent 产品(记忆/工作流/RAG 一体)──► Mastra
要复杂状态机/条件分支/人工介入 ─────────► LangGraph
已在 OpenAI 生态、要 tracing 与多 agent ─► OpenAI Agents SDK
全押 GCP/Gemini ────────────────────────► Google ADK

混合用法是常态,不是妥协:厂商 SDK 当底层接入、统一层做业务、Agent 框架管循环——三层各司其职。真正的坑在于两层抽象都持有循环控制权(例如统一层和 Agent 框架都想驱动工具循环),那会让调试成本翻倍。判据:谁持有循环,谁就必须是唯一的那个。

6. 一个跨三家可用的读取骨架(承接上一篇)​

三家的帧结构不同,但读取层可以统一 —— 这一段直接沿用 流式传输那篇 的骨架,只换「结束判定」与「取正文」两个策略:

async function readStream(res, { isDone, extractText }) {
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
let buf = '';
const out = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
buf += value;
const frames = buf.split('\n\n');
buf = frames.pop(); // 不完整的尾巴留到下一轮
for (const f of frames) {
const data = f.split('\n').find((l) => l.startsWith('data: '))?.slice(6);
if (data === undefined) continue; // 具名 event 帧也走这里,按需读 f 的 event:
if (isDone(data)) return out; // ★ 各家不同:'[DONE]' 或 message_stop 事件
out.push(extractText(JSON.parse(data))); // ★ 各家不同:剥 choices[0].delta 还是取 delta.text
}
}
return out;
}
// 三家只差这两个策略
const openai = { isDone: (d) => d === '[DONE]', extractText: (o) => o.choices?.[0]?.delta?.content ?? '' };
const anthropic= { isDone: (d) => JSON.parse(d).type === 'message_stop', extractText: (o) => o.delta?.text ?? '' };
const vercel = { isDone: (d) => d === '[DONE]', extractText: (o) => (o.type === 'text-delta' ? o.delta : '') };

这 20 行就是上一篇结论的可复用形态:协议细节会变、矩阵会过时,但「fetch + ReadableStream + 按 \n\n 切帧 + 策略化解析」这套骨架对所有 SSE 系协议都成立。

7. 局限​

  • 只实测了 3 个包(ai / openai / @anthropic-ai/sdk);Mastra、LangGraph、Agents SDK、ADK 均未实测,定位描述来自文档与二手横评;
  • 官方 SDK 的实测是指向本地假服务端:这能真实反映「SDK 发了什么请求、怎么消费流、怎么组装结果」,但无法反映真实服务的错误码、限流、超时行为;
  • 二手来源口径不一致(尤其版本号),已在 §4 标注;
  • 未覆盖计费、限流、SLA 等商业维度;
  • 版本迭代很快:本篇结论以文首标注的版本为准。

关联​

自测​

  1. 厂商 SDK / 统一接入层 / Agent 框架三者的职责差别是什么?为什么不能放一张表里比?
  2. 实测里 OpenAI 与 Anthropic 的 Accept 请求头是什么?流式是靠什么表达的?这与 MCP 有什么不同?
  3. 三家线格式在「怎么判断流结束」上分别怎么做?
  4. 为什么说 Anthropic SDK 的易用性更高一档?这是协议差异还是封装差异?
  5. 「框架只解决约三成生产需求」指哪些没被覆盖?
  6. 混合使用多层抽象时,最容易出问题的地方是什么?判据是什么?

参考​