跳到主要内容RAG 系统实战:Langchain 框架与纯手搓实现对比 | 极客日志PythonAI算法
RAG 系统实战:Langchain 框架与纯手搓实现对比
RAG 技术通过检索增强生成解决大模型幻觉问题。对比了 Langchain 框架与纯手搓两种实现路径,详解环境配置、文档加载、向量化及检索流程。Langchain 适合快速原型开发,纯手搓利于理解底层机制。提供避坑指南与最佳实践建议,帮助开发者构建准确可靠的 AI 问答系统。
RAG 系统实战:Langchain 框架与纯手搓实现对比
你是否曾为大模型'一本正经地胡说八道'而困扰?或者希望它能回答关于你个人笔记或公司内部文档的问题?今天,我们就来一起动手搭建一个简单 RAG 系统,让你的大模型从'闭卷考试'变成'开卷考试',大幅提升回答的准确性和实用性。
了解了 RAG 的基本原理后,我们来看看如何动手实现它。

一、什么是 RAG?——让大模型'开卷考试'
1.1 RAG 的核心思想

想象一下,你正在参加一场考试。如果只能靠记忆答题,遇到不熟悉的知识点就容易答错甚至瞎编——这就像没有 RAG 的传统大语言模型(LLM)。它们的知识被'固化'在训练数据中,无法获取新信息,也容易产生'幻觉'。
而 RAG(Retrieval-Augmented Generation,检索增强生成)技术,就是给大模型发了一本'参考书'。当用户提问时,系统会先从这本'参考书'(你的知识库)里查找相关信息,再把找到的内容和问题一起交给大模型去生成答案。
这种'开卷考试'的方式带来了三大优势:
- 事实更准确:答案基于真实文档,大幅减少胡编乱造。
- 知识可更新:只需更新你的知识库文件,就能让大模型掌握最新信息。
- 支持私有数据:你可以让它读取你的 PDF 报告、Word 文档、网页内容,解答专属问题。
1.2 RAG 的工作流程

RAG 系统的工作可以分为两个阶段:离线准备和在线查询。
在你开始使用之前,需要先完成'离线准备'阶段,也就是把你的知识文档处理好,方便后续快速查找。
这个过程包含五个核心步骤:
- 用户提问:'阿司匹林的禁忌症是什么?'
- 查询向量化:将你的问题转换成一串数字(向量),这是计算机理解语义的方式。
- 向量检索:在存储着无数文档片段向量的数据库中,找出与问题向量最相似的几个片段。
- 提示增强:把检索到的相关片段和原始问题拼接起来,形成一个新的、信息更丰富的提示词。
- 大模型生成:把这个增强后的提示词交给大模型,它就能结合这些参考资料,生成一个有据可依的答案。
整个过程通常在几秒内完成,为你提供精准可靠的信息。
二、两种实现方式对比:Langchain vs 纯手搓

现在,你已经知道了 RAG 是怎么工作的,接下来就要选择用什么方式来搭建它。主要有两种路径:使用成熟的框架 Langchain,或是从零开始 纯手搓 实现。
2.1 开发效率对比
- Langchain:这就像使用一套高级乐高积木。它已经为你预制好了'文档加载器'、'文本切分器'、'向量数据库接口'等模块。你只需要像搭积木一样把它们组装起来,几分钟内就能跑通整个流程,非常适合快速验证想法。
- 纯手搓:这就像是自己烧砖、砍木头,再盖房子。你需要手动调用底层库(如
sentence-transformers、faiss)来实现每一个环节。虽然耗时较长,但你能完全掌控每一个细节。
2.2 代码复杂度与维护成本
| 维度 | Langchain 实现 | 纯手搓实现 |
|---|
| 开发时间 | ~25 分钟 | ~180 分钟 |
| 代码行数 | 少(高层 API) | 多(底层集成) |
| 调试难度 | 低 | 高 |
| 典型应用场景 | 快速原型、企业系统 | 教学演示、边缘设备 |
总的来说,Langchain 抽象程度高,学习曲线稍陡,但后期扩展和维护非常方便。而纯手搓代码透明可控,适合教学理解底层机制,但在实际项目中维护成本较高。
通过这个对比,你应该能初步判断哪种方式更适合你当前的需求。接下来,我们将分别用这两种方式,带你一步步实现一个完整的 RAG 系统。
三、Langchain 实现:高效构建你的第一个 RAG 系统
如果你的目标是快速上手并看到效果,那么 Langchain 绝对是首选。让我们开始吧!
3.1 环境准备
首先,你需要安装必要的 Python 包。打开你的命令行工具,执行以下命令:
pip install langchain langchain-openai langchain-text-splitters chromadb faiss-cpu openai python-dotenv
同时,你需要一个 OpenAI API 密钥来调用 GPT 模型。为了安全起见,建议你创建一个 .env 文件来管理它:
# .env 文件内容
OPENAI_API_KEY=your_actual_api_key_here
这样,你的密钥就不会硬编码在 Python 脚本里了。
3.2 步骤详解与代码实现
步骤 1:加载文档
from langchain_community.document_loaders import TextLoader
loader = TextLoader("knowledge.txt", encoding="utf-8")
documents = loader.load()
- 功能说明:这一步负责读取你的原始文档。
TextLoader 支持 TXT 格式,如果是 PDF,你可以换成 PyPDFLoader。
步骤 2:文本切分
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", ".", "!", "?"]
)
chunks = text_splitter.split_documents(documents)
- 功能说明:将长文档切成小块,便于后续处理。
chunk_overlap 的作用就像你读书时前后翻页保持连贯,防止一句话被切断。
- 💡 小贴士:对于中文文档,确保分割符列表包含了中文句号'。'。
步骤 3:向量化并存入数据库
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
vectorstore = Chroma.from_documents(
chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
vectorstore.persist()
- 功能说明:
embeddings 模型是关键,它能让语义相近的句子(如'猫爱吃鱼'和'猫咪喜欢吃鱼')拥有相似的向量表示。
- 替代方案:不想依赖 API?可以换用开源的
HuggingFaceEmbeddings 和 BAAI/bge-small-zh 模型。
步骤 4:创建检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
- 功能说明:定义了一个检索策略,每次查询都会返回最相关的 Top-3 结果。
- 高级选项:可以设置
search_type="mmr" 来使用最大边际相关性算法,避免返回重复冗余的结果。
步骤 5:初始化语言模型
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
- 功能说明:接入 GPT-3.5 模型进行回答生成。
temperature=0 表示输出尽可能确定和稳定。
- 替代建议:国内用户可以尝试接入通义千问、百川等国产大模型的 API。
步骤 6:构建 RAG 生成链
from langchain_core.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser
prompt = ChatPromptTemplate.from_template(
"请基于以下上下文回答问题,若无法找到答案请回答'我不知道'。\n"
"上下文:{context}\n\n问题:{question}"
)
rag_chain = ({"context": retriever, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser())
result = rag_chain.invoke("阿司匹林的禁忌症是什么?")
print("AI 回答:", result)
- 功能说明:
RunnablePassthrough() 的作用是保留用户的原始问题,并将其传递给下一个组件。
- ✅ 已完成:恭喜!你已经成功用 Langchain 搭建了一个功能完整的 RAG 系统。
四、纯手搓实现:深入理解 RAG 底层机制
现在,让我们放下高级框架,亲手实现一遍 RAG 的核心逻辑,这能帮助你彻底理解它的本质。
4.1 不依赖框架的意义
通过纯手搓实现,你将清晰地看到数据是如何在各个组件间流动的。这对于学习原理、在资源受限的设备上部署,或者进行极致的性能优化都至关重要。
4.2 步骤详解与代码实现
步骤 1:加载并切分文本
with open("knowledge.txt", "r", encoding="utf-8") as f:
text = f.read()
chunk_size = 500
overlap = 50
chunks = [text[i:i + chunk_size] for i in range(0, len(text), chunk_size - overlap)]
print(f"✅ 切分为 {len(chunks)} 个文本块")
- 功能说明:这是一种最基础的切分方法,通过固定长度和重叠来生成文本块。
- 缺陷提醒:这种方法缺乏语义感知,可能会在句子中间切断,导致信息丢失。相比之下,Langchain 的递归切分器要智能得多。
步骤 2:调用嵌入模型生成向量
from sentence_transformers import SentenceTransformer
import numpy as np
embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
chunk_vectors = embedding_model.encode(chunks, show_progress_bar=True)
dimension = chunk_vectors.shape[1]
- 功能说明:
SentenceTransformer 库让本地运行嵌入模型变得非常简单。
- 推荐模型:对于中文场景,强烈推荐使用
BAAI/bge-small-zh 模型,效果远超通用模型。
步骤 3:构建 Faiss 向量索引
import faiss
index = faiss.IndexFlatL2(dimension)
index.add(np.array(chunk_vectors))
faiss.write_index(index, "faiss_index.bin")
- 功能说明:Faiss 是一个高效的向量相似度搜索库,特别适合处理大规模向量数据。
- 性能优化:对于更大的数据集,可以改用
IndexHNSWFlat 等近似最近邻索引,以换取更快的检索速度。
步骤 4:相似度检索
query = "阿司匹林的禁忌症是什么?"
query_vector = embedding_model.encode([query])
distances, indices = index.search(np.array(query_vector), k=3)
retrieved_chunks = [chunks[i] for i in indices[0]]
context = "\n".join(retrieved_chunks)
- 功能说明:
index.search() 返回的是距离值(越小越相似)和对应的向量索引。
- 改进建议:在某些场景下,使用余弦相似度比欧氏距离更能反映语义相关性。
步骤 5:拼接 Prompt 并调用 LLM 生成
from transformers import pipeline
generator = pipeline("text-generation", model="gpt2")
prompt = f"根据以下资料回答问题:\n{context}\n\n问题:{query}\n回答:"
output = generator(prompt, max_new_tokens=150, do_sample=False)
answer = output[0]['generated_text'][len(prompt):].strip()
print("AI 回答:", answer)
- 功能说明:这是整个流程的最后一步,也是最关键的一步。大模型基于你提供的上下文来生成最终答案。
- 注意事项:
max_new_tokens 参数控制生成的最大长度,避免模型无限制地生成下去。
五、环境配置与新手避坑指南
为了让你的 RAG 之旅更加顺畅,这里有一份详细的避坑指南。
5.1 推荐开发环境
- Python 版本:强烈推荐使用 3.9 – 3.11 版本。避免使用 3.12 及以上版本,因为其对
typing 模块的修改可能导致 langchain 等库出现 ImportError: cannot import name 'AsyncGenerator' 的错误。
- 内存要求:建议 ≥16GB。向量计算和模型加载会消耗大量内存。
- GPU 建议:如果有 NVIDIA 显卡(显存 ≥8GB),可以显著加速嵌入模型和大模型的推理速度。
5.2 虚拟环境与依赖管理
永远不要在全局环境中安装 Python 包!使用虚拟环境可以避免不同项目间的依赖冲突。
python -m venv rag_env
source rag_env/bin/activate
rag_env\Scripts\activate
pip install langchain sentence-transformers faiss-cpu openai chromadb PyPDF2
5.3 新手常见问题与解决方案
| 问题类型 | 表现 | 解决方案 |
|---|
| Python 版本冲突 | ImportError: cannot import name 'AsyncGenerator' | 使用 Python 3.9–3.11 |
| API 密钥未设置 | AuthenticationError | 检查 .env 文件中的 OPENAI_API_KEY 是否正确 |
| 中文路径报错 | invalid utf-8 sequence | 将项目放在纯英文路径下,例如 C:\projects\rag_demo |
| 模型下载失败 | .model.part 残留 | 检查网络连接,或使用代理;也可尝试离线预载模型 |
5.4 最佳实践建议
- 使用
.env 文件:管理 API 密钥等敏感信息,保障安全。
- 合理设置文本块大小:建议在 200–500 字符之间,并启用 50 字符左右的重叠,以保持语义完整。
- 约束大模型行为:在提示词中明确指令,如'仅基于以上资料回答,不知道就说'我不知道'',有效防止幻觉。
- 生产环境容器化:使用 Docker 打包应用,保证在任何服务器上都能一致运行。
六、总结与学习建议
通过本文的学习,相信你已经掌握了 RAG 系统的核心概念和两种主流实现方式。
- 初学者建议:优先使用 Langchain 快速搭建原型,体验 RAG 带来的强大能力。
- 进阶学习:再通过 纯手搓 的方式,亲手实现一遍,从而深刻理解向量检索、嵌入模型等底层机制。
- 未来探索:当你熟练掌握基础 RAG 后,可以进一步探索 GraphRAG(利用图结构进行复杂推理)或 多模态 RAG(支持图片、音频等非文本信息的检索)等更高级的形态。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- 随机西班牙地址生成器
随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online