LangChain RAG 检索增强生成
本文最后更新于 2026年8月11日
未完待续
RAG原理已经在下述文章中说过:
在langchain中,同样有一套API支持RAG类大模型应用的开发,包括文档加载器,文本分割器,嵌入模型,向量数据库以及检索器。
在langchain中,RAG的顺序也与其他框架一致:加载=>分割=>嵌入=>存储=>检索
1.嵌入模型
以Qwen的嵌入模型为例,因为langchain官方没有主动兼容Qwen,因此需要从langchain-community引入Qwen的嵌入模型对象DashScopeEmbeddings
langchain-community即将在未来停止维护,langchain官方建议迁移到:https://docs.langchain.com/oss/python/integrations/embeddings
pip install langchain-community
pip install dashscope然后就可以使用Qwen嵌入模型qwen3.7-text-embedding将文本转换为向量
import os
from dotenv import load_dotenv
from langchain_community.embeddings import DashScopeEmbeddings
load_dotenv(encoding='utf-8')
QWEN_KEY = os.getenv('QWEN_KEY')
embeddings = DashScopeEmbeddings(
model="qwen3.7-text-embedding",
dashscope_api_key=QWEN_KEY
)
text = "This is a test document."
query_result = embeddings.embed_query(text)
print(len(query_result))
print( query_result[:10] )
doc_results = embeddings.embed_documents([
"Hi there!",
"Oh, hello!",
"What's your name?",
"My friends call me World",
"Hello World!"
])
print("文本向量数量:", len(doc_results), ",文本向量长度:", len(doc_results[0]), sep='')
1024
[0.021845249459147453, 0.015001866035163403, 0.02622891217470169, -0.04522478207945824, -0.023976197466254234, 3.26301233144477e-05, 0.0807080939412117, -0.025961020961403847, 0.016852745786309242, 0.00046804724843241274]
文本向量数量:5,文本向量长度:10242.向量数据库
以Milvus为例,使用前需建好db和collection,并且建好字段:文本text,向量vector(1024),元数据metadata,还要指定id自增
langchain向量数据库对象位于langchain_community.vectorstores.milvus模块,使用Milvus类操作Milvus向量数据库
import os
from dotenv import load_dotenv
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.vectorstores.milvus import Milvus
from langchain_core.documents import Document
load_dotenv(encoding='utf-8')
QWEN_KEY = os.getenv('QWEN_KEY')
embeddings = DashScopeEmbeddings(
model="qwen3.7-text-embedding",
dashscope_api_key=QWEN_KEY
)
texts = [
"中国河北发生滦河第一号洪水",
"菲律宾和中国就南海问题进行交涉",
"武大靖被韩国人在ins上谩骂",
"日本政府决定将核污染水进行排海",
"中华人民共和国全运会在天津开幕",
"在中国的调节下,沙特和伊朗和解",
"谷爱凌在2022北京冬奥会上获得滑雪冠军",
"缅甸曼德勒发生8.0级地震",
"无法忍受北约东扩,俄罗斯进攻乌克兰",
"湘潭大学周立人因投毒被判处死刑",
"全红婵在东京奥运会获得跳水金牌"
]
documents = [
Document(
page_content=e,
metadata={"source": "1.doc"}
)
for e in texts
]
MILVUS_HOST = os.getenv('MILVUS_HOST')
MILVUS_PORT = os.getenv('MILVUS_PORT')
MILVUS_KEY = os.getenv('MILVUS_KEY')
vector_store = Milvus(
embedding_function=embeddings,
collection_name='test_01',
# 数据库连接信息
connection_args={
"host": MILVUS_HOST,
"port": MILVUS_PORT,
"user": "root",
"password": MILVUS_KEY,
"db_name": 'rag_demo',
},
auto_id=True, # 集合主键是自增的,插入时无需提供id
text_field="text", # 集合里存文本的字段,对应page_content
vector_field="vector", # 集合里的向量字段
metadata_field='metadata', # 集合元数据字段,对应metadata
)
vector_store.add_documents(documents)
根据向量检索,先将检索的文字转换向量,再根据向量进行检索
import os
from dotenv import load_dotenv
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.vectorstores.milvus import Milvus
load_dotenv(encoding='utf-8')
QWEN_KEY = os.getenv('QWEN_KEY')
MILVUS_HOST = os.getenv('MILVUS_HOST')
MILVUS_PORT = os.getenv('MILVUS_PORT')
MILVUS_KEY = os.getenv('MILVUS_KEY')
embeddings = DashScopeEmbeddings(
model="qwen3.7-text-embedding",
dashscope_api_key=QWEN_KEY,
)
vector_store = Milvus(
embedding_function=embeddings,
collection_name='test_01',
connection_args={
"host": MILVUS_HOST,
"port": MILVUS_PORT,
"user": "root",
"password": MILVUS_KEY,
"db_name": 'rag_demo',
},
auto_id=True,
text_field="text",
vector_field="vector",
metadata_field='metadata',
)
retriever = vector_store.as_retriever(search_kwargs={'k': 5}) #前5
res = retriever.invoke('体育')
for item in res:
print(item)page_content='中华人民共和国全运会在天津开幕' metadata={'source': '1.doc'}
page_content='谷爱凌在2022北京冬奥会上获得滑雪冠军' metadata={'source': '1.doc'}
page_content='武大靖被韩国人在ins上谩骂' metadata={'source': '1.doc'}
page_content='全红婵在东京奥运会获得跳水金牌' metadata={'source': '1.doc'}
page_content='捕捉文档的来源、与其他文档的关系以及其他信息。一个人Document通常代表较大文档的一部分' metadata={'source': '1.doc'}3.文档加载
未完待续
4.文档分割
因为一个文档可能很大,无法一次发送全部内容给大模型,因此需要文档分割器将文档分割,langchain提供了多种文档分割器
pip install langchain-text-splitters大部分文本分割器都继承于TextSplitter,该基类定义了文本分割的核心方法:
split_text()将文本字符串分割为字符串列表split_documents()将Document对象列表分割成更小文本的Document对象列表create_documents()通过字符串列表创建Document对象
下列是langchain常用的文本分割器,位于langchain_text_splitters
RecursiveCharacterTextSplitter递归按字符分割,是用的最多的一种CharacterTextSplitter按指定字符分割MarkdownHeaderTextSplitter按markdown标题分割PythonCodeTextSplitter专门用于分割python代码TokenTextSplitter按token数量分割HTMLHeaderTextSplitter按HTML标题分割
以最常用的RecursiveCharacterTextSplitter为例,进行文档切割,RecursiveCharacterTextSplitter的常见参数有:
chunk_size:文本块最大长度(重要)
单文本块最大字符数,可通过
length_function自定义计数,适配模型上下文窗口(如 GPT-3.5 设 3000 左右)。chunk_overlap:文本块重叠长度(重要)
相邻块重叠字符数,保留上下文,需小于
chunk_size,建议为其10%-20%,类似电视剧的下一集开头会重复播放一两分钟上一集的尾巴。separators:递归拆分分隔符
按优先级拆分,超尺寸则用下一分隔符,最后强制拆分,可自定义领域分隔符。
length_function:长度计算函数
默认按字符计数,可自定义(如tiktoken按token 计数,适配大模型)。
keep_separator:是否保留分隔符
默认
False丢弃;True保留于块末尾,助力保留原格式。is_separator_regex:分隔符是否为正则
默认
False按字符串匹配;True按正则解析,支持复杂规则。
例:设置最大长度:chunk_size=100,重叠长度:chunk_overlap=30,长度计算函数就用python内置的len:length_function=len
因为重叠的30也会占据除第一块外每一块的空间,所以247大小的文字会被分割为100/100/100/37,分割后共337
from langchain_text_splitters import RecursiveCharacterTextSplitter
content = (
'VBlog是一款个人博客系统,VBlog的名字是从网络热词Vlog而来,意思是微小的个人博客,'
'满足个人站长分享博客内容之用,基于BS架构,前端采用浏览器访问,后端使用Java语言编写,'
'和其他类型的博客网站一样,作者撰写博客,访客可以进入网站浏览博客内容。'
'网站由两部分组成,分别是后台管理系统,前台访客系统,站长可以在后台管理系统管理用户'
',角色和权限,并添加网站的文章相册和轮播图公告板等内容,对网站进行内容编辑和管理,'
'访客进入访客系统可以浏览作者编写的博文和图片,并搜索感兴趣的主题进行分别阅读'
)
print('原始大小 ', len(content))
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=100,
chunk_overlap=30,
length_function=len
)
# 除了split_text外,还可以`text_splitter.split_documents()`将一整个文档对象分割
split_text = text_splitter.split_text(content)
# 构建为文档
documents = text_splitter.create_documents(split_text)
for doc in documents:
print(doc.page_content)
print('块大小 ', len(doc.page_content))
print('*'*20)
原始大小 247
VBlog是一款个人博客系统,VBlog的名字是从网络热词Vlog而来,意思是微小的个人博客,满足个人站长分享博客内容之用,基于BS架构,前端采用浏览器访问,后端使用Java语言编写,和其他类型的博客
块大小 100
********************
端采用浏览器访问,后端使用Java语言编写,和其他类型的博客网站一样,作者撰写博客,访客可以进入网站浏览博客内容。网站由两部分组成,分别是后台管理系统,前台访客系统,站长可以在后台管理系统管理用户,角
块大小 100
********************
台管理系统,前台访客系统,站长可以在后台管理系统管理用户,角色和权限,并添加网站的文章相册和轮播图公告板等内容,对网站进行内容编辑和管理,访客进入访客系统可以浏览作者编写的博文和图片,并搜索感兴趣的主
块大小 100
********************
客进入访客系统可以浏览作者编写的博文和图片,并搜索感兴趣的主题进行分别阅读
块大小 37
********************将分割好的文档,嵌入并存储milvus
... ...
documents = text_splitter.create_documents(split_text)
for doc in documents:
doc.metadata = {
'source': '1.pdf',
'user_id': 100
}
vector_store.add_documents(documents)5.检索
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.vectorstores.milvus import Milvus
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough, RunnableParallel, RunnableSequence
load_dotenv(encoding='utf-8')
QWEN_KEY = os.getenv('QWEN_KEY')
MILVUS_HOST = os.getenv('MILVUS_HOST')
MILVUS_PORT = os.getenv('MILVUS_PORT')
MILVUS_KEY = os.getenv('MILVUS_KEY')
DS_KEY = os.getenv('DSKEY')
prompt_template = PromptTemplate(
template="""
请使用以下提供的文本内容来回答问题。仅使用提供的文本信息,
如果文本中没有相关信息,请回答"抱歉,提供的文本中没有这个信息"。
文本内容:
{context}
问题:{question}
""",
input_variables=['question', 'context'],
)
llm = init_chat_model(
model='deepseek-chat',
model_provider='openai',
api_key=DS_KEY,
base_url='https://api.deepseek.com'
)
parser = StrOutputParser()
embeddings = DashScopeEmbeddings(
model="qwen3.7-text-embedding",
dashscope_api_key=QWEN_KEY
)
vector_store = Milvus(
embedding_function=embeddings,
collection_name='test_01',
connection_args={
"host": MILVUS_HOST,
"port": MILVUS_PORT,
"user": "root",
"password": MILVUS_KEY,
"db_name": 'rag_demo',
},
auto_id=True,
text_field="text",
vector_field="vector",
metadata_field='metadata'
)
retriever = vector_store.as_retriever()
chain = RunnableSequence(
RunnableParallel({
'question': RunnablePassthrough(), # 输入的内容原样占位
'context': retriever, # retriever对象同样是Runnable,能被自动invoke()
}),
prompt_template,
llm,
parser
)
resp = chain.invoke('Vblog是什么?特点是?')
print(resp)根据提供的文本,VBlog是一款个人博客系统,VBlog的名字是从网络热词Vlog而来,意思是微小的个人博客,满足个人站长分享博客内容之用,基于BS架构,前端采用浏览器访问,后端使用Java语言编写。关于其他特点,文本中没有提供更多信息。还能进一步简写为LCEL
chain = (
{
'question': RunnablePassthrough(),
'context': retriever,
}
| prompt_template
| llm
| parser
)"如果文章对您有帮助,可以请作者喝杯咖啡吗?"
微信支付
支付宝