windlass.interfaces.evaluator¶
evaluator
¶
The evaluation interface.
Evaluation answers "is this system actually any good?", and it is the part teams skip until something breaks in production. Windlass makes it a first-class component so a regression suite is a few lines rather than a project.
The unit of work is a :class:EvalSample — a question, the system's answer, the
contexts it retrieved, and optionally a reference answer. Metrics score samples;
evaluators run metrics over a dataset and aggregate.
Implementers override one coroutine, :meth:Evaluator.aevaluate_sample.
Example
from windlass.providers.evaluation.builtin import BuiltinEvaluator from windlass.interfaces.evaluator import EvalSample ev = BuiltinEvaluator(metrics=["exact_match"]) sample = EvalSample(question="2+2?", answer="4", reference="4") ev.evaluate([sample]).summary["exact_match"] 1.0
EvalSample
¶
Bases: WindlassModel
One evaluated interaction.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Sample identifier, used to correlate results. |
question |
str
|
The user's input. |
answer |
str
|
What the system produced. |
contexts |
list[str]
|
Retrieved texts that were placed in the prompt. Required by faithfulness and context-precision metrics. |
reference |
str
|
The ground-truth answer, when you have one. |
reference_contexts |
list[str]
|
The ideal contexts, for retrieval recall metrics. |
metadata |
dict[str, Any]
|
Anything else worth slicing results by (tenant, model, ...). |
Example
EvalSample(question="q", answer="a").id != "" True
from_answer
classmethod
¶
Build a sample straight from a RAG answer.
This is the ergonomic path: run your pipeline, feed the answers to the evaluator, done.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
answer
|
RAGAnswer
|
What :meth: |
required |
reference
|
str
|
Ground truth, when available. |
''
|
Returns:
| Type | Description |
|---|---|
EvalSample
|
A populated sample. |
Example
from windlass.core.types import RAGAnswer EvalSample.from_answer(RAGAnswer(answer="a", question="q")).question 'q'
Source code in src\windlass\interfaces\evaluator.py
Evaluator
¶
Evaluator(
*,
metrics: Sequence[str] | None = None,
threshold: float = 0.5,
llm: Any = None,
concurrency: int | None = None,
name: str | None = None,
**config: Any
)
Bases: Component
Abstract evaluation backend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metrics
|
Sequence[str] | None
|
Metric names to compute. Which names are valid depends on the
backend; ask it with :meth: |
None
|
threshold
|
float
|
Score at or above which a result counts as passing. |
0.5
|
llm
|
Any
|
Judge model for metrics that need one. Many quality metrics are themselves LLM calls. |
None
|
concurrency
|
int | None
|
Maximum simultaneous sample evaluations. |
None
|
name
|
str | None
|
Component name for traces. |
None
|
**config
|
Any
|
Backend-specific options. |
{}
|
Attributes:
| Name | Type | Description |
|---|---|---|
metrics |
list[str]
|
The configured metric names. |
threshold |
The configured pass threshold. |
Example
Implementing an evaluator takes one method::
class LengthEvaluator(Evaluator):
provider_name = "length"
async def aevaluate_sample(self, sample):
score = min(1.0, len(sample.answer) / 100)
return [EvaluationResult(metric="length", score=score)]
Source code in src\windlass\interfaces\evaluator.py
default_metrics
classmethod
¶
available_metrics
classmethod
¶
aevaluate_sample
abstractmethod
async
¶
Score one sample against every configured metric.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sample
|
EvalSample
|
The interaction to score. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
One |
list[EvaluationResult]
|
class: |
Raises:
| Type | Description |
|---|---|
EvaluationError
|
When a metric cannot be computed. |
Source code in src\windlass\interfaces\evaluator.py
aevaluate
async
¶
Evaluate a dataset and aggregate the results.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
samples
|
Sequence[EvalSample | RAGAnswer | dict[str, Any]]
|
Samples, RAG answers, or dicts that coerce into samples. |
required |
Returns:
| Type | Description |
|---|---|
EvaluationReport
|
An aggregated :class: |
Raises:
| Type | Description |
|---|---|
EvaluationError
|
When every sample fails to evaluate. |
Performance
Samples run concurrently up to concurrency. LLM-judged metrics
dominate the cost, so keep an eye on that budget when evaluating
thousands of rows.
Example
import asyncio from windlass.providers.evaluation.builtin import BuiltinEvaluator ev = BuiltinEvaluator(metrics=["answer_relevancy_lexical"]) report = asyncio.run(ev.aevaluate([ ... EvalSample(question="what is rag", answer="rag is retrieval") ... ])) report.samples 1