005.大模型 RAG 检索生成和评估实战

一:RAG 检索阶段概念

Pasted image 20260717103945.png

1.1. 检索阶段的主要流程

很多人以为检索就是简单地“用向量查一下相似度”,但实际上,一个真正能用的、工业级别的 RAG 系统,其检索模块是一套精心设计的完整流程:

1. 文档向量化(Embedding)

向量的本质是把语义信息用数学形式表示,方便计算“语义相似度”,嵌入(Embedding)模型承担着将文本转换为向量表示的核心职责。

2. 查询向量化(Query Embedding)

3. 相似度计算与召回(Retrieval)

召回阶段的核心目标非常明确:在毫秒级别内,从数百万甚至更多的文档片段中,初步筛选出几十到几百个“可能相关”的备选项。 它的首要任务是“找得全”(高召回率),确保不错过任何可能有用的信息,哪怕里面混入了一些不相关的内容也没关系,因为后续还有精排环节来清洗。目前,主流的“划范围”方法有两种,它们各有千秋:

1. 向量召回 —— 像一位“理解语意”的聪明助手

2. 关键词召回 —— 像一位“严格抠字眼”的严谨助手

正因为上述两种方法优缺点互补,所以在实践中,最常用且效果最好的优化手段就是“混合检索”。它的策略很简单:让聪明的助手(向量召回)和严谨的助手(关键词召回)同时开工,然后把它们找到的结果合并起来。”

4. 重排 / 精排(Rerank),精准锁定最佳答案

Pasted image 20260717104726.png

经过召回阶段,系统已经从知识库中快速捞出了一批(比如几十条)“可能相关”的候选文档。但它们的质量参差不齐。如果直接把这一大堆内容塞给大模型,很容易让它陷入困惑,导致答案冗长或不精准。因此,精排(Rerank)的核心使命就是:从“差不多”的备选项中,精准地筛选出“最相关”的少数精华。

与召回阶段快速但粗略的向量相似度比较不同,重排器(Reranker)像一个高度专注的面试官,它会进行一场深度的一对一评估

优化精排环节的几个关键点:

两者协同,共同为大模型的精准生成奠定了坚实的信息基础。

5. 检索结果融合与评估

结果融合就是让专家团达成共识。当使用多个检索器(如 Vector + BM25)时,我们就像一个决策者拥有两个各有所长的专家顾问团。结果融合的目标就是综合他们的意见,形成一个更全面、更可靠的最终名单。

通过融合,系统实现了“1 + 1 > 2”的效果,既利用了语义搜索的智能,又保留了关键词搜索的精准。


二:LlamaIndex 检索生成核心组件

LlamaIndex 的核心魅力在于其模块化和可扩展的设计哲学。理解其核心组件 —— Retriever、QueryEngine 和 ChatEngine —— 是构建高效、精准 RAG 系统的第一步。Retriever 负责从索引中召回候选节点;Node Postprocessor(如 Reranker)对候选进行过滤与精排;最终由 Response Synthesizer 整合上下文生成答案。QueryEngineChatEngine 则分别封装了单轮与多轮的查询流程。

import json
from llama_index.core.schema import Document

# 讲文档转化为 documents 信息
documents = []
# 创建 Document 对象
doc = Document(
    text=text,
    metadata={
        'element_type':'Text',
        'file_name':'甬兴证券 -AI 行业点评报告:海外科技巨头持续发力 AI,龙头公司中报业绩亮眼 .pdf'
    },
)

documents.append(doc)
documents
# 加载句子切分器
from llama_index.core.node_parser import SentenceSplitter

# 初始化 SentenceSplitter
sentence_splitter = SentenceSplitter(
    chunk_size=512,      # 这里 chunk_size 表示 token 近似或字符近似,视版本调整
    chunk_overlap=64     # 重叠部分选择大小为 64
)

nodes = sentence_splitter.get_nodes_from_documents(documents)
len(nodes)

向量入库

from llama_index.core import VectorStoreIndex,StorageContext

# 直接基于 nodes 构建索引
index = VectorStoreIndex(nodes)

# 2. 索引持久化
index.storage_context.vector_store.persist("vector_store.json")

2.1. LlamaIndex 检索阶段组件

1. Retriever 与 as_retriever 深度解析

Retriever(检索器)是 RAG 流程的起点,其核心职责是从索引(Index)中根据用户查询(Query)高效地召回最相关的文档片段(Nodes)。检索器的质量直接决定了后续生成环节的上下文质量,是整个系统的基石。

工作原理

当用户发起查询时,Retriever 首先对查询进行编码(通常是转换为向量),然后在索引中进行相似度计算或关键词匹配,最后返回一个按相关性排序的 NodeWithScore 列表。每个 NodeWithScore 包含了文档片段本身及其与查询的相关性得分。

这个过程可以简化为以下步骤:

2. 常见 Retriever 类型与选择

LlamaIndex 提供了丰富的 Retriever 实现,以适应不同的数据类型和应用场景。

Retriever 类型 主要原理 适用场景 优点 缺点
VectorIndexRetriever 向量检索(Dense Retrieval) 语义相似度匹配,处理长尾、复杂的自然语言查询。 能理解语义和概念,泛化能力强。 对关键词不敏感,向量构建成本高。
BM25Retriever 关键词匹配(Sparse Retrieval) 精确匹配关键词、术语、代码片段等。 速度快,对关键词精准匹配效果好。 无法理解语义,同义词、近义词问题。
HybridRetriever 混合检索 兼顾语义和关键词,是生产环境首选。 结合两者优点,鲁棒性、召回率更高。 配置相对复杂,需要平衡权重。

as_retriever() - 精准检索器

使用场景:

# 学术研究场景,可以多检索几篇文章
retriever = index.as_retriever(
    similarity_top_k=5,  # 学术研究需要更多参考资料
)

# 返回检索到的节点
response_nodes = retriever.retrieve("B200A 芯片的存储器规格和预计何时被原始设备制造商拿到是什么?")

# 打印检索到的信息
for node in response_nodes:
    print(f"相似度: {node.score:.4f}")
    print(f"内容: {node.text[:200]}...")
    print(f"元数据: {node.metadata}")
    print("=" * 60)

显示创建 Retriever

from llama_index.core.retrievers import VectorIndexRetriever

# 4. 显式创建 VectorIndexRetriever 以获得更多控制
vector_retriever = VectorIndexRetriever(
    index=index,
    similarity_top_k=5,
    # filters=MetadataFilters(...) # 可选的元数据过滤
)

query = "B200A 芯片的存储器规格和预计何时被原始设备制造商拿到是什么?"
retriever_nodes = vector_retriever.retrieve(query)

# 打印结果
for node in retriever_nodes:
    print(f"Score: {node.score:.4f}")
    print(f"Content: {node.get_content()}")
    print("=" * 60)

选择建议

3. BM25 检索策略

BM25(Best Matching 25) 是一种经典的基于关键词统计的文档检索算法,属于“概率检索模型”(Probabilistic Retrieval Model)的一种改进形式。简单来说:BM25 不依赖向量 embedding,而是依靠词频统计(TF-IDF)和文档长度加权,来计算「查询词与文档内容的相关度分数」。给定一个查询(query),计算每个文档的得分 score(D, Q) **,这个得分表示文档  D  对查询  Q 的相关程度。

它基于两个核心假设:

  1. 词频越高,越相关
  2. 文档越短,越相关(同样的词出现次数占比更高)
  3. 稀有词越重要(即 IDF 越高)。
# 关键词策略检索
from llama_index.retrievers.bm25 import BM25Retriever

# 1. 创建向量检索器
vector_retriever = index.as_retriever(similarity_top_k=5)
# 2. BM25 检索器
bm25_retriever = BM25Retriever.from_defaults(
        nodes=nodes,
        similarity_top_k=5
)
# 3. 执行 BM25 检索
query = "B200A 的存储器规格"
results = bm25_retriever.retrieve(query)

# 打印结果
for node in results:
    print(f"Score: {node.score:.4f}")
    print(f"Content: {node.get_content()}")
    print("-" * 20)

4. 混合检索策略 QueryFusionRetriever

from llama_index.core.retrievers import QueryFusionRetriever

# 1. 创建 QueryFusionRetriever
retriever = QueryFusionRetriever(
    retrievers=[vector_retriever, bm25_retriever],  # 集成不同的检索器
    similarity_top_k=5,                 # 最终返回的文档数量
    num_queries=1,                      # 为原始查询生成的变体数量,(默认为 1,不生成变体)
    mode="reciprocal_rerank",           # 结果融合模式,'reciprocal_rerank' 是常用且效果好的模式
    use_async=True,                     # 是否异步执行
    verbose=True,                       # 是否打印调试信息
    retriever_weights=[0.6,0.4]         # 检索器权重,用于加权融合
)

# 2. 执行检索
query = "B200A 芯片的存储器规格和预计何时被原始设备制造商拿到是什么?"
query_nodes = retriever.retrieve(query)

# 打印结果
for node in query_nodes:
    print(f"Score: {node.score:.4f}")
    print(f"Content: {node.get_content()}")
    print("=" * 60)

reciprocal_rerank 模式的特别之处

各模式对比及选型建议

模式 使用场景 优点 缺点
reciprocal_rerank 多种检索器融合、得分尺度差异大、希望利用排名信息 鲁棒,兼容异构检索器 若某检索器结果质量极差,可能仍被提升;需要调 k 参数
relative_score_fusion 各检索器得分可归一化、你希望保留“得分强弱”信息 得分直观,可加权不同检索器 需要归一化逻辑、可能受极端得分影响
simple 快速原型、检索器性能差异不大、速度优先 实现最简单、速度快 融合效果最弱,可能不能充分利用多个检索器优势

5. 重新排序器(ReRank):从粗排到精排的精度提升

检索器返回的 Top-K 节点列表虽然已经过初步排序,但其排序依据(如向量相似度或关键词匹配分数)可能无法完全契合查询的真实意图,存在“语义鸿沟”或“词汇鸿沟”问题。重新排序器 它对检索器返回的候选文档列表进行二次处理和重新排序,旨在将最相关、最优质的文档片段提升至顶部,从而显著提升输入到大模型上下文信息的质量。

rerank 工作机制

一个典型的 Rerank 流程可以分解为以下步骤:

  1. 输入候选集:接收来自 Retriever 的、已初步排序的 NodeWithScore 列表。
  2. 精细化评分:利用专门的、通常更复杂且计算成本更高的重排模型,对每个候选节点与查询的相关性进行精细化评估。这个模型能更好地理解上下文和语义关联。
  3. 分数归一化与重排序:将不同 Retriever(如向量检索和关键词检索)返回的、量纲不一的分数,或重排模型产生的新分数,进行归一化处理。然后,所有候选节点根据新的、统一的标准进行重新排序。
  4. Top-N 截断:返回重新排序后得分最高的 N 个节点(通常 N < K),作为精炼后的最终上下文提供给大模型。

常见 Reranker 类型与选择

Reranker 类型 主要原理 适用场景 优点 缺点
Cross-Encoder Reranker 使用交叉编码器模型将查询和文档同时输入模型,进行深度注意力交互,输出精细的相关性分数。 对精度要求极高的场景,如问答、证据检索。 精度最高,能深刻理解语义关联和上下文。 计算成本高,速度较慢。
LLM-as-Judge Reranker 将查询和候选文档列表提交给大语言模型,指令其根据相关性进行排序或评分。 当需要复杂推理和指令遵从来判断相关性时。 灵活性强,可利用大模型的强大理解和推理能力。 延迟高、成本高,且结果可能存在不一致性。
API-Based Reranker (如 Cohere, Jina) 调用专门优化的重排云服务 API。 生产环境中追求精度与性能平衡,且不愿自维护模型的场景。 效果优异,开箱即用,无需本地计算资源。 产生 API 调用费用,依赖网络。
from llama_index.core.postprocessor import SentenceTransformerRerank
from llama_index.core.query_engine import RetrieverQueryEngine

# 模型本地路径
local_model_path = "/root/autodl-tmp/bge-reranker-base"

# 1. 定义精排序器 (Reranker)
reranker = SentenceTransformerRerank(
    model=local_model_path,
    top_n=3, # 精选出最相关的 4 个文档送给 LLM,
)

# 2. 构建 QueryEngine
query_engine = RetrieverQueryEngine.from_args(
    retriever=retriever,
    node_postprocessors=[reranker]
)

# 3. 执行查询
query = "B200A 芯片的存储器规格和预计何时被原始设备制造商拿到是什么?"
response = query_engine.query(query)

LlamaIndex 支持多种 Reranker 实现,从本地模型到云服务,以适应不同的性能与精度要求。

选择建议

维度 Reranker / Cross-encoder Embedding / Bi-encoder
精度(精排) 非常高(尤其 top-k 精准度) 好(对语义召回友好)
检索速度 慢(仅适合 top-N 精排) 非常快(适合大规模)
可扩展性 低(GPU 批量处理) 高(ANN 索引 + 本地向量库)
资源消耗 高(GPU,显存) 低到中(存储向量 / CPU 或 GPU)
交互信息 丰富(token 级别) 弱(向量级别)
训练复杂度 需要大量正 / 负样本,训练慢 contrastive 学习,高效且易于扩展
支持在线更新 不方便(需重新排序) 文档可增量嵌入并插入索引
设备友好 需 GPU / 加速 可在 CPU / ONNX / 量化后部署
典型用途 精排、排序微调 初始检索、语义搜索、召回

6. 响应合成:从信息到答案的生成

响应合成是 RAG 管道的最终环节,负责将检索到的相关信息转化为自然、连贯且准确的答案。该阶段通过 get_response_synthesizer() 创建的核心模块,协调大语言模型利用检索到的文档片段和用户查询,生成最终的响应。响应合成阶段是将 RAG 系统价值最终呈现给用户的关键环节,合理配置合成策略和参数,能够显著提升生成答案的准确性、相关性和可用性,完成从"信息检索"到"知识交付"的完整闭环。

响应合成的工作机制

一个完整的响应合成流程包含以下关键步骤:

  1. 上下文组织:将检索和重排后得到的 Top-K 个 NodeWithScore 节点按照相关性顺序组织成连续的上下文文本,通常会在节点间添加分隔符以保持结构清晰。
  2. 提示工程与填充:使用精心设计的提示模板,将用户查询和整理好的上下文文本填入模板中的相应位置。高质量的提示模板会明确指示模型基于上下文回答问题,并对无法回答的情况做出规范。
  3. LLM 推理生成:将填充完整的提示输入给 LLM,模型基于提供的上下文和查询进行推理,生成符合要求的自然语言回答。
  4. 后处理与格式化:对模型生成的原始回答进行必要的后处理,如提取关键信息、格式化输出、添加引用标注等,确保最终输出的质量和可用性。

get_response_synthesizer 方法:这是创建响应合成器的标准方式。通过该方法可以灵活配置:

response_synthesizer = get_response_synthesizer(
    response_mode="compact",  # 选择合成模式
    llm=llm,                  # 指定使用的 LLM
    text_qa_template=my_qa_prompt,  # 自定义提示模板
    streaming=True            # 是否启用流式输出
)

常见响应合成模式与选择

合成模式 工作原理 适用场景 优点 缺点
Refine (迭代优化) 依次处理每个节点,基于前一个答案和当前节点内容迭代优化回答。 处理复杂、多维度信息,需要深度整合的场景。 答案质量高,能充分利用所有信息。 速度慢,token 消耗大,成本高。
Compact (压缩优化) 在保证上下文完整性的前提下,压缩提示长度,智能处理长文档。 上下文较长但需要一次性处理的场景。 平衡质量与效率,节省 token。 压缩可能损失部分细节信息。
Tree Summarize (树状总结) 采用分层总结策略,先总结子树,再汇总成最终答案。 处理大量检索结果,需要结构化整合。 处理大量节点能力强,答案结构化好。 实现复杂,延迟较高。
Simple Summarize (简单总结) 将所有上下文一次性输入模型生成回答。 快速原型开发,上下文较短的场景。 实现简单,速度快。 受限于模型上下文长度。
No Text (无文本) 只返回检索到的节点,不生成回答。 需要原始数据的分析场景,或自定义生成逻辑。 灵活性强,节省生成成本。 需要额外处理才能得到最终答案。

选择建议

2.2. 生成查询阶段组件

1. QueryEngine 与 as_query_engine 深度解析

QueryEngine(查询引擎)是用户与 RAG 系统交互的入口。它接收用户的自然语言查询,调用内部的 Retriever 获取相关信息,然后将这些信息与原始查询一同交给 LLM,最终生成一个连贯、准确的答案。QueryEngine(查询引擎)将 RetrieverNode PostprocessorResponse Synthesizer 组合为面向单轮问答的统一入口。

  1. as_query_engine() - 端到端问答引擎

    使用场景:

    • 智能客服系统 - 直接回答用户问题
    • 知识库问答 - 基于文档内容生成准确答案
    • 教育助手 - 解释概念并提供详细解答
# 基础查询
query_engine = index.as_query_engine(
    similarity_top_k=3,
    response_mode="refine",  # 对多个检索结果进行精炼
    streaming=True  # 流式输出
)

# 正常响应
response = query_engine.query("B200A 芯片的存储器规格和预计何时被原始设备制造商拿到是什么?")
print(response)

# 流式响应处理
# streaming_response = query_engine.query("B200A 芯片的存储器规格和预计何时被原始设备制造商拿到是什么?")
# for token in streaming_response.response_gen:
#     print(token, end=" ", flush=True)

响应模式(Response Modes)Response Synthesizer 支持多种响应模式,如:

  1. 显示创建 QuerEngine
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.retrievers import VectorIndexRetriever

# 1. 从已有的 index 创建 retriever
retriever = VectorIndexRetriever(
    index=index,
    similarity_top_k=3
)

# 2. 构造 QueryEngine
query_engine = RetrieverQueryEngine(
    retriever=retriever,          # 传入检索器
    response_synthesizer=None,    # 可以自定义响应合成器
    node_postprocessors=None     # 可传入 reranker、过滤器
)

# 3. 直接查询
response = query_engine.query("B200A 芯片的存储器规格和预计何时被原始设备制造商拿到是什么?")
print(response)

2. ChatEngine 与 as_chat_engine 深度解析

ChatEngine(聊天引擎)是 QueryEngine 的有状态版本,专为多轮对话设计。它内置了内存管理(Memory)机制,能够在对话中保持上下文连贯性。

  1. as_chat_engine() - 多轮对话引擎

    使用场景:

    • 多轮对话系统 - 保持对话上下文
    • 虚拟助手 - 理解用户意图的连续对话
    • 教学系统 - 基于前文进行深入讨论
chat_engine = index.as_chat_engine(
    chat_mode="context",  # "context", "condense_question", "react", "openai"
    system_prompt="你是一个关于 LlamaIndex 的问答助手。请基于提供的上下文回答问题。",  # 系统提示词
    memory=None          # 对话记忆管理
)

# 多轮对话
response1 = chat_engine.chat("文章主要讲述了哪几家海外 AI 巨头?")
print(f"AI: {response1}")
print("=" * 60)
     
response2 = chat_engine.chat("这几家 AI 巨头都研发了哪些产品?")
print(f"AI: {response2}")  # 能理解这是基于前一个问题的后续

# 重置对话历史
chat_engine.reset()
  1. 带 memory 记忆的对话

    ChatMemoryBuffer 的作用与机制:ChatMemoryBuffer 会维护一个循环缓冲区(FIFO),存储最近 N 轮的聊天记录。

    每次调用 chat_engine.chat("新问题") 时:

    • 系统会从 memory 中提取历史对话(按 token 限制截断)
    • 拼接成 LLM 的输入上下文(包括 system prompt + 历史 + 当前 query)
    • 将当前问答再存回 memory
      • 不使用 memory → 每次对话是独立问答。
      • 使用 ChatMemoryBuffer → 模型能理解上下文、指代、历史信息,支持真正的多轮智能对话。
from llama_index.core.memory import ChatMemoryBuffer

# 创建对话记忆
memory = ChatMemoryBuffer.from_defaults(token_limit=3000)

chat_engine = index.as_chat_engine(
    chat_mode="context",  # "context", "condense_question", "react", "openai"
    system_prompt="你是一个关于 LlamaIndex 的问答助手。请基于提供的上下文回答问题。",  # 系统提示词
    memory=memory          # 对话记忆管理
)

# 多轮对话
response1 = chat_engine.chat("文章主要讲述了哪几家海外 AI 巨头?")
print(f"AI: {response1}")
print("=" * 60)
     
response2 = chat_engine.chat("这几家 AI 巨头都研发了哪些产品?")
print(f"AI: {response2}")  # 能理解这是基于前一个问题的后续

# 重置对话历史
chat_engine.reset()

2.3. 混合检索 Retriever + 重排序 Rerank

Pasted image 20260724111626.png

  1. 模型选型:选择强大的 Embedding 模型(如 bge-m3)和强大的 Reranker 模型(如 bce-reranker-base_v1)是成功的关键。模型的能力上限决定了你的系统表现。
  2. similarity_top_ktop_n 的黄金组合:similarity_top_k(召回数)和 top_n(精排数)的比例是需要权衡的。一个常见的设置为 10:320:5。召回数太少,可能漏掉关键信息;太多则会增加 Reranker 的计算负担。你需要根据实际应用的延迟要求和评估结果来调整。
  3. 与混合检索结合:可以将 Reranker 应用于混合检索的结果之上,实现“多路召回 + 精排序”的终极架构,最大化检索质量。
from llama_index.core import VectorStoreIndex
from llama_index.retrievers.bm25 import BM25Retriever
from llama_index.core.postprocessor import SentenceTransformerRerank
from llama_index.core.retrievers import QueryFusionRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.response_synthesizers import get_response_synthesizer

# 1. 加载向量索引
index = VectorStoreIndex(nodes=nodes)

# 2. 向量检索器 (粗召回)
vector_retriever = index.as_retriever(
    similarity_top_k=5 # 扩大召回范围,为 Reranker 提供充足的候选
)

# 3. BM25 检索器 (关键词补充)
bm25_retriever = BM25Retriever.from_defaults(
    nodes=nodes,
    similarity_top_k=5
)

# 4. 混合检索器 (融合结果)
hybrid_retriever = QueryFusionRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    similarity_top_k=5,         # 融合后保留的候选数量
    num_queries=1,              # 保持简单,不生成子查询
    mode="reciprocal_rerank",   # 使用 RRF 融合策略
    use_async=True              # 必须开启异步
)
from llama_index.core.postprocessor import SentenceTransformerRerank

# 模型本地路径
local_model_path = "/root/autodl-tmp/bge-reranker-base"

# 5.定义精排序器 (Reranker)
reranker = SentenceTransformerRerank(
    model=local_model_path,
    top_n=3, # 精选出最相关的 4 个文档送给 LLM,
)
# 6. 创建响应合成,用大模型(LLM)根据 top-K 文档 + 用户问题,生成一个最终的回答。
response_synthesizer = get_response_synthesizer(
    response_mode="compact", 
    streaming=True
)

# 7. 构建 QueryEngine
query_engine = RetrieverQueryEngine.from_args(
    retriever=hybrid_retriever,
    node_postprocessors=[reranker],
    response_synthesizer=response_synthesizer
)

# 8. 执行查询
query = "B200A 芯片的存储器规格和预计何时被原始设备制造商拿到是什么?"
response = query_engine.query(query)

print("大模型回复内容:")
print(response)
print("=" * 60)

# 打印最终被送入 LLM 的 Source Nodes
print("\n--- Final Source Nodes (after Reranking) ---")
for node in response.source_nodes:
    print(f"Score: {node.score:.4f}, File: {node.text}")
    print("=" * 60)

2.4. 适用场景选择对比

使用场景 推荐方法 关键参数 注意事项
简单问答 as_query_engine() similarity_top_k=3-5 适合直接获取答案
文档搜索 as_retriever() similarity_top_k=10-20 返回原始节点,需后处理
多轮对话 as_chat_engine() chat_mode="context" 需要管理对话状态
复杂推理 as_chat_engine() 配置 memory 适合需要推理的场景
高精度检索 as_retriever()
+ 过滤器
filters
alpha=0.7
使用元数据提升精度

三:检索生成评估体系

3.1. 测试数据准备

评估 RAG 应用需要用到几个评估实体,分别是:

其中 Question 和 Ground Truth 通过用户提供,Context 通过检索得到,Answer / Response 是由 LLM 生成,后面我们在讲解的时候会沿用这些实体名称。在 LlamaIndex 中提供了生成测试数据集的功能,可以帮助我们快速生成测试数据集,无需人工干预。

from llama_index.core.evaluation import generate_question_context_pairs

custom_prompt = """
请基于以下文本内容,生成 {num_questions_per_chunk} 个有代表性的问题。
问题应当:
1. 覆盖文本关键信息;
2. 不要过于宽泛;
3. 使用自然中文。

---------------------
文本内容如下:
{context_str}
---------------------
"""
# 3. 自动生成评估数据集
# LlamaIndex 可以从文档中自动生成“问题 - 上下文”对,非常适合冷启动
# num_questions_per_chunk: 每个文档块生成多少问题
qa_dataset = generate_question_context_pairs(
    # llamaindex_documents,
    nodes=nodes,
    llm=Settings.llm,
    num_questions_per_chunk=1,
    qa_generate_prompt_tmpl=custom_prompt,
)

3.2. RAG 评估的核心维度

一个优秀的 RAG 系统需要在以下两个核心维度上表现出色:

1. 检索质量(Retrieval Quality):

无序评估

1. 召回率

2. 精确率

3. 命中率

4. 上下文相关性

有序评估

1. MRR 平均倒数排名

查询 第一个相关文档的位置 倒数排名得分
Q 1 第 1 位 1 / 1 = 1.0
Q 2 第 3 位 1 / 3 ≈ 0.33
Q 3 第 2 位 1 / 2 = 0.5

2. 归一化折损累计增益(NDCG)

LlamaIndex 框架内置评估器

# 建立问答数据字典
qa_dict = dict()

for i in range(0, len(qa_dataset.corpus.keys())):
    qa_dict[list(qa_dataset.queries.values())[i]] =  [list(qa_dataset.corpus.keys())[i]]
from llama_index.core import VectorStoreIndex

# 1. 加载向量索引
index = VectorStoreIndex(nodes=nodes)

# 2. 准备检索器:仅使用向量检索(避免 'hybrid' 报错)
retriever = index.as_retriever(
    similarity_top_k=10  # top_k 要足够大,避免命中率为 0
)

# 3. 探针检索,观察系统返回的 ID 形式(用于制作 ground truth)
probe_nodes = retriever.retrieve("B200A 芯片的存储器规格和预计何时被原始设备制造商拿到是什么?")

probe_ids = [n.node.node_id for n in probe_nodes]
print("检索返回的 ID 例子:", probe_ids)
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.evaluation import RetrieverEvaluator
from tqdm import tqdm

# 加载评估器
from llama_index.core.evaluation.retrieval.metrics import (
    Precision,   # 精确率
    Recall,      # 召回率
    MRR,         # 平均倒数排名
    HitRate,     # 命中率
    NDCG,        # 归一化折损累积增益
    AveragePrecision, # 平均精确率
)

# 1. 建立评估矩阵
metrics = [
    Precision(),
    Recall(),
    MRR(),
    HitRate(),
    NDCG(),
    AveragePrecision(),
]

# 2. 实例化 RetrieverEvaluator 评估器
retriever_evaluator = RetrieverEvaluator(metrics=metrics, retriever=retriever)

# 3. 运行评估
eval_results = []
for i, question in enumerate(list(qa_dataset.queries.values())):
    # 获取当前问题的真实相关文档 ID
    expected_ids = qa_dict[question]

    # 评估单个查询的检索结果
    eval_result = retriever_evaluator.evaluate(
        query=question,             # 提问问题数据
        expected_ids=expected_ids,  # 标准答案 id,list 格式
        retriever=retriever         # 检索器
    )
    eval_results.append(eval_result)
import pandas as pd
import matplotlib.pyplot as plt

# 1. 数据整理
results_data = []
for result in eval_results:
    row = {"query": result.query}
    for metric_name, metric_score in result.metric_dict.items():
        row[metric_name] = metric_score.score
    results_data.append(row)

# 2. 转换成 DataFrame 格式
df_results = pd.DataFrame(results_data)

# 3. 计算每个指标的平均值
mean_scores = df_results.mean(numeric_only=True)
print("=== 平均评估指标 ===")
print(mean_scores)

# 4. 将平均结果也加入 DataFrame 用于绘图
df_mean = mean_scores.reset_index()
df_mean.columns = ['Metric', 'Score']

# 5. 可视化评估结果
# 绘制平均指标条形图
plt.figure(figsize=(10, 6))
bars = plt.bar(df_mean['Metric'], df_mean['Score'], color=['skyblue', 'lightgreen', 'coral', 'gold', 'violet', 'lightgray'])
plt.title('Retriever Evaluation Metrics (Average Scores)')
plt.ylabel('Score')
plt.ylim(0, 1.5)

# 在柱子上添加数值标签
for bar in bars:
    height = bar.get_height()
    plt.text(bar.get_x() + bar.get_width()/2., height + 0.01,
             f'{height:.3f}', ha='center', va='bottom')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

2. 生成质量(Generation Quality):

Pasted image 20260724120431.png

1. 忠实度

2. 答案相关性

3. 事实依据性

4. 忠实度与事实依据性对比

维度 忠实度 事实依据性
核心问题 了吗? 你抄了吗?
评估参照物 你手边的参考资料(上下文) 外部事实 / 标准答案(真实世界)
检查的关系 答案 vs 上下文 答案 vs 客观事实
评估焦点 来源的纯粹性(是否无中生有) 内容的正确性(是否事实错误)

5. 指标家族与适用维度映射矩阵

(集合级 vs 排名质量 vs 生成质量)

指标家族 代表指标 是否考虑排序 主要适用阶段 典型用途
集合级(无序) Precision、Recall、F1、Hit Rate、Context Relevancy 检索 / 生成(粗评) 快速比较, 不区分顺序
排名质量 MRR、MAP、nDCG、Hit@k 检索 / 重排 精排优化、前 K 结果质量
生成质量 Faithfulness、Groundedness、Answer Relevance 否(侧重语义判定) 生成 事实一致性、证据追溯

3.3. LlamaIndex评估器(Evaluators)详解

LlamaIndex 提供了一系列预置的评估器,它们大多借助 LLM 的强大理解能力来对检索和生成结果进行打分。

评估器 评估维度 工作原理 输出 何时使用
RelevancyEvaluator 上下文相关性、答案相关性 LLM 判断检索到的每个 Node 或最终答案与查询的匹配程度。 1 - 5 分,并附带推理过程。 评估检索器或端到端 QueryEngine 的“相关性”。
FaithfulnessEvaluator 忠实度(无幻觉) LLM 逐句检查生成的答案,判断每一句话是否都能在提供的上下文中找到依据。 “YES” 或 “NO”,并指出是哪句话无法被支撑。 评估生成环节是否出现“幻觉”。
CorrectnessEvaluator 正确性 LLM 比较生成的答案和标准的参考答案(Ground Truth),判断语义是否一致。 1 - 5 分,并附带推理过程。 当你有高质量的“问题 - 标准答案”对时,用于评估端到端正确性。

1. 代码实现:运行评估流程

评估流程通常包含以下步骤:

from llama_index.core.evaluation import (
    SemanticSimilarityEvaluator,
    FaithfulnessEvaluator,
    RelevancyEvaluator
)
from llama_index.core.node_parser import SentenceSplitter

def quick_evaluation_demo():
    """快速评估演示"""

    print("1. 配置 API 密钥...")
    # 2. 初始化模型和评估器
    # embed_model = OpenAIEmbedding(model="text-embedding-3-large")
    # llm = OpenAI(model="gpt-4")

    # 创建语义相似度评估器,通过比较生成的答案与参考答案,两者在语义上的接近程度。(答案 vs 参考答案)
    semantic_evaluator = SemanticSimilarityEvaluator(
        embed_model=Settings.embed_model,
        similarity_threshold=0.8
    )
    # 创建忠实度评估器,检测幻觉,确保答案源于给定上下文(答案 vs 上下文)
    faithfulness_evaluator = FaithfulnessEvaluator(llm=Settings.llm)
    # 创建相关性评估器,评估答案与问题的匹配度及上下文的辅助作用(问题 vs 答案 vs 上下文)
    relevancy_evaluator = RelevancyEvaluator(llm=Settings.llm)

    # 3. 准备测试数据
    sample_document = """
    人工智能(Artificial Intelligence,AI)是计算机科学的一个分支,
    它企图了解智能的实质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器。

    机器学习是人工智能的一个核心研究领域,它使计算机有能力在不被明确编程的情况下进行学习。
    机器学习专注于计算机程序的开发,这些程序可以访问数据并使用它学习为自己。

    深度学习是机器学习的子集,它基于人工神经网络的表征学习方法。
    深度学习在图像、语音、文本等感知任务上取得了突破性进展。
    """

    # 切分文档
    splitter = SentenceSplitter(chunk_size=200, chunk_overlap=30)
    chunks = splitter.split_text(sample_document)

    print(f"文档已切分为 {len(chunks)} 个片段:")
    for i, chunk in enumerate(chunks):
        print(f"  片段 {i+1}: {chunk[:50]}...")

    # 4. 执行各项评估
    # 测试查询和响应
    test_query = "什么是人工智能?"
    test_response = "人工智能是计算机科学的一个分支,研究如何让机器表现出智能行为。"
    test_reference = "人工智能是计算机科学的一个分支,企图了解智能的实质"

    # 语义相似度评估
    semantic_result = semantic_evaluator.evaluate(
        query=test_query,
        response=test_response,
        reference=test_reference,
        contexts=[chunks[0]]
    )
    print(f"相似度分数: {semantic_result.score:.3f}")
    print(f"是否通过: {semantic_result.passing}")
    print(f"反馈: {semantic_result.feedback}")

    # 忠实度评估
    faithfulness_result = faithfulness_evaluator.evaluate(
        query=test_query,
        response=test_response,
        contexts=[chunks[0]]
    )
    print(f"忠实度分数: {faithfulness_result.score:.3f}")
    print(f"是否通过: {faithfulness_result.passing}")
    print(f"反馈: {faithfulness_result.feedback}")

    # 相关性评估
    relevancy_result = relevancy_evaluator.evaluate(
        query=test_query,
        response=test_response,
        contexts=[chunks[0]]
    )
    print(f"相关性分数: {relevancy_result.score:.3f}")
    print(f"是否通过: {relevancy_result.passing}")
    print(f"反馈: {relevancy_result.feedback}")

    # 5. 综合评估结果
    metrics = {
        '语义相似度': semantic_result.score,
        '忠实度': faithfulness_result.score,
        '相关性': relevancy_result.score
    }

    for metric, score in metrics.items():
        status = "✓ 通过" if score >= 0.7 else "✗ 未通过"
        print(f"{metric}: {score:.3f} {status}")

    avg_score = sum(metrics.values()) / len(metrics)
    print(f"\n平均分数: {avg_score:.3f}")
    overall_status = "✓ 整体通过" if avg_score >= 0.7 else "✗ 整体未通过"
    print(f"整体评估: {overall_status}")

    return "评估完成!"

quick_evaluation_demo()

3.4. 指标在 RAG 不同阶段的应用与意义

将指标与阶段对齐是评估有效性的前提。

RAG 阶段 - 核心指标映射表(目标、代表性指标、代表性场景)

阶段 核心目标 代表性指标 代表性场景
索引 分块与嵌入质量、索引完整性 嵌入一致性、召回抽查 长文档切分、知识库构建
检索 Top-K 相关性与排序质量 P@k、R@k、MRR、MAP、nDCG、Hit@k FAQ 检索、证据选取
生成 事实一致与相关性 Faithfulness、Groundedness、Relevance、幻觉检测 问答回答、摘要增强

四:使用开源框架进行评估测试

4.1. 使用 Deepeval 来进行评估

# 构建黄金数据集格式
qa_res_dict = dict()

for i in range(0, len(qa_dataset.corpus.keys())):
    qa_res_dict[list(qa_dataset.queries.values())[i]] = list(qa_dataset.corpus.values())[i]

# 展示数据集
qa_res_dict
from deepeval import evaluate
from deepeval.metrics import (
    AnswerRelevancyMetric,  # 答案相关性
    FaithfulnessMetric,      # 忠诚度(防幻觉)
    ContextualPrecisionMetric, # 上下文精确度
    ContextualRecallMetric,  # 上下文召回率
    # MultimodalAnswerRelevancyMetric # 多模态答案相关性
)
from deepeval.test_case import LLMTestCase
from deepeval.dataset import EvaluationDataset, Golden
from llama_index.core import VectorStoreIndex
from llama_index.retrievers.bm25 import BM25Retriever
from llama_index.core.postprocessor import SentenceTransformerRerank
from llama_index.core.retrievers import QueryFusionRetriever

# 1. 加载向量索引
index = VectorStoreIndex(nodes=nodes)

# 2. 向量检索器 (粗召回)
vector_retriever = index.as_retriever(
    similarity_top_k=6 # 扩大召回范围,为 Reranker 提供充足的候选
)

# 3. BM25 检索器 (关键词补充)
bm25_retriever = BM25Retriever.from_defaults(
    nodes=nodes,
    similarity_top_k=6
)

# 4. 混合检索器 (融合结果)
retriever = QueryFusionRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    similarity_top_k=6, # 融合后保留的候选数量
    num_queries=1, # 保持简单,不生成子查询
    mode="reciprocal_rerank",
    use_async=True # 必须开启异步
)

# 5. 定义精排序器 (Reranker)
reranker = SentenceTransformerRerank(
    model="/root/autodl-tmp/bge-reranker-base",
    top_n=3, # 精选出最相关的 4 个文档送给 LLM,
)

# 6. 定义评估指标
metrics = [
    AnswerRelevancyMetric(threshold=0.7),  # 答案相关性阈值 0.7
    FaithfulnessMetric(threshold=0.8),     # 忠诚度阈值 0.8
    ContextualPrecisionMetric(threshold=0.6),  # 上下文精确度
    ContextualRecallMetric(threshold=0.7),    # 上下文召回率
]

# 7. 准备测试用例(黄金数据集)
dataset = EvaluationDataset(
    goldens=[
        Golden(
            input=list(qa_res_dict.keys())[0],
            expected_output=list(qa_res_dict.values())[0]
        ),
        Golden(
            input=list(qa_res_dict.keys())[1],
            expected_output=list(qa_res_dict.values())[1]
        )
    ]
)
from llama_index.core.query_engine import RetrieverQueryEngine

# 8. 构建查询引擎
# query_engine = RetrieverQueryEngine(
#     retriever=retriever,
#     node_postprocessors=[reranker],
# )
query_engine = index.as_query_engine(similarity_top_k=5)

# 9. 执行端到端评估
def evaluate_rag():
    test_cases = []
    for golden in dataset.evals_iterator():
        # 运行查询
        response = query_engine.query(golden.input)

        # 提取检索上下文
        retrieval_context = [
            node.node.text for node in response.source_nodes
        ]

        # 创建测试用例
        test_case = LLMTestCase(
            input=golden.input,
            actual_output=response.response,
            expected_output=golden.expected_output,
            retrieval_context=retrieval_context
        )
        test_cases.append(test_case)

    # 一次性评估整批用例(关键:传入列表)
    run_result = evaluate(test_cases=test_cases, metrics=metrics)

    # 简要打印每个用例的得分(不同版本的 deepeval 输出结构可能稍有差异)
    try:
        for i, case_result in enumerate(run_result.results):
            print(f"Case {i+1}:")
            for m in case_result.metrics:
                print(f"- {getattr(m, 'name', 'metric')}: {getattr(m, 'score', 'N/A')}")
    except Exception:
        print(run_result)

    return run_result

# 10. 运行
evaluate_rag()

4.2. Trulens 框架评估

Pasted image 20260724145920.png

TruLens 作为一个开源的 LLM 应用评估框架,通过精心设计的"RAG 三元组"(RAG Triad)评估体系,为开发者提供了一套完整的、可扩展的评估解决方案。本文将从架构原理、评估指标、与 LlamaIndex 集成等多个维度,深入剖析 TruLens 的技术实现,为面试者和实践者提供全面的理解和应用指导。

from trulens.providers.openai import OpenAI as TruOpenAI
from trulens.core import Feedback, TruSession, Select
import numpy as np

def build_feedbacks(provider: TruOpenAI | None = None):
    """
    构建 RAG 三元组反馈函数:
    - Groundedness:答案是否基于提供的上下文,有推理链(CoT)
    - Answer Relevance:答案与用户查询的相关性
    - Context Relevance:上下文与用户查询的相关性

    使用新版 TruLens API(OTEL 模式):
    - 使用 .on_input(), .on_output(), .on_context() 方法
    - 这些方法会自动从 span attributes 中提取数据
    """

    # 若未显式传入 Provider,则默认使用 OpenAI Provider
    oai = provider or TruOpenAI()

    # Answer Relevance:答案与查询的相关性
    # 从 RECORD_ROOT.INPUT 和 RECORD_ROOT.OUTPUT 获取数据
    f_answer_relevance = (
        Feedback(oai.relevance_with_cot_reasons, name="Answer Relevance")
        .on_input()   # 自动从 SpanAttributes.RECORD_ROOT.INPUT 获取
        .on_output()  # 自动从 SpanAttributes.RECORD_ROOT.OUTPUT 获取
    )

    # Groundedness:答案与上下文的一致性
    # 从 RETRIEVAL.RETRIEVED_CONTEXTS 和 RECORD_ROOT.OUTPUT 获取数据
    # 注意:groundedness_measure_with_cot_reasons 的参数顺序是 (source, statement)
    # source 是上下文列表,statement 是答案
    f_groundedness = (
        Feedback(oai.groundedness_measure_with_cot_reasons, name="Groundedness")
        .on_context(collect_list=True)  # source: 从 SpanAttributes.RETRIEVAL.RETRIEVED_CONTEXTS 获取
        .on_output()  # statement: 从 SpanAttributes.RECORD_ROOT.OUTPUT 获取
    )

    # Context Relevance:上下文与查询的相关性
    # 从 RECORD_ROOT.INPUT 和 RETRIEVAL.RETRIEVED_CONTEXTS 获取数据
    # 注意:context_relevance_with_cot_reasons 的参数顺序是 (question, context)
    f_context_relevance = (
        Feedback(oai.context_relevance_with_cot_reasons, name="Context Relevance")
        .on_input()  # question: 从 SpanAttributes.RECORD_ROOT.INPUT 获取
        .on_context(collect_list=False)  # context: 从 SpanAttributes.RETRIEVAL.RETRIEVED_CONTEXTS 获取,每个上下文片段单独评估
        .aggregate(np.mean)  # 对所有上下文片段的分数取平均
    )

    return [f_groundedness, f_answer_relevance, f_context_relevance]

f_list = build_feedbacks()
from llama_index.core import VectorStoreIndex

# 建立 index 索引
index = VectorStoreIndex(nodes=nodes)

# 创建查询引擎
query_engine = index.as_query_engine(similarity_top_k=5)
from trulens.core.otel.instrument import instrument
from trulens.otel.semconv.trace import SpanAttributes
from typing import List

class LlamaIndexRAG:
    """
    LlamaIndex RAG 的最小封装:包含检索和生成两个步骤
    - 使用 @instrument 为方法打点,以便 TruApp 记录调用的入参与返回值
    - 明确不依赖 TruLlama 或 LangChain 插件,减少版本兼容问题
    """
    def __init__(self, query_engine):
        # 保存由 LlamaIndex 构建的 QueryEngine(例如 index.as_query_engine(...))
        self.query_engine = query_engine

    @instrument(
        span_type=SpanAttributes.SpanType.RETRIEVAL,
        attributes={
            SpanAttributes.RETRIEVAL.QUERY_TEXT: "query",
            SpanAttributes.RETRIEVAL.RETRIEVED_CONTEXTS: "return",
        }
    )
    def retrieve(self, query: str) -> List[str]:
        """检索相关上下文"""
        # 发起 LlamaIndex 查询
        resp = self.query_engine.query(query)

        # 兼容地提取上下文文本:常见在 resp.source_nodes
        contexts: List[str] = []
        source_nodes = getattr(resp, "source_nodes", [])

        # 遍历每个 SourceNode,尽可能获取原文内容
        for sn in source_nodes:
            text = None

            # 常见字段:sn.node 或 sn.source_node,内部可能有 text 或 get_content()
            node_obj = getattr(sn, "node", None) or getattr(sn, "source_node", None)
            if node_obj is not None:
                text = getattr(node_obj, "text", None)
                if text is None:
                    try:
                        text = node_obj.get_content()
                    except Exception:
                        pass

            # 进一步的回退尝试:直接取 sn.text 或调用 node.get_content()
            if text is None:
                text = getattr(sn, "text", None)
            if text is None:
                try:
                    node = getattr(sn, "node", None)
                    if node is not None and hasattr(node, "get_content"):
                        text = node.get_content()
                except Exception:
                    pass

            if text:
                contexts.append(text)

        return contexts

    @instrument(span_type=SpanAttributes.SpanType.GENERATION)
    def generate_answer(self, query: str, contexts: List[str]) -> str:
        """基于上下文生成答案"""
        # 这里我们直接使用 query_engine 的结果
        # 在实际应用中,这个方法会调用 LLM 生成答案
        resp = self.query_engine.query(query)

        # 兼容不同版本的响应对象:优先取 response,其次 message,最后转字符串
        ans = getattr(resp, "response", None)
        return ans

    # 把这个方法标记为“可观测”的应用调用
    @instrument(
        span_type=SpanAttributes.SpanType.RECORD_ROOT,
        attributes={
            SpanAttributes.RECORD_ROOT.INPUT: "query",
            SpanAttributes.RECORD_ROOT.OUTPUT: "return",
        }
    )
    def query(self, query: str) -> str:
        """执行完整的 RAG 流程:检索 + 生成"""
        contexts = self.retrieve(query)
        answer = self.generate_answer(query, contexts)
        return answer
from trulens.core import Feedback, TruSession
from trulens.apps.app import TruApp

def setup_rag_evaluator(index: VectorStoreIndex, top_k: int = 3):
    """
    设置 RAG 评估器(一次性初始化):
    1) 创建 QueryEngine
    2) 构建反馈函数
    3) 封装为 TruApp(将反馈函数传入)
    4) 初始化 TruSession

    返回:
    (tru_rag, session) - 可复用的评估组件
    """
    # 从索引创建 QueryEngine,指定 top_k 控制检索的上下文条数
    qe = index.as_query_engine(similarity_top_k=top_k)

    # 封装为自定义 RAG
    rag = LlamaIndexRAG(qe)

    # 初始化会话(连接默认 sqlite 或指定数据库)
    session = TruSession()

    # 构建反馈函数
    feedbacks = build_feedbacks()

    # 创建 TruApp,将反馈函数传入(OTEL 模式下反馈函数会自动运行)
    tru_rag = TruApp(
        rag,
        app_name="LlamaIndex RAG",
        app_version="v1",
        feedbacks=feedbacks  # 在 OTEL 模式下,反馈函数在这里传入
    )

    return tru_rag, session
def evaluate_single_query(tru_rag, session, query: str):
    """
    评估单个查询(可多次调用):
    1) 通过 TruApp 记录一次调用
    2) 反馈函数会自动运行(已在 TruApp 中配置)
    3) 返回结果

    参数:
    - tru_rag: TruApp 实例(由 setup_rag_evaluator 创建)
    - session: TruSession 实例
    - query: 用户查询

    返回:
    (answer, record)
    """
    # 记录一次查询调用:with 语句块结束后可通过 recording.get() 获取 Record
    # 在 OTEL 模式下,反馈函数会自动运行
    with tru_rag as recording:
        answer = tru_rag.app.query(query)

    record = recording.get()

    # 注意:在 OTEL 模式下,反馈函数已经自动运行
    # 不需要手动调用 session.run_feedback_functions()
    # 反馈结果会自动保存到数据库,可以通过 Dashboard 查看

    return answer, record
from trulens.dashboard import run_dashboard

def evaluate_query(
    index: VectorStoreIndex,
    query: str,
    top_k: int = 4,
    run_ui: bool = True,
):
    """
    对单个查询做端到端评估(兼容旧版 API):
    1) 由索引构建 QueryEngine;
    2) 用 LlamaIndexRAG 封装并通过 TruApp 记录一次调用;
    3) 反馈函数自动运行(在 OTEL 模式下);
    4) 可选启动 Dashboard 展示评估结果。

    返回:
    (record, session, answer)
    """
    tru_rag, session = setup_rag_evaluator(index, top_k)
    answer, record = evaluate_single_query(
        tru_rag, session, query
    )

    # 可选启动 Dashboard,查看记录、反馈分数与详细轨迹
    if run_ui:
        run_dashboard(session)

    return record, session, answer
query = "B200A 芯片的存储器规格是多少,预计何时由 OEMs 正式拿到?"
record, session, answer = evaluate_query(index, query)

Trulens 启动 UI 页面展示

Pasted image 20260724150713.png

4.3. 其他开源框架对比

工具名称 核心定位 突出特点 适用场景
RAGAS 专项评估框架 专为 RAG 设计,提供上下文相关性、答案忠实度等核心指标的成熟实现,被认为是该领域的领导者。 快速衡量 RAG 流水线整体表现,进行快速迭代和基准测试
DeepEval 集成测试框架 提供超过 14 种评估指标,与 pytest
无缝集成,支持以编写单元测试的方式评估 RAG 系统。
适合 CI / CD 管道,追求评估自动化和工程化的团队。
TruLens 深度诊断工具 采用" Triad "评估模型,能追踪完整的 RAG 调用链,帮助定位性能瓶颈是在检索端还是生成端。 当需要深入调试和根本原因分析,而不仅仅是获得一个分数时。
Arize Phoenix 可观测性平台 开源的 AI 可观测性工具,提供评估和追踪能力,非常适合在本地开发环境中进行早期调试和迭代。 开发阶段的模型观测、评估和问题排查。

4.4. 参数调优:从评估到优化

评估的最终目的是指导优化。当你获得了一份评估报告后,应该如何解读并采取行动?

1. 低上下文相关性 (Low Context Relevancy)

2. 低忠实度 (Low Faithfulness)

3. 答案相关性 (Low AnswerRelevancy)

4. 事实依据性 (Low Groundedness)

5. 低召回率 (Low Recall / Low Hit Rate)

6. 迭代优化的循环

参数调优不是一次性的过程,而是一个持续迭代的循环:

  1. 建立基线:使用默认参数运行一次完整的评估,记录所有指标,作为后续改进的基线。
  2. 单变量原则:每次只调整一个参数(如 chunk_sizetop_k 或增加 Reranker)。
  3. 再次评估:运行完整的评估流程,观察核心指标的变化。
  4. 分析与决策:如果指标有提升,保留该改动;如果没有或变差,则回退。分析变化的原因,为下一步调整提供思路。
  5. 重复循环:持续这个过程,直到达到满意的性能水平。

通过这个系统化的评估与调优框架,你可以将 RAG 系统的优化从“玄学”变为“科学”,有据可依地提升系统在各个维度的表现。


五:常见问题与优化建议

Q1: Precision 高但 Recall 低,怎么办?

Q2: Faithfulness 低(幻觉严重),怎么办?

Q3: 评估成本太高,怎么办?

Q4: 没有标准答案怎么评估?


六:生产环境 RAG 评估定位

在企业级应用中,RAG(Retrieval-Augmented Generation)并不是“一次性上线”的静态系统,而是一个可监控、可迭代优化的智能系统。 评估不是“离线跑几次指标”,而是:贯穿 RAG 全生命周期的质量闭环体系(Continuous RAG Evaluation Loop)

在生产环境中,RAG 评估应该是"全程监控 + 重点突破"的模式。平时通过监控体系保持系统健康,当发现具体问题时,采用"单阶段深度评估 + 全链路影响分析"的方法进行精准优化。

目标 推荐做法
持续改进 建立自动化评估管道(Ragas / TruLens + LlamaIndex)
指标追踪 对 Precision、Recall、MRR、Faithfulness 等进行时序监控
版本控制 对每次索引、模型、prompt、retriever 参数版本化
人工复核 周期抽样人工检查模型输出
异常报警 结合日志系统(Prometheus / Grafana)监控延迟、异常