将 VitePress 文档数据向量化,配合 RAG 实现 AI 助手插件

本文最后更新于 2025年8月26日 晚上

VitePress 是一个很不错的文档引擎,将 Markdown 转化为静态网站,具有快速部署、易于维护的特点。 那么,既然是文档内容,普通的搜索还是有些不够智能,如果我们将 Markdown 数据向量化,就可以利用向量数据库进行高效的检索和生成,从而实现 AI 助手插件。

RAG 检索

RAG(Retrieval-Augmented Generation)是一种结合了检索和生成的 AI 技术架构。它通过先检索相关文档片段,再基于这些片段生成回答,有效解决了 LLM(Large Language Model) 的知识局限性和幻觉问题。

当然,RAG 检录肯定不是把文档内容全部作为 token 传入模型进行处理,而是先需要把数据进行向量化,排除无关信息,标注语义标签:

flowchart LR
    A[🔍 文档解析] --> C[✂️ 文本分块]
    C --> D[🧠 向量化处理]
    D --> E[💾 向量数据库存储]
    
    F[👤 用户提问] --> G[🔄 查询向量化]
    G --> H[🎯 相似度检索]
    H --> J[🤖 LLM 生成回答]
    J --> K[💬 智能回复]
    
    E --> H
    
    subgraph 数据准备阶段
        direction TB
        A
        C
        D
        E
    end
    
    subgraph 查询响应阶段
        direction TB
        F
        G
        H
        J
        K
    end
    
    classDef dataPrep fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,color:#2e7d32
    classDef queryResp fill:#e3f2fd,stroke:#2196f3,stroke-width:2px,color:#1565c0
    classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px,color:#7b1fa2
    classDef ai fill:#ffebee,stroke:#f44336,stroke-width:2px,color:#c62828
    classDef user fill:#e0f2f1,stroke:#009688,stroke-width:2px,color:#00695c
    
    class A,C dataPrep
    class D,E storage
    class F,G,H,I user
    class J ai

从上图也可以看出,最后 RAG 的效果受限于:

  • 数据向量化的能力: 我们一般用向量数据库去存储数据进而把数据进行向量化(隐式调用向量数据模型,不同的模型效果其实也不一样)。
  • LLM 的能力: 前主流的 LLM 模型都具备一定的知识库能力,但不同的 LLM 模型效果也不同,就如 Claude 和 DeepSeek 的效果肯定是不一样的。

当然,文档内容是否全面、质量高低是更重要的因素。

本次实践,我们就把 薄荷输入法 文档数据向量化,然后再用 Go 构造 RAG 插件,并在VitePress 中添加 AI 助手插件:

在 VitePress 实现的效果

需要注意,除了 VitePress,你还需要准备:

  • LLM 模型 API: 用于 RAG 检索,推荐使用 DeepSeek 的 API,比较省钱。
  • 向量化数据模型: 可以直接使用后文 CNB 的知识库流水线,自动向量化数据并提供 API。

向量化数据

向量化是 RAG 系统的基础,它将文本转换为高维向量表示,进而实现更精度的语义识别、查询和生成。

比如: 我们平时阅读文章时,会看到很多词条,比如“人工智能”、“深度学习”、“机器学习”等等,这些词条就是文本,我们可以通过向量化技术将其转换为向量表示,进而实现更精准的语义识别、查询和生成;亦或者对内容词条进行聚类,形成知识库,进而实现更精准的语义识别、查询和生成

graph TB
    %% 定义样式
    classDef docStyle fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000
    classDef processStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000
    classDef vectorStyle fill:#e8f5e8,stroke:#388e3c,stroke-width:2px,color:#000
    classDef dbStyle fill:#fff3e0,stroke:#f57c00,stroke-width:2px,color:#000
    classDef modelStyle fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:#000
    
    %% 文档处理流程
    A[📄 原始文档<br/>文本、PDF、网页等] --> B[✂️ 文档分块<br/>Chunking]
    B --> C[🧮 向量化处理<br/>Embedding]
    
    %% 向量化模型
    D[🤖 向量模型<br/>Word2Vec/BERT/GPT等] --> C
    
    %% 向量数据库存储
    C --> E[(🗄️ 向量数据库<br/>Vector Database)]
    
    %% 向量数据库功能
    E --> F[🔍 相似度搜索<br/>Similarity Search]
    E --> G[📊 向量索引<br/>Vector Index]
    E --> H[⚡ 高效检索<br/>Fast Retrieval]
    
    %% 查询处理
    I[❓ 用户查询] --> J[🔍 查询向量化]
    D --> J
    J --> F
    
    %% 应用样式
    class A,I docStyle
    class B,J processStyle
    class C vectorStyle
    class E,F,G,H dbStyle
    class D modelStyle
    
    %% 分组展示
    subgraph "📚 向量化数据处理"
        direction TB
        A
        B
        C
        D
    end
    
    subgraph "🗄️ 向量数据库功能"
        direction TB
        E
        F
        G
        H
    end
    
    subgraph "🔎 查询处理"
        direction TB
        I
        J
    end

什么是向量数据库,可以参考: 什么是向量数据库 – Cloudflare

向量化数据需要 Embedding 模型,常见的模型有 Word2Vec、GloVe、FastText、BERT、ELMo、GPT、T5 等。具体可以看看 python 配合 tensorflow 的向量模型库:TensorFlow Embedding

可以直接使用向量数据库这样的 SaaS 产品,比如:

向量流水线

最近发现 CNB 有提供知识库构建服务(目前是免费的),其实就是 SaaS 的向量数据服务:

CNB的知识库

我们只需要把 Markdown 文档作为 Git 仓库托管到 CNB,然后使用 CNB 的流水线向量化数据;就可以使用 CNB 的知识库 API 接口,使用关键词进行检索:

flowchart LR
    A[📁 Git 仓库<br/>Markdown 文档] --> B[🔄 CNB 流水线<br/>自动构建知识库]
    B --> C[💾 知识库<br/>向量化存储]
    
    D[🔍 用户查询] --> C
    C --> E[💬 智能回答<br/>包含来源链接]
    
    %% 样式定义
    classDef source fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000
    classDef pipeline fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000
    classDef storage fill:#e8f5e8,stroke:#388e3c,stroke-width:2px,color:#000
    classDef query fill:#fff3e0,stroke:#f57c00,stroke-width:2px,color:#000
    
    %% 应用样式
    class A source
    class B pipeline
    class C storage
    class D,E query

CNB 知识库

到了我们本次的正题,首先就是用 CNB 的知识库向量化我们的 VitePress 项目内 Markdown 文档,向量后,可以使用 CNB 的 API 接口进行检索,接口调用的效果如下:

CNB 知识库接口调用(向量化的数据)

使用 CNB 的知识库很简单,你可以和我一样直接把 VitePress 文档项目托管到 CNB:

VitePress 托管到 CNB

知识库流水线

正如 CNB 的流水线文档所说,CNB 的知识库流水线可以自动向量化数据并自动存储:

1
2
3
4
5
6
7
8
main:
push:
- name: "向量化数据"
stages:
- name: build knowledge base
image: cnbcool/knowledge-base
settings:
include: "**/**.md"

上述的意思:

  • 当代码仓库 main 分支有新的提交时,触发流水线。
  • 使用 cnbcool/knowledge-base 知识库插件镜像,向量化所有 Markdown 文件。

参考我的 VitePress: Mintimate/rime/DocVitePressOMR/.cnb.yml

触发效果

当代码仓库 main 分支有新的提交时,触发流水线:

触发效果

流水线构建完毕后,前台还可以看到一个有意思的“彩蛋”:

CNB的知识库彩蛋

你可以直接点击这个“彩蛋”按钮进行前台知识库的检索。但是通常情况肯定使用 CNB 的知识库 API 在其他应用上集成,比如 VitePress。

知识库 API

参考 CNB 的 API 文档:

比如,查询知识库的信息就是:

查询知识库信息

获得的知识库信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"id": 1951463277186875392,
"embedding_model": {
"name": "hunyuan",
"dimension": 1024
},
"last_commit_sha": "30bd8be1a32e8138ffa76db1ea01cf33e8c914af",
"include": "**/**.md",
"exclude": "",
"statistics": {
"count": 48,
"size": 353882
}
}

而我们查询数据库,就是需要 POST 请求 CNB 的知识库 API,比如:

1
2
3
4
5
6
7
## 向量数据查询接口
curl -X "POST" "https://api.cnb.cool/Mintimate/rime/DocVitePressOMR/-/knowledge/base/query" \
-H 'Authorization: **********' \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
"query": "薄荷输入法是什么"
}'

其中:

  • Authorization 是 CNB 的 API 访问令牌,可以在 CNB 的个人中心获取。
  • Mintimate/rime/DocVitePressOMR 是 CNB 的仓库地址,也就是你的 VitePress 项目仓库地址。
  • "query": "薄荷输入法是什么" 是查询语句。

RAG API 接口

接下来,向量数据已经通过 CNB 的知识库 API 接口实现。 至于 LLM 的 API,正如开头所说的,使用 DeepSeek 的即可,我这里就使用 DeepSeek 的 R1 模型 API,具体的信息:

  • 模型 ID: deepseek-reasoner
  • API 调用地址: https://api.deepseek.com/v1

接下来,我们就需要把 RAG API 和 CNB 的知识库 API 结合起来,实现一个知识库的检索 API。你可以直接用我写的 Go 语言版本的:

你可以在config.yml内填写你的 LLM API 地址和 CNB 的知识库 API 地址:

1
2
3
4
5
6
7
8
9
10
# AI 服务配置
ai:
base_url: "https://api.example.com/ai"
api_key: "your_api_key_here"
model: "your_model_name"

# 知识库配置
knowledge:
base_url: "https://api.example.com/knowledge/base/query"
token: "your_token_here"

最后 API 请求的效果:

RAG API 接口调用

需要注意:

  • /api/v1/chat/stream: 是流式 API,返回的是一个流式数据。便于大模型流式 Token 的生成。

VitePress 插件

VitePress 本身是基于 Vite 前端构建和 Vue 框架的静态网站生成器,所以我们可以使用 Vue 来实现一个组件,用于在 VitePress 中嵌入知识库检索框。

流式请求

大模型的回复是流式的,所以我们需要使用流式请求,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 使用异步迭代器处理流式数据
const processStream = async () => {
try {
let result
while (!(result = await reader.read()).done) {
const chunk = decoder.decode(result.value, { stream: true })
buffer += chunk

// 按行分割处理SSE数据
const lines = buffer.split('\n')
buffer = lines.pop() || '' // 保留最后一个不完整的行

for (const line of lines) {
const trimmedLine = line.trim()
if (trimmedLine.startsWith('data:')) {
try {
// 提取data:后的JSON数据
const jsonStr = trimmedLine.substring(5).trim()
if (jsonStr && jsonStr !== '[DONE]' && !jsonStr.includes('"success":true')) {
const data = JSON.parse(jsonStr)
if (data.content) {
accumulatedText += data.content

// 提取思考内容
const thinkMatch = accumulatedText.match(/<think>([\s\S]*?)(?:<\/think>|$)/)
if (thinkMatch) {
thinkContent = thinkMatch[1]
messages.value[aiMessageIndex].thinkContent = thinkContent
messages.value[aiMessageIndex].thinkHtml = convertToHtml(thinkContent)
}

// 提取回答内容
const answerMatch = accumulatedText.match(/<answer>([\s\S]*?)(?:<\/answer>|$)/)
if (answerMatch) {
answerContent = answerMatch[1]
messages.value[aiMessageIndex].text = answerContent
messages.value[aiMessageIndex].html = convertToHtml(answerContent)
}

// 智能滚动到底部
nextTick(() => {
smartScrollToBottom()
})
}
}
} catch (e) {
// 忽略JSON解析错误,继续处理下一行
console.warn('解析SSE数据失败:', trimmedLine, e)
}
} else if (trimmedLine.startsWith('event:done')) {
// 处理完成事件
isStreamComplete = true
return // 直接返回,结束处理
}
}
}
} finally {
// 确保 reader 被正确关闭
reader.releaseLock()
}
}

解释一下:

  • reader.read() 读取流式数据,返回一个 Promise 对象,done 表示流式数据是否已经读取完毕。
  • buffer 保存了读取到的流式数据。在读取过程中,会按行分割处理 SSE 数据,并将每一行数据保存到 buffer 中。
  • thinkContentanswerContent 分别保存了思考内容和回答内容。主要用于适配 Go 的后端 API 返回。

流式请求的效果:

流式请求效果

滑动与防抖动

一般大模型的交互,都是自动滚动到底部的,所以我们需要在用户滑动页面时,判断是否需要自动滚动到底部。同时,为了防止用户频繁滑动,我们还需要添加防抖动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const scrollToBottom = () => {
if (messagesContainer.value) {
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
}
}
// 智能滚动到底部
const smartScrollToBottom = () => {
if (!isUserScrolling.value && shouldAutoScroll()) {
scrollToBottom()
}
}

// 监听用户滚动
const handleScroll = () => {
isUserScrolling.value = true

// 清除之前的定时器
if (scrollTimeout.value) {
clearTimeout(scrollTimeout.value)
}

// 500ms后认为用户停止滚动
scrollTimeout.value = setTimeout(() => {
isUserScrolling.value = false
}, 500)
}

AI 插件源码

最终,我们就可以在 VitePress 中嵌入知识库检索框了,相关的源码可以在我的 VitePress 上找到:

最终效果

最终的效果很简单,首先是 VitePress 上多了一个 AI 标签:

VitePress AI 按钮插件

点击 AI 按钮,就会弹出知识库检索框:

VitePress AI 检索框

然后,输入关键词,就可以搜索到相关的知识库信息了:

VitePress AI 检索结果

同时适配了思考内容和回答内容,可以更好的展示给用户:

思考内容

END

个人知识库、RAG 检索这个概念,其实已经很久了。年初很火的 Dify、Anything LLM 都是不错的自部署方案,再到后来进一步降低门槛,有了各厂商像IMA这样的 SaaS 方案。

我们今天介绍的方案,其实也是自部署方案的一种,只是更面向接口方案。当然,我提供的 Go 代码,使用 GPL-3.0 协议开源,你可以自由使用,也可以在此基础上进行二次开发。

最后,如果你觉得本篇教程对你有帮助,欢迎加入我们的开发者交流群: 812198734 ,一起交流学习,共同进步。



将 VitePress 文档数据向量化,配合 RAG 实现 AI 助手插件
https://www.mintimate.cn/2025/08/24/knowledgeRagCnb/
作者
Mintimate
发布于
2025年8月24日
许可协议