🚀 Vercel AI SDK 使用指南:文本补全 (useCompletion)

10 阅读4分钟

在 AI 应用开发中,除了常见的对话机器人(Chatbot),我们经常需要一种“单轮文本生成”的场景,比如:智能代码补全邮件草稿生成文章润色自动摘要

Vercel AI SDK 提供的 useCompletion 也就是为此而生。它比 useChat 更轻量,专注于处理单次的 Prompt-to-Text 流式响应。

本文将带你从零实现一个基于 Next.js 的流式文本补全应用,并包含错误处理加载状态以及国内模型适配等实战技巧。

🛠️ 环境准备

首先,确保你安装了最新的 AI SDK 核心库和 React 集成库:

Bash

npm install ai @ai-sdk/react zod

注:Vercel AI SDK 目前已升级至 3.x/4.x+ 版本,请确保你的 ai 包版本是最新的。


💻 第一步:构建后端 API (Route Handler)

我们需要创建一个 API 路由来处理前端的请求,并调用大模型进行流式输出。

新建文件:app/api/completion/route.ts

TypeScript

import { streamText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';

// 1. 配置模型提供商
// 💡 小贴士:如果你使用国内模型(如 DeepSeek、通义千问等),
// 可以通过 baseURL 和 apiKey 进行配置兼容 OpenAI 协议的接口。
const openai = createOpenAI({
  // 若使用国内中转或本地模型,在此替换 baseURL
  // baseURL: 'https://api.deepseek.com/v1', 
  apiKey: process.env.OPENAI_API_KEY,
});

// 允许流式响应最长持续 30 秒
export const maxDuration = 30;

export async function POST(req: Request) {
  // 从请求体中解析 prompt
  const { prompt }: { prompt: string } = await req.json();

  // 2. 调用模型
  const result = streamText({
    model: openai('gpt-4o-mini'), // 或使用 'deepseek-chat' 等
    prompt: prompt,
    // 可选:设置系统提示词,规范输出风格
    system: '你是一个专业的助手,请以简洁、专业的风格通过 Markdown 格式回答用户的问题。',
  });

  // 3. 返回流式响应
  return result.toDataStreamResponse();
}

关键点解析:

  • streamText: 核心函数,用于生成流式文本。
  • toDataStreamResponse() : 将流转换为前端 useCompletion 能识别的格式。

🎨 第二步:构建前端 UI (Client Component)

在前端,我们使用 useCompletion hook 来管理输入状态、提交请求和渲染流式结果。

新建或修改页面:app/page.tsx

TypeScript

'use client';

import { useCompletion } from '@ai-sdk/react';

export default function CompletionPage() {
  // 解构 useCompletion 提供的核心对象和方法
  const {
    completion,       // 当前生成的补全结果(实时更新)
    input,            // 输入框的当前值
    handleInputChange,// 绑定到 input onChange 的处理函数
    handleSubmit,     // 绑定到 form onSubmit 的处理函数
    isLoading,        // 是否正在生成中
    stop,             // 用于停止生成的函数
    error             // 错误对象(如有)
  } = useCompletion({
    api: '/api/completion', // 指定后端接口地址
    // 可选:错误回调
    onError: (err) => {
      console.error('生成出错:', err);
      alert('生成失败,请稍后重试');
    },
    // 可选:完成回调
    onFinish: (prompt, completion) => {
      console.log('生成完成:', completion);
    }
  });

  return (
    <div className="max-w-2xl mx-auto p-4 py-10">
      <h1 className="text-2xl font-bold mb-4">AI 文本补全助手</h1>

      <form onSubmit={handleSubmit} className="space-y-4">
        {/* 输入区域 */}
        <div>
          <label className="block text-sm font-medium mb-1">请输入提示词:</label>
          <textarea
            className="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-500 min-h-[100px] text-black"
            value={input}
            onChange={handleInputChange}
            placeholder="例如:帮我写一封请假邮件..."
            disabled={isLoading}
          />
        </div>

        {/* 按钮区域 */}
        <div className="flex gap-2">
          <button
            type="submit"
            disabled={isLoading || !input.trim()}
            className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 disabled:opacity-50"
          >
            {isLoading ? '生成中...' : '开始生成'}
          </button>
          
          {isLoading && (
            <button
              type="button"
              onClick={stop}
              className="bg-red-500 text-white px-4 py-2 rounded-md hover:bg-red-600"
            >
              停止生成
            </button>
          )}
        </div>
      </form>

      {/* 错误提示 */}
      {error && (
        <div className="mt-4 p-3 bg-red-100 text-red-700 rounded-md">
          出错了:{error.message}
        </div>
      )}

      {/* 结果展示区域 */}
      {(completion || isLoading) && (
        <div className="mt-8 border-t pt-6">
          <h2 className="text-lg font-semibold mb-2">生成结果:</h2>
          <div className="prose bg-gray-50 p-6 rounded-lg whitespace-pre-wrap text-gray-800">
            {completion}
          </div>
        </div>
      )}
    </div>
  );
}

⚙️ 进阶配置:传递自定义参数

很多时候,我们需要传额外的参数给后端(比如用户选择的 tone 或 creative level)。可以通过 body 选项实现。

前端修改

TypeScript

const { handleSubmit } = useCompletion({
  api: '/api/completion',
  // 在这里添加额外的请求体
  body: {
    userId: 'user_123',
    tone: 'professional' // 用户选择的语气
  }
});

后端修改 (app/api/completion/route.ts)

TypeScript

export async function POST(req: Request) {
  // 解析额外的 body 参数
  const { prompt, tone }: { prompt: string, tone: string } = await req.json();

  const result = streamText({
    model: openai('gpt-4o-mini'),
    // 将参数整合进 prompt 或 system message
    system: `你是一个助手,请用 ${tone || 'neutral'} 的语气回答。`,
    prompt: prompt,
  });
  
  return result.toDataStreamResponse();
}

🌟 总结

useCompletion 是构建非对话式 AI 应用的利器。相比于 useChat,它不需要维护复杂的 messages 数组,非常适合**“输入->输出”**的直观场景。

核心要点回顾:

  1. 后端:使用 streamText 处理流,配合 toDataStreamResponse 返回。
  2. 前端:使用 useCompletion 一键获取 inputcompletionisLoading 状态。
  3. 兼容性:通过 createOpenAIbaseURL 配置,可以轻松接入 DeepSeek、通义千问等国内优质模型。

希望这篇教程能帮你在 Next.js 项目中快速落地 AI 功能!如果有问题,欢迎在评论区交流。 👇