usechat 必须配合 @ai-sdk/deepseek 适配 deepseek 官方 api 的非标准流式格式,因 usechat 仅支持 openai 兼容的 sse(data: {...}\n\n),而 deepseek 原生返回纯 json lines;需通过中间路由返回 text/event-stream,并正确设置响应头、使用流式写入,同时确保 ai、@ai-sdk/react、@ai-sdk/deepseek 版本对齐且初始化传入 apikey 和 baseurl(https://api.deepseek.com/v1)。
☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 多模态理解力帮你轻松跨越从0到1的创作门槛☜☜☜

必须用 useChat + @ai-sdk/deepseek 组合,且后端路由要返回 text/event-stream,否则前端收不到流式 chunk。
为什么 useChat 不能直接连 DeepSeek 官方 API
useChat 是协议绑定的:它只认 OpenAI 兼容的流式格式(data: {...} 分块、末尾双换行),而 DeepSeek 官方 API 的流式响应默认是纯 JSON Lines({"choices":[{"delta":{"content":"a"}}]}),没有 data: 前缀,也没有 SSE 标准头。
- 直接 fetch DeepSeek 官方
/v1/chat/completions?stream=true,前端useChat会静默失败或卡在 loading - 必须经过一层适配:要么用
@ai-sdk/deepseek提供的封装(它内部做了格式转换),要么自己写中间路由转发并重写响应体 -
@ai-sdk/deepseek的核心作用就是把 DeepSeek 原生流 → 转成 OpenAI 兼容流,同时处理好Content-Type和 chunk 边界
@ai-sdk/deepseek 的正确安装与初始化方式
不是简单 import 就能用。它依赖 ai 包的底层流式管道,且必须和 @ai-sdk/react 版本对齐,否则 useChat 拿不到 onChunk 或 isLoading 更新。
统一LLM网关 - 一个API对接70+AI模型,使用单一API密钥即可调用GPT、Claude、Gemini、Qwen、Deepseek、Grok等主流模型。
- 安装命令必须是:
npm install ai @ai-sdk/react @ai-sdk/deepseek(不能漏掉ai) - 初始化时需显式传入
apiKey和baseUrl,DeepSeek 的baseUrl是"https://api.deepseek.com/v1",不是文档里写的/v1/结尾带斜杠的版本 - 代码中要这样写:
const { messages, input, append, isLoading } = useChat({ api: '/api/chat-deepseek', ... }),其中/api/chat-deepseek是你自己的 Next.js Route Handler,不是 DeepSeek 官方地址
Route Handler 必须手动设置流式响应头
Next.js App Router 的 route.ts 默认不设 Transfer-Encoding: chunked,也不发 text/event-stream,useChat 就会当成普通 JSON 一次性读取,失去流式效果。
- 必须在
route.ts开头加:export const dynamic = 'force-dynamic' - 响应前必须设置:
res.headers.set('Content-Type', 'text/event-stream')和res.headers.set('Cache-Control', 'no-cache') - 不能用
return Response.json(...),得用res.stream()或手动res.write()+res.end()(Vercel AI SDK 内部已封装,但你要确保它被调用) - 常见错误:忘记
res.headers.set('X-Vercel-CDN-Cache-Control', 'no-cache'),导致 Vercel Edge 缓存了第一个 chunk,后续消息全乱序
前端渲染时 v-html 和 Markdown 解析的坑
DeepSeek 流式返回的 content 是纯文本片段,不是完整 Markdown;如果直接 {{ message.content }} 渲染,换行、粗体、列表都会丢失,且 XSS 风险未处理。
- 不要用
v-html直接插原始 content,尤其当 content 来自delta.content时——它可能是半截 HTML 标签(比如只返回<strong>hello</strong>) - 推荐做法:累积
message.content到完整字符串后再解析,用marked或remark转义 + 渲染,且开启sanitize: true - 更稳妥的是等整个消息
status === 'complete'后再触发渲染,避免中间状态污染 DOM
最易忽略的一点:DeepSeek 的流式 chunk 里 finish_reason 出现在最后一个包,但 useChat 不会自动标记该消息为完成——你得监听 onFinish 回调,手动更新 messages 的 status 字段,否则打字机动画停不下来。










