本文是「从零搭建私人 RAG 知识库」专栏的第一篇。这个专栏不会只停留在概念介绍,而是以 TypeScript 项目 CorpRAG 为主线,逐步完成一个基于 LangChain、Ollama、LanceDB 和 MCP 的私人知识库。
前言:为什么我要做一个私人知识库?
在一个长期迭代的项目中,真正容易丢失的往往不是代码,而是代码背后的上下文。
例如:
- 为什么用户认证最终选择 JWT,而不是 Session?
- 为什么某张表要拆分,而不是继续增加字段?
- 某个业务规则是谁提出的,当时解决了什么问题?
- 某项技术方案有哪些限制,又有哪些替代方案?
- 半年前踩过的坑,为什么半年后又踩了一次?
这些信息可能散落在 Markdown、会议纪要、聊天记录、需求文档和开发者的记忆里。随着项目变大、人员发生变化,仅靠目录分类和文件名搜索,很难快速找到真正需要的答案。
因此,我想做的不是一个单纯的"文档管理工具",而是一个可以通过自然语言查询的私人知识库。
这个知识库主要保存两类内容:
- 技术决策:架构选型、组件取舍、接口设计、性能方案、故障复盘等。
- 业务决策:规则变更、流程设计、需求背景、边界条件、方案结论等。
当我问:
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 相似度搜索
↓
返回正文、来源和文档类型
如果没有指定 BADR 或 FADR,检索层会分别查询两类知识,再合并结果,让业务决策和技术决策都有机会被召回。
4. 为什么通过 MCP 对外提供能力?
项目会注册类似下面的 MCP 工具:
text
ext_query
fr_query
ga_query
rebuild_kb
这样,支持 MCP 的 AI 客户端就能根据用户问题主动调用某个系统的知识库,而不需要把所有私有文档一次性塞进对话上下文。
它带来几个直接好处:
- 只在需要时检索;
- 只返回最相关的片段;
- 控制发送给模型的上下文长度;
- 保留文档来源;
- 知识库更新后可以重新索引,无需修改客户端 Prompt。
六、这个专栏准备写什么?
整个专栏会分为"基础知识学习"和"项目实战"两个阶段。我们会先理解 RAG 链路中各个组件的作用和基本用法,再把它们组合起来,逐步完成 CorpRAG 私人知识库。
1. 基础知识学习
初步规划如下:
- MCP 的基本介绍和基础使用:理解 MCP 解决什么问题,以及如何向 AI 客户端提供工具。
- Ollama 的基本介绍和基础使用:学习本地模型的下载、管理、运行和 API 调用。
- Vector Store 的基本介绍和基础使用:理解向量数据库如何保存向量并执行相似度检索。
- Embedding 模型的基本介绍和基础使用:理解文本如何转换成向量,以及如何选择和调用向量模型。
- 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:
先从自己的知识库中找到可信资料,再让大模型基于资料回答。