前言
其实此处应该有前言,介绍我的学习计划,但是我有点偷懒了,所以先空着吧
今日目标
今天的目标非常简单,我们打算基于Next.js,Vercel AI SDK和OpenAI SDK来跑通第一个 AI 聊天应用,并实现“打字机”流式效果。
简单来说我们今天要做的就是 make it work ,make it better 的时候就交给后面再说,简单来说就是我们要先找信心和对 AI 聊天机器人祛媚。
创建应用
1 | npx create-next-app@latest ai-learning-day1 |
通过这一行命令创建一个Next.js应用
1 | ? Would you like to use the recommended Next.js defaults? › - Use arrow-keys. Return to submit. |
直接回车就好,这个推荐配置就够用了
安装依赖
1 | npm install ai@^4.0.0 @ai-sdk/react@^1.0.0 @ai-sdk/openai@^1.0.0 zod@^3.23.8 |
那么在这次依赖安装中我们安装了哪些依赖呢?
ai(Core)
角色:
核心引擎/大脑。作用:这是
Vercel AI SDK的子模块。它不依赖于任何特定的框架(React/Vue)或特定的模型(OpenAI/DeepSeek)。它定义了标准:怎么处理流式传输(Streaming)?
怎么定义一条消息(User/Assistant)?
怎么处理工具调用(Tool Calling)?
@ai-sdk/react(UI Hooks)
角色:
React 适配器。作用:核心引擎是纯 JS 写的,React 组件看不懂。这个包把核心引擎的功能封装成了 React Hooks,比如
useChat。它负责监听
input变化。它负责把后端传回来的流(Stream)实时渲染到屏幕上。
它负责管理
Loading状态。
@ai-sdk/openai(Provider)
角色:
翻译官/驱动程序。作用:
ai核心库不知道 OpenAI 的接口长什么样,也不知道 DeepSeek 的接口长什么样。这个包专门负责把标准指令翻译成 OpenAI 兼容的 API 请求格式。- 注意:因为 DeepSeek 是兼容 OpenAI 格式的,所以我们直接用这个包就能驱动 DeepSeek,不需要专门下载 DeepSeek 的包。
zod(Schema Validation)
角色:
安检员/数据校验器。作用:在 AI 开发中,
Zod无处不在。结构化输出:当你要求 AI 返回 JSON 时,
Vercel SDK会用Zod来检查 AI 返回的数据格式对不对。工具调用:当你给 AI 定义工具(比如“查天气”)时,参数的类型定义也是用
Zod描述的。
还有一件事你可能会感到很奇怪,为什么这些依赖需要显示地指明版本号呢?
主要是我在安装的时候需要了两个坑
同名包干扰:
npm上存在一个同名的旧包ai(v6),导致我误装了错误的库,必须强制指定安装Vercel的ai@4.x。版本不兼容:
Vercel AI SDK依赖稳定的zod v3,而npm试图安装最新的v4,导致冲突。我通过显式锁定zod@^3.23.8解决了这个问题。
因为我们这次学习的教程是快速开始和找信心,那我们就不尝试去解决这些问题了,就先显示地指定版本去安装依赖
配置 API Key
我们这次打算先使用deepseek的api,如果没有的话就先上官网申请一个,然后充个十块钱的
在项目根目录创建一个.env.local文件,随后添加以下内容
1 | DEEPSEEK_API_KEY=你的APIKey |
这个.env.local其实也是在告诉你这玩意是你要自己在本地保存好的,不能随便发到网上,注意检查在.gitignore是否忽略了它,毕竟这玩意是可以直接花你钱包里面的钱的,当心点吧。
编写后端
在 App Router 中,我们需要一个 API 路由来转发请求。
新建文件:app/api/chat/route.ts
1 | import { createOpenAI } from '@ai-sdk/openai'; |
编写前端
修改文件:app/page.ts
1 | 'use client'; // 必须标记为 Client Component |
运行和查看结果
1 | npm run dev |
此时打开localhost:3000,就能看到运行的效果了,我们礼貌打个招呼
嗯嗯效果不错呢,那我们打开网络面板看一下网络请求的响应
而且我们也观察到了AI在输出的过程中也是几个字几个字蹦出来的,那么我们到底怎么处理这个响应以及怎么实现这种打字机的效果的呢?接下来我们就要讲到流式输出了
流式输出
那么以下这些玩意到底是什么呢?
1 | f:{"messageId":"msg-k7d3B5mCInIcg0DVyzXioZcg"} |
其实这就是 Vercel AI SDK 的数据流协议 (Data Stream Protocol)。做个比喻,如果我们之前的API请求的方法如果是接收短信的话,那么我们现在的流式输出就是打电话,不用等对面一次性编辑完所有消息之后再接收,我们可以几个字几个字地接收。
让我们解剖一下这段数据的含义:
0:(Text Delta):代表文本增量。你可以看到 “你好”、”!” 是被切分成一个个小块传回来的。这就是为什么前端能实现“打字机”效果——因为数据本身就是一点点蹦出来的。d:/e:(Data/End):代表元数据。比如usage告诉我们这次消耗了多少 Token(钱),finishReason告诉我们 AI 是说完了还是被截断了。
前端如何处理?
如果让我手动去解析这一堆 0:"..." 的字符串,那绝对是场噩梦。但这正是 Vercel AI SDK 的价值所在。
我在前端使用的 useChat 这个React Hook,它封装了消息管理、流式数据处理和状态管理的逻辑:
它发起请求,建立连接。
它监听这个流式数据。
每当收到一个 0:"...",它就自动把它拼接到当前的对话内容里。
结果: 开发者完全不需要关心这些底层协议,只需要直接使用 messages 数组,就能看到文字像水流一样自动在页面上生成。
这就是为什么我们在 route.ts 里要写 result.toDataStreamResponse() —— 我们不是在返回数据,而是在建立一条管道。
而且我们也在使用这个来管理loading状态,是不是感觉其功能还是挺强大的?
结语
是不是写了这个之后感觉其实所谓的 AI 应用也没有那么高大上?不错不错,建立了信心之后后面的东西就好学了,明天的事我们明天再聊吧,哦呀粟米。