Vercel AI SDK 怎么接第三方 API?消息转换与前端流配对
Vercel AI SDK 接第三方 API,要同时接通两段:服务端按上游协议调用模型,前端按 AI SDK UI 协议接收结果。把上游 SSE 原样返回给 useChat,即使 HTTP 200,也可能无法显示。本文核对日期为 2026-10-25,依据当前 v7 文档及固定源码;未安装运行 SDK,也未联调云岚API。
先记版本,再选服务端模型适配器
本次源码快照中,ai 为 7.0.153、@ai-sdk/react 为 4.0.157、@ai-sdk/openai-compatible 为 3.0.78。它们的版本号并不相同;按项目锁文件记录实际组合,不把不同代教程的函数混用。固定源码快照
面向 Chat Completions 兼容端点,可用 createOpenAICompatible 创建提供方,再用 chatModel(准确模型ID) 明确选择聊天模型。兼容适配器配置
别将它与 @ai-sdk/openai 的同名调用习惯混为一谈:核对快照中,后者默认模型工厂走 Responses,显式 .chat() 才选择 Chat 路线。只改 baseURL 不会把不支持的 Responses 变成 Chat。OpenAI 适配器路由源码
浏览器发 UIMessage,服务器转换后再调用
前端 useChat 可配 new DefaultChatTransport({ api: '/api/chat' }),这里填的是自己的应用路由,不是上游 /chat/completions。提供方 Key 留在服务端;路由还要检查登录、会话归属和输入范围,不能只隐藏 Key 就开放给任何人调用。Transport 定义
UIMessage 包含 id、role、parts 等界面信息,不能直接当上游消息数组。下面是服务端处理片段;调用前须完成应用权限和长度限制。配置后调用会请求模型,本文只做静态核对:
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import {
validateUIMessages, convertToModelMessages, streamText,
toUIMessageStream, createUIMessageStreamResponse,
} from 'ai';
const apiKey = process.env.APP_API_KEY;
const modelId = process.env.APP_MODEL_ID;
if (!apiKey || !modelId) throw new Error('Missing server configuration');
const provider = createOpenAICompatible({
name: 'yunlan',
baseURL: 'https://v2.cloudmist.cloud/v1',
apiKey,
});
export async function buildReply(messages) {
const checked = await validateUIMessages({ messages });
const result = streamText({
model: provider.chatModel(modelId),
messages: await convertToModelMessages(checked),
});
return createUIMessageStreamResponse({
stream: toUIMessageStream({ stream: result.stream }),
});
}
地址是本站基础前缀示意,模型由服务端配置,不接收浏览器任意指定的上游地址。validateUIMessages 做消息结构检查,不代替身份鉴权;convertToModelMessages 返回 Promise,须等待转换完成。校验源码、消息转换参考
在自己的 POST 路由中读取请求正文,完成检查后,将其中的 messages 交给 buildReply,再直接返回得到的 Response。不要把这个 Response 再包进普通 JSON,也不要先读取完整文本再声称实时流式。校验失败与模型调用失败应分别记录,发给浏览器的错误须脱敏;上面的函数没有替应用实现这些路由处理。
自定义 data-* 部分默认会被转换过滤。界面显示了附件摘要,不代表模型读到了它;确需传入时按 convertDataPart 明确转换。工具和文件也要按各自结构处理,不能把所有 parts 拼成一个字符串。
返回流必须与前端 transport 配对
| 前端选择 | 服务端返回方向 |
|---|---|
DefaultChatTransport |
UI Message Stream;上例转换并封装响应 |
TextStreamChatTransport |
纯文本流,使用 v7 的 toTextStream 与 createTextStreamResponse |
纯文本流只保留基础文字,不适合传递工具等结构。UI 流虽使用 SSE,却有自己的事件字段;自定义后端还须设置 x-vercel-ai-ui-message-stream: v1。只加这个头,不能把原始上游事件变成 UI 流。优先由配套 SDK 转换,不手拼一个“看起来像流”的响应。官方流协议
本文用 v7 的独立转换与响应函数,不照搬旧教程的 result.toUIMessageStreamResponse()。前端按 message.parts 渲染 text 部分,并处理状态与错误;不要只读取旧示例里的 message.content。当前 Chatbot 示例
后端有字、前端没字,按链路留下证据
先用无工具、无附件的短任务,写明预期,例如“将会议通知改成一句礼貌提醒,保留时间”。依次记录应用路由是否收到请求、转换后是否保留输入、实际上游路径、返回协议、前端 parts 与错误。
| 现象 | 下一步查哪层 |
|---|---|
自家 /api/chat 返回 404 |
路由位置、部署路径和 transport 地址 |
| 服务端收到请求,但上游认证失败 | 服务端实际配置、Key 所属服务与模型权限 |
| 上游有输出,浏览器报流解析错误 | 返回封装和 transport 是否属于同一协议与版本 |
| 页面有自定义资料,回答却没用到 | 转换后的模型消息是否真正包含该资料 |
本地开发与已部署应用也分别留记录。浏览器看到的错误未必是上游原始错误;同时查看对应服务端日志,按时间和本地脱敏编号关联,不在截图中暴露凭据。
收到 text 增量只是预览,还要区分消息结束、错误或中止;不要为了让页面停止转圈伪造完成。流事件检查见SSE 与终态,重发前看超时重试决策。再验收提醒是否保留原时间,传输完成不等于答案合格。
下载前后端配对检查包,按同一配置填写空白 CSV;人工案例仅供讨论,没有预填成功记录。已有云岚API账号先在控制台确认模型与权限,新项目先完成首次接入,再加入所需能力。
常见问题
两边都用 SSE,为什么不能直接透传?
SSE 是传输格式,事件正文各有约定。上游的 choice 或响应事件,不是默认 transport 期待的 UIMessageChunk;需要正确转换。
把 baseURL 改了,为什么请求仍走 Responses?
地址与模型适配器的路线是两件事。核对导入包、调用的工厂方法及实际路径,不能靠改 URL 后缀掩盖协议不匹配。
页面里有历史,下一轮就自动带给模型吗?
还要看本次发送和转换后的消息。自定义请求裁剪、data 部分过滤都可能丢信息;持久化与会话隔离另按多轮上下文处理。
普通聊天通过,就能打开工具和结构化输出吗?
不能直接推定。适配器选项不会创造上游能力;按目标模型和渠道分别确认支持,再验收对应返回结构及业务结果。
