news 2026/9/17 20:25:06

Kibana Evals 评估器模式详解:从 CODE 断言到 LLM-as-Judge 与 RAG 评估的完整实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kibana Evals 评估器模式详解:从 CODE 断言到 LLM-as-Judge 与 RAG 评估的完整实践

Kibana Evals 评估器模式详解:从 CODE 断言到 LLM-as-Judge 与 RAG 评估的完整实践

【免费下载链接】kibanaYour window into all of your data项目地址: https://gitcode.com/GitHub_Trending/ki/kibana

Kibana 仓库内置了一套基于@kbn/evals包的 LLM 评估(Evals)体系,用于对 Agent、RAG 检索、工具调用链等 AI 功能进行系统化打分。本文以技能参考文档 evaluator-patterns.md 为主体,完整讲解其中五种评估器模式——CODE 确定性评估器、LLM-as-Judge 准则评估器、工具调用评估器、RAG 检索评估器与基于 Trace 的评估器,并结合仓库中真实存在的kbn-evals-suite-workflows评估套件源码,说明这些模式在 Kibana 项目内的落地方式,帮助你在编写新的 eval spec 时正确选择与组合评估器。

评估体系背景:runExperiment 与两种评估器入口

在 Kibana 的 eval 规范文件中,评估器不是孤立存在的,而是与 dataset(数据集)和 task(任务函数)一起,通过executorClient.runExperiment提交给执行器。评估器的职责是:拿到每个 example 的input(输入)、output(任务实际输出)、expected(期望输出)和metadata(示例级元数据),返回一个带score(0/1 或数值分)、可选label(PASS/FAIL)和explanation(解释文本)的结果。

runExperiment接受评估器的入口有两种(见配套文档 SKILL.md 的 “Evaluators” 一节):

  1. 内联数组—— 直接传入评估器对象数组,适合简单的套件;
  2. selectEvaluators—— 类型化的包装器,强制Example/TaskOutput泛型,为inputoutputexpectedmetadata提供完整的 TypeScript 类型安全。

下文各节即参考文档中给出的各类型评估器的扩展示例,均来自真实 eval 套件。

CODE 评估器:用 selectEvaluators 获得类型安全的确定性检查

kind: 'CODE'的评估器不调用 LLM,因此速度快、结果确定、成本为零,适合二值断言(“有没有返回文档”“URL 是否合法”“是否包含必备词”等)。参考文档从llm-tasks套件提取的完整示例如下:

import { selectEvaluators, type Example, type TaskOutput } from '@kbn/evals'; type MyExample = Example & { input: { searchTerm: string; products?: string[] }; metadata?: { minDocs?: number; requiredTerms?: string[] }; }; type MyTaskOutput = TaskOutput & { success: boolean; documents: Array<{ title: string; url: string; content: string }>; }; await executorClient.runExperiment( { datasets: [dataset], task }, selectEvaluators<MyExample, MyTaskOutput>([ { name: 'NonEmptyDocuments', kind: 'CODE', evaluate: async ({ output, metadata }) => { const minDocs = typeof metadata?.minDocs === 'number' ? metadata.minDocs : 1; const count = output?.documents?.length ?? 0; return { score: count >= minDocs ? 1 : 0, metadata: { minDocs, count } }; }, }, { name: 'RequiredTermsInContent', kind: 'CODE', evaluate: async ({ output, metadata }) => { const requiredTerms = metadata?.requiredTerms ?? []; if (requiredTerms.length === 0) return { score: 1 }; const text = (output?.documents ?? []) .slice(0, 3) .map((d) => `${d.title}\n${d.content}`) .join('\n'); const ok = requiredTerms.every((term) => text.toLowerCase().includes(term.toLowerCase()) ); return { score: ok ? 1 : 0, metadata: { requiredTerms } }; }, }, { name: 'HasValidUrl', kind: 'CODE', evaluate: async ({ output }) => { const urls = (output?.documents ?? []).map((d) => d.url); const ok = urls.some((u) => typeof u === 'string' && u.startsWith('https://')); return { score: ok ? 1 : 0, metadata: { urls: urls.slice(0, 3) } }; }, }, ]) );

这个示例体现了参考文档总结的三条要点:

  • selectEvaluators<ExampleType, OutputType>([...])提供类型安全。通过把ExampleTaskOutput两个泛型参数化,evaluate回调中的output.documentsmetadata.minDocs等字段访问都受 TypeScript 约束,字段拼错或类型不符会在编译期暴露;
  • kind: 'CODE'意味着无 LLM 调用,快速且确定,适合放在每个 example 上高频运行;
  • 在返回结果里带上metadata有助于调试。如示例中的{ minDocs, count }{ urls: urls.slice(0, 3) },当某个 example 得分 0 时,报告里可以直接看到当时的阈值与命中数,而不必重放任务。

编写 CODE 评估器时值得注意的防御式写法:metadata?.minDocstypeof === 'number'校验后再回退默认值 1;output?.documents?.length ?? 0处理任务输出缺失的情况;requiredTerms.length === 0时直接给 1 分——“没有断言就不扣分”,这与后文 LLM 评估器中 “No criteria specified” 直接 PASS 的约定是一致的。

LLM-as-Judge 准则评估器:evaluators.criteria

对于无法用确定代码判定的主观质量(“回答是否正确识别了风险最高的用户”),参考文档给出的是 LLM-as-Judge 模式:通过evaluators.criteria([...])把一组自然语言准则交给 judge 模型逐条打分。从security-solution-evals套件提取的用法:

const mainCriteriaResult = await evaluators .criteria([ 'The response correctly identifies the top users.', 'The response includes risk scores for each user.', 'The response includes risk levels for each user.', ]) .evaluate({ input, output, expected, metadata });

关键语义:每条 criterion 独立评估,judge 对每条准则返回 0 或 1 的分数及解释。准则应当写成可验证的陈述句(“包含了 X”“按 Y 排序”),而不是模糊形容词,否则 judge 的打分方差会变大。

把 criteria 包装成可复用评估器

参考文档进一步示范了把criteria封装为具名评估器的惯用法——准则本身存放在数据集的expected.criteria中,由评估器在运行时取出:

function createCriteriaEvaluator({ evaluators }: { evaluators: DefaultEvaluators }) { return { name: 'Criteria', kind: 'LLM' as const, evaluate: async ({ input, output, expected, metadata }: { input: MyExample['input']; output: MyTaskOutput; expected: MyExample['output']; metadata: MyExample['metadata']; }) => { const criteria = expected.criteria ?? []; if (criteria.length === 0) { return { score: 1, label: 'PASS', explanation: 'No criteria specified.' }; } return evaluators.criteria(criteria).evaluate({ input, expected, output, metadata }); }, }; }

这种工厂模式(createXxxEvaluator({ evaluators })返回{ name, kind, evaluate }对象)的意义在于:

  • 评估器获得namekind: 'LLM'标记,最终报告中可按名称聚合得分;
  • 判定逻辑与准则数据分离——准则写在数据集的expected里,同一评估器可服务整个数据集的不同 example;
  • 空准则短路返回 PASS,避免对无期望值的示例浪费 judge 调用。

工具调用评估器:验证 Agent 的轨迹而不仅是回答

对 Agent 类功能,光看最终文本不够,还要验证它是否调用了正确的工具。参考文档从security-solution-evals提取的createToolCallsEvaluator实现了两层断言:

function createToolCallsEvaluator({ evaluators }: { evaluators: DefaultEvaluators }) { return { name: 'ToolCalls', kind: 'LLM' as const, evaluate: async ({ input, output, expected, metadata }) => { const toolCalls = expected.toolCalls ?? []; const steps = output.steps ?? []; if (toolCalls.length === 0) { return { score: 1, label: 'PASS', explanation: 'No tool call assertions.' }; } const results = []; for (const assertion of toolCalls) { const called = steps.some( (s) => s.type === 'tool_call' && s.tool_id === assertion.id ); if (!called) { results.push({ score: 0, label: 'FAIL', explanation: `Tool "${assertion.id}" was not called.`, }); continue; } if (assertion.criteria?.length) { const criteriaResult = await evaluators .criteria(assertion.criteria) .evaluate({ input, expected: { criteria: assertion.criteria }, output, metadata }); results.push(criteriaResult); } else { results.push({ score: 1, label: 'PASS', explanation: `Tool "${assertion.id}" called.` }); } } const allPassed = results.every((r) => r.label === 'PASS'); const scores = results.map((r) => r.score ?? 0); const avg = scores.reduce((a, b) => a + b, 0) / scores.length; return { score: allPassed ? avg : 0, label: allPassed ? 'PASS' : 'FAIL', explanation: results.map((r) => r.explanation).join(' '), }; }, }; }

实现逻辑值得逐点理解:

  1. 轨迹级检查(CODE 部分):从任务输出的steps里筛选type === 'tool_call'tool_id匹配断言的步骤,确认工具确实被调用过;
  2. 质量级检查(LLM 部分):若断言附带criteria(如“ES|QL 查询应按 risk score 降序”),则再委托给evaluators.criteria让 judge 评估工具调用的参数或结果是否符合预期;
  3. 聚合规则:所有断言全部 PASS 时得分取各断言分的平均值,任何一条 FAIL 则整体 0 分——这是一种“一票否决”的严格聚合;
  4. 无断言短路toolCalls为空时直接 PASS,保持与 criteria 评估器一致的约定。

配套的数据集写法把断言声明在output(expected)中:

{ input: { question: 'Which users have the highest risk scores?' }, output: { criteria: [ 'Return 10 users with the highest risk scores.', 'Return the risk levels of those users.', ], toolCalls: [ { id: 'security.entity_analytics.risk_score', criteria: ['The ES|QL query should sort by risk score descending.'], }, ], }, }

可以看到,一条 example 同时驱动了主答案准则(criteria)与轨迹断言(toolCalls),两者各自独立评估、互不干扰。

RAG 评估器:Precision@K / Recall@K / F1@K 与 ground truth

针对“检索质量”这一 RAG 场景,@kbn/evals提供了三个指标评估器及一个工厂:

import { createPrecisionAtKEvaluator, createRecallAtKEvaluator, createF1AtKEvaluator, createRagEvaluators, } from '@kbn/evals'; import type { GroundTruth, RetrievedDoc } from '@kbn/evals';

createRagEvaluators工厂一次性创建三者,核心是把“任务原始输出”与“数据集中的 ground truth”解耦为两个抽取函数:

const ragEvals = createRagEvaluators({ k: 5, extractRetrievedDocs: (output) => output.documents.map((d) => ({ id: d.id, content: d.content })), extractGroundTruth: (expected) => expected.groundTruth, });
  • k: 5表示只考察检索结果的前 5 条;
  • extractRetrievedDocs负责从任务输出中抽出{ id, content }形式的检索文档列表;
  • extractGroundTruth负责从expected中取出 ground truth 映射。

数据集中的 ground truth 采用“文档 ID → 相关性分数”的映射形式:

{ input: { question: 'How do I set up payments?' }, output: { expected: 'You can start accepting payments using Wix Payments...', groundTruth: { knowledge_base: { 'doc_hash_abc123': 1, 'doc_hash_def456': 1, }, }, }, }

即每个 example 预先声明“哪些文档对本 query 是相关的(值为 1)”,评估器据此计算前 K 条检索结果命中的精度、召回与 F1。这套设计与经典 IR 评估一致,属于确定性的 CODE 型评估,不依赖 judge 模型。

Trace 评估器:从追踪集群读取 token 与延迟指标

参考文档的第六节介绍了evaluators.traceBasedEvaluators中预置的五个数值型评估器:

const { inputTokens, outputTokens, cachedTokens, toolCalls, latency } = evaluators.traceBasedEvaluators;

它们按当前 example 的 trace ID 到 tracing ES 集群查询 span 并提取指标:

评估器含义数据来源
inputTokens输入 token 数gen_ai.usage.*span 属性
outputTokens输出 token 数gen_ai.usage.*span 属性
cachedTokens缓存命中 token 数gen_ai.usage.*span 属性
toolCalls工具调用 span 数量tool call spans
latency总 span 时长(秒)span duration

运行前提:EDOT(Elastic Developer Observability Testing,可观测性追踪链路)必须在运行中,且TRACING_ES_URL指向一个实际接收 traces 的 ES 实例。若不满足该前提,这类评估器拿不到数据,本地迭代时应以 CODE 评估器为主。

组合多种评估器类型

真实的 eval 套件通常同时提交 CODE 与 LLM 两类评估器,由执行器对每个 example 逐个运行并在最终报告中聚合分数。参考文档给出的组合示例:

await executorClient.runExperiment( { datasets: [dataset], task }, [ createCriteriaEvaluator({ evaluators }), createToolCallsEvaluator({ evaluators }), { name: 'HasResponse', kind: 'CODE', evaluate: async ({ output }) => ({ score: output?.messages?.length > 0 ? 1 : 0, }), }, ] );

该组合体现了参考文档推荐的分工策略:用低成本的 CODE 评估器(HasResponse)做基础健康检查,用 LLM 评估器(CriteriaToolCalls)做质量判断,二者互不替代——LLM judge 不应承担“有没有输出”这种确定性问题,CODE 断言也不应试图判断语义质量。

仓库实证:kbn-evals-suite-workflows 套件如何应用这些模式

参考文档中的模式并非孤立示例,仓库中的kbn-evals-suite-workflows评估套件展示了同样的落地方式。

在 workflow_creation.spec.ts 中,spec 文件从@kbn/evals导入selectEvaluatorsEvaluationDataset类型,通过自定义的evaluatefixture 扩展出evaluateCreateDataset,在 fixture 内部调用executorClient.runExperiment({ datasets: [dataset], task }, ...)提交实验——与参考文档 “Combining Multiple Evaluator Types” 一节的骨架完全一致。该套件的 task 会调用chatClient.converse取得messagesstepserrors,再从中提取工作流 YAML 作为任务输出,供评估器断言。

同目录的 evaluators.ts 则展示了评估器工厂在实际套件中的工程化扩展,比参考文档更进一步:

  • 它从@kbn/evals导入DefaultEvaluatorsEvaluatorExample以及getToolCallStepscreateTrajectoryEvaluator,与参考文档中的evaluators.criteriaselectEvaluators出自同一包;
  • 定义了skipInfraErrors高阶包装器:当任务输出中捕获到超时、ECONNREFUSED、5xx 等基础设施错误特征(见INFRA_ERROR_PATTERN)时,让被包装的评估器返回score: null, label: 'N/A',把“环境问题”与“模型质量问题”区分开,避免环境抖动拉低均分(源码注释明确说明这是 “not a model quality issue”);
  • 定义了skipNegativeCases:对metadata.category === 'negative'的反向用例(模型应当拒绝的请求)返回 N/A,避免无意义的负样本污染指标;
  • 提供了createCriteriaEvaluatorcreateValidationPassEvaluatorcreateToolTrajectoryEvaluatorcreateEfficiencyEvaluator等一组工厂,与参考文档 “Wrapping Criteria in a Reusable Evaluator” 的工厂模式一脉相承。

该套件的 spec 中还能看到这些包装器被组合使用,例如skipInfraErrors(skipNegativeCases(evaluator))这样的洋葱式封装——这正是参考文档模式在真实套件中被复用的直接证据。

运行与本地迭代

结合 SKILL.md 的 “Running Locally” 一节,编写好包含上述评估器的 spec 后可用scripts/evals入口脚本运行:

# 完整交互流程 node scripts/evals start # 指定被测模型与 judge 模型 node scripts/evals start --model <connector-id> --judge <connector-id> # 只跑匹配的单个测试(本地迭代推荐) node scripts/evals start --grep "my test name" # 服务已在运行时直接执行 node scripts/evals run --model <connector-id> --judge <connector-id>

脚本入口位于 scripts/evals.js。其中--judge参数对应 LLM-as-Judge 评估器所使用的 judge 连接器——使用evaluators.criteria的套件需要单独指定评分模型;trace 评估器则要求 EDOT 与TRACING_ES_URL就绪。

小结:如何选择评估器

场景推荐模式类型代价
输出结构断言(非空、URL 合法、含必备词)CODE 评估器 +selectEvaluatorsCODE
语义质量(回答是否覆盖要点)evaluators.criteria或包装成可复用评估器LLM中(每次 judge 调用)
Agent 轨迹(是否调用了指定工具/参数正确)createToolCallsEvaluator模式:steps 检查 + criteria 复核CODE + LLM
检索质量(有 ground truth 文档)createRagEvaluators(Precision/Recall/F1@K)CODE
Token 消耗、工具调用数、端到端延迟evaluators.traceBasedEvaluatorsCODE(依赖 EDOT)

参考文档给出的核心方法论可以概括为:能用确定代码判定的绝不动用 LLM judge;用 LLM judge 时把准则写成逐条独立、可验证的陈述句;通过selectEvaluators<ExampleType, OutputType>让类型系统在编译期兜底;并在评估器返回值中携带metadata/explanation,让每一次 0 分都能追溯到具体证据。掌握了这五点,即可在 Kibana 的 evals 体系中编写可维护、可调试的评估套件。

【免费下载链接】kibanaYour window into all of your data项目地址: https://gitcode.com/GitHub_Trending/ki/kibana

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 20:14:52

非线性边值问题求解:打靶法原理与Matlab实战实现

简介&#xff1a;本资源是一份面向数学建模、计算数学及工程数值分析学习者的实用技术文档&#xff0c;聚焦二阶非线性常微分方程边值问题的Matlab数值求解&#xff0c;特别适合高年级本科生与研究生开展课程设计、科研入门或算法复现。文档系统阐述打靶法原理——将y″f(x,y,y…

作者头像 李华