文章目录(TL;DR)
1. 引言
大模型虽然知识广博,但在面对私有知识库、实时数据和专业领域问题时,往往力不从心。RAG(Retrieval-Augmented Generation,检索增强生成)正是解决这一问题的经典方案:先检索相关资料,再让大模型基于资料生成答案。本文将从零搭建一个可运行的 RAG 项目,帮助你理解其核心原理与落地步骤。
2. RAG 的核心原理
RAG 的整体流程可以概括为「先检索,后生成」两个阶段。系统先把用户的提问转化为向量,在知识库中检索出最相关的文本片段,再把这些片段连同问题一起交给大模型,由模型组织出最终答案。
flowchart TD
A[用户提问] --> B[向量化]
B --> C[向量检索]
C --> D[召回相关文本片段]
D --> E[拼接提示词]
E --> F[大模型生成答案]
F --> G[返回结果]
相比直接让大模型回答,RAG 有三个明显优势:一是答案可以引用真实资料,降低幻觉;二是知识库可以随时更新,无需重新训练模型;三是可以接入企业私有数据,满足数据安全要求。
3. 技术选型
为了降低入门门槛,本文选用以下轻量技术栈,全部基于 Python 生态:
- 向量数据库:Chroma,支持本地运行,无需额外部署服务。
- 嵌入模型:使用 sentence-transformers 提供的开源模型,离线可用。
- 大模型:通过 OpenAI 兼容接口调用,方便替换为本地模型或其他服务。
- 文档处理:LangChain 负责文档加载、切分与检索管线的组装。
下表汇总了各组件的职责、优势、常见替代方案以及本文的选择理由,方便你在实际项目中按需替换。
| 组件 | 作用 | 优势 | 替代方案 | 选择理由 |
|---|---|---|---|---|
| 向量数据库(Chroma) | 存储文本向量并支持相似度检索 | 本地运行、零部署成本、API 简洁,适合入门与原型验证 | FAISS、Milvus、Qdrant、Weaviate、Elasticsearch | 无需额外服务即可跑通全流程,降低学习门槛;后续数据量增大可平滑迁移到 Milvus 等分布式方案 |
| 嵌入模型(sentence-transformers) | 把文本转换为语义向量 | 开源免费、离线可用、中文支持良好,模型体积适中 | OpenAI Embeddings、Cohere Embed、BGE-M3、text2vec | 避免调用外部 API 产生费用与网络依赖,便于本地复现;BGE 系列在中文场景表现稳定 |
| 大模型(OpenAI 兼容接口) | 基于检索结果生成最终答案 | 接口标准化,可无缝切换不同厂商或本地模型 | 本地模型(Ollama、vLLM)、通义千问、文心一言、DeepSeek | OpenAI 兼容协议已成为事实标准,只需修改 base_url 即可替换为本地或其他服务,灵活性最高 |
| 文档处理(LangChain) | 负责文档加载、文本切分与检索管线组装 | 组件丰富、生态成熟,可快速组合完整 RAG 流程 | LlamaIndex、Haystack、自研脚本 | 内置大量加载器与切分器,代码量少、上手快;后续如需更细粒度控制可改用 LlamaIndex |
在文档处理框架的选择上,LangChain 与 LlamaIndex 是当前构建 RAG 最常用的两个方案,二者各有侧重。LangChain 的优势在于生态庞大、组件丰富,除了检索问答,还能方便地串联工具调用、Agent 编排和各类外部服务,适合需要构建完整应用链路的场景;其不足是抽象层次较高,版本迭代较快,部分 API 变动频繁,学习成本相对更高。LlamaIndex 则更聚焦于「数据接入与检索」本身,提供了大量数据连接器、索引结构和查询引擎,在文档解析、索引构建与检索精度上往往更细致,代码也更直观;其短板是周边生态和通用工具链不如 LangChain 丰富,若需要复杂的 Agent 或多步工具编排,可能需要额外自行组装。
选择建议:如果你的项目以「快速搭建完整 RAG 应用」为目标,且后续可能扩展工具调用、Agent 等能力,优先选择 LangChain,本文也正是基于这一考虑选用它;如果项目核心是「大规模、多来源数据的接入与高效检索」,对索引和查询的精细控制要求更高,可以优先考虑 LlamaIndex。两者并非互斥,实际项目中也可以结合使用——用 LlamaIndex 做数据索引与检索,再用 LangChain 组装上层问答与工具链路。
整体来看,这套组合优先保证「开箱即用」:Chroma 免部署、嵌入模型离线可用、OpenAI 兼容接口便于替换、LangChain 快速组装。当项目进入生产阶段后,再根据数据规模、并发量和成本要求,将其中某个组件替换为更专业的方案即可。
4. 环境准备
首先创建虚拟环境并安装依赖包。建议使用 Python 3.10 及以上版本。
python -m venv venv
source venv/bin/activate
pip install langchain langchain-community chromadb sentence-transformers openai
安装完成后,可以编写一个简单的脚本验证嵌入模型是否正常工作。
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")
vector = model.encode("你好,世界")
print(vector.shape)
5. 构建知识库
知识库的构建分为三步:加载文档、切分文本、写入向量库。这里以一份本地文本文件为例,演示完整的入库流程。
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
1. 加载文档
loader = TextLoader("knowledge_base.txt", encoding="utf-8")
documents = loader.load()
2. 切分文本
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50
)
chunks = splitter.split_documents(documents)
3. 写入向量库
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-small-zh-v1.5"
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
print(f"已写入 {len(chunks)} 个文本块")
切分参数需要根据实际文档调整:chunk_size 控制每个文本块的长度,chunk_overlap 让相邻块保留部分重叠,避免关键信息被拦腰截断。
6. 实现检索问答
知识库构建完成后,就可以实现完整的检索问答流程。核心思路是:把用户问题向量化,在向量库中检索最相关的文本块,再交给大模型生成答案。下面给出一个完整的可运行示例,使用本地文本文件构建知识库,并通过检索问答链回答用户问题。
6.1 准备本地知识库文件
首先准备一份本地文本文件 knowledge_base.txt,内容可以是产品说明、技术文档或业务资料。这里以一段示例内容为例:
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合检索与生成的技术方案。
它先在大规模文档库中检索与问题相关的文本片段,再将这些片段作为上下文交给大模型生成答案。
RAG 的核心优势包括:降低幻觉、知识可更新、支持私有数据接入。
Chroma 是一个轻量级向量数据库,支持本地运行,无需额外部署服务。
BAAI/bge-small-zh-v1.5 是一个开源中文嵌入模型,由北京智源人工智能研究院发布。
6.2 完整可运行代码
下面的脚本会依次完成:加载本地文本文件、切分文本、写入向量库、构建检索问答链并回答用户问题。代码中包含详细注释,方便你理解每一步的作用。
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI
========== 1. 加载本地文本文件 ==========
将你的知识库内容放在 knowledge_base.txt 中,编码使用 utf-8
loader = TextLoader("knowledge_base.txt", encoding="utf-8")
documents = loader.load()
print(f"加载文档数:{len(documents)}")
========== 2. 切分文本 ==========
chunk_size 控制每个文本块的最大长度,chunk_overlap 让相邻块保留部分重叠
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50
)
chunks = splitter.split_documents(documents)
print(f"切分后文本块数:{len(chunks)}")
========== 3. 构建向量库 ==========
使用开源中文嵌入模型,离线可用
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-small-zh-v1.5"
)
将文本块向量化并持久化到本地目录 ./chroma_db
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
print("向量库构建完成,已持久化到 ./chroma_db")
========== 4. 配置大模型 ==========
通过 OpenAI 兼容接口调用,可替换为本地模型或其他服务
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.2,
api_key="your-api-key", # 替换为你的 API Key
base_url="https://api.openai.com/v1"
)
========== 5. 组装检索问答链 ==========
k=3 表示每次召回 3 个最相关的文本块作为上下文
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
return_source_documents=True
)
========== 6. 提问并输出结果 ==========
question = "RAG 的核心优势有哪些?"
result = qa_chain.invoke(question)
print("问题:", question)
print("答案:", result["result"])
print("\n参考来源:")
for i, doc in enumerate(result["source_documents"], 1):
print(f"{i}. {doc.page_content[:80]}...")
6.3 运行结果说明
运行上述脚本后,控制台会依次输出以下信息:
加载文档数:1
切分后文本块数:2
向量库构建完成,已持久化到 ./chroma_db
问题: RAG 的核心优势有哪些?
答案: 根据提供的资料,RAG 的核心优势包括:降低幻觉、知识可更新、支持私有数据接入。
参考来源:
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合检索与生成的技术方案...
RAG 的核心优势包括:降低幻觉、知识可更新、支持私有数据接入...
从运行结果可以看到:系统先加载并切分了本地文本,构建向量库后,针对用户问题召回了最相关的文本块,最终由大模型基于这些资料生成答案,并附带了参考来源。这样既保证了答案的准确性,也方便追溯信息出处。
参数 k 表示召回文本块的数量。k 值越大,模型看到的上下文越丰富,但也会增加 token 消耗,需要根据实际效果权衡。
7. 效果优化建议
基础版本跑通后,可以从以下几个方向继续优化:
- 优化切分策略:根据文档结构按标题、段落切分,比固定长度切分更符合语义边界。
- 调整召回数量:通过实验对比不同 k 值下的答案质量,找到最优配置。
- 增加重排序:在向量检索后接入 reranker 模型,对召回结果做二次精排。
- 改进提示词:在提示词中要求模型「仅依据给定资料回答」,进一步降低幻觉。
- 混合检索:结合关键词检索与向量检索,提升对专有名词和精确匹配的召回效果。
下面给出一个可运行的混合检索示例,使用 EnsembleRetriever 将 BM25 关键词检索与 Chroma 向量检索结合起来,兼顾精确匹配与语义召回。
from langchain.retrievers import BM25Retriever, EnsembleRetriever
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
========== 1. 准备文档 ==========
docs = [
"RAG 的核心优势包括:降低幻觉、知识可更新、支持私有数据接入。",
"Chroma 是一个轻量级向量数据库,支持本地运行,无需额外部署服务。",
"BAAI/bge-small-zh-v1.5 是一个开源中文嵌入模型,由北京智源人工智能研究院发布。",
"混合检索结合关键词检索与向量检索,提升对专有名词和精确匹配的召回效果。",
]
========== 2. 构建 BM25 关键词检索器 ==========
bm25_retriever = BM25Retriever.from_texts(docs)
bm25_retriever.k = 2 # 关键词检索召回 2 个结果
========== 3. 构建 Chroma 向量检索器 ==========
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
vectorstore = Chroma.from_texts(docs, embedding=embeddings)
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 2}) # 向量检索召回 2 个结果
========== 4. 组装混合检索器 ==========
weights = [0.5, 0.5] # BM25 与向量检索的权重各占一半,可按需调整
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=weights
)
========== 5. 执行混合检索 ==========
question = "Chroma 是什么?"
results = ensemble_retriever.invoke(question)
print(f"问题:{question}")
print(f"召回 {len(results)} 个结果:")
for i, doc in enumerate(results, 1):
print(f"{i}. {doc.page_content}")
========== 运行结果(示例) ==========
问题:Chroma 是什么?
召回 2 个结果:
Chroma 是一个轻量级向量数据库,支持本地运行,无需额外部署服务。
混合检索结合关键词检索与向量检索,提升对专有名词和精确匹配的召回效果。
参数说明:k 控制单个检索器的召回数量,值越大上下文越丰富但 token 消耗越高;weights 用于调节 BM25 与向量检索的权重占比,两者之和应为 1,当查询包含较多专有名词时可适当调高 BM25 权重,语义模糊时则调高向量检索权重。运行结果中,混合检索同时命中了包含「Chroma」关键词的文档,并通过向量相似度补充了语义相关的文档,从而提升整体召回质量。
9. 常见问题与排查
在运行 RAG 项目时,可能会遇到一些典型问题。下面针对最常见的四类问题,给出具体的报错现象、原因分析和解决方案。
9.1 嵌入模型下载失败
报错现象:首次运行嵌入模型时,控制台提示网络连接失败或下载超时,例如 ConnectionError 或 OSError: We couldn't connect to 'https://huggingface.co'。
原因分析:sentence-transformers 首次使用时会从 Hugging Face 下载模型权重,网络受限或无法访问外网时就会失败。
解决方案:可以提前手动下载模型到本地目录,再通过 model_name 指定本地路径加载,避免运行时联网。
from sentence_transformers import SentenceTransformer
方式一:指定本地模型目录(需提前下载好模型文件)
model = SentenceTransformer("./models/bge-small-zh-v1.5")
方式二:使用镜像源下载后缓存到本地
先设置环境变量 HF_ENDPOINT=https://hf-mirror.com 再执行:
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")
9.2 Chroma 持久化目录冲突
报错现象:重复运行入库脚本时,提示 chroma_db 目录已存在,或报错 UniqueConstraintError,导致向量写入失败。
原因分析:Chroma 在同一个持久化目录中重复写入相同文档时,会因主键冲突而报错;目录被占用时也会出现异常。
解决方案:入库前先清理旧目录,或改用带时间戳的目录名,避免重复写入冲突。
import shutil
from langchain_community.vectorstores import Chroma
方式一:入库前清理旧目录
shutil.rmtree("./chroma_db", ignore_errors=True)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
方式二:使用带时间戳的目录,避免冲突
import time
persist_dir = f"./chroma_db_{int(time.time())}"
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory=persist_dir
)
9.3 OpenAI 接口超时
报错现象:调用大模型时提示 TimeoutError 或 APIConnectionError,长时间无响应后中断。
原因分析:网络不稳定、接口地址不可达,或默认请求超时时间过短。
解决方案:适当调大超时时间,并确认 base_url 与 api_key 配置正确;也可以切换到本地模型避免网络依赖。
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.2,
api_key="your-api-key", # 替换为你的 API Key
base_url="https://api.openai.com/v1",
timeout=60, # 调大超时时间,单位秒
max_retries=3 # 增加重试次数
)
9.4 中文切分效果差
报错现象:检索结果不准确,答案经常答非所问;或切分后的文本块把完整句子拦腰截断,语义不完整。
原因分析:默认的 RecursiveCharacterTextSplitter 按字符长度切分,对中文缺少语义边界感知,容易在句子中间断开。
解决方案:使用按标点符号优先切分的分隔符列表,让切分点尽量落在句号、问号等语义边界上。
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""]
)
chunks = splitter.split_documents(documents)
通过调整分隔符顺序,让切分器优先在句号、问号等完整语义边界处断开,能显著提升中文场景下的检索质量。
8. 总结
参考资料
本文在编写过程中参考了以下官方文档、开源项目与推荐阅读资料,方便你进一步深入学习 RAG 相关技术。
官方文档与开源项目
- LangChain 官方文档:LangChain overview - Docs by LangChain,提供文档加载、文本切分、向量存储、检索问答链等组件的完整 API 说明与使用示例,是上手 LangChain 的首选资料。
- Chroma 官方文档:Introduction - Chroma Docs,介绍向量数据库的安装、持久化配置、集合管理与相似度检索等核心用法,适合了解 Chroma 的底层机制。
- sentence-transformers 官方文档:SentenceTransformers Documentation — Sentence Transformers documentation,提供大量预训练嵌入模型的使用方式、微调方法与性能对比,是选择嵌入模型的重要参考。
- BGE 模型(BAAI/bge-small-zh-v1.5):https://huggingface.co/BAAI/bge-small-zh-v1.5,北京智源人工智能研究院发布的开源中文嵌入模型,支持语义检索与重排序,中文场景表现稳定。
- Hugging Face 模型库:https://huggingface.co/models,可检索并下载各类开源嵌入模型、生成模型与重排序模型,是获取预训练模型的主要渠道。
推荐阅读的 RAG 相关论文
- Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks:Lewis 等人于 2020 年提出的 RAG 原始论文,系统阐述了「先检索、后生成」的经典框架,是理解 RAG 原理的必读文献。
- Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection:通过让模型自我反思检索结果与生成内容的质量,进一步提升答案的准确性与可追溯性,是 RAG 优化方向的重要参考。
- Lost in the Middle: How Language Models Use Long Contexts:分析了长上下文下模型对中间位置信息的利用不足问题,对理解检索片段排序与提示词组织有重要启发。
推荐阅读的博客与教程
- LangChain 官方 RAG 教程:Retrieval Augmented Generation (RAG) with Deep Agents - Docs by LangChain,从零开始构建一个完整的 RAG 应用,包含代码示例与常见问题说明,适合作为实战练习。
- Chroma 官方快速入门:https://docs.trychroma.com/getting-started,通过简单示例快速掌握向量库的创建、写入与查询,适合快速上手。
- BGE 模型技术博客:GitHub - FlagOpen/FlagEmbedding: Retrieval and Retrieval-augmented LLMs · GitHub,介绍 BGE 系列模型的训练思路、使用方式与评测结果,对理解中文嵌入模型的选择很有帮助。
本文从零搭建了一个可运行的 RAG 项目,覆盖了文档加载、文本切分、向量入库、检索问答的完整链路。RAG 的核心价值在于把大模型的生成能力与外部知识库结合起来,让答案更可靠、更可更新。建议你用自己的文档替换示例数据,动手跑通全流程,再逐步尝试优化策略,相信会有更深的体会。
ding development by creating an account on GitHub." data-link-icon="https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239" data-link-title="GitHub - FlagOpen/FlagEmbedding: Retrieval and Retrieval-augmented LLMs · GitHub" href="https://github.com/FlagOpen/FlagEmbedding" title="GitHub - FlagOpen/FlagEmbedding: Retrieval and Retrieval-augmented LLMs · GitHub">GitHub - FlagOpen/FlagEmbedding: Retrieval and Retrieval-augmented LLMs · GitHub,介绍 BGE 系列模型的训练思路、使用方式与评测结果,对理解中文嵌入模型的选择很有帮助。
本文从零搭建了一个可运行的 RAG 项目,覆盖了文档加载、文本切分、向量入库、检索问答的完整链路。RAG 的核心价值在于把大模型的生成能力与外部知识库结合起来,让答案更可靠、更可更新。建议你用自己的文档替换示例数据,动手跑通全流程,再逐步尝试优化策略,相信会有更深的体会。
mented LLMs · GitHub,介绍 BGE 系列模型的训练思路、使用方式与评测结果,对理解中文嵌入模型的选择很有帮助。
本文从零搭建了一个可运行的 RAG 项目,覆盖了文档加载、文本切分、向量入库、检索问答的完整链路。RAG 的核心价值在于把大模型的生成能力与外部知识库结合起来,让答案更可靠、更可更新。建议你用自己的文档替换示例数据,动手跑通全流程,再逐步尝试优化策略,相信会有更深的体会。
ding development by creating an account on GitHub." data-link-icon="https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239" data-link-title="GitHub - FlagOpen/FlagEmbedding: Retrieval and Retrieval-augmented LLMs · GitHub" href="https://github.com/FlagOpen/FlagEmbedding" title="GitHub - FlagOpen/FlagEmbedding: Retrieval and Retrieval-augmented LLMs · GitHub">GitHub - FlagOpen/FlagEmbedding: Retrieval and Retrieval-augmented LLMs · GitHub,介绍 BGE 系列模型的训练思路、使用方式与评测结果,对理解中文嵌入模型的选择很有帮助。
本文从零搭建了一个可运行的 RAG 项目,覆盖了文档加载、文本切分、向量入库、检索问答的完整链路。RAG 的核心价值在于把大模型的生成能力与外部知识库结合起来,让答案更可靠、更可更新。建议你用自己的文档替换示例数据,动手跑通全流程,再逐步尝试优化策略,相信会有更深的体会。

776

被折叠的 条评论
为什么被折叠?



