返回博客

用 DeepSeek + MySQL 实现“听得懂人话”的博客搜索

2025-12-20
AI
8 分钟

在构建我的 AI Generative UI博客时,我遇到了一个经典难题:搜索。

传统的 SQL LIKE %关键词% 搜索非常“笨”。

  • 用户搜:“怎么让网页变快?”
  • 文章标题:《Next.js 性能优化实战》
  • 结果:搜不到。因为没有字面匹配。

业界的标准解法是 RAG(检索增强生成):引入向量数据库(Vector DB),做 Embedding,搞余弦相似度计算。

但对于一个刚开始创建个人博客,引入一套全新的向量基础设施,是否属于过度设计?

在权衡了成本与效果后,我探索出了一条“降维打击”的技术路径:利用 DeepSeek 的强大语义理解能力 + MySQL 的简单查询,实现了 90% 的向量检索体验,而成本几乎为零。

本文将分享这一方案的具体实现,以及如何通过“搜索日志”构建内容生产的闭环。

核心思路:AI 意图提取 (Query Expansion)

向量检索的本质是将“语义”映射到坐标系。但 LLM 本身就是一个最强的语义处理器。

我们不需要把数据库里的文章变成向量,我们只需要把用户的问题“翻译”成数据库能听懂的关键词。

流程对比:

传统搜索:用户输入 -> 直接丢给 SQL -> 查不到。 我的方案:用户输入 -> DeepSeek 思考并提取核心技术词 -> 丢给 SQL -> 命中!

比如:

用户问:“我想学怎么部署网站”

AI 提取:“Docker Nginx 部署 上线” SQL 查询:... WHERE title LIKE '%Docker%' OR title LIKE '%部署%' ...

技术实现 (Next.js + Vercel AI SDK)

第一步:定义 AI 工具 (The Brain)

利用 Vercel AI SDK 的 tool 定义,赋予 AI “查询文章”的能力。

最关键的是 parameters 中的 Prompt 设计,我限制了关键词的数量,防止 AI 过于发散。


// ... streamText 配置中
tools: {
  getArticles: tool({
    description: '查询或推荐技术博客文章',
    parameters: z.object({
      // ✨ 核心魔法:Prompt Engineering
      keywords: z.string().describe('将用户的问题转换为 1-2 个最核心的技术关键词,用空格分隔。例如把"怎么让网站不卡"转换为"性能优化 渲染原理"'),
    }),
    execute: async ({ keywords }) => {
      console.log(`🤖 AI 提取的搜索词: ${keywords}`);
      
      // 1. 调用数据库查询
      const articles = await searchArticles(keywords);
      
      // 2. (重要) 记录搜索日志,具体见下文
      await logSearch(keywords, articles.length);
      
      if (!articles.length) return { message: '暂无相关文章' };
      
      // 3. 只返回 AI 需要的字段(节省 Token)
      return articles.map(a => ({
        title: a.title,
        summary: a.summary,
        date: a.created_at
      }));
    },
  }),
}

第二步:数据库查询 (The Muscle)

不需要复杂的全文检索引擎,简单的 SQL OR 查询配合 AI 提取的精准关键词,效果出奇的好。

'use server';
import pool from '@/lib/db';

export async function searchArticles(keywords: string) {
  // 将空格分隔的关键词拆分
  const keywordList = keywords.split(' ').filter(k => k.trim());
  
  if (keywordList.length === 0) return [];

  // 动态拼接 SQL: title LIKE %A% OR title LIKE %B%
  // 也可以加上 summary 字段的匹配
  const conditions = keywordList.map(() => `(title LIKE ? OR summary LIKE ?)`).join(' OR ');
  const params = keywordList.flatMap(k => [`%${k}%`, `%${k}%`]);
  
  const sql = `
    SELECT id, title, slug, summary, created_at 
    FROM articles 
    WHERE ${conditions} 
    ORDER BY created_at DESC 
    LIMIT 5
  `;
  
  const [rows] = await pool.query(sql, params);
  return JSON.parse(JSON.stringify(rows));
}

第三步:数据闭环——搜索日志 (The Insight)

这是整个系统中最具运营价值的一环。既然所有搜索都经过 AI 提取,那么记录这些关键词,我就能知道用户真正关心什么。

CREATE TABLE `search_logs` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `keywords` VARCHAR(255) NOT NULL,
  `result_count` INT DEFAULT 0, -- 搜到了几篇?
  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

埋点逻辑:

在 AI 执行工具时,顺手记录一下。如果 result_count 为 0,说明这是用户的痛点,也就是我下一篇文章的选题。

export async function logSearch(keywords: string, count: number) {
  // Fire-and-forget,不阻塞主线程
  const sql = 'INSERT INTO search_logs (keywords, result_count) VALUES (?, ?)';
  pool.query(sql, [keywords, count]).catch(err => console.error(err));
}

效果演示

我们通过在聊天窗口发送搜索文章,AI搜索如果如下:

图片

同时后台也能监测到所有数据

图片

现在的体验是这样的:

  • 用户在博客首页问:“有没有关于 TypeScript 的高级教程?”
  • DeepSeek 提取关键词:TypeScript 泛型 工具类型
  • 后端 SQL 查询匹配文章。
  • 前端利用 Generative UI 直接弹出文章卡片(而不是文本列表)。

总结

在 AI 时代,我们容易陷入“唯技术论”,觉得不用 Vector DB 就不够 AI。

但工程化的本质是Trade-off(权衡)。

通过DeepSeek (意图理解) + MySQL (可靠存储) 的组合,我用最简的架构实现了:

1.语义级搜索体验(解决了 SQL 僵化问题)。 2.零额外成本(无需向量库费用)。 3.数据驱动运营(通过日志发现内容缺口)。

这就是独立开发者的生存之道:用最简单的技术,解决最核心的问题。

留言