高级RAG(七): 评估句子-窗口检索

本文介绍了如何通过TruLens评估LlamaIndex的句子窗口检索效果,重点关注窗口大小的选择,以实现最佳的检索性能。实验表明,较小的窗口尺寸可能更优,因为它在成本和上下文相关性之间找到了平衡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

👉👉👉承接各类AI相关应用开发项目(包括但不限于大模型微调、RAG、AI智能体、NLP、机器学习算法、运筹优化算法、数据分析EDA等) !!!

👉👉👉 有意愿请私信!!!

 

在上一篇博客中我们介绍了LlamaIndex的句子窗口检索,它的主要思想是将文档按句子来切割,每个句子会被切割成一个文档,同时将每条句子周围的若干条句子作为“窗口”数据保存在文档的元数据中。当用户发起检索时会将问题与每个句子文档向量进行匹配,当匹配到相似度最高的文档后,将该文档的“窗口”数据以及问题一起发送给LLM, 最后由LLM来生成最终的回复, 如下图所示:

那么句子窗口检索的效果怎么样,如何来设置一个合适窗口的尺寸(sentence_window_size)以达到最佳的检索效果呢?下面我们来介绍如何使用TruLens来评估句子窗口检索的效果,如果对TruLens还不熟悉的朋友可以先看一下我之前写的TruLens 评估-扩大和加速LLM应用程序评估这篇博客。

一、环境配置

我们首先需要安装如下python包:

pip install llama_index trulens-eval trafilatura torch sentence-transformers

接下来我们需要做一些初始化的工作,比如导入openai或者gemini等大模型的api_key:

import os

os.environ['OPENAI_API_KEY']="your_api_key"

下面我们需要导入在后续实验中所需要使用到的所有python包:

import os
from llama_index.llms import OpenAI
from llama_index import ServiceContext, VectorStoreIndex, StorageContext
from llama_index.node_parser import SentenceWindowNodeParser
from llama_index.indices.postprocessor import MetadataReplacementPostProcessor
from llama_index.indices.postprocessor import SentenceTransformerRerank
from llama_index import load_index_from_storage
from llama_index.readers.web import TrafilaturaWebReader
from llama_index import Document
from trulens_eval import Tru
from trulens_eval import Feedback,TruLlama
from trulens_eval import OpenAI as fOpenAI
from trulens_eval.feedback import Groundedness
import numpy as np
import nest_asyncio

import pandas as pd
pd.set_option("display.max_colwidth", None)

二、加载数据

这里我们仍然使用前几篇博客中使用的数据即从百度百科的网页中抓取一篇关于恐龙的文章:

url="https://baike.baidu.com/item/恐龙/139019"
docs = TrafilaturaWebReader().load_data([url])

#将全角标点符号转换成半角标点符号+空格
for d in docs:
    d.text=d.text.replace('。','. ')
    d.text=d.text.replace('!','! ')
    d.text=d.text.replace('?','? ')

docs

这里我们使用的是LlamaIndex提供的网页爬虫工具TrafilaturaWebReader来爬取百度百科上的这篇文章,然后我们会将文章中全角标点符号如句号、感叹号、问号全部转换成半角标点符号+空格,至于为什么要将全角的标点符号替换成半角标点符号,我在句子-窗口检索这篇博客中已经说明过了,不清楚的朋友可以看一下这篇博客。

三、创建向量数据库和查询引擎

接下来我们需要定义两个主要的函数,它们分别用来创建向量数据库和查询引擎,这里我们还会使用bge的embedding模型和rerank模型,对这些还不熟悉的朋友可以看看我之前的博客

#定义创建向量数据库函数
def build_sentence_window_index(
    documents,
    llm,
    embed_model="local:BAAI/bge-small-zh-v1.5",
    sentence_window_size=3,
    save_dir="sentence_index",
):
    # create the sentence window node parser w/ default settings
    node_parser = SentenceWindowNodeParser.from_defaults(
        window_size=sentence_window_size,
        window_metadata_key="window",
        original_text_metadata_key="original_text",
    )
    sentence_context = ServiceContext.from_defaults(
        llm=llm,
        embed_model=embed_model,
        node_parser=node_parser,
    )
    if not os.path.exists(save_dir):
        sentence_index = VectorStoreIndex.from_documents(
            documents, service_context=sentence_context
        )
        sentence_index.storage_context.persist(persist_dir=save_dir)
    else:
        sentence_index = load_index_from_storage(
            StorageContext.from_defaults(persist_dir=save_dir),
            service_context=sentence_context,
        )

    return sentence_index

#定义查询引擎函数
def get_sentence_window_query_engine(
    sentence_index, similarity_top_k=6, rerank_top_n=2
):
    # define postprocessors
    postproc = MetadataReplacementPostProcessor(target_metadata_key="window")
    rerank = SentenceTransformerRerank(
        top_n=rerank_top_n, model="BAAI/bge-reranker-base"
    )

    sentence_window_engine = sentence_index.as_query_engine(
        similarity_top_k=similarity_top_k, node_postprocessors=[postproc, rerank]
    )
    return sentence_window_engine

#创建向量数据库
index = build_sentence_window_index(
    docs,
    llm=OpenAI(model="gpt-3.5-turbo",temperature=0.1),
    save_dir="./sentence_index",
)

#创业查询引擎
query_engine = get_sentence_window_query_engine(index, similarity_top_k=6)

这里需要说明一下的是,我们在创建查询引擎时设置了similarity_top_k=6,它的意思是每次检索的时候会返回6个相关文档,但是由于我们使用了rerank模型并且设置了rerank_top_n=2,所以这6个相关文档经过rerank模型的重新排序以后最后只会保留2个最相关的文档。

四、TruLens 评估

接下来我们要开始对句子窗口检索的效果进行评估,那么评估的一般过程是这样的:

  1. 句子窗口参数(sentence_window_size)从1开始逐渐增加。
  2. 用RAG 三元组评估应用版本
  3. 跟踪实验以选择最佳句子窗口大小(sentence_window_size)
  4. 注意tonken使用成本和上下文相关性(context Relevance)之间的权衡
  5. 注意窗口大小和基础性(groundedness)之间的权衡
  6. 注意上下文相关性(context Relevance)和基础性(groundedness)之间的关系

这里我们需要说明的是我们从sentence_window_size=1开始实验,然后组件增加sentence_window_size,我们每次都会评估RAG 三元组即Context Relevance,Groundedness和Answer Relevance的分数,在此基础上我们希望找到一个最佳的sentence_window_size值,同时我们还要考虑tonken使用成本和上下文相关性(context Relevance)之间平衡,当窗口的尺寸越大,上下文相关性越高这就意味着所需要的上下文信息也越多那么token的成本也会相应的增加。另外我们期望的是,当我们不断增加窗口尺寸时,上下文相关性会增加到一定程度,基础性(groundedness)也会增加,然后超过这个点,我们会看到上下文相关性要么变平缓要么减少,基础性也可能遵循类似的模式。此外,我们也肯能在实践中看到上下文相关性和基础性(groundedness)之间非常有趣的关系。当上下文相关性较低时,基础性(groundedness)也往往较低。这是因为llm通常会试图通过利用预训练阶段的知识来填补检索到的上下文片段中的空白,这就是所谓的“幻觉”产生的答案。

下面我们首先创建一组问题,然后使用句子窗口检索方法进行检索最后LLM给出这些问题的答案,然后将这组问题和答案交给TruLens进行评估,然后TruLens会给出每个问题的Context Relevance,Groundedness和Answer Relevance三个评估指标的分数,如果对这些评估指标还不熟悉的朋友可以查看一下我之前写的TruLens 评估-扩大和加速LLM应用程序评估这篇博客,这里需要说明一下的是,TruLens在评估时,其内部仍然是通过LLM来对question,context, answer进行评估的,也就是说整个评估过程LLM也都是全程参与的,那么说的通俗一点就是LLM在检索和评估的过程中即当球员又当裁判!

#定义问题
eval_questions = [
                 "恐龙分哪几类?",
                 "体型最大的是哪种恐龙?",
                 "恐龙是怎么繁殖的?",
                 "恐龙是冷血动物吗?",
                 "恐龙为什么会灭绝? 什么时候灭绝的?",
                ]

这里为了演示,我们只创建了5个问题,那么在实际的应用中我们可能需要创建10-20组问题,接下来我们需要定义一个评估记录器,它会创建三个评估指标qa_relevance、qs_relevance、groundedness对这些还不熟悉的朋友可以看一下我的上一篇博客:TruLens 评估-扩大和加速LLM应用程序评估

#设置线程的并发执行
nest_asyncio.apply()
 
#创建评估器对象
tru = Tru()

#定义评估记录器
def get_prebuilt_trulens_recorder(query_engine, app_id):
    openai = fOpenAI()

    qa_relevance = (
        Feedback(openai.relevance_with_cot_reasons, name="Answer Relevance")
        .on_input_output()
    )

    qs_relevance = (
        Feedback(openai.relevance_with_cot_reasons, name = "Context Relevance")
        .on_input()
        .on(TruLlama.select_source_nodes().node.text)
        .aggregate(np.mean)
    )

    grounded = Groundedness(groundedness_provider=openai)

    groundedness = (
        Feedback(grounded.groundedness_measure_with_cot_reasons, name="Groundedness")
            .on(TruLlama.select_source_nodes().node.text)
            .on_output()
            .aggregate(grounded.grounded_statements_aggregator)
    )

    feedbacks = [qa_relevance, qs_relevance, groundedness]
    tru_recorder = TruLlama(
        query_engine,
        app_id=app_id,
        feedbacks=feedbacks
    )
    return tru_recorder

#执行评估
def run_evals(eval_questions, tru_recorder, query_engine):
    for question in eval_questions:
        with tru_recorder as recording:
            response = query_engine.query(question)
            

#初始化评估数据库
Tru().reset_database()  

4.1 Sentence window size = 1

下面看我们来执行评估程序,这里我们首先设置的时Sentence window size = 1,当执行完评估程序以后,TruLens会开启一个基于streamlit的web应用程序, 下面我们可以在这个web应用程序中查看评估结果:

sentence_index_1 = build_sentence_window_index(
    docs,
    llm=OpenAI(model="gpt-3.5-turbo", temperature=0.1),
    sentence_window_size=1,
    save_dir="sentence_index_1",
)

sentence_window_engine_1 = get_sentence_window_query_engine(
    sentence_index_1
)

tru_recorder_1 = get_prebuilt_trulens_recorder(
    sentence_window_engine_1,
    app_id='sentence window engine 1'
)


run_evals(eval_questions, tru_recorder_1, sentence_window_engine_1)

Tru().run_dashboard()

 这里我们看到了这5个问题的总成绩(红框中),下面我们还可以看到明细的记录:

当我们点选其中任意一条明细的记录,我们还可以看到它给出的评分理由:

4.2 Sentence window size = 3

接下来我们将Sentence window size = 3,看看评估结果会怎么样:

sentence_index_3 = build_sentence_window_index(
    docs,
    llm=OpenAI(model="gpt-3.5-turbo", temperature=0.1),
    embed_model="local:BAAI/bge-small-zh-v1.5",
    sentence_window_size=3,
    save_dir="sentence_index_3",
)
sentence_window_engine_3 = get_sentence_window_query_engine(
    sentence_index_3
)

tru_recorder_3 = get_prebuilt_trulens_recorder(
    sentence_window_engine_3,
    app_id='sentence window engine 3'
)

run_evals(eval_questions, tru_recorder_3, sentence_window_engine_3)

tru.run_dashboard()

这里当我看到当我们设置Sentence window size = 3时,RAG三元组指标都有所下降,下面我们看一下明细数据:

经过这两轮的评估,在本次实验中 Sentence window size =1 似乎是个较为理想的值,因为它的各项指标均好于Sentence window size =3。

五、总结

今天我们详细介绍了如何使用trulens对LlamaIndex的句子窗口检索结果进行评估,基于成本的考虑我们只进行了两轮评估,从评估的结果上看 Sentence window size =1 似乎是个较为理想的值,因为它的各项指标均由于Sentence window size =3,希望今天的内容对大家学习RAG有所帮助。

<think>嗯,用户现在提出了一个关于RAG检索增强生成)的需求,看起来用户对检索增强生成的关键词和实现方法感兴趣。首先,我需要回顾一下之前的对话历史。之前用户似乎提到了系统级指令,要求使用特定的LaTeX格式和中文回答,还要生成相关问题。不过当前的查询集中在RAG技术上。用户明确提到要参考站内引用,但系统说明这些引用仅供参考,真正需要关注的是用户当前的问题。用户现在的问题是查找与RAG相关的关键词或实现方法。因此,重点应该放在RAG的核心概念、技术术语以及常用的实现方式上。首先,我需要确定用户可能的背景。用户可能是开发者、研究人员或者刚接触RAG的技术爱好者。由于用户询问关键词和实现方法,可能是在做项目或研究前的资料收集。用户可能希望了解如何搭建一个RAG系统,或者需要关键术语来进一步搜索资料。接下来,我需要梳理RAG的关键组成部分。检索增强生成通常分为检索和生成两个阶段。在检索阶段,重要的技术包括嵌入模型(如BERT、SentenceTransformers)、向量数据库(如Faiss、Milvus、Chroma)、以及检索策略(如密集检索、混合检索)。生成阶段则涉及语言模型(如GPT、Llama)和集成方式。此外,预处理和优化方法也很关键,比如分块策略和查询重写。用户可能还需要具体的实现方法。比如,使用LangChain这样的框架可以简化流程,或者直接通过API调用嵌入模型和向量数据库。是否要提到开源工具和云服务?比如HuggingFace的Transformers库,或者云服务如AzureAISearch,这些对实现有帮助。另外,评估指标如检索召回率、生成相关性也是用户可能关心的,但用户的问题更偏向于实现而非评估,可能需要优先前者。需要判断用户是否需要区分基础实现和高级优化。比如,基础的流程是文本嵌入->向量检索->结果传递给LLM生成。而高级方法可能包括迭代检索、上下文压缩或检索微调。但在回答中应该覆盖这两层,同时将基础部分放在前面,高级部分作为扩展。关键词方面,用户可能希望得到术语列表。比如嵌入模型、向量数据库、大语言模型、分块策略、RAG框架等。还要注意术语的英文对应词,如“hybridretrieval”对应混合检索,方便用户进一步搜索英文资料。最后,生成的相关问题需要考虑用户可能的进一步需求。比如应用场景(客服、文档摘要)、不同方法的优缺点(成本、准确性)、评估方法或框架选择等。这样用户可以根据回答深入探究特定方向。需要确保回答符合之前的系统指令:行内公式用$...$(虽然RAG可能较少需要公式,但如有相关指标如召回率$R@k$可以应用),独立公式用$$...$$。另外,引用文献时添加脚注,如引用原始RAG论文[^1]。同时使用中文,结构清晰分点说明。</think> 以下是与 **RAG检索增强生成)** 相关的核心关键词和实现方法详解,结合最新技术实践整理: --- ### **一、核心关键词** 1. **检索阶段** - **嵌入模型**:Sentence Transformers、BERT、BGE - **向量数据库**:Faiss、Milvus、Chroma、Pinecone - **检索策略**:密集检索(Dense Retrieval)、稀疏检索(BM25)、混合检索(Hybrid) - **查询重写**:Query Expansion、HyDE(假设性文档嵌入)[^1] 2. **生成阶段** - **大语言模型(LLM)**:GPT-4、Llama 3、Claude - **上下文注入**:Prompt Engineering、ReRanking(如Cohere Rerank) - **知识增强**:External Knowledge Integration、Self-RAG[^2] 3. **系统优化** - **分块策略**:滑动窗口(Sliding Window)、语义分块(Semantic Chunking) - **评估指标**:检索召回率(Recall@k)、答案相关性(BERTScore) - **迭代检索**:Reciprocal Rank Fusion(RRF)、多步检索(Multi-step Retrieval) --- ### **二、主流实现方法** #### **基础流程** ```mermaid graph LR A[用户提问] --> B(查询嵌入) B --> C{向量数据库检索} C --> D[Top-k相关文档] D --> E[文档注入LLM上下文] E --> F[LLM生成答案] ``` #### **关键技术实现** 1. **LangChain框架(Python)** ```python from langchain_community.vectorstores import FAISS from langchain_community.embeddings import HuggingFaceEmbeddings # 1. 加载嵌入模型 embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-base-zh") # 2. 构建向量数据库 docs = ["文本1", "文本2", ...] # 知识库文档 vectorstore = FAISS.from_texts(docs, embeddings) # 3. RAG流程 retriever = vectorstore.as_retriever() results = retriever.get_relevant_documents("用户问题") ``` 2. **LlamaIndex优化方案** - 支持 **分层索引**(知识图谱+向量索引) - 自动查询路由(Query Routing)到最佳数据源 - 细粒度访问控制(按权限过滤文档) 3. **高级技术** - **FLARE架构**:主动检索机制(预测知识缺口触发检索- **RA-DIT框架**:联合训练检索器与生成器[^3] - **DSPy编程模型**:声明式优化RAG流水线 --- ### **三、开源工具推荐** | 工具名称 | 核心功能 | 适用场景 | |----------------|------------------------------|-------------------| | **LlamaIndex** | 结构化/非结构化数据索引 | 企业知识库 | | **Haystack** | 端到端流水线构建 | 研究原型开发 | | **Quivr** | 本地化私有部署 | 敏感数据处理 | | **Unstructured**| PDF/PPT复杂文档解析 | 多格式文件处理 | --- ### **四、关键挑战与解决方向** 1. **文档边界问题** → 解决方案:动态上下文整合(Dynamic Context Integration) 2. **噪声干扰** → 解决方案:ReRank模块(如BAAI的bge-reranker) 3. **知识实时性** → 解决方案:增量索引更新(Delta Indexing) 4. **多模态检索** → 解决方案:CLIP跨模态嵌入[^4] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-派神-

感谢您慷慨解囊,我会更加努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值