【Python】Haystack2

2.6 检索性能评估(Retrieval Performance Evaluation):量化 RAG 系统的“智慧”

在构建和优化 RAG(检索增强生成)系统时,仅仅凭直觉或少数几个测试用例来判断其性能是远远不够的。为了系统地改进 RAG 流程,我们必须能够客观、量化地评估其各个组件的性能,特别是核心的检索器(Retriever)。检索器负责从海量文档中召回与用户查询最相关的上下文,其性能直接决定了后续 LLM 生成答案的质量和准确性。

2.6.1 为什么需要评估检索器?

对检索器进行严格的性能评估是至关重要的,原因如下:

  1. 指导优化方向: 评估结果能够清晰地揭示检索器在哪些方面表现良好,哪些方面存在不足(例如,是召回率不够高导致漏掉了相关文档,还是精确率不够高导致引入了太多噪音)。这些量化指标为我们调整模型、修改参数、引入新策略提供了数据驱动的依据。
  2. 比较不同策略: 面对多种检索器类型(BM25、稠密检索器、混合检索)和优化策略(查询扩展、重排),评估能够帮助我们公平地比较它们的效果,选择最适合特定应用场景和数据集的方案。
  3. 迭代改进: RAG 系统的开发是一个迭代过程。每次修改后,都需要通过评估来验证改进是否有效,或者是否引入了新的问题。
  4. 识别系统瓶颈: 即使 LLM 表现出色,如果检索器提供的上下文质量不高,LLM 也可能生成不准确、不完整或产生幻觉的答案。评估检索器有助于识别并解决这个潜在的瓶颈。
  5. 建立质量基线: 通过评估,可以建立起 RAG 系统的性能基线,为未来的改进设定目标。

2.6.2 评估基础:相关性标注与测试集构建

所有检索评估的基础都是相关性标注(Relevance Labeling),即对于一个给定的查询,人工判断文档库中的哪些文档是“相关”的,以及它们的相关程度。这通常涉及到构建一个高质量的测试集(Test Set)

  • 查询-文档相关性对(Query-Document Relevance Pairs):

    • 这是评估的最小单元。对于测试集中的每个查询,我们需要一个列表,列出文档库中所有(或至少是顶层)与该查询相关的文档。
    • 相关性等级: 简单的标注可能只区分“相关”和“不相关”。更精细的标注会使用多级相关性,例如:
      • 0:不相关
      • 1:弱相关/背景信息
      • 2:中等相关/部分相关
      • 3:强相关/直接包含答案
    • 人工标注: 尽管耗时耗力,人工标注通常是最准确的“黄金标准”。专业领域(如医疗、法律)的标注需要领域专家参与。
    • 在 Haystack 中的体现: Haystack 提供了 Label 对象来封装这种相关性信息。一个 Label 对象可以包含一个查询、一个或多个相关的文档,以及这些文档中可能的答案范围。
  • 测试集构建:

    • 查询集: 包含一组具有代表性的用户查询,这些查询应覆盖真实用户可能提出的各种类型和难度。
    • 文档库: 用于检索的文档集合,应与实际部署时使用的文档库相似。
    • 相关性标注: 对于测试集中的每个查询,都必须明确标注出其在文档库中对应的相关文档。
    • 挑战: 构建一个大型、高质量的标注测试集是信息检索领域的一大挑战,因为它需要大量的人力投入和专业知识。通常会采用众包(crowdsourcing)或内部专家团队进行标注。

2.6.3 核心评估指标

检索器评估的指标旨在量化其在“召回相关信息”和“避免召回无关信息”方面的能力。以下是一些最常用的检索评估指标:

2.6.3.1 召回率 (Recall)

  • 定义: 召回率衡量的是在所有真正相关的文档中,有多少比例被检索器成功地**召回(retrieved)**了。它反映了检索器“查全”的能力。
  • 公式:
    [
    \text{Recall} = \frac{|\text{检索到的相关文档}|}{|\text{所有相关文档}|}
    ]
    其中:
    • |检索到的相关文档|: 检索器返回的文档中,同时也被人工标注为相关文档的数量。
    • |所有相关文档|: 在整个文档库中,被人工标注为与查询相关的所有文档的数量。
  • 重要性:
    • 高召回率意味着检索器不太可能漏掉重要的相关信息。
    • 在某些应用场景(如法律文件审查、医学诊断支持)中,漏召回的代价可能非常高,因此召回率是极其重要的指标。
  • 局限性: 召回率越高,通常也意味着检索器返回的文档数量越多,可能引入更多不相关的文档(降低精确率)。在只关注召回率时,无法体现返回文档的质量。

2.6.3.2 精确率 (Precision)

  • 定义: 精确率衡量的是在检索器召回的所有文档中,有多少比例是真正相关的。它反映了检索器“查准”的能力。
  • 公式:
    [
    \text{Precision} = \frac{|\text{检索到的相关文档}|}{|\text{检索到的文档总数量}|}
    ]
    其中:
    • |检索到的相关文档|: 检索器返回的文档中,同时也被人工标注为相关文档的数量。
    • |检索到的文档总数量|: 检索器为给定查询返回的所有文档的数量(通常是 top_k)。
  • 重要性:
    • 高精确率意味着检索器返回的文档大多数都是有用的,噪音较少。
    • 在 RAG 系统中,向 LLM 传递高度精确的上下文可以减少其处理无关信息的负担,降低幻觉,并提高答案的质量和效率。如果精确率低,LLM 可能会被误导。
  • 局限性: 精确率越高,可能意味着检索器只返回了最明显的相关文档,从而漏掉了一些虽然相关但不是那么“明显”的文档(降低召回率)。

2.6.3.3 F1-分数 (F1-Score)

  • 定义: F1-分数是精确率和召回率的调和平均值。它提供了一个综合性的指标,特别适用于精确率和召回率都很重要,并且需要在这两者之间进行平衡的场景。
  • 公式:
    [
    F1 = 2 \cdot \frac{\text{Precision} \cdot \text{Recall}}{\text{Precision} + \text{Recall}}
    ]
  • 重要性:
    • 当检索器返回的文档数量可能变化时,F1-分数比单独的精确率或召回率更能反映系统的整体性能。
    • F1-分数对精确率和召回率的低值比较敏感,这意味着如果其中一个指标很低,F1-分数也会显著下降。

2.6.3.4 平均精确率 (Average Precision, AP)

  • 定义: AP 旨在衡量排名靠前的相关文档的重要性。对于一个查询,AP 是在检索器每次召回一个相关文档时,计算当前的精确率(即从第 1 个文档到当前相关文档位置的精确率),然后对这些精确率求平均。
  • 计算过程:
    1. 遍历检索器返回的文档列表。
    2. 每当遇到一个相关文档时,计算当前时刻的精确率(即:从列表开头到当前位置,相关文档的数量 / 当前位置的总文档数量)。
    3. 将所有相关文档处的精确率求和,再除以查询的所有相关文档总数。
  • 重要性: AP 对检索结果的排名质量敏感。如果相关文档出现在排名靠前的位置,AP 值会更高。这比简单精确率更能反映用户体验,因为用户通常倾向于查看排名靠前的结果。
  • 局限性: 仍然只考虑二元相关性(相关/不相关)。

2.6.3.5 平均平均精确率 (Mean Average Precision, mAP)

  • 定义: mAP 是在整个查询集上计算的 AP 的平均值。它是衡量信息检索系统整体性能的非常重要的单一指标。
  • 公式:
    [
    \text{mAP} = \frac{1}{|Q|} \sum_{i=1}^{|Q|} AP_i
    ]
    其中:
    • |Q|: 查询的总数量。
    • AP_i: 第 (i) 个查询的平均精确率。
  • 重要性: mAP 综合了所有查询的性能,并对排名靠前的相关文档给予了更多的权重,因此是评估信息检索系统时一个非常全面和常用的指标。

2.6.3.6 倒数排名平均值 (Mean Reciprocal Rank, MRR)

  • 定义: MRR 衡量的是在检索结果中,第一个相关文档出现的位置。对于每个查询,它计算第一个相关文档排名的倒数(如果第一个相关文档在第 1 位,倒数是 1;在第 2 位,倒数是 1/2;以此类推)。所有查询的倒数排名平均值就是 MRR。如果某个查询没有相关文档被召回,则其倒数排名为 0。
  • 公式:
    [
    \text{MRR} = \frac{1}{|Q|} \sum_{i=1}^{|Q|} \frac{1}{\text{rank}_i}
    ]
    其中:
    • |Q|: 查询的总数量。
    • rank_i: 第 (i) 个查询的第一个相关文档在检索结果中的排名。
  • 重要性:
    • MRR 在问答(QA)系统和搜索引擎等场景中特别有用,因为用户通常只关注排在最前面的一个或少数几个答案。高 MRR 意味着系统能够很快地找到用户真正想要的答案。
    • 它不关心除了第一个相关文档之外的任何其他相关文档的排名。

2.6.3.7 归一化折损累计增益 (Normalized Discounted Cumulative Gain, NDCG)

  • 定义: NDCG 是一种更为复杂和全面的评估指标,它考虑了以下两点:
    1. 相关性等级: 它允许相关性是多级的(例如,非常相关、中等相关、不相关),并为不同等级的相关性分配不同的“增益”(gain)。
    2. 位置折损(Positional Discount): 排名靠前的文档比排名靠后的文档对用户更有价值。因此,排名靠后的相关文档的增益会被“折损”(discounted)。
  • 计算步骤:
    1. CG (Cumulative Gain): 简单累加检索结果列表中每个文档的相关性增益。
      [
      \text{CG}p = \sum{i=1}^{p} rel_i
      ]
      其中 (p) 是考虑的排名位置,(rel_i) 是位置 (i) 上文档的相关性增益(通常是人工标注的相关性分数)。
    2. DCG (Discounted Cumulative Gain): 在 CG 的基础上,对每个文档的相关性增益进行位置折损。
      [
      \text{DCG}p = rel_1 + \sum{i=2}^{p} \frac{rel_i}{\log_2(i)}
      ]
      或者更通用的形式:
      [
      \text{DCG}p = \sum{i=1}^{p} \frac{2^{rel_i} - 1}{\log_2(i+1)}
      ]
      (后者对相关性增益的非线性处理更常见,因为 (20-1=0),(21-1=1),(2^2-1=3),高相关性文档的增益权重更大)
    3. IDCG (Ideal Discounted Cumulative Gain): 为了归一化,我们需要计算理想情况下的 DCG。IDCG 是将所有相关文档按照它们最高的可能相关性等级排序时得到的 DCG 值。
    4. NDCG (Normalized Discounted Cumulative Gain): 最后,NDCG 是实际 DCG 与理想 DCG 的比值。
      [
      \text{NDCG}_p = \frac{\text{DCG}_p}{\text{IDCG}_p}
      ]
  • 重要性:
    • NDCG 是最能反映用户体验的指标之一,因为它同时考虑了相关性等级和排名。
    • 适用于需要多级相关性标注的复杂信息检索任务。
    • 值介于 0 和 1 之间,1 表示完美排序。

2.6.4 Haystack 中的检索评估

Haystack 提供了内置的工具和结构来简化检索器的评估过程。核心是使用 Label 对象来构建评估数据集,然后利用检索器或管道的 eval() 方法进行计算。

  • Label: 在 Haystack 中,Label 对象用于表示一个查询和与之相关的文档(以及可选的答案信息)。它通常包括:
    • query: 用户查询字符串。
    • document_ids: 与查询相关的文档的 ID 列表。
    • is_correct_document: 布尔值,表示文档是否正确(在问答场景中,可以表示文档是否包含正确答案)。
    • gold_document_ids: 对于检索评估,这个字段可以用来存储所有相关的文档 ID,作为“黄金标准”。
    • score: 如果是多级相关性,可以存储相关性分数。
  • EvaluationDataset: Haystack 不直接提供一个 EvaluationDataset 类,但你可以通过 Label 对象的集合来构建评估数据集。通常,我们会创建一个包含多个 Label 对象的列表或文件,其中每个 Label 对应一个评估实例。
  • retriever.eval() 方法: Haystack 的 Retriever 组件(例如 BM25RetrieverEmbeddingRetriever)都带有一个 eval() 方法。这个方法接收一个 Label 对象的列表,并计算检索性能指标。
  • 评估流程:
    1. 准备数据: 创建或加载包含查询-相关文档对的 Label 对象列表。这些 Label 定义了“真相”。
    2. 初始化检索器: 创建你想要评估的检索器实例,并确保其连接到包含所有文档的 DocumentStore
    3. 运行评估: 调用检索器的 eval() 方法,传入你的 Label 列表。
    4. 分析结果: eval() 方法会返回一个 EvaluationResult 对象,其中包含了各种评估指标的计算值。

2.6.5 代码示例:使用 Haystack 进行检索评估

这个示例将演示如何创建一个模拟的评估数据集 (Label 对象列表),然后使用 BM25RetrieverEmbeddingRetriever 对其进行评估,并打印出它们的性能指标。

import os # 导入操作系统模块
from haystack.document_stores import InMemoryDocumentStore # 导入内存文档存储
from haystack.nodes import BM25Retriever, EmbeddingRetriever, Document # 导入 BM25Retriever, EmbeddingRetriever, Document
from haystack.schema import Document, Label # 导入 Document 和 Label 类
from haystack.pipelines import Pipeline # 导入 Pipeline 类
from sentence_transformers import SentenceTransformer # 从 sentence_transformers 库导入 SentenceTransformer
from typing import List, Dict # 导入类型提示

# --- 配置参数 ---
EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2" # Sentence-Transformers 嵌入模型
EMBEDDING_DIM = 384 # all-MiniLM-L6-v2 的嵌入维度

# --- 1. 准备文档库和模拟数据 ---
# 创建一个内存文档存储
document_store_eval = InMemoryDocumentStore(use_bm25=True, use_embedding_storage=True, embedding_dim=EMBEDDING_DIM) # 初始化内存文档存储

# 准备一些文档
docs_eval = [
    Document(id="doc1", content="Haystack 是一个用于构建问答系统和 LLM 应用的开源框架。"), # 文档 1
    Document(id="doc2", content="大语言模型(LLMs)在自然语言理解和生成方面表现出色。"), # 文档 2
    Document(id="doc3", content="RAG(检索增强生成)结合了检索和生成,以减少 LLM 的幻觉问题。"), # 文档 3
    Document(id="doc4", content="自然语言处理是人工智能的一个重要分支,涵盖文本分析和语义理解。"), # 文档 4
    Document(id="doc5", content="苹果公司发布了新款 iPhone 15 和 Mac 电脑,创新技术引发关注。", meta={
   
   "category": "科技公司"}), # 文档 5
    Document(id="doc6", content="水果是健康饮食的重要组成部分,如苹果、香蕉和橙子,富含维生素。", meta={
   
   "category": "食物"}), # 文档 6
    Document(id="doc7", content="RAG 系统通过检索外部知识来增强大型语言模型的生成能力。"), # 文档 7
    Document(id="doc8", content="深度学习是机器学习的一个子领域,推动了人工智能的快速发展。") # 文档 8
]

# 对文档进行嵌入并写入 DocumentStore
model_embedder_eval = SentenceTransformer(EMBEDDING_MODEL) # 加载嵌入模型

for doc in docs_eval: # 遍历文档列表
    doc.embedding = model_embedder_eval.encode(doc.content) # 生成文档嵌入
print(f"已为 {
     
     len(docs_eval)} 个文档生成嵌入。") # 打印生成嵌入的文档数量

document_store_eval.write_documents(docs_eval) # 将文档写入文档存储
print(f"\n已写入 {
     
     len(docs_eval)} 个文档到 DocumentStore。") # 打印写入文档数量

# --- 2. 创建模拟评估数据集 (Label 列表) ---
# 每个 Label 代表一个查询和与之相关的“黄金标准”文档。
# 在实际场景中,这些 Label 通常是从标注工具或现有数据集加载的。
# 这里我们手动创建一些示例 Label。
eval_labels: List[Label] = [ # 初始化一个 Label 列表
    Label(
        query="Haystack 框架是什么?", # 查询
        document_ids=["doc1"], # 相关的文档 ID
        is_correct_document=[True], # 对应文档是否正确
        gold_document_ids=["doc1", "doc2", "doc3", "doc4"] # 这是一个用于评估的“相关文档池”,即这个查询的所有相关文档
    ),
    Label(
        query="RAG 解决了什么问题?", # 查询
        document_ids=["doc3", "doc7"], # 相关的文档 ID
        is_correct_document=[True, True], # 对应文档是否正确
        gold_document_ids=["doc3", "doc7"] # 黄金标准相关文档
    ),
    Label(
        query="AI 发展方向", # 查询
        document_ids=["doc4", "doc8"], # 相关的文档 ID
        is_correct_document=[True, True], # 对应文档是否正确
        gold_document_ids=["doc2", "doc4", "doc8"] # 黄金标准相关文档 (注意 doc2 也和 AI 发展相关)
    ),
    Label(
        query="苹果公司最新产品", # 查询
        document_ids=["doc5"], # 相关的文档 ID
        is_correct_document=[True], # 对应文档是否正确
        gold_document_ids=["doc5"] # 黄金标准相关文档
    ),
    Label(
        query="健康饮食", # 查询
        document_ids=["doc6"], # 相关的文档 ID
        is_correct_document=[True], # 对应文档是否正确
        gold_document_ids=["doc6"] # 黄金标准相关文档
    )
]
print(f"\n已创建 {
     
     len(eval_labels)} 个评估标签。") # 打印评估标签数量

# --- 3. 初始化需要评估的检索器 ---
bm25_retriever_eval = BM25Retriever(document_store=document_store_eval, top_k=5) # 初始化 BM25Retriever,检索前 5 个文档
embedding_retriever_eval = EmbeddingRetriever( # 初始化 EmbeddingRetriever
    document_store=document_store_eval, # 传入文档存储
    embedding_model=EMBEDDING_MODEL, # 传入嵌入模型
    model_format="sentence_transformers", # 模型格式
    top_k=5, # 检索前 5 个文档
    similarity_metric="cosine" # 使用余弦相似度
)
print("BM25Retriever 和 EmbeddingRetriever 已初始化。") # 打印初始化信息

# --- 4. 运行检索器评估 ---
print("\n--- 评估 BM25Retriever ---") # 打印评估 BM25Retriever 信息
# eval 方法将针对 eval_labels 中的每个查询运行检索器,并计算指标
bm25_results = bm25_retriever_eval.eval(
    labels=eval_labels, # 评估标签列表
    document_store=document_store_eval, # 传入文档存储(用于获取实际文档内容和 ID)
    metrics=["recall", "mrr", "map", "ndcg"], # 指定要计算的指标
    strict_on_mismatch=False # 如果设置为 True,则如果 Label 中的 document_ids 不存在于 document_store 中会报错。
)

print("\n--- 评估 EmbeddingRetriever ---") # 打印评估 EmbeddingRetriever 信息
embedding_results = embedding_retriever_eval.eval(
    labels=eval_labels, # 评估标签列表
    document_store=document_store_eval, # 传入文档存储
    metrics=["recall", "mrr", "map", "ndcg"], # 指定要计算的指标
    strict_on_mismatch=False # 如果设置为 True,则如果 Label 中的 document_ids 不存在于 document_store 中会报错。
)

# --- 5. 解析并打印评估结果 ---
def print_eval_results(retriever_name: str, results: Dict): # 定义一个打印评估结果的函数
    print(f"\n--- {
     
     retriever_name} 评估报告 ---") # 打印报告标题
    for metric_name, metric_value in results.items(): # 遍历结果字典
        # 结果通常包含 query_recall, document_recall, mrr, map 等
        # 对每个指标,它可能有一个按查询的列表和一个平均值
        if isinstance(metric_value, dict) and "value" in metric_value: # 如果是字典且包含 'value' 键
            print(f"  {
     
     metric_name.replace('_', ' ').capitalize()}: {
     
     metric_value['value']:.4f}") # 打印指标的平均值
        elif isinstance(metric_value, float): # 如果是浮点数
            print(f"  {
     
     metric_name.replace('_', ' ').capitalize()}: {
     
     metric_value:.4f}") # 打印指标值
        else: # 其他情况
            # 对于 query_recall_per_query, mrr_per_query 等,可能是一个列表,这里只打印平均值
            print(f"  {
     
     metric_name.replace('_', ' ').capitalize()}: {
     
     metric_value}") # 打印原始值
    print("-" * 30) # 分隔线

# 打印 BM25Retriever 的评估结果
print_eval_results("BM25Retriever", bm25_results) # 调用函数打印 BM25Retriever 结果

# 打印 EmbeddingRetriever 的评估结果
print_eval_results("EmbeddingRetriever", embedding_results) # 调用函数打印 EmbeddingRetriever 结果

# 你也可以查看每个查询的详细结果,例如:
# print("\nBM25 Retriever Recall Per Query:")
# for q, r in bm25_results["recall_per_query"].items():
#    print(f"  Query: '{q}' -> Recall: {r:.4f}")

# print("\nEmbedding Retriever MRR Per Query:")
# for q, mrr in embedding_results["mrr_per_query"].items():
#    print(f"  Query: '{q}' -> MRR: {mrr:.4f}")

代码说明:

  • 文档和嵌入: 首先,我们创建了一个 InMemoryDocumentStore,并填充了一些示例文档。对于 EmbeddingRetriever 的评估,这些文档需要预先生成嵌入并存储。
  • eval_labels (评估数据集): 这是评估的关键。eval_labels 是一个 Label 对象的列表。
    • 每个 Label 对象包含一个 query
    • document_ids: 这是检索器应该召回的正确文档的 ID 列表。这些 ID 必须与 document_store_eval 中的文档 ID 匹配。
    • is_correct_document: 与 document_ids 对应的布尔值列表,通常都设为 True,表示这些文档是相关的。
    • gold_document_ids: 非常重要,这是所有与查询相关的真实相关文档的 ID 列表。它代表了分母,例如在计算召回率时,|所有相关文档| 就是 gold_document_ids 的长度。这个列表可能包含一些即使在 top_k 结果中也未被召回的文档。在评估精确率、MRR 等指标时,eval() 方法会使用 gold_document_ids 来判断哪些检索到的文档是相关的。
  • 初始化检索器: 我们初始化了 BM25RetrieverEmbeddingRetriever,并设置了 top_k=5,这意味着它们会尝试召回 5 个文档。评估将基于这 5 个召回文档来计算指标。
  • retriever.eval() 方法:
    • labels=eval_labels: 传入我们准备好的评估标签列表。
    • document_store=document_store_eval: 需要传入文档存储,以便 eval() 方法能够根据 Label 中的 document_ids 找到对应的文档内容,并与检索器实际召回的文档进行比较。
    • metrics=["recall", "mrr", "map", "ndcg"]: 指定我们希望计算的评估指标。Haystack 的 eval() 方法会自动计算这些指标。
    • strict_on_mismatch=False: 这是一个安全参数。如果设置为 True,当 Label 中指定的 document_idsdocument_store 中找不到时会抛出错误。在调试阶段,设置为 False 可能更方便。
  • 打印结果: eval() 方法返回一个字典,其中包含了各种评估指标的计算结果。我们定义了一个 print_eval_results 函数来美化输出。结果中通常会包含总体的平均值(例如 recallmrrmapndcg)以及每个查询的详细指标(例如 recall_per_querymrr_per_query)。

通过这样的评估流程,我们可以量化地比较不同检索器的性能,或者在对检索器进行参数调整、模型更新后,客观地衡量这些改变是否带来了积极的提升。在实际项目中,构建一个持续更新的、高质量的评估数据集是确保 RAG 系统长期性能的关键。

2.7 生成器(Generator):从知识到答案的飞跃

在 RAG(检索增强生成)系统中,检索器(Retriever)和重排器(Re-ranker)的职责是识别并提供与用户查询最相关的上下文信息。然而,这些上下文通常是原始的文档片段,可能分散在不同的段落中,或者包含冗余信息。RAG 流程的最后一步,也是最关键的一步,就是将这些分散的知识点整合提炼,以自然语言的形式生成用户所需的最终答案。这一任务由**生成器(Generator)**组件承担。

2.7.1 生成器的核心作用与演进

生成器在 RAG 管道中扮演着“知识整合者”和“答案讲述者”的角色。它的核心作用包括:

  1. 信息整合与综合: 从多个检索到的文档片段中提取关键信息,并将其综合成一个连贯、全面的答案。
  2. 自然语言生成: 将结构化或半结构化的信息(如文档片段)转化为流畅、语法正确、易于理解的自然语言文本。
  3. 遵循指令与格式: 根据用户查询的意图和预设的提示词(Prompt),以特定的格式、语气和风格生成答案。
  4. 减少幻觉: 结合外部检索到的真实信息,可以有效减少大型语言模型(LLMs)在独立生成时可能出现的“幻觉”(hallucination)现象,即生成看似合理但实际上不准确或捏造的信息。

生成器的演进:
在大型语言模型(LLMs)崛起之前,问答系统中的生成器通常采取抽取式(Extractive)问答的形式。抽取式问答模型(如 BERT 上的 SQuAD 模型)的任务是从给定文本中精确地识别并提取出包含答案的句子或短语。这种方法虽然简单高效,但其局限性在于只能从原文中“抽取”,无法进行灵活的综合、改写或推理,对于需要跨文档信息整合或概括性回答的复杂问题束手无策。

随着 Transformer 架构的突破以及 GPT-3、GPT-4、Llama 等超大规模预训练语言模型的出现,生成式(Generative)问答成为了主流。这些 LLMs 拥有强大的文本生成能力,能够根据给定的上下文和指令,灵活地生成全新的、连贯的、高质量的答案。RAG 范式正是充分利用了 LLMs 的生成能力,并通过检索机制为其提供了“外部知识”,从而弥补了 LLMs 知识滞后和容易幻觉的缺点。

2.7.2 Haystack 中的生成器类型:PromptNode 的核心地位

在 Haystack 框架中,PromptNode 是目前最主要、最灵活的生成器组件。它是一个高度可配置的节点,能够与各种大型语言模型(无论是基于 API 的云服务还是本地部署的开源模型)进行交互,并根据自定义的提示词(Prompt)生成答案。

虽然 Haystack 早期版本可能存在名为 AnswerGenerator 的节点,但随着 PromptNode 的发展和功能强化,PromptNode 已经成为了执行生成任务的首选。PromptNode 的设计理念是**“一切皆提示”**,这意味着无论是简单的问答,还是复杂的摘要、翻译、代码生成等任务,都可以通过设计合适的提示词来驱动 LLM 完成。

PromptNode 的主要特性:

  • 广泛的模型支持: PromptNode 支持连接到市面上几乎所有主流的 LLM 服务和开源模型:
    • 商业 API: OpenAI (GPT-3.5, GPT-4), Cohere, Anthropic (Claude), Google Gemini 等。
    • Hugging Face Hub 模型: 能够加载并使用 Hugging Face Hub 上托管的大量预训练或微调的开源 LLM(例如 Llama, Mistral, Falcon 等)。
    • 本地模型: 可以集成通过 Hugging Face transformers 库加载到本地设备(CPU/GPU)上的 LLM。
  • 灵活的提示词工程: 允许用户定义复杂的 PromptTemplate,以精确控制 LLM 的行为和输出格式。
  • 输出解析: 能够对 LLM 返回的原始文本进行解析,提取出结构化的答案、相关文档引用等信息。
  • 可扩展性: 作为 Haystack 管道的一部分,可以与其他检索器、重排器等节点无缝协作。

PromptNode 的重要参数:

  • model_name_or_path: 指定要使用的 LLM 模型。对于 API 服务,这是模型名称(如 “gpt-3.5-turbo”)。对于 Hugging Face 模型,这是模型在 Hub 上的 ID 或本地路径。
  • api_key: 如果使用商业 API 服务,需要提供对应的 API 密钥。
  • max_length: 生成答案的最大令牌数(token)。
  • temperature: 控制生成文本的随机性。较高的值(例如 0.7-1.0)会生成更多样化的答案,较低的值(例如 0.2-0.5)会生成更确定、更保守的答案。
  • top_p: 核采样参数。与 temperature 类似,也用于控制生成文本的随机性,但以不同的方式。
  • top_k: 限制采样的词汇范围。
  • default_prompt_template: 一个 PromptTemplate 对象,定义了发送给 LLM 的默认提示词结构。
  • model_kwargs: 传递给底层 LLM 模型的额外参数(如 stop_words)。
  • streaming: 是否启用流式生成答案(对于某些 LLM API 支持)。
  • </
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

宅男很神经

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值