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,文本向量长度:1024

2.向量数据库

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
)

"如果文章对您有帮助,可以请作者喝杯咖啡吗?"

微信二维码

微信支付

支付宝二维码

支付宝


LangChain RAG 检索增强生成
https://blog.liuzijian.com/post/2026/01/24/langchain-rag/
作者
Liu Zijian
发布于
2026年1月24日
更新于
2026年8月11日
许可协议