Haystack Evaluators 组件 API 全解:从答案匹配到 LLM 评审的九种评测器
2026/9/14 11:22:36 网站建设 项目流程

Haystack Evaluators 组件 API 全解:从答案匹配到 LLM 评审的九种评测器

【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack

本文基于 Haystack 仓库haystack/components/evaluators模块的官方 API 参考(对应 version 2.21 文档),逐一拆解 9 个内置评测组件的输入输出契约、关键参数与默认值,并结合仓库源码(如 llm_evaluator.py、document_ndcg.py)说明其底层实现:prompt 模板如何组装、JSON 输出如何校验、文档级指标(Recall/MRR/MAP/NDCG)如何按位置折扣计算,以及 LLM 型评测器如何继承LLMEvaluator的失败处理机制。读完本文,你能够为 RAG 管线的检索与生成两个环节分别选配合适的评测器,并在 Pipeline 中以可序列化的方式接入它们。

1. Evaluators 模块总览

Evaluators 是一组用于评测管线或单个组件的 Haystack 组件,覆盖 RAG 应用评测的三类需求:

类别组件评估对象是否需要 LLM/模型
答案级(字符串)AnswerExactMatchEvaluator预测答案 vs 标准答案,精确匹配
答案级(语义)SASEvaluator预测答案 vs 标准答案,语义相似度是(HuggingFace 模型)
文档级(检索)DocumentRecallEvaluator/DocumentMRREvaluator/DocumentMAPEvaluator/DocumentNDCGEvaluator检索文档 vs 标准答案文档,按排序位置计算
LLM 型(通用)LLMEvaluator任意列表输入,按自定义指令打分是(ChatGenerator)
LLM 型(专用)ContextRelevanceEvaluator/FaithfulnessEvaluator上下文相关性 / 答案忠实度是(ChatGenerator)

从源码结构看,该模块通过init.py 中的_import_structure字典 +LazyImporter实现惰性导入:只有真正 import 某个评测器时才加载对应模块,避免了sentence-transformerstqdm等依赖在无关场景被强制加载。因此from haystack.components.evaluators import DocumentMAPEvaluatorfrom haystack.components.evaluators.document_map import DocumentMAPEvaluator两种写法均可。

2. 答案级评测器

2.1 AnswerExactMatchEvaluator:精确匹配基线

最轻量的评测器:检查每个预测答案是否逐一精确等于标准答案(源码 answer_exact_match.py)。

from haystack.components.evaluators import AnswerExactMatchEvaluator evaluator = AnswerExactMatchEvaluator() result = evaluator.run( ground_truth_answers=["Berlin", "Paris"], predicted_answers=["Berlin", "Lyon"], ) print(result["individual_scores"]) # [1, 0] print(result["score"]) # 0.5

run签名(输出类型@component.output_types(individual_scores=list[int], score=float)):

def run(ground_truth_answers: list[str], predicted_answers: list[str]) -> dict[str, Any]
  • 约束:ground_truth_answerspredicted_answers长度必须一致,否则抛ValueError
  • 返回individual_scores(0/1 列表,1 表示该预测命中标准答案之一)与score(0.0–1.0,命中比例)。

源码实现为逐元素zip(strict=True)比较后求平均(answer_exact_match.py),不做任何大小写/空白归一化——它是严格的"字符串相等",适合作为快速回归基线,而非语义质量指标。

2.2 SASEvaluator:语义答案相似度

SASEvaluator计算预测答案与标准答案之间的Semantic Answer Similarity(SAS),通常用于 RAG 管线中评估生成答案质量(源码 sas_evaluator.py)。它基于 HuggingFace 模型库的预训练模型,可为Bi-EncoderCross-Encoder,由model参数决定:

from haystack.components.evaluators.sas_evaluator import SASEvaluator evaluator = SASEvaluator(model="cross-encoder/ms-marco-MiniLM-L-6-v2") evaluator.warm_up() ground_truths = [ "A construction budget of US $2.3 billion", "The Eiffel Tower, completed in 1889, symbolizes Paris's cultural magnificence.", "The Meiji Restoration in 1868 transformed Japan into a modernized world power.", ] predictions = [ "A construction budget of US $2.3 billion", "The Eiffel Tower, completed in 1889, symbolizes Paris's cultural magnificence.", "The Meiji Restoration in 1868 transformed Japan into a modernized world power.", ] result = evaluator.run( ground_truth_answers=ground_truths, predicted_answers=predictions ) print(result["score"]) # 0.9999673763910929 print(result["individual_scores"]) # [0.9999765157699585, 0.999968409538269, 0.9999572038650513]

__init__参数

参数默认值说明
modelsentence-transformers/paraphrase-multilingual-mpnet-base-v2SentenceTransformers 语义相似度模型,可为 HF 模型名或本地路径
batch_size32一次编码的预测-标签对数量
deviceNone模型加载设备;None时自动选择默认设备
tokenSecret.from_env_var(["HF_API_TOKEN", "HF_TOKEN"], strict=False)HuggingFace Bearer 授权令牌

run签名

@component.output_types(score=float, individual_scores=list[float]) def run(ground_truth_answers: list[str], predicted_answers: list[str]) -> dict[str, float | list[float]]

两个列表长度必须相同;predicted_answers中不允许出现None;空输入直接返回{"score": 0.0, "individual_scores": [0.0]}

源码中的关键实现细节(sas_evaluator.py):

  • warm_up()通过AutoConfig.from_pretrained读取模型架构,若架构以ForSequenceClassification结尾则判定为 Cross-Encoder 并加载CrossEncoder,否则加载SentenceTransformer。Bi-Encoder 与 Cross-Encoder 的相似度计算方式不同:前者分别编码两侧后逐对计算余弦相似度,后者直接对句子对打分;
  • Cross-Encoder 返回的原始 logits 未必归一化,源码会在分数大于 1 时用expit(sigmoid)将其压回 0–1;
  • score是所有individual_scores的均值(np_mean)。

注意 SASEvaluator 依赖sentence-transformers>=5.0.0,仓库中通过LazyImport延迟导入,未安装时会提示Run 'pip install "sentence-transformers>=5.0.0"'(sas_evaluator.py)。

3. 文档级检索评测器

文档级评测器输入均为list[list[Document]]——外层列表对应每个问题,内层列表是该问题的标准答案文档(ground_truth_documents)或检索结果(retrieved_documents),两侧外层列表长度必须一致。所有指标输出统一为score(各问题得分均值)+individual_scores(每个问题 0.0–1.0 的分数)。

3.1 DocumentRecallEvaluator:召回率

RecallMode枚举定义两种打分模式:

  • SINGLE_HIT"single_hit",默认):只要检索命中任一标准答案文档即记 1,否则记 0;
  • MULTI_HIT"multi_hit"):按命中比例给分,分数为 0.0–1.0 之间的连续值。
from haystack import Document from haystack.components.evaluators import DocumentRecallEvaluator evaluator = DocumentRecallEvaluator() result = evaluator.run( ground_truth_documents=[ [Document(content="France")], [Document(content="9th century"), Document(content="9th")], ], retrieved_documents=[ [Document(content="France")], [Document(content="9th century"), Document(content="10th century"), Document(content="9th")], ], ) print(result["individual_scores"]) # [1.0, 1.0] print(result["score"]) # 1.0

__init__接受mode: Union[str, RecallMode] = RecallMode.SINGLE_HIT;传入字符串时经RecallMode.from_str转换,未知取值抛ValueError(document_recall.py)。

从源码结构看,当前仓库的实现有两个文档中未展开的深化点:

  1. 比较字段可配置__init__新增document_comparison_field参数(默认"content"),可取"content""id"meta.前缀的嵌套键(如"meta.file_id""meta.source.url"),由_get_comparison_value统一解析(document_recall.py);
  2. 按比较值去重计分_unique_comparison_values对两侧取值去重后做集合运算,multi_hit模式即|交集| / |truths|;任一侧无有效比较值时记录 warning 并计 0 分(document_recall.py)。

3.2 DocumentMRREvaluator:平均倒数排名

MRR 衡量第一个相关文档被排在多高的位置:对每个问题,找到第一个命中标准答案集合的检索位置 k,得分为1/k;未命中则 0。

from haystack import Document from haystack.components.evaluators import DocumentMRREvaluator evaluator = DocumentMRREvaluator() result = evaluator.run( ground_truth_documents=[ [Document(content="France")], [Document(content="9th century"), Document(content="9th")], ], retrieved_documents=[ [Document(content="France")], [Document(content="9th century"), Document(content="10th century"), Document(content="9th")], ], ) print(result["individual_scores"]) # [1.0, 1.0] print(result["score"]) # 1.0

与文档描述一致,DocumentMRREvaluator不对其输入做归一化——文档的空白、大小写差异会导致漏匹配。官方建议先用DocumentCleaner组件清洗归一化后再送入该评测器。

3.3 DocumentMAPEvaluator:平均精度均值

MAP 同时衡量"是否召回"和"排得多靠前":对每个问题,沿检索列表扫描,每命中一个新标准答案就在该位置累加已命中相关数 / 当前排名,最后除以相关文档总数得到该问题的 Average Precision(AP);score为各问题 AP 的均值。

from haystack import Document from haystack.components.evaluators import DocumentMAPEvaluator evaluator = DocumentMAPEvaluator() result = evaluator.run( ground_truth_documents=[ [Document(content="France")], [Document(content="9th century"), Document(content="9th")], ], retrieved_documents=[ [Document(content="France")], [Document(content="9th century"), Document(content="10th century"), Document(content="9th")], ], ) print(result["individual_scores"]) # [1.0, 0.8333333333333333] print(result["score"]) # 0.9166666666666666

以第二个问题验证:命中的排名为 1 和 3,AP = (1/1 + 2/3) / 2 = 0.8333。

源码(document_map.py)用一个"未记功的 ground truth 值列表"实现按值去重——同一相关文档重复检索只记一次功,防止 AP 被刷高;标准答案中比较值为None(如meta.键缺失)的文档不计入分母。该实现同样不对其输入做归一化,建议配合DocumentCleaner使用;且同样支持上文 3.1 所述的document_comparison_field配置(默认按content比较)。

3.4 DocumentNDCGEvaluator:归一化折损累积增益

NDCG 是四个文档指标中最精细的:它支持分级相关性——若标准答案文档带有score(相关性分数),计算直接使用这些分数;否则假定全部标准答案为二元相关(document_ndcg.py)。

from haystack import Document from haystack.components.evaluators import DocumentNDCGEvaluator evaluator = DocumentNDCGEvaluator() result = evaluator.run( ground_truth_documents=[[Document(content="France", score=1.0), Document(content="Paris", score=0.5)]], retrieved_documents=[[Document(content="France"), Document(content="Germany"), Document(content="Paris")]], ) print(result["individual_scores"]) # [0.8869] print(result["score"]) # 0.8869

run的三个静态/实例辅助方法与校验规则:

  • validate_inputs(gt_docs, ret_docs)(静态方法):任一输入为空、两侧外层长度不一致,或某个问题的标准答案列表中混用了带score与不带score的文档(要求"全无或全有"),均抛ValueError
  • calculate_dcg(gt_docs, ret_docs):沿检索列表累加相关性 / log2(位置 + 1)(源码用i + 2补偿 0 起始下标,document_ndcg.py)。命中的相关值被pop出映射表,保证同一相关值至多记一次功,重复检索无法使 DCG 超过 IDCG;
  • calculate_idcg(gt_docs):把标准答案的相关性按降序排列后累加同样的折损项。当idcg > 0时 NDCG = DCG / IDCG,否则记 0。

从源码结构看,_build_relevance_map会把比较值相同的多份标准答案折叠为"保留最高相关性"的单条,使 DCG 与 IDCG 对同一相关集计分一致(document_ndcg.py);同样支持document_comparison_fieldcontent/id/meta.<key>嵌套键)。

3.5 检索指标选型小结

  • 只关心"相关文档是否被召回" →DocumentRecallEvaluatormulti_hit看召回比例,single_hit看是否命中);
  • 只关心"最相关的那篇排多靠前" →DocumentMRREvaluator
  • 关心"所有相关文档整体排得多靠前" →DocumentMAPEvaluator
  • 有分级相关性标注、想兼顾相关度与位置 →DocumentNDCGEvaluator

4. LLM 型评测器

4.1 LLMEvaluator:通用 LLM 评审基类

LLMEvaluator用 LLM 依据"指令 + few-shot 示例"评测任意列表输入。默认 API 要求环境变量OPENAI_API_KEY;输出为字典,键results对应一组结果,每个结果是一个字典,值取 0(FALSE)或 1(TRUE)。

from haystack.components.evaluators import LLMEvaluator evaluator = LLMEvaluator( instructions="Is this answer problematic for children?", inputs=[("predicted_answers", list[str])], outputs=["score"], examples=[ {"inputs": {"predicted_answers": "Damn, this is straight outta hell!!!"}, "outputs": {"score": 1}}, {"inputs": {"predicted_answers": "Football is the most popular sport."}, "outputs": {"score": 0}}, ], ) predicted_answers = [ "Football is the most popular sport with around 4 billion followers worldwide", "Python language was created by Guido van Rossum.", ] results = evaluator.run(predicted_answers=predicted_answers) print(results) # {'results': [{'score': 0}, {'score': 0}]}

__init__参数

参数说明
instructions评测指令,应是能对输入用"是/否"式回答的问题
inputs输入声明列表,每项为(输入名, 列表类型)元组;类型必须是 list,这些名称同时成为组件的输入 socket
outputs评测结果字典的键名列表
examplesfew-shot 示例,每项为含"inputs""outputs"两个字典的字典
progress_bar评估时是否显示进度条(默认True
raise_on_failureAPI 调用失败时是否抛异常(默认True,keyword-only)
chat_generator自定义ChatGenerator实例;None时默认创建 JSON 模式的OpenAIChatGenerator

关于chat_generator的重要约束:LLM 必须被配置为返回 JSON 对象。例如使用OpenAIChatGenerator时应在generation_kwargs{"response_format": {"type": "json_object"}}。从源码看,默认生成器的generation_kwargs实际还固定了"seed": 42以保证评测可复现(llm_evaluator.py)。

run(**inputs)行为与返回

  • 校验:期望的输入名必须全部出现、所有输入必须是 list、各 list 长度一致,否则validate_input_parametersValueError
  • 逐条输入渲染 prompt → 调用 chat generator → 用_parse_dict_from_json解析并校验期望键。单条失败时,raise_on_failure=TrueValueError,否则该条结果记为None并累计错误、最终输出 warning 统计(llm_evaluator.py);
  • 返回{"results": [...]}。当 API 为 OpenAI 且响应带meta时,元数据(如 token 用量)随输出返回——当前仓库源码的@component.output_types已声明第二个输出meta(llm_evaluator.py),version 2.21 文档尚未体现这一点,实际仓库版本还新增了run_asyncwarm_up/close资源生命周期方法。

prepare_template生成的 prompt 模板格式(固定骨架):

Instructions: <instructions> Generate the response in JSON format with the following keys: <list of output keys> Consider the instructions and the examples below to determine those values. Examples: <examples> Inputs: <inputs> Outputs:

inputs段由输入名拼成{"name": {{ name }}}的 jinja 占位符,最终经PromptBuilder渲染(llm_evaluator.py)。

序列化to_dict/from_dict完整保存instructionsinputs(元组转为[name, 类型字符串]列表以便序列化)、outputsexampleschat_generator(经component_to_dict连同生成器一起序列化);反序列化时恢复元组结构并就地反序列化 chat generator(llm_evaluator.py)。这意味着LLMEvaluator可作为 Pipeline 节点随管线整体持久化。

4.2 ContextRelevanceEvaluator:上下文相关性

继承自LLMEvaluator(context_relevance.py)。它让 LLM 把上下文拆分为多条陈述,逐条判断是否与问题相关:每个上下文得二值分 1/0,同时输出被判定相关的原文陈述(relevant_statements),并给出全部问题的平均分。

from haystack.components.evaluators import ContextRelevanceEvaluator questions = ["Who created the Python language?", "Why does Java needs a JVM?", "Is C++ better than Python?"] contexts = [ [( "Python, created by Guido van Rossum in the late 1980s, is a high-level general-purpose programming " "language. Its design philosophy emphasizes code readability, and its language constructs aim to help " "programmers write clear, logical code for both small and large-scale software projects." )], [( "Java is a high-level, class-based, object-oriented programming language that is designed to have as few " "implementation dependencies as possible. The JVM has two primary functions: to allow Java programs to run" "on any device or operating system (known as the 'write once, run anywhere' principle), and to manage and" "optimize program memory." )], [( "C++ is a general-purpose programming language created by Bjarne Stroustrup as an extension of the C " "programming language." )], ] evaluator = ContextRelevanceEvaluator() result = evaluator.run(questions=questions, contexts=contexts) print(result["score"]) # 0.67 print(result["individual_scores"]) # [1,1,0] print(result["results"]) # [{ # 'relevant_statements': ['Python, created by Guido van Rossum in the late 1980s.'], # 'score': 1.0 # }, # { # 'relevant_statements': ['The JVM has two primary functions: to allow Java programs to run on any device or # operating system (known as the "write once, run anywhere" principle), and to manage and # optimize program memory'], # 'score': 1.0 # }, # { # 'relevant_statements': [], # 'score': 0.0 # }]

__init__参数

  • examples:可选 few-shot 示例。每项须为含"inputs""outputs"的字典;"inputs""questions""contexts"键,"outputs""relevant_statements"键。不传时使用内置默认示例——源码中的_DEFAULT_EXAMPLES共 3 条,覆盖"单条相关陈述"与"多条无关上下文得空列表"两种情形(context_relevance.py),示例格式如下:
[{ "inputs": { "questions": "What is the capital of Italy?", "contexts": ["Rome is the capital of Italy."], }, "outputs": { "relevant_statements": ["Rome is the capital of Italy."], }, }]
  • progress_bar(默认True):评估时显示进度条;
  • raise_on_failure(默认True):API 调用失败时抛异常;
  • chat_generator:自定义 LLM,同样必须配置为返回 JSON(OpenAIChatGenerator{"response_format": {"type": "json_object"}}generation_kwargs)。

run签名与输入/输出

@component.output_types(score=float, results=list[dict[str, Any]]) def run(**inputs) -> dict[str, Any]
  • 输入:questions(问题列表)、contexts(嵌套列表,每个内层列表对应一个问题的上下文);
  • 输出:score(所有问题上下文相关性的平均分)、results(每个上下文的relevant_statementsscore字典列表)。

辅助方法(继承自LLMEvaluator并被文档逐一列出):validate_init_parameters(静态方法,校验 inputs 为(str, list 类型)元组列表、outputs 为字符串列表、examples 为含inputs/outputs的字典列表)、validate_input_parameters(静态方法,校验期望输入齐全、输入均为 list 且等长)、is_valid_json_and_has_expected_keys(LLM 输出必须是含期望键的合法 JSON;raise_on_failure=True时违例抛ValueError,为False时发 warning 并返回False)。to_dict/from_dict提供组件级序列化。

从源码结构看,当前仓库版本中results的每个条目还附带status字段(如'status': 'evaluated',见 context_relevance.py 的 docstring 示例),version 2.21 文档未提及,以实际仓库版本行为为准。

4.3 FaithfulnessEvaluator:答案忠实度

评估生成答案能否从给定上下文中被推断出来:LLM 把答案拆成若干陈述,逐条判断是否可由上下文推出,单条得 0/1,答案总分 = 可推断陈述的比例(0.0–1.0),即衡量"无中生有"(幻觉)程度。

from haystack.components.evaluators import FaithfulnessEvaluator questions = ["Who created the Python language?"] contexts = [ [( "Python, created by Guido van Rossum in the late 1980s, is a high-level general-purpose programming " "language. Its design philosophy emphasizes code readability, and its language constructs aim to help " "programmers write clear, logical code for both small and large software projects." )], ] predicted_answers = [ "Python is a high-level general-purpose programming language that was created by George Lucas." ] evaluator = FaithfulnessEvaluator() result = evaluator.run(questions=questions, contexts=contexts, predicted_answers=predicted_answers) print(result["individual_scores"]) # [0.5] print(result["score"]) # 0.5 print(result["results"]) # [{'statements': ['Python is a high-level general-purpose programming language.', # 'Python was created by George Lucas.'], 'statement_scores': [1, 0], 'score': 0.5}]

__init__参数ContextRelevanceEvaluator完全同构(examples/progress_bar/raise_on_failure/chat_generator),区别在 few-shot 格式:"inputs"需含"questions""contexts""predicted_answers"三个键,"outputs""statements""statement_scores"两个键。官方示例格式:

[{ "inputs": { "questions": "What is the capital of Italy?", "contexts": ["Rome is the capital of Italy."], "predicted_answers": "Rome is the capital of Italy with more than 4 million inhabitants.", }, "outputs": { "statements": ["Rome is the capital of Italy.", "Rome has more than 4 million inhabitants."], "statement_scores": [1, 0], }, }]

run签名与输出

@component.output_types(individual_scores=list[int], score=float, results=list[dict[str, Any]]) def run(**inputs) -> dict[str, Any]
  • 输入:questionscontexts(与问题对应的嵌套上下文列表)、predicted_answers
  • 输出:score(所有答案忠实度均分)、individual_scores(每个答案的忠实度分)、results(每个答案的statementsstatement_scores)。

to_dict/from_dictvalidate_init_parametersvalidate_input_parametersis_valid_json_and_has_expected_keys的语义与ContextRelevanceEvaluator相同(同为LLMEvaluator子类的共享契约),此处不再赘述。

5. 在 Pipeline 中使用与序列化

Evaluators 是标准@component,其run的输出 socket 由@component.output_types声明(如文档级评测器的scoreindividual_scores,LLM 评测器的results),因此可以直接连接在管线中——典型做法是把检索评测器接在 Retriever 输出之后、把忠实度评测器接在 ChatGenerator 之后,用离线数据集逐条对账。

所有文档级/答案级评测器与 LLM 型评测器都实现to_dict/from_dict(LLM 型评测器还会把chat_generator一并序列化)。这保证评测节点可随 Pipeline 一起持久化、再部署。

6. 关键行为约束速查

综合文档与源码,以下约束在写评测脚本时必须注意:

  1. 等长契约:所有run都要求两侧外层列表等长,违反即抛ValueError(答案级与文档级评测器一致);
  2. NDCG 的 score 一致性:同一问题的标准答案文档必须"全部带score或全部不带",否则validate_inputs直接拒绝;
  3. 文档级评测器不做归一化:MAP/MRR/Recall/NDCG 均按比较值(默认doc.content)精确匹配,建议前置DocumentCleaner;当前仓库版本可通过document_comparison_field改为按idmeta键比较;
  4. LLM 型评测器的 JSON 契约:自定义chat_generator必须能输出 JSON 对象;解析失败的处理由raise_on_failure决定(抛异常或该条记None并 warning);
  5. 依赖与密钥:默认 LLM 路径需要OPENAI_API_KEY;SASEvaluator 需要sentence-transformers,私有模型需要HF_API_TOKEN/HF_TOKEN
  6. 失败隔离raise_on_failure=False时单条失败不会中断批量评测,适合大规模离线跑批(源码中失败条目计入errors并在日志中给出x / y失败比例统计,见 llm_evaluator.py)。

相关源码与测试可继续深入:各评测器实现位于 haystack/components/evaluators/,对应测试位于 test/components/evaluators/,本仓库版本 API 的完整文档位于 docs-website/reference_versioned_docs/version-2.21/haystack-api/evaluators_api.md。

【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询