RAG检索中使用一个 长上下文重排序器(Long Context Reorder) 对检索到的文档进行进一步的处理和排序,优化输出顺序

使用一个长上下文重排序器对检索到的文档进行进一步的处理和排序,优化输出顺序。

优化前的检索内容,顺序混乱:

优化后的检索内容,按顺序排列:

使用 LongContextReorder 对检索得到的文档进行重新排序。这一步的目的是在有较长上下文时,优化文档的顺序,使得后续生成回答或处理时能更好地利用整体信息,而不是单纯按照检索的相关性顺序。

示例代码如下:

python 复制代码
from langchain_community.vectorstores import Chroma
from langchain_community.document_transformers import (
    LongContextReorder,
)
from langchain_community.embeddings import HuggingFaceEmbeddings

# Load blog
import bs4
from langchain_community.document_loaders import WebBaseLoader

# 设置一个常见的浏览器 User-Agent 字符串
os.environ["USER_AGENT"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"

loader = WebBaseLoader(
    web_paths=("https://www.yixue.com/%E6%80%8E%E6%A0%B7%E7%9C%8B%E8%A1%80%E5%B8%B8%E8%A7%84%E5%8C%96%E9%AA%8C%E5%8D%95",),
    bs_kwargs=dict(
        parse_only=bs4.SoupStrainer(
            class_=("mw-body-content")
        )
    ),
)

blog_docs = loader.load()

# Split
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=300, 
    chunk_overlap=50)

# Make splits
splits = text_splitter.split_documents(blog_docs)

# Get embeddings.
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")

# Create a retriever
retriever = Chroma.from_documents(splits, embedding=embeddings).as_retriever(
    search_kwargs={"k": 10}
)
question = "血红蛋白(Hb)测定人体正常范围是多少?"

# Get relevant documents ordered by relevance score
docs = retriever.invoke(query)


reordering = LongContextReorder()
reordered_docs = reordering.transform_documents(docs)

下面我来详细解释这段代码的作用和每个部分的含义,并用通俗的语言和例子说明。


1. 背景和总体思路

整体流程包括:

  • 加载网页内容:从指定的 URL 下载博客页面。
  • 文本拆分:将加载的长文档拆分成更小的片段,便于后续处理。
  • 生成嵌入向量:利用预训练的语言模型将每个文本片段转换为数值向量,表示其语义信息。
  • 构建向量数据库:把所有文本片段的嵌入向量存储在向量数据库(这里使用 Chroma)中,以便快速检索。
  • 检索相关文档:根据用户的查询(例如"什么是任务分解")从向量数据库中找出最相关的文本片段。
  • 文档重排序:使用一个长上下文重排序器对检索到的文档进行进一步的处理和排序,优化输出顺序。

2. 代码详细解释

a. 导入所需的模块
python 复制代码
from langchain_community.vectorstores import Chroma
from langchain_community.document_transformers import LongContextReorder
from langchain_community.embeddings import HuggingFaceEmbeddings

这些导入模块提供了:

  • Chroma:一个向量数据库,用于存储和检索文本嵌入。
  • LongContextReorder:用于重新排序文档,使得在长文本处理时能够更好地利用上下文。
  • HuggingFaceEmbeddings:利用 HuggingFace 提供的预训练模型生成文本的嵌入向量。
b. 加载网页内容
python 复制代码
import bs4
from langchain_community.document_loaders import WebBaseLoader

# 设置 User-Agent,模拟常见浏览器
os.environ["USER_AGENT"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"

loader = WebBaseLoader(
    web_paths=("https://www.yixue.com/%E6%80%8E%E6%A0%B7%E7%9C%8B%E8%A1%80%E5%B8%B8%E8%A7%84%E5%8C%96%E9%AA%8C%E5%8D%95",),
    bs_kwargs=dict(
        parse_only=bs4.SoupStrainer(
            class_=("mw-body-content")
        )
    ),
)
blog_docs = loader.load()

这里使用 WebBaseLoader 来加载一个博客页面,传入了目标 URL。使用 BeautifulSoup 的 SoupStrainer 只解析页面中类名为 "mw-body-content" 的部分(这样可以过滤掉无关内容,只保留主要文章内容)。设置 User-Agent 主要是为了让网站认为这是来自真实浏览器的请求,防止被屏蔽。

举例说明 :假设你要加载一篇博客文章,但网页中有大量广告、侧边栏等噪音信息。利用 SoupStrainer 只选取文章主要内容,能保证后续处理的文本质量更高。

c. 文本拆分
python 复制代码
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=300, 
    chunk_overlap=50)
splits = text_splitter.split_documents(blog_docs)

这里的作用是将加载到的长文档拆分成多个较小的文本块:

  • chunk_size=300:每个块大约300个字符(或token)。
  • chunk_overlap=50:相邻文本块之间有50个字符的重叠,确保上下文连贯,不会因为拆分而丢失关键信息。

举例说明:如果一篇文章有3000个字符,拆分后可能生成大约10个文本块,每个块之间有一部分内容重复,这样在检索时就能更好地捕捉到上下文信息。

d. 生成文本嵌入
python 复制代码
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")

使用 HuggingFace 提供的预训练模型 "all-MiniLM-L6-v2" 将每个文本块转换为一个高维向量,这个向量代表了文本的语义信息,便于后续比较文本之间的相似度。

举例说明:假设有两个文本块分别描述"如何进行任务分解"和"任务分解的步骤",经过嵌入后,这两个文本的向量在空间中距离较近,因为它们主题相似。

e. 构建向量数据库和检索器
python 复制代码
retriever = Chroma.from_documents(splits, embedding=embeddings).as_retriever(
    search_kwargs={"k": 10}
)
question = "血红蛋白(Hb)测定人体正常范围是多少?"
docs = retriever.invoke(query)
  • Chroma.from_documents:将之前拆分的文本块和生成的嵌入存储到一个 Chroma 向量数据库中。
  • as_retriever(search_kwargs={"k": 10}):构造一个检索器,设置查询时返回最相关的10个文本块。
  • retriever.invoke(query):根据输入的查询"什么是任务分解"检索出相关的文本块。

举例说明:当用户查询"血红蛋白(Hb)测定人体正常范围是多少"时,系统会自动搜索数据库中与该查询最匹配的文本块,并返回这10个最相关的内容。这样用户能快速得到与问题高度相关的答案片段。

f. 文档重排序
python 复制代码
reordering = LongContextReorder()
reordered_docs = reordering.transform_documents(docs)

最后使用 LongContextReorder 对检索得到的文档进行重新排序。这一步的目的是在有较长上下文时,优化文档的顺序,使得后续生成回答或处理时能更好地利用整体信息,而不是单纯按照检索的相关性顺序。

举例说明:假如检索出来的10个文本块中有些虽然相关但顺序零散(可看前面给的输出效果对比图),通过重排序可以把那些逻辑连续、信息衔接紧密的片段放在一起,形成更连贯的答案。


总结

整段代码展示了如何从一个网页中加载内容,经过文本拆分、生成嵌入向量,再通过向量数据库进行语义检索,最后对检索结果进行重排序,最终实现一个高效的文档问答系统。

  • 加载网页:提取文章核心内容。
  • 拆分文本:避免单一长文本处理困难,保证上下文完整。
  • 生成嵌入:将文本转换为能量化语义的向量。
  • 向量检索:根据查询找出最相关的文本片段。
  • 重排序:优化片段顺序,形成连贯的回答。

这种方法在知识问答、搜索引擎、文档摘要等应用场景中非常实用。通过将长文档拆分和向量化,可以快速准确地找到用户所需的信息。

相关推荐
风象南9 小时前
Claude Code这个隐藏技能,让我告别PPT焦虑
人工智能·后端
曲幽10 小时前
FastAPI压力测试实战:Locust模拟真实用户并发及优化建议
python·fastapi·web·locust·asyncio·test·uvicorn·workers
Mintopia10 小时前
OpenClaw 对软件行业产生的影响
人工智能
陈广亮10 小时前
构建具有长期记忆的 AI Agent:从设计模式到生产实践
人工智能
会写代码的柯基犬11 小时前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
Mintopia11 小时前
OpenClaw 是什么?为什么节后热度如此之高?
人工智能
爱可生开源社区11 小时前
DBA 的未来?八位行业先锋的年度圆桌讨论
人工智能·dba
叁两14 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
敏编程14 小时前
一天一个Python库:jsonschema - JSON 数据验证利器
python