从零搭建私人 RAG 知识库:让项目决策真正“可检索、可追溯”

本文是「从零搭建私人 RAG 知识库」专栏的第一篇。这个专栏不会只停留在概念介绍,而是以 TypeScript 项目 CorpRAG 为主线,逐步完成一个基于 LangChain、Ollama、LanceDB 和 MCP 的私人知识库。

前言:为什么我要做一个私人知识库?

在一个长期迭代的项目中,真正容易丢失的往往不是代码,而是代码背后的上下文。

例如:

  • 为什么用户认证最终选择 JWT,而不是 Session?
  • 为什么某张表要拆分,而不是继续增加字段?
  • 某个业务规则是谁提出的,当时解决了什么问题?
  • 某项技术方案有哪些限制,又有哪些替代方案?
  • 半年前踩过的坑,为什么半年后又踩了一次?

这些信息可能散落在 Markdown、会议纪要、聊天记录、需求文档和开发者的记忆里。随着项目变大、人员发生变化,仅靠目录分类和文件名搜索,很难快速找到真正需要的答案。

因此,我想做的不是一个单纯的"文档管理工具",而是一个可以通过自然语言查询的私人知识库。

这个知识库主要保存两类内容:

  1. 技术决策:架构选型、组件取舍、接口设计、性能方案、故障复盘等。
  2. 业务决策:规则变更、流程设计、需求背景、边界条件、方案结论等。

当我问:

EXT 系统当时为什么选择这套部署方案?

系统不只是匹配"部署方案"四个字,而是从已有决策文档中找出语义最相关的片段,并把来源一并返回。后续还可以让大模型基于这些资料组织答案。

这就是本专栏要实现的目标:

把散落的项目知识转化为可检索、可追溯、可持续维护的个人知识资产。


一、RAG 是什么?

RAG 是 Retrieval-Augmented Generation 的缩写,中文通常译为"检索增强生成"。

最简单的理解是:

大模型回答问题之前,先去指定的知识库中查资料,再根据查到的资料回答。

普通大模型主要依赖训练阶段学到的知识。它通常不了解我们的私有项目,也不知道团队刚刚做出的业务决策。如果直接提问,模型可能回答"不知道",也可能给出一个听起来合理、实际上并不符合项目情况的答案。

RAG 在用户问题和大模型之间增加了一个检索过程:

text 复制代码
用户问题
   ↓
从私人知识库检索相关片段
   ↓
把"问题 + 相关片段"交给大模型
   ↓
生成基于项目资料的回答

如果把大模型比作一个擅长阅读和总结的人,那么 RAG 就像是在回答问题前,先把与问题有关的资料放到他的桌面上。

一句话概括:

RAG = 先查资料,再组织答案。


二、RAG 能解决哪些问题?

1. 让大模型使用私有知识

项目文档、公司制度、产品手册和内部决策通常不会出现在模型的训练数据中。RAG 可以在不重新训练模型的情况下,让模型临时读取这些资料。

2. 降低模型幻觉

大模型很擅长组织语言,但并不保证每句话都符合事实。RAG 会把检索到的资料作为回答依据,并通过 Prompt 要求模型"只能根据上下文回答",从而减少无依据的发挥。

需要注意的是,RAG 只能降低幻觉,不能保证完全消除幻觉。文档质量、检索准确率、提示词约束和模型能力都会影响最终结果。

3. 让答案可以追溯

一个可信的知识库不仅要返回答案,还要返回答案来源,例如:

text 复制代码
结论:EXT 系统采用方案 A。
来源:data/EXT-FADR.md

有了来源,使用者就能回到原始文档继续确认,而不是只能相信模型生成的一段文字。

4. 降低知识查找成本

传统关键词搜索要求使用者知道文档中出现了哪些词。向量检索关注的是语义,即使提问和原文没有使用完全相同的措辞,也可能找到相关内容。

例如,用户问"登录状态如何保存",知识库中的原文是"系统使用 Redis 持久化会话信息",两段文字的关键词并不完全一致,但语义可能很接近。

5. 避免频繁微调模型

如果目标只是让模型了解经常变化的业务资料,通常没有必要每次都重新训练或微调模型。更新文档并重建索引,往往比重新训练模型更简单、更便宜,也更容易追踪内容变化。


三、一个完整的 RAG 系统如何工作?

RAG 可以拆成两个核心阶段。

1. 索引阶段:把资料放进知识库

索引阶段通常在文档新增或修改后执行:

text 复制代码
Markdown / PDF / Word 等原始文档
                 ↓
              加载文档
                 ↓
              清洗内容
                 ↓
              文本分块
                 ↓
         Embedding 生成向量
                 ↓
  文本片段 + 向量 + 元数据写入向量库

为什么要进行文本分块?

因为一篇完整文档可能同时包含多个主题。如果直接为整篇文档生成一个向量,向量表达的语义会过于宽泛,检索结果也不够精确。因此,需要先把长文档切成多个相对完整的小片段,也就是 Chunk。

2. 查询阶段:找到资料并回答问题

用户每次提问时执行查询流程:

text 复制代码
用户问题
   ↓
Embedding 生成查询向量
   ↓
向量库执行相似度搜索
   ↓
返回 Top-K 个相关片段
   ↓
构造 Prompt 上下文
   ↓
大模型生成答案

索引和查询必须使用同一个 Embedding 模型。不同模型产生的向量可能维度不同,即使维度相同,它们所处的语义空间也不一定一致,无法直接比较。


四、开发 RAG 需要掌握哪些知识?

RAG 并不是某一个框架或某一个模型,而是多个组件组成的一条数据处理链路。本专栏会重点学习以下内容。

1. LangChain:串起 RAG 流程

LangChain 不是大模型,而是用于连接文档、模型、向量库和检索器的开发框架。

在本项目中,它主要负责提供统一抽象:

  • Document:表示知识库文档及其元数据;
  • TextSplitter:把长文档切成多个片段;
  • Embeddings:把文本转换成向量;
  • VectorStore:保存向量并执行相似度检索;
  • Retriever:用统一接口查询相关文档。

掌握 LangChain 的重点不是记住所有 API,而是理解每个对象在数据流中的位置。

2. Ollama:在本地运行模型

Ollama 用于下载、管理和运行模型,并通过 HTTP API 向应用程序提供模型能力。

本项目当前使用 Ollama 运行 Embedding 模型:

text 复制代码
TypeScript 应用
      ↓
OllamaEmbeddings
      ↓ HTTP
Ollama 服务
      ↓
nomic-embed-text
      ↓
文本向量

这里需要区分四个概念:

  • Ollama:模型运行服务;
  • nomic-embed-text:实际执行向量计算的模型;
  • OllamaEmbeddings:LangChain 对 Ollama Embedding API 的封装;
  • LanceDB:保存和搜索向量的数据库。

Ollama 本身不是某个具体模型,Embedding 模型也不负责生成自然语言答案。

3. Embedding 模型:让文本可以进行语义计算

Embedding 模型会把文本转换为一组数字:

text 复制代码
"系统如何进行用户认证?"
           ↓
[0.12, -0.37, 0.88, ..., 0.24]

含义相近的文本,其向量通常也更加接近。向量数据库正是利用这种特性进行语义检索。

本项目默认使用:

text 复制代码
nomic-embed-text

后续需要重点理解:

  • embedDocuments()embedQuery() 的区别;
  • 模型维度和语义空间;
  • 为什么更换模型后需要重建索引;
  • 中文和中英混合文档该如何选择模型;
  • 如何评估模型的实际检索效果。

4. Vector Store:保存和检索向量

向量库负责保存以下信息:

  • 文档片段正文;
  • Embedding 模型生成的向量;
  • 来源、系统、文档类型等元数据。

用户提问时,向量库会比较查询向量与文档向量的距离,并返回最相关的若干片段。

本项目选择 LanceDB,原因是它可以直接在本地目录中运行,适合个人项目和本地知识库实验,不需要先部署一个独立的数据库服务。

需要掌握的重点包括:

  • 相似度搜索;
  • Top-K;
  • 元数据过滤;
  • 表结构与数据隔离;
  • 索引重建和增量更新;
  • 检索结果的相关性评估。

5. MCP:把知识库能力提供给 AI 客户端

MCP 是 Model Context Protocol。它用于以标准方式把工具、资源和提示词能力提供给支持 MCP 的 AI 客户端。

严格来说,MCP 不是实现 RAG 检索的必需组件。即使没有 MCP,我们也可以通过 HTTP API、命令行或普通函数调用知识库。

在 CorpRAG 中,MCP 的角色是"对外接口层":

text 复制代码
AI 客户端
   ↓ MCP 工具调用
CorpRAG
   ↓
查询 LanceDB
   ↓
返回相关知识片段和来源
   ↓
AI 客户端中的大模型组织最终答案

这种方式把"知识检索"和"答案生成"解耦了。CorpRAG 专注于维护和检索私人知识,支持 MCP 的宿主客户端负责理解用户意图和生成最终回答。

因此,本专栏不仅会介绍传统的"检索后直接调用大模型"流程,也会实践"将 RAG 检索器封装为 MCP 工具"的方案。


五、本专栏的实战项目:CorpRAG

本专栏将以当前 TypeScript 项目 CorpRAG 为主线,而不是使用脱离实际场景的零散 Demo。

项目当前采用的核心技术栈如下:

组件 选择 作用
开发语言 TypeScript 编写知识库服务
RAG 框架 LangChain.js 统一文档、分块、Embedding 和检索流程
模型服务 Ollama 在本地运行 Embedding 模型
Embedding 模型 nomic-embed-text 将文档和问题转换为向量
向量数据库 LanceDB 本地保存向量并执行相似度搜索
对外协议 MCP 向 AI 客户端暴露知识库查询工具
文档格式 Markdown 保存技术决策和业务决策

1. 知识如何组织?

项目按照业务系统区分知识,例如:

text 复制代码
EXT
FR
GA

每个系统又包含两类文档:

  • BADR:业务决策记录;
  • FADR:技术决策记录。

文档加载后会被转换成 LangChain 的 Document 对象,并保留以下元数据:

ts 复制代码
{
  system: "ext",
  docType: "FADR",
  source: "data/EXT-FADR.md"
}

这些元数据可以用于知识隔离、结果过滤和答案溯源。

2. 当前索引链路

项目中的索引过程可以概括为:

text 复制代码
系统 Markdown 文档
   ↓ document-loader.ts
LangChain Document[]
   ↓ text-splitter.ts
文档片段 Chunk[]
   ↓ embeddings.ts
nomic-embed-text 生成向量
   ↓ vector-store.ts
按系统和文档类型写入 LanceDB

当前默认配置为:

ts 复制代码
embeddingModel: "nomic-embed-text"
chunkSize: 500
chunkOverlap: 50
topK: 3

这些值不是放之四海而皆准的标准答案,而是实战的起点。后续会通过真实问题验证检索结果,再调整分块大小、重叠长度和召回数量。

3. 当前查询链路

查询时,项目会根据系统和可选的文档类型打开对应向量表:

text 复制代码
用户问题
   ↓
ext_query / fr_query / ga_query
   ↓
OllamaEmbeddings 生成查询向量
   ↓
LanceDB 相似度搜索
   ↓
返回正文、来源和文档类型

如果没有指定 BADRFADR,检索层会分别查询两类知识,再合并结果,让业务决策和技术决策都有机会被召回。

4. 为什么通过 MCP 对外提供能力?

项目会注册类似下面的 MCP 工具:

text 复制代码
ext_query
fr_query
ga_query
rebuild_kb

这样,支持 MCP 的 AI 客户端就能根据用户问题主动调用某个系统的知识库,而不需要把所有私有文档一次性塞进对话上下文。

它带来几个直接好处:

  • 只在需要时检索;
  • 只返回最相关的片段;
  • 控制发送给模型的上下文长度;
  • 保留文档来源;
  • 知识库更新后可以重新索引,无需修改客户端 Prompt。

六、这个专栏准备写什么?

整个专栏会分为"基础知识学习"和"项目实战"两个阶段。我们会先理解 RAG 链路中各个组件的作用和基本用法,再把它们组合起来,逐步完成 CorpRAG 私人知识库。

1. 基础知识学习

初步规划如下:

  1. MCP 的基本介绍和基础使用:理解 MCP 解决什么问题,以及如何向 AI 客户端提供工具。
  2. Ollama 的基本介绍和基础使用:学习本地模型的下载、管理、运行和 API 调用。
  3. Vector Store 的基本介绍和基础使用:理解向量数据库如何保存向量并执行相似度检索。
  4. Embedding 模型的基本介绍和基础使用:理解文本如何转换成向量,以及如何选择和调用向量模型。
  5. LangChain 的基本介绍和基础使用 :认识 Document、文本分块、Embedding、VectorStore 和 Retriever 等核心抽象。

2. CorpRAG 项目实战

了解以上基础内容后,我们会进入完整的项目实战:

  • 用 Markdown 持续记录项目知识;
  • 用 LangChain 组织 RAG 数据流;
  • 用 Ollama 和 nomic-embed-text 生成向量;
  • 用 LanceDB 保存和检索知识;
  • 用 MCP 把检索能力交给 AI 客户端;
  • 让每个结论都尽可能回到原始资料。

实战部分会围绕文档加载、文本分块、向量生成、索引构建、语义检索和 MCP 工具封装逐步展开,最终形成一套可检索、可追溯、可持续维护的私人知识库。


七、开始之前,需要先建立正确预期

RAG 并不是"把文档扔进向量库,系统就会自动变聪明"。最终效果取决于整条链路:

text 复制代码
文档质量
  × 分块质量
  × Embedding 模型
  × 检索策略
  × Prompt 约束
  × 大模型能力
  = 最终回答质量

检索不到正确资料时,问题可能出在文档、分块、模型或查询上;检索资料正确但回答错误时,问题可能出在 Prompt 或生成模型上。

因此,开发 RAG 最重要的能力不是会调用某一个高级 API,而是能够看懂数据从文档进入系统、再从系统返回答案的完整过程。

这也是本专栏坚持以项目实战为主的原因。


总结

这个专栏要完成的,不是一个通用搜索引擎,也不是一个无所不知的聊天机器人,而是一个服务于个人和项目的知识基础设施:

  • 用 Markdown 持续记录项目知识;
  • 用 LangChain 组织 RAG 数据流;
  • 用 Ollama 和 nomic-embed-text 生成向量;
  • 用 LanceDB 保存和检索知识;
  • 用 MCP 把检索能力交给 AI 客户端;
  • 让每个结论都尽可能回到原始资料。

最后,再用一句话记住 RAG:

先从自己的知识库中找到可信资料,再让大模型基于资料回答。

相关推荐
钱六两1 小时前
#6、Spring AI RAG 深度技术解析 — 从原理到生产实践
ai编程
颜进强1 小时前
Embedding 模型介绍:热门模型对比与应用场景
前端·后端·ai编程
颜进强1 小时前
LanceDB 基础使用:用 TypeScript 完成第一次向量检索
前端·后端·ai编程
xyphf_和派孔明1 小时前
企业级微前端项目完整创建步骤,第二步:配置主应用
前端
武子康1 小时前
低延迟不是更快地猜:EOU / Barge-in / Turn Protocol 必须统一(4 种结束 + Generation Fencing + 9 类可复现场景)
人工智能·后端·llm
默_笙1 小时前
🔑 让 AI 学会"读"网页:我用 Cheerio 爬了一篇掘金文章,然后切成小块存进了向量库
前端·javascript
颜进强1 小时前
Ollama 从入门到实践:本地模型运行、API 调用
前端·后端·ai编程
颜进强1 小时前
Embedding 基础使用:用 Ollama 和 LangChain.js 生成文本向量
前端·后端·ai编程
颜进强1 小时前
Vector Store 入门:什么是向量数据库,主流产品如何选择
前端·后端·ai编程