大模型 RAG 实战:从零手把手构建知识库问答系统,建议收藏
本文详解如何利用 RAG 技术解决大模型“幻觉”问题,从零构建基于私有知识库的问答系统。涵盖文档分割、向量化存储、检索增强生成及提示词工程等核心环节,结合 LangChain、Ollama 等工具实现完整代码,并探讨了增量更新与混合检索等进阶优化方案。
一、理解 RAG 的核心价值
1.1 什么是 RAG?
RAG 是一种混合架构,它将用户的查询与知识库中的相关内容相结合,然后交给大语言模型生成回答。简单来说,就是"先检索,再生成"。
1.2 为什么需要 RAG?
大语言模型虽然强大,但存在两个固有缺陷:
| 问题 | 表现 | RAG 的解决方案 |
|---|---|---|
| 知识过时 | 模型训练完成后无法获取新信息 | 知识库可随时更新 |
| 幻觉问题 | 模型可能编造不存在的答案 | 强制基于检索到的文档回答 |
1.3 RAG 的优势
- 事实准确:回答基于真实文档,减少幻觉
- 可更新:无需重新训练模型即可更新知识
- 可追溯:可以标注答案来源
- 成本低廉:相比微调模型,成本更低
二、系统架构设计
2.1 整体架构图

整体架构
2.2 核心组件职责
| 组件 | 职责 | 技术选型 |
|---|---|---|
| DocumentChunker | 将文档分割为语义完整的片段 | LangChain TextSplitters |
| EmbeddingStore | 存储和检索向量表示 | Chroma + Ollama Embeddings |
| KnowledgeBase | 协调整个知识库的构建和管理 | 自定义类 |
| RAGChat | 处理用户提问,协调检索和生成 | LangChain + Ollama |
三、离线阶段:构建知识库
3.1 文本分割:DocumentChunker
文档分割是 RAG 的第一步,也是最关键的一步。分割的好坏直接影响检索效果。
为什么需要分割?
- 大语言模型有上下文长度限制
- 向量检索需要更小的语义单元
- 合理的分割能提高检索精度
分割策略
// 项目配置
const chunkSize = 800; // 每个 chunk 的字符数
const chunkOverlap = 100; // 相邻 chunk 的重叠字符数
参数说明:
chunkSize:太小会丢失上下文,太大会降低检索精度,推荐 500-1000chunkOverlap:确保边界信息的连续性,推荐 50-150
LangChain TextSplitters API
LangChain 提供了多种分割器,本项目使用两种:
// Markdown 文档专用分割器
const mdSplitter = new MarkdownTextSplitter({
chunkSize: 800,
chunkOverlap: 100
});
// 递归字符分割器(通用型)
const textSplitter = new RecursiveCharacterTextSplitter({
chunkSize: 800,
chunkOverlap: 100,
// 按优先级递归尝试的分隔符
separators: ["/n/n", "/n", "。", "!", "?", ";", ",", " ", ""]
});
递归分割的工作原理:
-
首先尝试用
"/n/n"分割(段落级别) -
如果结果仍然太大,尝试用
"/n"分割(句子级别) -
继续尝试标点符号,直到满足 chunkSize 要求
支持多文件处理
export class DocumentChunker {
constructor(filepath: string | string[]) {
// 支持单个文件或文件数组
}
async splitText(): Promise<string[]> {
if (Array.isArray(this.filepath)) {
// 并行处理多个文件
const allChunks = awaitPromise.all(
this.filepath.map(fp =>this.processFile(fp))
);
return allChunks.flat();
}
returnawaitthis.processFile(this.filepath);
}
}
3.2 向量化与存储:EmbeddingStore
什么是向量?
向量是文本的数值表示,相似的文本在向量空间中距离更近。通过计算向量之间的相似度,可以找到与查询最相关的文档。
向量化流程

向量化流程
LangChain Embeddings API
import { OllamaEmbeddings } from "@langchain/ollama";
const embeddings = new OllamaEmbeddings({
model: "qwen3-embedding:4b",
baseUrl: "http://localhost:11434"
});
// 单个文本向量化
const vector = await embeddings.embedQuery("查询文本");
// 批量向量化
const vectors = await embeddings.embedDocuments(["文档1", "文档2"]);
Chroma 向量数据库
Chroma 是一个开源的向量数据库,专为 AI 应用设计:
import { Chroma } from "@langchain/community/vectorstores/chroma";
const vectorStore = new Chroma(embeddings, {
collectionName: "my-knowledge-base",
clientParams: { host: 'localhost', port: 8000 },
collectionMetadata: {
"hnsw:space": "cosine" // 余弦相似度
}
});
相似度算法选择:
| 算法 | 特点 | 适用场景 |
|---|---|---|
| Cosine | 关注方向,忽略长度 | 文本向量(推荐) |
| Euclidean | 关注绝对距离 | 需要精确匹配 |
| Dot Product | 关注对齐程度 | 归一化后的向量 |
在使用Chroma API之前,需要安装 Chroma 客户端,比如使用 docker 安装:
# 拉取镜像
docker pull ghcr.io/chroma-core/chroma:latest
# 运行容器
docker run -d /
--name chroma-server /
-p 8000:8000 /
-v chroma-data:/chroma/chroma/ /
-e IS_PERSISTENT=TRUE /
-e CHROMA_WORKERS=$(nproc) /
ghcr.io/chroma-core/chroma:latest
文档去重机制
export class EmbeddingStore {
private documentHashes = new Set<string>();
async addDocumentsWithDeduplication(documents: string[]) {
const uniqueDocs = documents.filter(doc => {
const hash = this.calculateHash(doc);
if (this.documentHashes.has(hash)) {
return false; // 已存在,跳过
}
this.documentHashes.add(hash);
return true;
});
awaitthis.vectorStore.addDocuments(uniqueDocs);
}
}
3.3 知识库管理:KnowledgeBase
KnowledgeBase 是整个系统的协调器,负责协调整个知识库的构建和更新。
export class KnowledgeBase {
staticasync create(filepath: string | string[]): Promise<KnowledgeBase> {
const kb = new KnowledgeBase(filepath);
await kb.init();
return kb;
}
privateasync init() {
const chunks = await this.documentChunker.splitText();
awaitthis.embeddingStore.setVectors(chunks);
}
}
使用工厂方法的好处:
- 确保异步初始化完成后再返回实例
- 隐藏构造函数,强制使用正确的初始化方式
- 便于后续扩展(如连接池、缓存等)
四、在线阶段:问答流程
4.1 检索相关上下文
当用户提问时,系统首先在向量数据库中检索最相关的文档片段。

检索上下文
VectorStore API 使用
// 相似度搜索(返回文档列表)
const docs = await vectorStore.similaritySearch(query, k);
// 相似度搜索(返回文档和分数)
const results = await vectorStore.similaritySearchWithScore(query, k);
// 格式: [[Document, score], ...]
// 按相似度阈值过滤
const filtered = results.filter(([_, score]) => score > 0.8);
4.2 提示词工程
提示词(Prompt)是控制模型输出的关键,好的提示词能显著提升回答质量。
基础提示词
请基于以下上下文回答问题。
上下文:{context}
问题:{question}
回答:
优化后的提示词
你是一个专业的问答助手,请基于提供的参考文档回答用户的问题。
【参考文档】
{context}
【用户问题】
{question}
【回答要求】
1. **只使用参考文档中的信息回答,不要编造内容**
2. **如果参考文档中没有相关信息,请明确说明"文档中未找到相关答案"**
3. **回答时要条理清晰,使用恰当的段落分隔**
4. **如果答案是列表形式,请使用项目符号**
回答:
优化要点:
- 明确角色定位
- 使用分隔符增强可读性
- 列出具体要求,减少模型理解偏差
- 处理无答案的情况
LangChain PromptTemplate API
import { PromptTemplate } from "@langchain/core/prompts";
const promptTemplate = PromptTemplate.fromTemplate(`
你是一个专业的问答助手,请基于提供的参考文档回答用户的问题。
【参考文档】
{context}
【用户问题】
{question}
【回答要求】
1. **只使用参考文档中的信息回答,不要编造内容**
2. **如果参考文档中没有相关信息,请明确说明"文档中未找到相关答案"**
3. **回答时要条理清晰,使用恰当的段落分隔**
4. **如果答案是列表形式,请使用项目符号**
回答:
`);
// 格式化提示词
const prompt = await promptTemplate.format({
context: "检索到的文档内容...",
question: "用户的问题"
});
4.3 生成回答
使用 LangChain 的链式调用机制,将检索、提示词构建和生成串联起来。
链式调用基础
import { StringOutputParser } from "@langchain/core/output_parsers";
// 创建链
const chain = llm
.pipe(new StringOutputParser()) // 解析输出为字符串
.pipe((text) => text.trim()); // 后处理
// 调用链
const result = await chain.invoke("用户输入");
完整的 RAG 链
export class RAGChat {
async answer(question: string): Promise<void> {
// 步骤1:检索相关上下文
const contextList = await this.knowledgeBase.getEmbeddingStore()
.retrieveContext(question);
const context = contextList?.join("/n/n");
// 步骤2:构建提示词
const prompt = await this.promptTemplate.format({
context,
question,
});
// 步骤3:调用模型生成回答
const chain = this.llm.pipe(new StringOutputParser());
const answer = await chain.stream(prompt);
// 步骤4:流式输出
await answer.pipeTo(writable);
}
}
async function main() { try { console.log("正在初始化 RAG 系统..."); const knowledgeBase = await KnowledgeBase.create( path.resolve(__dirname, "../xxxx.md") ); const ragChat = new RAGChat({ knowledgeBase }); console.log("RAG 系统初始化完成!"); console.log("输入您的问题(输入 'exit' 或 'quit' 退出):/n"); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const askQuestion = () => { rl.question("> ", async (prompt) => { const trimmedPrompt = prompt.trim(); if (!trimmedPrompt) { askQuestion(); return; } if (trimmedPrompt === "exit" || trimmedPrompt === "quit") { console.log("再见!"); rl.close(); return; } await ragChat.answer(trimmedPrompt); console.log(); askQuestion(); }); }; askQuestion(); } catch (error) { console.error("系统启动失败:", error instanceof Error ? error.message : String(error)); process.exit(1); }}
使用 RunnableSequence
对于更复杂的场景,可以使用 RunnableSequence:
import { RunnableSequence } from"@langchain/core/runnables";
const ragChain = RunnableSequence.from([
{
// 步骤1:检索上下文
context: (input) => retriever.invoke(input.question),
question: (input) => input.question
},
// 步骤2:格式化提示词
(input) => promptTemplate.format(input),
// 步骤3:调用模型
llm,
// 步骤4:解析输出
new StringOutputParser()
]);
const response = await ragChain.invoke({ question: "用户问题" });
五、进阶功能:增量更新
生产环境中,知识库需要频繁更新而不需要重新处理所有文档。
5.1 文件变更检测
通过文件哈希值判断文件是否发生变化:
class KnowledgeBase {
private fileHashes = new Map<string, string>();
private calculateFileHash(filepath: string): string {
const content = fs.readFileSync(filepath, 'utf-8');
return crypto.createHash('md5').update(content).digest('hex');
}
private isFileChanged(filepath: string): boolean {
const currentHash = this.calculateFileHash(filepath);
const previousHash = this.fileHashes.get(filepath);
if (previousHash !== currentHash) {
this.fileHashes.set(filepath, currentHash);
return true;
}
return false;
}
}
5.2 增量更新流程

增量更新流程
5.3 实现代码
export class KnowledgeBase {
async update(filepath: string | string[]): Promise<void> {
const filepaths = Array.isArray(filepath) ? filepath : [filepath];
let updated = false;
for (const fp of filepaths) {
if (this.isFileChanged(fp)) {
const chunks = await this.documentChunker.processFile(fp);
awaitthis.embeddingStore.deleteDocumentsBySource(fp);
awaitthis.embeddingStore.addDocumentsWithDeduplication(chunks, fp);
updated = true;
}
}
if (updated) {
console.log('知识库更新完成');
}
}
}
六、常见问题与解决方案
Q1: 检索结果不相关怎么办?
可能原因:
- chunkSize 设置不当
- 嵌入模型不适合当前语言
- 分割策略不合理
解决方案:
-
调整 chunkSize(推荐 500-1000)
-
尝试不同的嵌入模型
-
优化分割策略,增加 chunkOverlap
-
使用混合检索(关键词+向量)
Q2: 如何处理超长文档?
解决方案:
- 使用章节级别分割
- 添加目录索引
- 实现分块检索策略
Q3: 模型回答不够准确?
解决方案:
- 优化提示词模板
- 增加 k 值获取更多上下文
- 添加相似度阈值过滤
- 使用更强的模型
Q4: 如何提升系统性能?
优化方向:
- 批量向量化处理
- 使用向量索引(如 HNSW)
- 实现缓存机制
- 并行处理多个文件
七、总结与展望
7.1 RAG 技术总结
RAG 技术通过"检索+生成"的方式,让大语言模型能够基于私有知识库回答问题,具有事实准确、可更新、可追溯等优势。
本项目从零开始构建了一个完整的 RAG 问答系统,涵盖了:
- ✅ 文本分割与向量化
- ✅ 向量存储与检索
- ✅ 提示词工程
- ✅ 链式调用机制
- ✅ 增量更新功能
7.2 RAG 当前面临的挑战
尽管 RAG 技术已经取得了显著进展,但在实际应用中仍存在一些挑战:
| 挑战 | 具体表现 |
|---|---|
| 检索准确性 | 语义鸿沟问题,向量检索可能错过相关文档 |
| 上下文长度限制 | 模型上下文窗口有限,难以容纳大量检索内容 |
| 多轮对话能力弱 | 难以有效利用对话历史进行检索 |
| 推理能力有限 | 跨文档的复杂推理能力不足 |
| 延迟问题 | 检索+生成的链路延迟较高 |
| 知识更新成本 | 向量化存储和索引更新仍有开销 |
7.3 新兴技术与解决方案
针对上述挑战,研究界和工业界正在积极探索多种解决方案:
1. 混合检索(Hybrid Retrieval)
问题解决: 提高检索准确性
结合关键词检索(BM25)和向量检索的优势:

混合检索
技术代表:
- LangChain 的
BM25Retriever+VectorStoreRetriever - Elasticsearch 的 dense_vector + text 字段混合查询
2. 重排序(Reranking)
问题解决: 提升检索结果的相关性
在初步检索后,使用专门的排序模型对结果进行精排:
// 检索 Top-50 个候选
const candidates = await vectorStore.similaritySearch(query, 50);
// 使用重排序模型精排
const reranker = new CohereRerank();
const rankedResults = await reranker.compressDocuments(candidates, query);
// 取 Top-5
const topResults = rankedResults.slice(0, 5);
技术代表:
- Cohere Rerank API
- BGE-Reranker(开源)
- ColBERT(Late Interaction)
3. 长上下文模型(Long Context LLMs)
问题解决: 突破上下文长度限制
新一代支持超长上下文的模型,可以直接处理更多检索内容:
| 模型 | 上下文长度 | 特点 |
|---|---|---|
| GPT-4 Turbo | 128K | 支持长文档直接处理 |
| Claude 3 | 200K | 优秀的长文档理解能力 |
| Qwen-Long | 10M | 超长上下文,适合海量检索 |
| Gemini 1.5 | 1M | 多模态长上下文 |
4. 智能体 RAG(Agentic RAG)
问题解决: 增强推理和多轮对话能力
将 RAG 与 Agent 结合,使用工具调用和思维链增强推理能力:

Agentic RAG
技术代表:
- LangGraph(Agent 工作流)
- AutoGPT、BabyAGI
- ReAct 框架
5. 图谱 RAG(GraphRAG)
问题解决: 增强跨文档推理能力
将知识库构建为知识图谱,支持结构化查询和推理:

图谱 RAG
技术代表:
- Microsoft GraphRAG
- Neo4j + LLM
- LlamaIndex Knowledge Graphs
6. 自适应检索(Adaptive RAG)
问题解决: 优化延迟和准确性
根据问题类型动态决定是否需要检索,以及检索多少内容:
// 判断是否需要检索
const needsRetrieval = await router.predict({
query: userQuestion
});
if (needsRetrieval === "yes") {
// 根据问题复杂度调整检索数量
const k = determineRetrievalDepth(userQuestion);
const context = await retrieve(userQuestion, k);
return generateAnswer(context, userQuestion);
} else {
// 直接使用模型内部知识
return llm.invoke(userQuestion);
}
技术代表:
- Self-RAG(带自我反思的 RAG)
- Adaptive RAG(自适应检索)
- Corrective RAG(纠错型 RAG)
7. 微调 RAG(Fine-tuned Embeddings)
问题解决: 提升领域特定检索效果
针对特定领域微调嵌入模型:
| 方案 | 特点 | 适用场景 |
|---|---|---|
| BGE-M3 | 多语言、多功能 | 通用场景 |
| E5 | 基于对比学习 | 问答系统 |
| Jina Embeddings | 开源高效 | 成本敏感场景 |
| 自定义微调 | 领域定制 | 垂直领域 |
7.5 总结
RAG 技术正在快速演进,从最初的"简单检索+生成"模式,发展到今天融合混合检索、重排序、智能体、知识图谱等多种技术的综合方案。
对于开发者而言,选择合适的 RAG 方案需要考虑:
-
应用场景:问答、对话、知识管理、代码辅助等
-
数据特性:文本类型、数据规模、更新频率
-
性能要求:延迟、准确性、成本
-
团队能力:技术栈、维护成本
希望本文能够帮助你理解 RAG 的核心概念,并在实际项目中应用这些技术。随着技术的不断发展,RAG 的应用场景将会越来越广泛,让我们一起探索更多可能性!
最后
对于正在迷茫择业、想转行提升,或是刚入门的程序员、编程小白来说,有一个问题几乎人人都在问:未来10年,什么领域的职业发展潜力最大?
答案只有一个:人工智能(尤其是大模型方向)
当下,人工智能行业正处于爆发式增长期,其中大模型相关岗位更是供不应求,薪资待遇直接拉满——字节跳动作为AI领域的头部玩家,给硕士毕业的优质AI人才(含大模型相关方向)开出的月基础工资高达5万—6万元;即便是非“人才计划”的普通应聘者,月基础工资也能稳定在4万元左右。
再看阿里、腾讯两大互联网大厂,非“人才计划”的AI相关岗位应聘者,月基础工资也约有3万元,远超其他行业同资历岗位的薪资水平,对于程序员、小白来说,无疑是绝佳的转型和提升赛道。

对于想入局大模型、抢占未来10年行业红利的程序员和小白来说,现在正是最好的学习时机:行业缺口大、大厂需求旺、薪资天花板高,只要找准学习方向,稳步提升技能,就能轻松摆脱“低薪困境”,抓住AI时代的职业机遇。
如果你还不知道从何开始,我自己整理一套全网最全最细的大模型零基础教程,我也是一路自学走过来的,很清楚小白前期学习的痛楚,你要是没有方向还没有好的资源,根本学不到东西!
下面是我整理的大模型学习资源,希望能帮到你。

👇👇扫码免费领取全部内容👇👇

最后
1、大模型学习路线

2、从0到进阶大模型学习视频教程
从入门到进阶这里都有,跟着老师学习事半功倍。

3、 入门必看大模型学习书籍&文档.pdf(书面上的技术书籍确实太多了,这些是我精选出来的,还有很多不在图里)

4、 AI大模型最新行业报告
2026最新行业报告,针对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。

5、面试试题/经验

【大厂 AI 岗位面经分享(107 道)】

【AI 大模型面试真题(102 道)】

【LLMs 面试真题(97 道)】

6、大模型项目实战&配套源码

适用人群

四阶段学习规划(共90天,可落地执行)
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
-
硬件选型
-
带你了解全球大模型
-
使用国产大模型服务
-
搭建 OpenAI 代理
-
热身:基于阿里云 PAI 部署 Stable Diffusion
-
在本地计算机运行大模型
-
大模型的私有化部署
-
基于 vLLM 部署大模型
-
案例:如何优雅地在阿里云私有部署开源大模型
-
部署一套开源 LLM 项目
-
内容安全
-
互联网信息服务算法备案
-
…
👇👇扫码免费领取全部内容👇👇

3、这些资料真的有用吗?
这份资料由我和鲁为民博士(北京清华大学学士和美国加州理工学院博士)共同整理,现任上海殷泊信息科技CEO,其创立的MoPaaS云平台获Forrester全球’强劲表现者’认证,服务航天科工、国家电网等1000+企业,以第一作者在IEEE Transactions发表论文50+篇,获NASA JPL火星探测系统强化学习专利等35项中美专利。本套AI大模型课程由清华大学-加州理工双料博士、吴文俊人工智能奖得主鲁为民教授领衔研发。
资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的技术人员,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)