Jeux de données
Un ensemble de données est l'ensemble de cas de test qui pilote une évaluation hors ligne. Dans l'SDK d'évaluation, un ensemble de données est soit une liste de dicts Python, où chaque dict est un enregistrement d'entrée et où vous définissez les clés pour correspondre à ce que votre fonction de tâche attend, soit un ensemble de données Studio que le SDK récupère pour vous.
Structure des enregistrements
Chaque enregistrement est un dictionnaire simple. Les clés dépendent de vous :
dataset = [
{"sentence": "Hello, how are you?", "groundtruth": "English"},
{"sentence": "Bonjour, comment ça va?", "groundtruth": "French"},
{"sentence": "Hola, ¿cómo estás?", "groundtruth": "Spanish"},
]Dans votre tâche et votre notation, accédez à l'enregistrement via ctx.input_record :
from mistralai.evaluations import TaskContext, ScorerContext
async def task(ctx: TaskContext) -> str:
return ctx.input_record["sentence"] # access any key you defined
def scorer(ctx: ScorerContext) -> int:
return 1 if ctx.input_record["groundtruth"].lower() == str(ctx.output).lower() else 0Utiliser un ensemble de données Studio
Si vous gérez vos enregistrements dans Studio, passez l'ensemble de données à evaluation.run() avec Dataset, par slug ou par ID. Le SDK récupère chaque enregistrement avant le démarrage de l'exécution :
from mistralai.evaluations import Dataset, Evaluation, Evaluator, Project
run = await client.evaluation.run(
project=Project(name="Language Detection"),
evaluation=Evaluation(name="Managed Dataset Eval"),
dataset=Dataset(slug="language-detection-golden-set"),
task=task,
evaluators=[Evaluator(name="accuracy", scorer=scorer)],
)| Sélecteur | Utilisez-le quand |
|---|---|
Dataset(slug="language-detection-golden-set") | Vous voulez un script lisible et stable. |
Dataset(id="018f879d-20cd-7e9f-a1bc-2f4f08b6f170") | Vous disposez déjà de l'UUID de l'ensemble de données. |
Passez exactement un seul des deux : id ou slug. L'ensemble de données doit déjà exister : le sélectionner par slug ne le crée jamais.
Le payload de chaque enregistrement devient l'enregistrement d'entrée, tel quel : ctx.input_record contient le même dict qu'un enregistrement en ligne équivalent. Pour l'exemple de détection de langue, utilisez des enregistrements Studio structurés ainsi :
payload = {"sentence": "Bonjour, comment ça va?", "groundtruth": "French"}Lorsque vous importez ces enregistrements depuis JSONL, encapsulez chaque payload dans le format d'importation de jeu de données Studio :
{"payload":{"sentence":"Bonjour, comment ça va?","groundtruth":"French"},"properties":{}}Les propriétés des enregistrements et les informations sur leur source ne sont pas ajoutées à l'enregistrement d'entrée. Sur les exécutions enregistrées dans Studio, l'exécution conserve l'ID de l'ensemble de données, et chaque enregistrement d'entrée conserve l'ID de l'enregistrement Studio dont il provient.
Les ensembles de données Studio ne sont pas des instantanés
Chaque exécution lit les enregistrements qui existent au moment de son démarrage, jusqu'à 10 000. Les modifications ultérieures de l'ensemble de données n'affectent que les exécutions futures. Si des enregistrements sont ajoutés ou supprimés pendant que le SDK les récupère, l'exécution échoue avant de démarrer plutôt que de s'exécuter sur un ensemble incohérent.
Dataset est accepté par evaluation.run(). Optimization et Retry failed records prennent une liste d'enregistrements.
Transformer les enregistrements avant l'exécution
Quand vos enregistrements Studio ne correspondent pas à la structure attendue par votre tâche, ou quand vous avez besoin d'une liste d'enregistrements, par exemple pour optimiser, récupérez-les et transformez-les vous-même.
Récupérez les enregistrements avec le SDK Mistral et adaptez le payload de chaque enregistrement vous-même. L'endpoint de liste est paginé. Cet utilitaire récupère chaque page et valide les enregistrements sous la forme d'un LangItem :
from typing import Any, TypedDict
from mistralai.evaluations import Mistral
class LangItem(TypedDict):
sentence: str
groundtruth: str
def as_dict(value: Any) -> dict[str, Any]:
if isinstance(value, dict):
return value
if hasattr(value, "model_dump"):
return value.model_dump(mode="json")
return dict(value)
def require_string(value: Any, *, record_id: str, field: str) -> str:
if isinstance(value, str) and value:
return value
raise ValueError(f"Dataset record {record_id} is missing {field}")
def to_lang_item(record: Any) -> LangItem:
payload = as_dict(record.payload)
return {
"sentence": require_string(payload.get("sentence"), record_id=record.id, field="payload.sentence"),
"groundtruth": require_string(
payload.get("groundtruth"),
record_id=record.id,
field="payload.groundtruth",
),
}
async def fetch_eval_dataset(client: Mistral, dataset_id: str) -> list[LangItem]:
dataset: list[LangItem] = []
page = 1
page_size = 100
while True:
result = await client.beta.observability.datasets.list_records_async(
dataset_id=dataset_id,
page_size=page_size,
page=page,
)
records = result.records.results
dataset.extend(to_lang_item(record) for record in records)
if not result.records.next:
break
page += 1
return datasetPassez ensuite la liste obtenue à dataset :
import asyncio
import os
from mistralai.evaluations import (
Evaluation, Evaluator, Mistral, Project, ScorerContext, TaskContext,
)
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
async def task(ctx: TaskContext) -> str:
response = await client.chat.complete_async(
model="mistral-small-latest",
messages=[
{
"role": "user",
"content": (
"What language is this sentence in? Reply with ONLY the language name. "
f"Sentence: {ctx.input_record['sentence']}"
),
}
],
)
return str(response.choices[0].message.content)
def scorer(ctx: ScorerContext) -> int:
groundtruth = ctx.input_record["groundtruth"].lower()
predicted = str(ctx.output).lower()
return 1 if groundtruth == predicted else 0
async def main():
dataset_id = os.environ["MISTRAL_DATASET_ID"]
dataset = await fetch_eval_dataset(client, dataset_id=dataset_id)
run = await client.evaluation.run(
project=Project(name="Language Detection"),
evaluation=Evaluation(name="Managed Dataset Eval"),
dataset=dataset,
task=task,
evaluators=[
Evaluator(
name="accuracy",
description="1 if the detected language matches the groundtruth.",
scorer=scorer,
),
],
metadata={
"studio_dataset_id": dataset_id,
"record_count": len(dataset),
},
)
run.show(level="records")
asyncio.run(main())Validez les champs dont vous avez besoin depuis record.payload, puis renvoyez la structure de dict exacte attendue par votre tâche et vos scorers.
Sécurité des types avec TypedDict
Utilisez TypedDict pour rendre les schémas des enregistrements explicites et obtenir une autocomplétion dans l'IDE :
from typing import TypedDict
class LanguageRecord(TypedDict):
sentence: str
groundtruth: str
dataset: list[LanguageRecord] = [
{"sentence": "Hello, how are you?", "groundtruth": "English"},
{"sentence": "Bonjour, comment ça va?", "groundtruth": "French"},
]Que mettre dans les enregistrements
Les enregistrements peuvent contenir tout ce dont votre tâche ou votre notation a besoin :
| Type de champ | Objectif | Exemple |
|---|---|---|
| Entrées de tâche | Ce que la tâche traite | prompt, contexte, texte, question |
| Vérité de référence | Sortie de référence pour la notation | expected, groundtruth, reference_answer |
| Métadonnées | Contexte supplémentaire pour les notateurs ou les juges LLM | category, difficulty, grading_guidance |
Incluez la vérité de référence dans les enregistrements lorsque vous souhaitez comparer la sortie de la tâche à une réponse connue comme correcte :
dataset = [
{
"prompt": "What is the capital of France?",
"expected": "Paris",
"difficulty": "easy",
},
{
"prompt": "Explain the difference between precision and recall.",
"expected": "Precision measures true positives over predicted positives; recall measures true positives over actual positives.",
"difficulty": "medium",
},
]
def accuracy_scorer(ctx: ScorerContext) -> int:
return 1 if ctx.input_record["expected"].lower() in str(ctx.output).lower() else 0Bonnes pratiques
Gardez les jeux de données ciblés
Un jeu de données construit autour d'une seule tâche ou capacité produit des signaux plus clairs qu'une collection large et multithème. Maintenez des jeux de données séparés pour des objectifs d'évaluation distincts (par exemple, language_detection, qa_factual, code_generation).
Sélectionnez des données représentatives
- Incluez des cas particuliers et des modes de défaillance, pas uniquement des exemples faciles.
- Équilibrez votre jeu de données : si 90 % des enregistrements sont des cas faciles, l'évaluation ne révélera pas de problèmes réels.
- Supprimez les enregistrements où même un humain ne pourrait pas noter la réponse de manière fiable (les entrées ambiguës ajoutent du bruit).
Versionnez vos jeux de données
Figez votre ensemble de données entre les exécutions si vous voulez suivre les performances dans le temps. Même de petites modifications d'enregistrements peuvent rendre les exécutions incomparables. Les ensembles de données Studio sont modifiables : chaque exécution lit leurs enregistrements en l'état. Utilisez des noms explicites comme qa_baseline_2025_06 plutôt que test_data.
La qualité de la vérité de référence est cruciale
Une vérité terrain inexacte ou ambiguë produit des scores bruités. Si vous utilisez un LLM comme juge (voir Évaluateurs), ajoutez un champ grading_guidance pour fournir au juge des consignes de notation explicites par enregistrement.
Organisation dans Studio
Le SDK d'évaluation organise les résultats par Projets, Évaluations et Exécutions dans Studio, et non par l'ensemble de données lui-même. Passez votre ensemble de données à evaluation.run() :
from mistralai.evaluations import Evaluation, Project
run = await client.evaluation.run(
project=Project(name="Language Detection"),
evaluation=Evaluation(name="Accuracy Eval"),
dataset=dataset, # your list of dicts, or a Dataset
task=task,
evaluators=[...],
)Les balises et métadonnées de l'exécution vous aident à retracer quelle version du jeu de données a été utilisée :
run = await client.evaluation.run(
...
tags=["dataset:qa_baseline_2025_06", "model:mistral-small"],
metadata={"dataset_version": "2025-06", "record_count": len(dataset)},
)