从零开始搭建AI驱动的个人博客:Next.js + OpenAI实战

791 阅读4分钟

最近在重构我的个人博客时,突然想到:能不能给博客加点AI的功能?比如智能搜索、内容推荐、自动标签等。说干就干,花了一个周末的时间,我把博客改造成了一个AI驱动的智能系统。今天就来分享下这个有趣的实践经历。

为什么选择 Next.js + OpenAI?

老实说,最开始我在犹豫要不要直接用一些现成的博客系统。但仔细想想,自己搭建有几个好处:

  1. 完全的控制权

    • 可以自由定制AI功能
    • 随时添加新特性
    • 性能优化更灵活
  2. 技术栈现代化

    • Next.js 13+ 的App Router真的好用
    • React Server Components减少了很多样板代码
    • TypeScript让开发体验更好
  3. AI集成便利性

    • OpenAI的API足够强大
    • Next.js的API路由很适合做AI集成
    • 部署和维护都很方便

项目初始化

温馨提示:确保你的Node.js版本 >= 16.8.0,我就因为版本问题折腾了半天😅

  1. 创建项目
npx create-next-app@latest ai-blog
cd ai-blog
  1. 安装依赖
npm install @openai/api prisma @prisma/client tailwindcss
  1. 环境配置
OPENAI_API_KEY=your_api_key
DATABASE_URL=your_database_url

核心功能实现

1. 智能搜索功能

这是我最喜欢的功能,可以用自然语言搜索博客内容:

// app/api/search/route.ts
import { OpenAIApi, Configuration } from 'openai'
import { prisma } from '@/lib/prisma'
import { NextResponse } from 'next/server'

const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY
})

const openai = new OpenAIApi(configuration)

export async function POST(req: Request) {
  try {
    const { query } = await req.json()
    
    // 1. 使用OpenAI生成搜索向量
    const embedding = await openai.createEmbedding({
      model: 'text-embedding-ada-002',
      input: query
    })
    
    // 2. 向量搜索
    const posts = await prisma.post.findMany({
      where: {
        published: true
      },
      select: {
        id: true,
        title: true,
        content: true,
        embedding: true
      }
    })
    
    // 3. 计算相似度并排序
    const results = posts
      .map(post => ({
        ...post,
        similarity: cosineSimilarity(embedding.data[0].embedding, post.embedding)
      }))
      .sort((a, b) => b.similarity - a.similarity)
      .slice(0, 5)
    
    return NextResponse.json(results)
  } catch (error) {
    console.error('搜索失败:', error)
    return NextResponse.json(
      { error: '搜索服务暂时不可用,请稍后再试' },
      { status: 500 }
    )
  }
}

// 余弦相似度计算
function cosineSimilarity(vecA: number[], vecB: number[]): number {
  const dotProduct = vecA.reduce((acc, val, i) => acc + val * vecB[i], 0)
  const normA = Math.sqrt(vecA.reduce((acc, val) => acc + val * val, 0))
  const normB = Math.sqrt(vecB.reduce((acc, val) => acc + val * val, 0))
  return dotProduct / (normA * normB)
}

2. 智能标签生成

这个功能帮我省了很多手动打标签的时间:

// app/api/tags/route.ts
import { OpenAIApi, Configuration } from 'openai'
import { NextResponse } from 'next/server'

const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY
})

const openai = new OpenAIApi(configuration)

export async function POST(req: Request) {
  try {
    const { content } = await req.json()
    
    const completion = await openai.createChatCompletion({
      model: 'gpt-3.5-turbo',
      messages: [
        {
          role: 'system',
          content: '你是一个专业的技术博客标签生成器,需要生成准确、相关的技术标签。'
        },
        {
          role: 'user',
          content: `请为以下技术博客内容生成3-5个标签:\n\n${content}`
        }
      ],
      temperature: 0.3  // 保持输出的一致性
    })
    
    const tags = completion.data.choices[0].message?.content
      ?.split(',')
      .map(tag => tag.trim())
      .filter(Boolean) || []
    
    return NextResponse.json({ tags })
  } catch (error) {
    console.error('标签生成失败:', error)
    return NextResponse.json(
      { error: '标签生成服务暂时不可用,请稍后再试' },
      { status: 500 }
    )
  }
}

3. 内容推荐系统

基于用户阅读历史推荐相关文章:

// app/api/recommendations/route.ts
import { OpenAIApi, Configuration } from 'openai'
import { prisma } from '@/lib/prisma'
import { NextResponse } from 'next/server'

export async function POST(req: Request) {
  try {
    const { userId, currentPostId } = await req.json()
    
    // 1. 获取用户阅读历史
    const readHistory = await prisma.readHistory.findMany({
      where: { userId },
      include: { post: true },
      orderBy: { createdAt: 'desc' },
      take: 5
    })
    
    // 2. 生成用户兴趣描述
    const userInterests = analyzeUserInterests(readHistory)
    
    // 3. 基于兴趣推荐文章
    const recommendations = await prisma.post.findMany({
      where: {
        id: { not: currentPostId },
        published: true,
        // 添加更多筛选条件
      },
      orderBy: {
        // 可以基于多个因素排序
        createdAt: 'desc'
      },
      take: 3
    })
    
    return NextResponse.json(recommendations)
  } catch (error) {
    console.error('推荐失败:', error)
    return NextResponse.json(
      { error: '推荐服务暂时不可用,请稍后再试' },
      { status: 500 }
    )
  }
}

实战踩坑记录

  1. API 密钥管理

    • ❌ 千万不要把密钥放在前端代码里
    • ✅ 使用环境变量
    • ✅ 在生产环境中使用密钥管理服务
  2. 性能优化

    • 实现请求缓存
    • 优化向量计算
    • 使用增量更新
  3. 成本控制

    • 合理使用API
    • 实现缓存机制
    • 选择合适的模型

效果展示

经过这次改造,我的博客有了这些新功能:

  1. 智能搜索

    • 支持自然语言搜索
    • 理解搜索意图
    • 结果更准确
  2. 自动标签

    • 准确率达到90%
    • 节省了大量时间
    • 标签更专业
  3. 智能推荐

    • 相关性明显提高
    • 用户停留时间增加
    • 阅读量提升

写在最后

这次给博客加入AI功能的经历,让我深刻体会到AI不仅能提升用户体验,还能大大减轻我们的运营负担。虽然过程中遇到了不少挑战,但最终的效果还是很令人满意的。

如果你也想给自己的博客加入AI功能,建议可以从一个小功能开始尝试,慢慢扩展。毕竟每个博客的需求都不一样,找到最适合自己的方案才是关键。

有什么问题欢迎在评论区讨论,我们一起学习进步!

如果觉得有帮助,别忘了点赞关注,我会继续分享更多实战经验~