文档
开发指南AI 功能集成

AI 功能集成

🚀 Cheatsheet (快速执行)

目标: 10分钟内为你的应用添加 AI 聊天功能,让用户可以与 AI 对话

最快上手方式:

# 1. 获取 OpenAI API Key
# 访问 https://platform.openai.com/account/api-keys
# 创建新的 API Key

# 2. 配置环境变量
echo 'OPENAI_API_KEY=sk-你的密钥' >> .env.local

# 3. 已预装 Vercel AI SDK
# bun add ai @ai-sdk/openai

# 4. 创建聊天页面
# 访问 /app/ai/chatbot 测试 AI 对话功能

立即生效: 用户可以在聊天界面与 AI 进行对话,支持流式响应

❓ 为什么要集成 AI 功能

对 MVP 的核心价值:

  • 产品差异化: AI 是现在最火的功能,用户看到就觉得很先进
  • 用户粘性: AI 助手能让用户每天都来使用你的产品
  • 收费理由: AI 功能消耗成本,天然的付费功能点
  • 数据收集: 通过对话了解用户需求,优化产品方向

真实场景举例: 你做了一个创业工具,加入 AI 商业计划书生成功能,用户输入"我想做一个外卖app",AI 自动生成市场分析、竞品分析、盈利模式,用户觉得太牛了,立马付费升级。

🤔 为什么选择 Vercel AI SDK

我们的实战经验:

  • 开发体验最好: 专为 React 设计,几行代码就能实现流式聊天
  • 多提供商支持: 一套代码支持 OpenAI、Claude、Gemini,随时切换
  • 流式响应: 像 ChatGPT 一样的打字效果,用户体验好
  • 类型安全: 完整的 TypeScript 支持,减少bug

对比其他方案:

直接调用 OpenAI API:  需要自己处理流式响应,复杂
LangChain:            功能太重,学习成本高,适合复杂场景
原生 fetch:           没有 React 集成,体验差
Vercel AI SDK: 专为 Next.js 优化,开箱即用

🧠 简要原理 & 最简案例

AI 对话流程: 用户输入消息 → 发送到 API 路由 → 调用 AI 服务 → 流式返回回答 → 实时显示

最简聊天示例:

// 1. API 路由 (app/api/ai/chat/route.ts)
import { openai } from '@ai-sdk/openai'
import { streamText } from 'ai'

export async function POST(req: Request) {
  const { messages } = await req.json()
  
  const result = await streamText({
    model: openai('gpt-3.5-turbo'),
    messages,
  })
  
  return result.toDataStreamResponse()
}

// 2. 聊天组件 (components/ChatBox.tsx)
'use client'
import { useChat } from 'ai/react'

export function ChatBox() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    api: '/api/ai/chat',
  })
  
  return (
    <div>
      {/* 消息列表 */}
      {messages.map(message => (
        <div key={message.id}>
          <strong>{message.role}:</strong> {message.content}
        </div>
      ))}
      
      {/* 输入框 */}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} placeholder="问我任何问题..." />
        <button type="submit">发送</button>
      </form>
    </div>
  )
}

🛠️ 实际操作步骤

1. 配置 AI 提供商 (5分钟)

获取 OpenAI API Key:

1. 访问 https://platform.openai.com
2. 注册并登录账号
3. Billing > 添加支付方式 (必须,否则无法使用)
4. API keys > Create new secret key
5. 复制密钥:sk-proj-...

环境变量配置:

# .env.local
OPENAI_API_KEY=sk-proj-你的完整密钥

# 可选:其他 AI 提供商
ANTHROPIC_API_KEY=sk-ant-你的Claude密钥
GOOGLE_GENERATIVE_AI_API_KEY=你的Gemini密钥

2. 创建基础聊天功能 (10分钟)

聊天页面组件:

// app/ai/chatbot/page.tsx
import { ChatInterface } from '@/modules/ai/ChatInterface'

export default function ChatbotPage() {
  return (
    <div className="container mx-auto max-w-4xl p-4">
      <h1 className="text-2xl font-bold mb-6">AI 聊天助手</h1>
      <ChatInterface />
    </div>
  )
}

完整聊天界面:

// components/ai/ChatInterface.tsx
'use client'
import { useChat } from 'ai/react'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'

export function ChatInterface() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: '/api/ai/chat',
  })
  
  return (
    <div className="flex flex-col h-[600px] border rounded-lg">
      {/* 消息区域 */}
      <ScrollArea className="flex-1 p-4">
        {messages.length === 0 && (
          <div className="text-center text-gray-500 mt-20">
            <p>👋 你好我是 AI 助手有什么可以帮助你的吗?</p>
          </div>
        )}
        
        {messages.map(message => (
          <div key={message.id} className={`mb-4 ${
            message.role === 'user' ? 'text-right' : 'text-left'
          }`}>
            <div className={`inline-block p-3 rounded-lg max-w-[80%] ${
              message.role === 'user' 
                ? 'bg-blue-500 text-white' 
                : 'bg-gray-100 text-gray-900'
            }`}>
              {message.content}
            </div>
          </div>
        ))}
        
        {isLoading && (
          <div className="text-left mb-4">
            <div className="inline-block p-3 rounded-lg bg-gray-100">
              <div className="flex space-x-1">
                <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"></div>
                <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{animationDelay: '0.1s'}}></div>
                <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{animationDelay: '0.2s'}}></div>
              </div>
            </div>
          </div>
        )}
      </ScrollArea>
      
      {/* 输入区域 */}
      <form onSubmit={handleSubmit} className="p-4 border-t flex gap-2">
        <Input
          value={input}
          onChange={handleInputChange}
          placeholder="输入你的问题..."
          disabled={isLoading}
          className="flex-1"
        />
        <Button type="submit" disabled={isLoading}>
          发送
        </Button>
      </form>
    </div>
  )
}

3. 增强 API 路由功能 (10分钟)

智能路由选择:

// app/api/ai/chat/route.ts
import { openai } from '@ai-sdk/openai'
import { anthropic } from '@ai-sdk/anthropic'
import { streamText } from 'ai'
import { auth } from '@/lib/auth'

export async function POST(req: Request) {
  try {
    const session = await auth()
    if (!session?.user) {
      return Response.json({ error: '请先登录' }, { status: 401 })
    }
    
    const { messages } = await req.json()
    
    // 根据用户套餐选择模型
    const user = session.user
    const model = user.planType === 'pro' 
      ? openai('gpt-4') 
      : openai('gpt-3.5-turbo')
    
    const result = await streamText({
      model,
      messages,
      system: `你是一个有用的AI助手。请用中文回答用户的问题。
      如果用户提到创业、商业计划等话题,请提供实用的建议。`,
      
      // 安全过滤
      maxTokens: 1000,
      temperature: 0.7,
    })
    
    return result.toDataStreamResponse()
    
  } catch (error) {
    console.error('AI API 错误:', error)
    return Response.json({ error: 'AI 服务暂时不可用' }, { status: 500 })
  }
}

4. 添加专用 AI 功能 (15分钟)

文案生成功能:

// app/api/ai/generate-content/route.ts
import { openai } from '@ai-sdk/openai'
import { generateText } from 'ai'

export async function POST(req: Request) {
  const { prompt, type } = await req.json()
  
  const prompts = {
    blog: `请写一篇关于"${prompt}"的博客文章,包括标题、引言、正文和结论。`,
    social: `为"${prompt}"写 5 条不同风格的社交媒体文案。`,
    email: `写一封关于"${prompt}"的营销邮件,包括主题行和正文。`,
    slogan: `为"${prompt}"想 10 个创意广告标语。`
  }
  
  const result = await generateText({
    model: openai('gpt-3.5-turbo'),
    prompt: prompts[type] || prompt,
  })
  
  return Response.json({ content: result.text })
}

内容生成组件:

// components/ai/ContentGenerator.tsx
'use client'
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { Select } from '@/components/ui/select'

export function ContentGenerator() {
  const [prompt, setPrompt] = useState('')
  const [type, setType] = useState('blog')
  const [result, setResult] = useState('')
  const [loading, setLoading] = useState(false)
  
  const handleGenerate = async () => {
    setLoading(true)
    try {
      const response = await fetch('/api/ai/generate-content', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ prompt, type })
      })
      
      const data = await response.json()
      setResult(data.content)
    } catch (error) {
      alert('生成失败,请重试')
    } finally {
      setLoading(false)
    }
  }
  
  return (
    <div className="space-y-4">
      <div>
        <label className="block text-sm font-medium mb-2">内容主题</label>
        <Textarea
          value={prompt}
          onChange={(e) => setPrompt(e.target.value)}
          placeholder="例如:如何提高工作效率"
          rows={3}
        />
      </div>
      
      <div>
        <label className="block text-sm font-medium mb-2">内容类型</label>
        <Select value={type} onValueChange={setType}>
          <option value="blog">博客文章</option>
          <option value="social">社交媒体</option>
          <option value="email">营销邮件</option>
          <option value="slogan">广告标语</option>
        </Select>
      </div>
      
      <Button onClick={handleGenerate} disabled={loading || !prompt}>
        {loading ? '生成中...' : '开始生成'}
      </Button>
      
      {result && (
        <div className="border rounded p-4 bg-gray-50">
          <h3 className="font-medium mb-2">生成结果:</h3>
          <div className="whitespace-pre-wrap">{result}</div>
        </div>
      )}
    </div>
  )
}

💡 高级 AI 功能

文件上传分析

// app/api/ai/analyze-file/route.ts
import { openai } from '@ai-sdk/openai'
import { generateText } from 'ai'

export async function POST(req: Request) {
  const formData = await req.formData()
  const file = formData.get('file') as File
  
  // 读取文件内容
  const content = await file.text()
  
  const result = await generateText({
    model: openai('gpt-3.5-turbo'),
    prompt: `请分析以下文档内容,并提供:
    1. 内容摘要
    2. 关键要点
    3. 改进建议
    
    文档内容:
    ${content}`,
  })
  
  return Response.json({ analysis: result.text })
}

AI 角色设定

// lib/ai/personas.ts
export const aiPersonas = {
  assistant: {
    name: '通用助手',
    system: '你是一个友善、专业的AI助手,能够帮助用户解决各种问题。',
  },
  business: {
    name: '商业顾问',
    system: '你是一位经验丰富的商业顾问,专注于帮助创业者和企业家解决商业问题。',
  },
  writer: {
    name: '写作助手',
    system: '你是一位专业的写作助手,擅长各种类型的文案创作和内容优化。',
  },
  teacher: {
    name: '学习导师',
    system: '你是一位耐心的老师,善于用简单易懂的方式解释复杂概念。',
  }
}

// 使用示例
export function getPersonaPrompt(personaId: string, userMessage: string) {
  const persona = aiPersonas[personaId] || aiPersonas.assistant
  return `${persona.system}\n\n用户问题:${userMessage}`
}

成本控制和限流

// lib/ai/usage.ts
export async function checkAIUsage(userId: string) {
  const user = await db.user.findUnique({ 
    where: { id: userId },
    include: { usage: true }
  })
  
  const today = new Date().toISOString().split('T')[0]
  const dailyUsage = user.usage?.find(u => u.date === today)
  
  const limits = {
    free: 10,    // 免费用户每天 10 次
    pro: 100,    // Pro 用户每天 100 次
    enterprise: -1 // 企业版无限制
  }
  
  const limit = limits[user.planType] || limits.free
  const used = dailyUsage?.count || 0
  
  return {
    allowed: limit === -1 || used < limit,
    remaining: limit === -1 ? -1 : Math.max(0, limit - used),
    resetTime: new Date(Date.now() + 24 * 60 * 60 * 1000) // 明天同一时间
  }
}

🎯 用户体验优化

错误处理和重试

// hooks/useAIChat.ts
import { useChat } from 'ai/react'
import { useState } from 'react'

export function useAIChat() {
  const [retryCount, setRetryCount] = useState(0)
  const maxRetries = 3
  
  const chat = useChat({
    api: '/api/ai/chat',
    onError: (error) => {
      console.error('AI 聊天错误:', error)
      
      if (retryCount < maxRetries) {
        setTimeout(() => {
          setRetryCount(prev => prev + 1)
          // 重试逻辑
        }, 1000 * (retryCount + 1)) // 递增延迟
      }
    },
    onFinish: () => {
      setRetryCount(0) // 成功后重置重试计数
    }
  })
  
  return {
    ...chat,
    canRetry: retryCount < maxRetries,
    isRetrying: retryCount > 0
  }
}

消息持久化

// lib/ai/history.ts
export async function saveChatMessage(userId: string, role: string, content: string) {
  return await db.chatMessage.create({
    data: {
      userId,
      role,
      content,
      timestamp: new Date()
    }
  })
}

export async function getChatHistory(userId: string, limit = 50) {
  return await db.chatMessage.findMany({
    where: { userId },
    orderBy: { timestamp: 'desc' },
    take: limit
  })
}

🆘 常见问题解决

问题1: API 调用失败

// 检查 API Key 配置
console.log('OpenAI Key:', process.env.OPENAI_API_KEY?.slice(0, 10) + '...')

// 添加错误处理
try {
  const result = await streamText({ model, messages })
  return result.toDataStreamResponse()
} catch (error) {
  if (error.message?.includes('API key')) {
    return Response.json({ error: 'API 密钥配置错误' }, { status: 401 })
  }
  
  if (error.message?.includes('quota')) {
    return Response.json({ error: 'API 额度不足,请充值' }, { status: 429 })
  }
  
  throw error
}

问题2: 流式响应中断

// 客户端重连机制
const { messages, input, handleInputChange, handleSubmit, isLoading, error } = useChat({
  api: '/api/ai/chat',
  onError: (error) => {
    console.error('聊天错误:', error)
    // 可以显示重试按钮
  },
  // 自动重试配置
  initialMessages: [],
  keepLastMessageOnError: true
})

问题3: 响应时间过长

// 设置超时时间
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 30000) // 30秒超时

const result = await streamText({
  model,
  messages,
  abortSignal: controller.signal
})

clearTimeout(timeoutId)

📊 AI 使用统计

使用情况面板

// components/ai/UsageStats.tsx
export function AIUsageStats() {
  const { data: stats } = useAIUsage()
  
  return (
    <div className="grid grid-cols-3 gap-4">
      <div className="bg-white p-4 rounded shadow">
        <h3 className="text-sm text-gray-600">今日使用</h3>
        <p className="text-2xl font-bold">{stats?.todayCount}</p>
        <p className="text-xs text-gray-500">剩余 {stats?.remaining} 次</p>
      </div>
      
      <div className="bg-white p-4 rounded shadow">
        <h3 className="text-sm text-gray-600">本月费用</h3>
        <p className="text-2xl font-bold">${stats?.monthlyCost}</p>
      </div>
      
      <div className="bg-white p-4 rounded shadow">
        <h3 className="text-sm text-gray-600">最常用功能</h3>
        <p className="text-lg font-medium">{stats?.topFeature}</p>
      </div>
    </div>
  )
}

📎 延伸阅读

官方文档:

最佳实践:

工具推荐:


下一步: AI 功能配置完成后,查看 [文件存储]./storage) 了解如何实现文件上传和AI文档分析功能。