Vercel AI SDK 6/7 通过类型安全与框架原生集成解决 Route Handlers 的样板代码问题,避免了请求/响应类型在前后端的重复定义。
在过去的数月里,在 Next.js 中构建 LLM 驱动的应用时,标准建议一直是将流式响应通过 API 路由处理器(Route Handlers)进行路由。这堪称 AI 工程的"Hello World":你创建一个 POST 端点,处理传入请求,使用 ReadableStream 将响应流式返回,然后在客户端解析它。
它在演示场景下运行完美。在生产环境中却一败涂地。
随着应用规模增长,维护这些手动端点所需的样板代码成了一笔沉重的负担。你不只是在构建一个聊天界面;你是在构建一个复杂的状态机,需要处理认证、错误状态和类型安全。当你依赖路由处理器来处理这些时,你是在与框架对抗,而不是与它协作。
当你超越简单原型时,路由处理器会引入三类特定的摩擦,拖慢开发速度并增加 bug 出现的可能性。
在标准的 API 模式中,你在服务端定义请求/响应的形状,然后在客户端的 fetch 调用中手动复制这些类型。如果你修改了后端模型或更新了响应对象,客户端并不知情。最终你会遇到"类型漂移"——前端和后端悄无声息地失去同步,导致运行时错误,而这类错误只在用户真正触发 AI 功能时才会暴露。
每个路由处理器都是一个新的公开端点。你必须手动管理 CORS 头、验证认证令牌,并确保 API 密钥不会泄露。每个端点都是另一个安全漏洞的暴露面。如果没必要,为什么要,把内部服务暴露为公开的 HTTP 端点呢?
使用标准的 React useState hook 手动管理流式数据块,往往会让人感觉"迟滞"。你需要负责手动将数据块追加到缓冲区、管理加载状态,并确保 React 的协调不会导致不必要的重新渲染。这是框架应该为你处理的手动管道工作。
Vercel AI SDK 中向 Server Actions 的转变,代表了我们思考 AI 功能方式的根本性变化。我们不再将 AI 视为外部 API 调用,而是将其视为一种安全的后端事务。
通过将执行移至服务端,你可以保障 API 密钥的安全。你可以像调用标准异步工具一样调用流式函数,而 SDK 会处理通过网络流式传输数据的所有繁重工作。
为了构建可扩展的 AI 功能,我采用了一种三层架构:
核心服务层:将模型编排隔离在独立的服务文件中。这允许你替换模型或更新提示词,而无需触碰 UI 代码。
Streamable Value:在 Server Action 内部使用 SDK 的 createStreamableValue 来包装你的流。这将复杂的流式响应转换为 React 理解的一等公民对象。
本地 React 客户端:在客户端组件中使用 readStreamableValue 在一个 transition 中消费这个 streamable value。这利用了 React 内置的 useTransition 来自动处理 pending 状态。
以下是你如何在一个单一文件结构中实现这种简洁、生产就绪模式的方法:
// app/actions.ts
'use server'
import { streamText } from 'ai'
import { openai } from '@ai-sdk/openai'
import { createStreamableValue } from 'ai/rsc'
export async function generateResponse(prompt: string) {
const stream = createStreamableValue('')
// We run the stream in the background
;(async () => {
const { textStream } = await streamText({
model: openai('gpt-4o'),
prompt,
})
for await (const delta of textStream) {
stream.update(delta)
}
stream.done()
})()
return { output: stream.value }
}
而在客户端,集成是无缝的:
// app/client-component.tsx
'use client'
import { useState, useTransition } from 'react'
import { readStreamableValue } from 'ai/rsc'
import { generateResponse } from './actions'
export default function Chat() {
const [data, setData] = useState('')
const [isPending, startTransition] = useTransition()
const handleAction = async () => {
startTransition(async () => {
const { output } = await generateResponse('Explain RAG in 3 bullet points.')
for await (const delta of readStreamableValue(output)) {
setData((prev) => prev + delta)
}
})
}
}
将 LLM 流视为安全的后端事务,是构建健壮 AI 应用的关键。通过将你的流逻辑移入 Server Actions,你可以降低代码复杂度、提升类型安全性,并充分利用 React 生态系统的力量。
如果你仍在构建手动 API 路由来流式传输文本,是时候重构了。Vercel AI SDK 6 的工具链旨在让你的代码更简洁、更安全,而且显著更容易维护。你准备好做出转变了吗?