谁来评判评判者?在 Elasticsearch Workflows 中使用 LLM-as-a-Judge

📅 2026/7/8 16:32:21 👁️ 阅读次数 📝 编程学习
谁来评判评判者?在 Elasticsearch Workflows 中使用 LLM-as-a-Judge

作者:来自 Elastic Jeffrey Rengifo

了解你的 RAG agent 是否已准备好投入生产。仅使用 Elasticsearch Workflows 和两个 Claude 模型,从正确性(correctness)、忠实性(faithfulness)和检索质量(retrieval quality)三个维度对其进行评分。

Agent Builder 现已正式发布。立即开始你的 Elastic Cloud Trial,并查看 Agent Builder 文档。


Elasticsearch Workflows内部构建完整的 RAG 评测流水线:无需外部评测框架,也无需额外基础设施。一个小模型基于你的知识库回答问题;一个更强的模型则从正确性(correctness)、忠实性(faithfulness)和上下文相关性(context relevance)三个维度对每个回答进行评分。

在一个包含 35 个 HotpotQA 测试用例的测试中,Claude Haiku 4.5 获得了 0.74 的正确性评分和 0.90 的忠实性评分,足以精确定位retrieval或推理过程究竟在哪一步出现问题。知识库、评判标准、workflow 执行过程以及评分结果全部保存在同一个系统中。

前提条件

  • 一个 Elastic Cloud 集群,或自托管的 Elasticsearch / Kibana 9.4+。如果还没有,可以免费试用。

  • Python 3.13

什么是 LLM-as-a-Judge?

LLM-as-a-Judge 使用一个更强大的语言模型,自动对较弱模型生成的结果进行评分,从而替代一致性较差且难以扩展的人工评审。

手工评估一个 RAG 系统对于少量问题来说是可行的,但无法扩展。如果你修改了检索策略、提示词(prompt)或模型,就需要重新检查所有答案。这不仅耗时,而且不同评审人员之间的评分标准也往往不一致。

LLM-as-a-Judge 模式通过使用一个更强大的语言模型来评估较弱模型的输出,从而解决这一问题。其核心思想非常简单:如果一个语言模型能够可靠地判断一个答案是否正确,那么你就可以在几分钟内自动完成数百个测试用例的质量评估。

在本文中,整个流水线包含两个阶段:

  • 阶段 1(回答):Claude Haiku 4.5 接收问题以及从知识库中检索出的相关内容(passages),生成回答。这是需要进行评测的模型。

  • 阶段 2(评判):Claude Sonnet 4.6 接收相同的问题、标准答案(ground-truth answer,即已知正确答案)、候选答案(阶段 1 的输出)以及检索到的 passages,并从三个维度对回答进行评分。

RAG 的三个评测指标:正确性(correctness)、忠实性(faithfulness)和上下文相关性(context relevance)

指标衡量内容低分意味着什么
correctness(正确性)候选答案是否与标准答案一致?agent 回答错误。
faithfulness(忠实性)回答是否基于检索到的 passages?agent 产生了幻觉(编造了内容)。
context_relevance(上下文相关性)检索到的 passages 是否与问题相关?retrieval 检索到了错误的文档。

这三个指标结合起来,能够告诉你问题出在哪里。如果correctness很高,而context_relevance很低,这就是一个危险信号:尽管检索效果不佳,模型仍然回答正确,很可能是依赖自身训练数据而不是检索结果完成了回答。对于像 Wikipedia 这样的公开知识,这种方式通常没有问题;但对于模型从未见过的私有数据,这种做法就会失效。

另一种互补的方法是结合 Elasticsearch 使用 Ragas 指标,从基于框架(framework-based)的角度探索这些评测维度。

如何加载并索引 HotpotQA 数据集

该流水线使用HotpotQAdistractor配置:多跳(multi-hop)问题,每个问题都配有 10 个上下文段落,并存储在两个 Elasticsearch 索引中。

你可以参考本文配套的完整 notebook,其中包含环境配置、连接以及所有辅助代码。

完成本教程需要以下信息:

  • ELASTICSEARCH_URL

  • ELASTICSEARCH_API_KEY(可按照连接信息指南获取)

  • KIBANA_URL(可在Kibana 访问页面找到)

该流水线使用在 Kibana 中配置好的两个 AI connectors:

  • Anthropic Claude Haiku 4.5:作为回答模型(体积小、成本低、速度快)。

  • Anthropic Claude Sonnet 4.6:作为评判模型(能力更强)。

整个流水线要回答的问题是:

Haiku 在这项任务上的表现是否足够好,可以用于生产环境?/Is Haiku good enough on this task to be used in production?

我们使用HotpotQAdistractor配置。每个问题都包含 10 个上下文段落:其中两个支持正确答案,另外八个是干扰项(distractors)。这些段落会被索引到知识库中,供第一阶段(Stage 1)进行 retrieval。

这些问题属于多跳(multi-hop)问题,也就是说,回答一个问题需要综合来自两个不同段落的信息。

我们会抽取部分问题作为样本,将其上下文段落展开后索引到知识库索引中,并将问题—答案对存储到单独的评判列表(judgement list)中。最终数据会存储到两个 Elasticsearch 索引中:

知识库:上下文段落

hotpot-knowledge-base保存回答模型进行 retrieval 时使用的上下文段落。每个document都包含一个title和一个passage

{"title": "Ed Wood (film)", "passage": "Ed Wood is a 1994 American biographical period comedy-drama film..."}

passage字段会复制到semantic_content(映射为 semantic_text),这样第一阶段(Stage 1)就可以使用自然语言query检索相关段落,而无需手动管理 embeddings。

Elasticsearch 会自动为已索引的段落生成 embeddings,并在执行 语义搜索(semantic search) 时自动为查询生成 embeddings。

INDEX_NAME = "hotpot-knowledge-base" if es_client.indices.exists(index=INDEX_NAME): es_client.indices.delete(index=INDEX_NAME) es_client.indices.create( index=INDEX_NAME, mappings={ "properties": { "title": {"type": "keyword"}, "passage": { "type": "text", "copy_to": "semantic_content", }, "semantic_content": { "type": "semantic_text", "inference_id": ".jina-embeddings-v5-text-small", }, } }, ) print(f"Created index: {INDEX_NAME}")

评判列表:问题与标准答案

hotpot-judgement-list保存评测用例。每个 document 都包含一个question和一个answer(标准答案,ground truth):

{"question": "Were Scott Derrickson and Ed Wood of the same nationality?", "answer": "yes"}

answer字段包含正确答案。第二阶段(Stage 2)的 judge 会将回答模型的输出与该答案进行比较。

Elasticsearch Workflow 如何运行评测

这篇使用 Elastic Workflows 构建自动化的介绍文章详细介绍了 trigger、step 和数据流的核心概念。

Workflow 定义

该 workflow 使用 YAML 定义,并通过 Workflows API 上传(Elastic 9.4+)。以下是完整定义:

WORKFLOW_YAML = """ name: agent_accuracy_eval description: > Batch evaluation. Loads the judgement list, iterates each case with a foreach, runs stage 1 (RAG answer with Haiku) and stage 2 (LLM judge with Sonnet), and indexes every score into the eval results index. enabled: true consts: kbIndex: hotpot-knowledge-base judgeIndex: hotpot-judgement-list resultsIndex: eval-results triggers: - type: manual steps: # Load the judgement list (all cases we want to evaluate). - name: load_cases type: elasticsearch.search with: index: "{{ consts.judgeIndex }}" query: match_all: {} size: 35 # One iteration = one evaluation. - name: eval_loop type: foreach foreach: "{{ steps.load_cases.output.hits.hits }}" steps: - name: retrieve type: elasticsearch.search with: index: "{{ consts.kbIndex }}" query: semantic: field: semantic_content query: "{{ foreach.item._source.question }}" size: 4 - name: agent_answer type: ai.prompt with: connector-id: Anthropic-Claude-Haiku-4-5 prompt: > You are a Wikipedia QA assistant. Answer the question using ONLY the passages provided. Keep the answer short (one line). If the passages do not contain the answer, reply "unknown". Question: {{ foreach.item._source.question }} Passages: 1. {{ steps.retrieve.output.hits.hits[0]._source.passage }} 2. {{ steps.retrieve.output.hits.hits[1]._source.passage }} 3. {{ steps.retrieve.output.hits.hits[2]._source.passage }} 4. {{ steps.retrieve.output.hits.hits[3]._source.passage }} - name: judge type: ai.prompt with: connector-id: Anthropic-Claude-Sonnet-4-6 prompt: > You are a STRICT evaluator. Score the candidate answer against the ground truth on three axes. Each score MUST be exactly one of these three values: 0.0, 0.5, or 1.0. Do not return any other number. correctness: 1.0 = candidate contains the ground truth answer exactly or an unambiguous synonym, and nothing factually wrong. 0.5 = partially correct (one side of a multi-hop right, or mostly right with minor noise). 0.0 = wrong, missing, or contradicts the ground truth. faithfulness: 1.0 = every factual claim is supported by the passages. 0.5 = mostly supported, one minor unsupported claim. 0.0 = contains at least one unsupported claim. context_relevance: 1.0 = the passages contain enough to answer the question. 0.5 = partial coverage (one hop covered, the other missing). 0.0 = passages do not cover the answer. Be harsh. If in doubt between two scores, pick the lower one. Question: {{ foreach.item._source.question }} Ground truth: {{ foreach.item._source.answer }} Candidate: {{ steps.agent_answer.output.content }} Passages: 1. {{ steps.retrieve.output.hits.hits[0]._source.passage }} 2. {{ steps.retrieve.output.hits.hits[1]._source.passage }} 3. {{ steps.retrieve.output.hits.hits[2]._source.passage }} 4. {{ steps.retrieve.output.hits.hits[3]._source.passage }} schema: type: object properties: correctness: type: number minimum: 0 maximum: 1 faithfulness: type: number minimum: 0 maximum: 1 context_relevance: type: number minimum: 0 maximum: 1 required: - correctness - faithfulness - context_relevance # Persist each scored case so the notebook can query it later. - name: save type: elasticsearch.index with: index: "{{ consts.resultsIndex }}" document: qid: "{{ foreach.item._source.qid }}" question: "{{ foreach.item._source.question }}" ground_truth: "{{ foreach.item._source.answer }}" candidate: "{{ steps.agent_answer.output.content }}" correctness: "{{ steps.judge.output.content.correctness }}" faithfulness: "{{ steps.judge.output.content.faithfulness }}" context_relevance: "{{ steps.judge.output.content.context_relevance }}" """

让我们逐步了解关键部分:

  • consts:用于定义索引名称的命名常量。这样可以保持 YAML 文件简洁,并且方便将同一个 pipeline 指向不同的数据集。

  • load_cases:一个elasticsearch.searchstep,用于从评判列表中获取所有测试用例。

  • eval_loop:一个foreachstep,用于遍历搜索结果。每次迭代都会运行以下四个嵌套 step:

    • retrieve:使用问题作为 query,在知识库上运行语义搜索,检索相关内容。

    • agent_answer:一个ai.promptstep,将问题以及检索到的前四个 passages 发送给 Haiku。connector-id字段引用 Kibana AI Connector。

    • judge:另一个ai.promptstep,这次调用 Sonnet。schema代码块强制模型返回一个具有固定字段的 JSON 对象。无需 markdown 代码块,无需 regex,也无需在读取时进行解析。minimummaximum约束确保评分保持在 0–1 范围内。

    • save:一个 elasticsearch.index step,用于将每个评分后的测试用例写入结果索引。由于 judge 输出已经通过schema解析为 JSON,因此可以直接引用各个字段:{{ steps.judge.output.content.correctness }}

上传并运行 workflow

Elastic 9.4 提供了用于 workflow 的 REST API。我们使用三个端点:

  • POST /api/workflows:根据 YAML 创建 workflow。

  • POST /api/workflows/{id}/run:启动一次执行。

  • GET /api/workflows/executions/{id}:轮询查询,直到执行完成。

对于 35 个测试用例,该 workflow 需要几分钟完成。每次迭代都会执行一次语义搜索、一次用于生成答案的大语言模型(LLM)调用、另一次用于评判的大语言模型调用,以及一次索引操作。

如何读取和解读 RAG 评测结果

每个评分后的测试用例已经作为一个 document 存储在eval-results索引中,并包含类型化的评分字段。无需进行解析:我们只需要查询该索引,并计算每个指标的平均值。

35 个测试用例的聚合 RAG 评测分数

下面的柱状图展示了 35 个测试用例中每个指标的平均分(使用 Haiku 作为回答模型):

在这次使用 Claude Haiku 4.5 作为回答模型的评测中,结果如下:

  • correctness:0.74:大约有 70% 的问题回答正确。对于多跳 Wikipedia 问题来说,这是一个合理的表现,但仍有提升空间。

  • faithfulness:0.90:当 Haiku 能够回答时,它能够保持基于 passages 生成答案。幻觉并不是这里的主要问题。

  • context_relevance:0.86:大多数情况下,语义检索能够找到相关 passages,但一些多跳问题需要的 passages 并没有出现在前四个检索结果中。

faithfulness(0.90)与 correctness(0.74)之间的差距说明,模型并不是在编造内容,而是在较难的问题上没有找到正确答案。这是 retrieval 或推理问题,而不是幻觉问题。增加检索 passages 的数量,或者针对最困难的问题切换到更强的模型,都是可行的改进方向。

RAG 评测结果对生产环境的意义

我们构建了一个完全运行在 Elasticsearch 内部的完整评测流水线。知识库、评判列表、workflow 执行过程以及评分结果全部存储在同一个系统中。

workflow YAML 可以进行版本控制,可以重复执行,并且可以通过 API 触发。如果你更新了知识库或更换了回答模型,只需要重新运行相同的 pipeline,然后将新的评分结果与旧结果进行比较。

对于这个特定数据集,结果表明 Haiku 具有较高的忠实性,但在多跳问题上并不总能回答正确。是否足够满足你的需求,取决于你的具体使用场景以及你自己的数据。评测 pipeline 的价值在于提供数据,让你能够基于可靠依据做出决策,而不是凭感觉判断。

下一步

接下来,你可以:

  • 扩展评判列表:添加符合实际生产流量的领域专属问题。

  • 替换回答模型:比较不同模型在同一测试集上的表现。

  • 调度 workflow:在新文档被索引后自动运行。

  • 构建 Kibana 仪表盘:基于eval-results索引跟踪质量随时间的变化。

相关文章:

  • 如何使用 Elasticsearch 构建 AI Agentic Workflows

  • Agent Builder 与 Workflows:创建能够执行操作的 AI agents

  • 使用 DeepEva 进行 RAG retrieval 评估与优化

这篇内容对你有多大帮助?

原文:LLM-as-a-Judge in Elasticsearch Workflows: RAG evaluation - Elasticsearch Labs