Skip to main content

evals-write-spec

Write LLM evaluation spec files with datasets, tasks, and evaluators using the @kbn/evals Playwright fixture. Use when authoring new eval specs, adding datasets or evaluators, or debugging evaluation test failures.

Datos de origen

Repositorio
elastic/kibana
Última actividad en el origen
3 de agosto de 2026 a las 17:06
Idioma detectado de SKILL.md
inglés
Estrellas
21.236
Forks
8623

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
3 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
evals-write-spec
disable-model-invocation
true
description
Write LLM evaluation spec files with datasets, tasks, and evaluators using the @kbn/evals Playwright fixture. Use when authoring new eval specs, adding datasets or evaluators, or debugging evaluation test failures.
# Write Eval Specs ## Spec File Anatomy Eval specs use the `evaluate` Playwright fixture (not `test`). A spec file follows this structure: ```ts import { evaluate, tags, selectEvaluators, type Example, type TaskOutput } from '@kbn/evals'; evaluate.describe('Suite name', { tag: tags.serverless.observability.complete }, () => { evaluate.beforeAll(async ({ fetch, log }) => { // one-time setup: install docs, create agents, load archives }); evaluate.afterAll(async ({ fetch, log }) => { // teardown: uninstall docs, delete agents, unload archives }); evaluate('test name', async ({ executorClient, connector }) => { await executorClient.runExperiment( { datasets: [dataset], task }, evaluators ); }); }); ``` When a suite has a custom `src/evaluate.ts`, import from there instead of `@kbn/evals`: ```ts import { evaluate } from '../src/evaluate'; ``` ## Tags Every `evaluate.describe` must have a tag. Common choices: | Tag | When to use | |-----|-------------| | `tags.serverless.observability.complete` | Observability domain evals | | `tags.serverless.security.complete` | Security domain evals | | `tags.serverless.search` | Search domain evals | | `tags.stateful.classic` | Stateful-only evals | Import tags from `@kbn/scout` or `@kbn/evals` (re-exported). ## Datasets A dataset is an array of examples with typed `input`, `output` (expected), and optional `metadata`: ```ts type MyExample = Example< { question: string }, { expectedAnswer: string }, { tags?: string[] } >; const dataset = { name: 'my-dataset', description: 'What this dataset tests', examples: [ { input: { question: 'What is 2+2?' }, output: { expectedAnswer: '4' }, metadata: { tags: ['math'] }, }, ], }; ``` Keep datasets focused. For local iteration, use `--grep` to run a subset: ```bash node scripts/evals start --grep "my test name" ``` ## Tasks The `task` function receives an example and returns the output to evaluate: ```ts task: async ({ input }) => { const result = await someKibanaApi(input.question); return { answer: result.content }; } ``` Tasks can use any fixture available in the `evaluate` callback: `fetch`, `inferenceClient`, `connector`, `esClient`, `kbnClient`, or custom fixtures like `chatClient`. ## Evaluators There are two ways to provide evaluators to `runExperiment`: 1. **Inline array** -- pass evaluator objects directly (simple suites) 2. **`selectEvaluators`** -- typed wrapper that enforces `Example`/`TaskOutput` generics ### CODE Evaluators Deterministic, no LLM call. Use for binary checks: ```ts { name: 'NonEmpty', kind: 'CODE', evaluate: async ({ output }) => ({ score: output?.documents?.length > 0 ? 1 : 0, }), } ``` ### LLM-as-Judge Criteria Use `evaluators.criteria(criteriaArray)` for subjective quality checks. The judge LLM scores each criterion: ```ts evaluators.criteria([ 'The response correctly identifies the top users.', 'The response includes risk scores.', ]).evaluate({ input, output, expected, metadata }) ``` ### Correctness Analysis Compares output against expected answer: ```ts evaluators.correctnessAnalysis().evaluate({ input, output, expected, metadata }) ``` ### Groundedness Analysis Checks if output is grounded in provided context: ```ts evaluators.groundednessAnalysis().evaluate({ input, output, expected, metadata }) ``` ### Trace-Based Evaluators Available from `evaluators.traceBasedEvaluators`: - `inputTokens`, `outputTokens`, `cachedTokens` -- token usage - `toolCalls` -- number of tool calls - `latency` -- span latency in seconds These read from the tracing ES cluster and require EDOT to be running. ### RAG Evaluators For retrieval-augmented generation with ground truth: ```ts import { createPrecisionAtKEvaluator, createRecallAtKEvaluator, createF1AtKEvaluator } from '@kbn/evals'; ``` See [evaluator-patterns.md](references/evaluator-patterns.md) for full examples. ## Available Fixtures | Fixture | Scope | Description | |---------|-------|-------------| | `executorClient` | worker | Runs experiments, exports scores to ES | | `inferenceClient` | worker | Inference REST client bound to connector | | `connector` | worker | The model connector being evaluated | | `evaluationConnector` | worker | The judge connector | | `evaluators` | worker | `DefaultEvaluators` (criteria, correctness, groundedness, trace-based) | | `fetch` | worker | `HttpHandler` for Kibana API calls | | `esClient` | worker | Elasticsearch client (Scout cluster) | | `kbnClient` | worker | Kibana client with retries | | `traceEsClient` | worker | ES client for trace queries | | `evaluationsEsClient` | worker | ES client for evaluation score storage | | `log` | worker | `ToolingLog` for structured logging | | `repetitions` | worker | Number of experiment repetitions | | `config` | worker | Scout server config (hosts, auth) | ## The `evaluateDataset` Pattern For suites with many specs that share the same task + evaluator wiring, extract a reusable helper: **`src/evaluate_dataset.ts`:** ```ts import type { DefaultEvaluators, EvalsExecutorClient } from '@kbn/evals'; import type { MyChatClient } from './chat_client'; export type EvaluateDataset = (opts: { dataset: { name: string; description: string; examples: MyExample[] }; }) => Promise<void>; export function createEvaluateDataset({ chatClient, evaluators, executorClient, }: { chatClient: MyChatClient; evaluators: DefaultEvaluators; executorClient: EvalsExecutorClient; }): EvaluateDataset { return async ({ dataset }) => { await executorClient.runExperiment( { datasets: [dataset], task: async ({ input }) => { const response = await chatClient.converse({ messages: [{ message: input.question }] }); return { messages: response.messages, steps: response.steps }; }, }, [myCriteriaEvaluator, myToolCallsEvaluator] ); }; } ``` **In the spec:** ```ts import { evaluate as base } from '../src/evaluate'; import type { EvaluateDataset } from '../src/evaluate_dataset'; import { createEvaluateDataset } from '../src/evaluate_dataset'; const evaluate = base.extend<{ evaluateDataset: EvaluateDataset }, {}>({ evaluateDataset: [ ({ chatClient, evaluators, executorClient }, use) => { use(createEvaluateDataset({ chatClient, evaluators, executorClient })); }, { scope: 'test' }, ], }); evaluate.describe('My suite', { tag: tags.serverless.search }, () => { evaluate('my test', async ({ evaluateDataset }) => { await evaluateDataset({ dataset: { name: '...', description: '...', examples: [...] } }); }); }); ``` ## Setup and Teardown Use `evaluate.beforeAll` / `evaluate.afterAll` for expensive one-time operations: - **Install product docs**: POST to `/internal/product_doc_base/install` - **Create agents/rules**: Use `fetch` or `kbnClient` - **Load ES archives**: Use `esArchiver.load(archivePath)` (requires custom fixture) Always clean up in `afterAll` -- delete agents, uninstall docs, unload archives. ## Running Locally ```bash # Full interactive flow node scripts/evals start # Specify model and judge node scripts/evals start --model <connector-id> --judge <connector-id> # Filter to a specific test node scripts/evals start --grep "my test name" # Run directly (services already running) node scripts/evals run --model <connector-id> --judge <connector-id> ``` ## Common Mistakes - Forgetting the `tag` on `evaluate.describe` -- Scout validates tags at runtime. - Missing `afterAll` cleanup -- leftover agents/docs pollute subsequent runs. - Overly large datasets for local iteration -- use `--grep` to target a single `evaluate()` block. - Importing `evaluate` from `@kbn/evals` when the suite has a custom `src/evaluate.ts` -- you'll miss custom fixtures. - Using `test` instead of `evaluate` -- the `evaluate` fixture provides all the evals-specific wiring. ## References - Evaluator type examples with real code: [references/evaluator-patterns.md](references/evaluator-patterns.md) - Suite scaffolding: use the `evals-create-suite` skill
Ver en GitHub