LangChain + 向量数据库 + RAG:入门与实践指南
目录
1.2 LangChain 能做什么(简单场景:问答、摘要、智能体)
1.3 环境搭建(安装 langchain、配置 API Key)
3.1 为什么需要向量数据库(LLM 的局限性:无长期记忆、无法检索外部知识)
3.3 常用向量数据库对比(Chroma、FAISS、Pinecone 等,侧重轻量级)
4.1 什么是 RAG —— 检索 + 生成,让 LLM 基于外部知识回答
4.4 完整代码示例:用 LangChain 构建一个 RAG 问答系统(10-20 行代码)
4.5 RAG 的优缺点与常见优化(简单提及:重排序、HyDE 等)
5.2 使用 LangChain + Chroma + OpenAI 实现完整流程
6.2 进阶方向推荐(LangGraph、LangSmith、生产部署)
第 1 章:LangChain 简介
1.1 什么是 LangChain
LangChain is an open source framework with a pre-built agent architecture and integrations for any model or tool, so you can build agents that adapt as fast as the ecosystem evolves.
LangChain 是一个用于开发由大语言模型(LLM,Large Language Model)驱动的应用程序的开源框架。它把调用 LLM 所需的通用组件(提示词模板、记忆、工具、数据加载等)封装成标准化的接口,让开发者可以像搭积木一样组合出复杂的智能应用。
简单来说,如果没有 LangChain,你需要自己处理:
-
不同 LLM 厂商的 API 差异
-
对话历史的存储与截断
-
从 PDF、网页等来源读取并分割文本
-
将外部知识检索后塞进提示词
而 LangChain 为你提供了这些能力的统一接口,大幅降低了开发门槛。
1.2 LangChain 能做什么(简单场景:问答、摘要、智能体)
LangChain 可以用于构建:
-
文档问答系统:上传一份 PDF,让 AI 回答其中的问题(RAG 的经典应用)。
-
聊天机器人:带有长期记忆和工具调用能力的客服、个人助理。
-
数据分析和代码生成:让 AI 连接数据库、执行代码、分析结果。
-
智能体(Agent):让 LLM 自己决定调用哪些工具(搜索、计算、API 调用)来完成复杂任务。
1.3 环境搭建(安装 langchain、配置 API Key)
安装 LangChain 及相关依赖
建议使用 Python 3.9 以上版本,创建一个虚拟环境后执行:
pip install langchain langchain-openai langchain-chroma chromadb
如果你希望使用其他模型(如 DeepSeek、通义千问)或向量数据库,只需替换对应的集成包。
配置 API 密钥
本教程以 OpenAI 为例。你需要注册 OpenAI 账号并获取 API Key。然后在 Python 脚本或 Jupyter Notebook 中设置环境变量:
import osos.environ["OPENAI_API_KEY"] = "your-api-key-here"
如果使用其他模型,请参考相应文档配置。
第 2 章:核心概念速览
在深入向量数据库和 RAG 之前,我们先快速了解 LangChain 的四个核心抽象。
2.1 模型 I/O(LLM、提示词、输出解析)
模型 I/O 负责与 LLM 的交互,包含三部分:
-
模型(Model):统一接口封装了 OpenAI、Anthropic、HuggingFace 等模型。例如
ChatOpenAI。 -
提示词模板(Prompt Template):将用户输入和固定指令组合成完整的提示词。
-
输出解析器(Output Parser):将模型返回的文本解析成结构化数据(如 JSON、Pydantic 对象)。
示例:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
model = ChatOpenAI(model="gpt-3.5-turbo")
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个乐于助人的助手。"),
("user", "{input}")
])
output_parser = StrOutputParser()
chain = prompt | model | output_parser
result = chain.invoke({"input": "什么是 LangChain?"})
print(result)
2.2 链(Chain)—— 把多个组件串起来
链将一个或多个组件串联成一个可执行的任务流。上面代码中的 prompt | model | output_parser 就是 LangChain 表达式语言(LCEL)定义的链。你可以用 | 管道符组合任意 Runnable 对象。
2.3 智能体(Agent)—— 让 LLM 自己选择工具
智能体让 LLM 自己决定下一步做什么——是直接回答,还是调用某个工具(如搜索、计算器、数据库查询)。它会反复推理,直到完成任务。对于简单场景,链更轻量;对于需要多步决策的复杂任务,智能体更合适。
2.4 记忆(Memory)—— 让对话有上下文
记忆组件负责在多次调用之间保存和加载上下文。最简单的 ConversationBufferMemory 会把所有对话历史存下来,每次调用时都塞给模型。更高级的记忆类型包括摘要记忆(定期压缩历史)、向量存储记忆(长期保存)等。
第 3 章:向量数据库 —— 让 LLM 拥有长期记忆
3.1 为什么需要向量数据库(LLM 的局限性:无长期记忆、无法检索外部知识)
大语言模型有一个致命弱点:它只知道自己训练时见过的知识,无法实时获取你私有的文档或最新的信息。例如,你问 GPT-3.5 “我们公司的最新报销政策是什么?”,它肯定不知道。
解决方法是:把私有文档提前存起来,当用户提问时,先找到相关的文档片段,再把片段和问题一起交给 LLM 回答。这就需要一种能够高效检索相似文本的数据库——向量数据库。
3.2 向量数据库原理简介
-
文本 → 向量(嵌入模型)
-
相似性搜索(余弦相似度、欧氏距离)
-
索引与检索
向量数据库的核心思想是:将文本转换为一串数字(向量),然后通过计算向量之间的相似度来找到相关内容。
步骤 1:文本 → 向量
使用“嵌入模型”(Embedding Model,如 OpenAI 的 text-embedding-3-small)将一段文本映射为固定长度的浮点数数组。语义相似的文本,其向量在空间中的距离也更近。
例如:
-
“猫咪真可爱” →
[0.12, -0.34, 0.56, ...] -
“小狗很萌” →
[0.11, -0.33, 0.57, ...](距离很近) -
“今天天气不错” →
[0.89, 0.12, -0.45, ...](距离较远)
文本是怎么分块的?按照什么依据来分段?
文本分块(Chunking)的目的是将长文档切成适合后续处理(嵌入、检索)的小段落。分块的核心依据是:保持语义完整性,同时控制块的大小。
常用的分块依据(策略)
-
按固定字符长度切分
-
例如每 500 个字符切一块,不管内容。
-
缺点:可能切断句子或段落,破坏语义。
-
-
按递归分隔符切分(推荐)
-
优先按段落
\n\n切;如果段落太长,再按句子。!?切;如果句子还太长,按逗号、空格等依次切。 -
常见工具:
RecursiveCharacterTextSplitter。
-
-
按语义边界切分
-
使用 NLP 模型(如句子分割器、段落检测)或 Markdown 标题结构。
-
例如:每个
##二级标题下的内容作为一个块。
-
-
按 Token 数量切分
-
用模型的 tokenizer 计算 token 数(如 OpenAI 的
tiktoken),保证每块不超过模型限制(如 512 tokens)。
-
实际例子
原文本:
第一章 总则
第一条 本制度适用于全体员工。
年假政策:员工每年享有15天带薪年假。
第二条 年假需提前3个工作日申请。
使用 RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=20) 可能会切成:
-
块1:
第一章 总则\n第一条 本制度适用于全体员工。\n年假政策:员工每 -
块2:
工每年享有15天带薪年假。\n第二条 年假需提前3个工作日申请。
注意块1末尾被截断,但 overlap 可以保留一些上下文。
实际开发中,通常需要针对你的文档类型(代码、法律文书、对话记录)调整分块大小和分隔符。
步骤 2:相似性搜索
用户提问时,同样将问题转为向量,然后到数据库中寻找最相似的 K 个文档向量。常用的相似度度量包括余弦相似度、欧氏距离等。数据库通过建立索引(如 HNSW、IVF)来加速搜索,即使上亿条数据也能毫秒级返回。
如何计算文本向量之间的空间距离?
有了向量,就可以计算两个文本的“语义相似度”。常用方法:
1. 余弦相似度(最常用)
公式:
similarity=
结果范围 [-1, 1]:1 表示完全相同方向(语义相同),0 表示无关,-1 表示相反。
直观理解:两个向量的夹角越小,语义越接近。
例子:
-
A =
[0.9, 0.1, 0.2](猫) -
B =
[0.85, 0.12, 0.18](猫科动物) -
计算点积和模长 → 余弦相似度 ≈ 0.99(非常接近)
2. 欧氏距离
计算两点之间的直线距离。值越小越相似。
distance=
实际中
向量数据库会使用近似最近邻(ANN)算法(如 HNSW、IVF)来快速找到与查询向量最相似的 K 个向量,而不是逐一计算所有距离(虽然原理上还是基于这些距离公式)。
什么是向量,维度,固定维度的向量
1. 什么是向量?
在数学和编程中,向量就是一个有序的数字列表。
例如:[0.5, -1.2, 3.8] 是一个 3 维向量。
向量可以表示空间中的一个点或一个方向。在文本处理中,我们用一个向量来代表一段文本的“语义坐标”。
2. 什么是维度?
维度就是向量中数字的个数。
-
[2, 3]是 2 维(可以想象成平面上的 x, y 坐标)。 -
[0.1, -0.5, 0.8, 0.2, -0.3]是 5 维。 -
嵌入模型的维度通常很高:384、768、1536 等。
高维度的意义:每个维度可以捕捉文本的一种“语义特征”(比如 1 号维度表示“是否涉及时间”,2 号维度表示“情绪积极程度”……但这些特征不是人可解释的,是模型自己学出来的)。
3. 什么是“固定维度的向量”?
意思是:同一个嵌入模型对任何输入文本,输出的向量长度都一样。
-
无论你输入“你好”还是整部《三体》小说,模型都输出例如 1536 个浮点数。
-
这就保证了所有文本都可以放在同一个向量空间里进行比较。
步骤 3:返回原文
找到相似向量后,返回对应的原始文本片段,供后续 LLM 使用。
3.3 常用向量数据库对比(Chroma、FAISS、Pinecone 等,侧重轻量级)
| 名称 | 特点 | 适用场景 |
| Chroma | 轻量级、嵌入式、开源,支持内存/磁盘持久化 | 开发测试、小型项目 |
| FAISS | Meta 出品,纯本地、高性能,不支持分布式 | 对性能要求高的本地应用 |
| Pinecone | 全托管云服务,自动索引和扩展 | 生产环境、不想运维 |
| Qdrant | 开源、支持 Docker 和云,功能丰富 | 需要控制部署的中大型项目 |
| PGVector | PostgreSQL 扩展,与关系数据共存 | 已有 PG 基础设施的场景 |
入门阶段推荐使用 Chroma,无需单独部署,安装后即可使用。
3.4 代码示例:使用 Chroma 存储与检索向量
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_core.documents import Document
# 准备一些文档片段
documents = [
Document(page_content="LangChain 是一个开发 LLM 应用的框架", metadata={"source": "doc1"}),
Document(page_content="向量数据库可以存储文本的向量表示", metadata={"source": "doc2"}),
Document(page_content="RAG 结合了检索和生成,能回答私有知识的问题", metadata={"source": "doc3"})
]
# 创建嵌入模型
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# 创建 Chroma 向量数据库(自动计算并存储向量)
vector_store = Chroma.from_documents(documents, embeddings)
# 执行相似性检索
query = "什么是 RAG?"
results = vector_store.similarity_search(query, k=1)
print(f"最相关的文档片段:{results[0].page_content}")
输出示例:
最相关的文档片段:RAG 结合了检索和生成,能回答私有知识的问题
第 4 章:RAG(检索增强生成)原理与实践
4.1 什么是 RAG —— 检索 + 生成,让 LLM 基于外部知识回答
RAG(Retrieval-Augmented Generation,检索增强生成)是一种让 LLM 先检索外部知识,再基于检索结果生成回答的技术模式。它的核心价值在于:
-
知识外挂:无需重新训练模型,即可让 LLM 掌握私有或最新信息。
-
减少幻觉:提供相关事实作为依据,模型编造内容的概率大大降低。
-
可溯源:可以告诉用户答案来自哪份文档。
4.2 RAG 工作流程详解
-
文档加载 → 切分 → 向量化 → 存储
-
用户提问 → 向量检索 → 召回相关片段 → 拼接提示词 → LLM 生成回答
一个典型的 RAG 流程包含两个阶段:
索引阶段(离线)
-
加载文档:从 PDF、网页、Markdown 等源读取文本。
-
分割文本:将长文档切分成适当大小的块(chunk),一般几百到上千字符。
-
向量化并存储:对每个块调用嵌入模型得到向量,存入向量数据库。
查询阶段(在线)
-
用户提问:接收用户输入。
-
检索相关块:将问题向量化,在数据库中搜索最相似的 K 个块。
-
构造提示词:将检索到的块作为上下文,与用户问题一起填入提示词模板。
-
生成回答:调用 LLM 生成最终答案。
下图直观展示了这个流程:
[用户提问] --> 向量检索 --> 召回相关块 --> 拼接提示词 --> LLM --> 答案
↑ ↑
[向量数据库] [系统指令 + 上下文]
4.3 RAG 的核心组件
文档加载器(Document Loader)
文本分割器(Text Splitter)
嵌入模型(Embedding Model)
向量数据库(Vector Store)
检索器(Retriever)
生成链(Generation Chain)
在 LangChain 中,构建一个 RAG 系统需要用到以下组件:
-
DocumentLoader:加载原始文档,例如
PyPDFLoader、WebBaseLoader。 -
TextSplitter:将文档分割成块,例如
RecursiveCharacterTextSplitter。 -
Embeddings:嵌入模型,将文本转为向量。
-
VectorStore:向量数据库,存储和检索向量。
-
Retriever:检索器,封装了向量数据库的查询逻辑,通常用
vector_store.as_retriever()创建。 -
Chain:将检索器和 LLM 组合起来,例如使用
create_retrieval_chain。
4.4 完整代码示例:用 LangChain 构建一个 RAG 问答系统(10-20 行代码)
# 1. 导入所需模块
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
# 2. 加载文档(假设有一个 policy.txt 文件)
loader = TextLoader("policy.txt", encoding="utf-8")
docs = loader.load()
# 3. 分割文本
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
# 4. 创建向量数据库
embeddings = OpenAIEmbeddings()
vector_store = Chroma.from_documents(chunks, embeddings)
retriever = vector_store.as_retriever(search_kwargs={"k": 3})
# 5. 定义提示词模板
system_prompt = (
"你是一个公司政策助手。请根据以下上下文回答用户的问题。"
"如果上下文里没有相关信息,就说你不知道,不要编造答案。\n\n"
"上下文:\n{context}\n"
)
prompt = ChatPromptTemplate.from_messages([
("system", system_prompt),
("human", "{input}")
])
# 6. 创建链
llm = ChatOpenAI(model="gpt-3.5-turbo")
combine_docs_chain = create_stuff_documents_chain(llm, prompt)
rag_chain = create_retrieval_chain(retriever, combine_docs_chain)
# 7. 提问
response = rag_chain.invoke({"input": "年假最多可以累积多少天?"})
print(response["answer"])
假设 policy.txt 内容包含:“员工每年有 10 天年假,最多可累积至 20 天。” 那么程序会检索到相关段落并给出准确回答。
4.5 RAG 的优缺点与常见优化(简单提及:重排序、HyDE 等)
优点:
-
可以利用私有知识,无需微调模型。
-
答案有据可循,方便用户核对。
-
知识库可以随时更新,只需重新索引。
缺点与挑战:
-
检索质量直接影响最终答案。如果检索到不相关的内容,LLM 可能被误导。
-
长上下文会增加 token 消耗和延迟。
-
对于多跳推理(需要结合多个分散信息)效果较差。
常见优化方法:
-
重排序(Re-ranking):先检索较多候选,再用一个更精准的模型重新排序。
-
HyDE(Hypothetical Document Embeddings):让 LLM 先针对问题生成一个假想答案,用假想答案去检索。
-
多向量检索:将文档分成句子、摘要等不同粒度,结合使用。
-
上下文压缩:提取检索结果中最相关的句子,而不是返回整个块。
第 5 章:快速实战 —— 从零搭建一个文档问答机器人
本章将综合前面所学,搭建一个可交互的文档问答机器人,并支持多轮对话记忆。
5.1 准备文档(PDF、网页或文本文件)
你可以使用任何文本文件(.txt)、PDF 或网页。这里我们准备一份名为 company_rules.txt 的示例文件:
公司考勤制度:
1. 上班时间为 9:00-18:00,午休 1 小时。
2. 迟到超过 30 分钟算半天旷工。
3. 请假需提前一天在系统中提交申请。
福利政策:
- 每年一次免费体检。
- 每月 500 元交通补贴。
- 入职满一年后享有 5 天额外福利假。
5.2 使用 LangChain + Chroma + OpenAI 实现完整流程
import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
# 设置 API Key(建议用环境变量)
os.environ["OPENAI_API_KEY"] = "your-key"
# 加载并分割文档
loader = TextLoader("company_rules.txt", encoding="utf-8")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)
chunks = splitter.split_documents(docs)
# 创建向量存储和检索器
embeddings = OpenAIEmbeddings()
vector_store = Chroma.from_documents(chunks, embeddings)
retriever = vector_store.as_retriever(search_kwargs={"k": 2})
# 提示词模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个公司内部助手。根据以下上下文回答问题。如果不知道就说不知道。\n上下文:{context}"),
("human", "{input}")
])
# 创建 RAG 链
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
combine_chain = create_stuff_documents_chain(llm, prompt)
rag_chain = create_retrieval_chain(retriever, combine_chain)
# 交互循环
print("问答机器人已启动(输入 'exit' 退出)")
while True:
question = input("\n你: ")
if question.lower() == "exit":
break
response = rag_chain.invoke({"input": question})
print(f"机器人: {response['answer']}")
运行结果:
你: 上班时间是几点?
机器人: 上班时间为 9:00-18:00,午休 1 小时。
你: 迟到怎么办?
机器人: 迟到超过 30 分钟算半天旷工。
5.3 增加记忆功能(支持多轮对话)
目前机器人每次回答都是独立的,无法记住上一轮对话。为了支持多轮对话(例如用户追问“那交通补贴呢?”),我们需要加入对话记忆。
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain
# 使用 ConversationalRetrievalChain 替代之前的链
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
conversation_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=retriever,
memory=memory,
combine_docs_chain_kwargs={"prompt": prompt} # 复用之前的提示词
)
# 交互
while True:
question = input("\n你: ")
if question.lower() == "exit":
break
result = conversation_chain.invoke({"question": question})
print(f"机器人: {result['answer']}")
5.4 测试与调试
-
测试检索质量:可以打印出每次检索到的文档块,确认是否相关。
result = rag_chain.invoke({"input": "福利假"})
for doc in result["context"]:
print(doc.page_content)
-
调整 chunk 大小:如果答案不完整,尝试增大
chunk_size;如果包含太多无关信息,减小它。 -
更换嵌入模型:中文文档建议使用支持多语言的模型,例如
text-embedding-3-small或智源的BAAI/bge-large-zh。 -
检查 API 成本:每次检索+生成会消耗 tokens,可以使用 LangSmith 或自行统计。
第 6 章:总结与下一步学习建议
6.1 回顾核心概念
-
LangChain:一个框架,用标准化的组件快速构建 LLM 应用。
-
向量数据库:存储文本的向量表示,通过相似性搜索快速找到相关内容。
-
RAG:检索 + 生成,让 LLM 回答私有知识问题,减少幻觉。
-
核心流程:文档加载 → 分割 → 向量化 → 存储 → 检索 → 生成。
6.2 进阶方向推荐(LangGraph、LangSmith、生产部署)
当你掌握了入门知识后,可以继续探索:
-
LangGraph:用于构建有状态、多步骤、可中断的复杂智能体(比普通的
AgentExecutor更强大)。 -
LangSmith:生产级的追踪、评估和调试工具。
-
生产部署:使用 FastAPI 包装 RAG 链,配合缓存、异步、限流等。
-
高级 RAG 技术:重排序、HyDE、Self-RAG、CRAG。
-
多模态 RAG:支持图像、表格、音频等。
6.3 资源链接(官方文档、社区、示例项目)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)