Evaluators
An evaluator scores each record of a run. It pairs a name with a scorer — a function that receives a ScorerContext and returns a score — plus an optional description and goal.
from mistralai.evaluations import Evaluator, Goal
Evaluator(
name="accuracy",
description="1 if the expected answer appears in the output.",
scorer=accuracy_scorer,
goal=Goal.gte(0.8),
)A scorer can be one of two kinds:
- Rule-based — a plain function that computes a score from the output. Deterministic, fast, and free.
- LLM-as-judge — a function that calls a model to grade the output. Use it for criteria that are hard to express in code, such as helpfulness, factual accuracy, or tone.
Both are just functions, so a single run can hold any mix of them. See Multiple evaluators to run several at once.
Rule-based scorers
A rule-based scorer returns a number (or a str/bool for categorical scores) computed directly from the record and output:
from mistralai.evaluations import ScorerContext
def contains_expected(ctx: ScorerContext) -> int:
return 1 if ctx.input_record["expected"].lower() in str(ctx.output).lower() else 0Numeric scores produce numeric statistics (avg, min, max, std, count); str/bool scores produce frequency distributions and a mode. See Configure statistics.
LLM-as-judge scorers
To use a model as a judge, call the Mistral API inside your scorer and return a numeric score:
from mistralai.evaluations import ScorerContext
JUDGE_PROMPT = """You are a strict grader. Given a user prompt and a model answer,
respond with a single integer between 0 and 5 (higher is better) measuring how helpful and on-topic
the answer is. Respond with only the integer, no other text.
User prompt: {prompt}
Model answer: {answer}
"""
def llm_judge(ctx: ScorerContext) -> int:
response = client.chat.complete(
model="mistral-large-latest",
messages=[
{
"role": "user",
"content": JUDGE_PROMPT.format(
prompt=ctx.input_record["prompt"], answer=ctx.output
),
}
],
temperature=0,
)
raw = str(response.choices[0].message.content).strip()
try:
return int(raw[0])
except (ValueError, IndexError):
return 0Use it in an evaluator like any other scorer:
from mistralai.evaluations import Evaluator, Goal
Evaluator(
name="helpfulness",
description="LLM judge: helpfulness score from 0 to 5.",
scorer=llm_judge,
goal=Goal.gte(3.5),
)Structured output with Score
For richer feedback, return a Score object with a value and a rationale. The rationale is stored alongside the score in Studio:
from pydantic import BaseModel
from mistralai.evaluations import ScorerContext, Score
class RecallJudgment(BaseModel):
score: float
comment: str
async def recall_scorer(ctx: ScorerContext) -> Score:
completion = await client.chat.parse_async(
model="mistral-large-latest",
messages=[
{
"role": "user",
"content": (
f"Evaluate recall between 0.0 and 1.0.\n\n"
f"Expected: {ctx.input_record['expected']}\n"
f"Got: {ctx.output}\n\n"
f"Provide a score and a short comment."
),
}
],
response_format=RecallJudgment,
)
judgment = completion.choices[0].message.parsed
return Score(value=judgment.score, rationale=judgment.comment)Reduce judge variance
LLM judges can be noisy. Set num_scores on the evaluator to score each generation multiple times and average the results:
Evaluator(
name="recall",
description="LLM judge: recall score (0.0 to 1.0).",
scorer=recall_scorer,
num_scores=3, # scored 3 times per generation, results are averaged
)Write good judge instructions
Craft your judge prompts with care:
- Be specific. Avoid vague criteria: describe exactly what a good response looks like for your use case.
- Don't assume the judge knows your context. Spell out what "good" means explicitly.
- Use boundary examples: "A score of 3 means the response partially answers the question but omits a key detail."
- Keep temperature low (for example,
temperature=0) for more deterministic judgments. - Test on a small sample before using a judge in a large evaluation run. Spot inconsistencies early.