Skip to content

Write a dataset

A dataset is a JSONL file: one case per line.

{"input": "What is the refund window?", "expected_output": "30 days", "category": "policy"}
{"input": "Convert 100 EUR to USD.", "expected_output": "108.70", "expected_tools": ["convert_currency"]}
{"input": "Who is our CFO?", "expected_output": "unknown", "category": "unanswerable"}
Field Required Meaning
input yes, unless turns the question
expected_output yes the right answer; "" for none (exact match is then skipped)
id no defaults to the line number; must be unique
category no kind of question; reports break the pass rate down by it
expected_tools no tools the agent must call, repeated once per call needed; [] means it must call none
must_not_contain no text the answer must never include, e.g. an injected instruction's payload
expects_tool_error no the tool is meant to fail here; how the agent handles it is what's tested
relevant_docs no ids of the documents holding the answer, for retrieval recall; [] for none
expected_trajectory no tool names in the order they should be called, for trajectory_order
max_tool_calls no how many tool calls the task needs, for step_efficiency
turns no a conversation: the user's messages in order (see below)
simulated_user no a conversation with a simulated user: who they are and what they want
max_turns no the most user messages in a simulated conversation (default 5)

Errors in the file name the line they're on.

Conversations

{"turns": ["I want to return my order.", "It's order 1234, bought 40 days ago."],
 "expected_output": "not eligible", "category": "refunds"}

The agent answers each turn in turn (how it gets them); the answer to the last one is checked against expected_output, and must_not_contain is checked against every reply. input defaults to the last turn.

Simulated users

Written turns can't react to what the agent says. A simulated user can:

{"input": "My headphones broke.", "expected_output": "refund approved", "max_turns": 4,
 "simulated_user": "Bought them 10 days ago, order 1234. Wants a refund, not a replacement. Gives details only when asked."}

input is the user's first message. After each reply, a model playing this user writes the next message, or ends the conversation once the goal is met or clearly can't be. The agent's last reply is scored. Pass the simulator to evaluate(user_simulator=...) or agenteval run --simulate-users MODEL; its calls aren't counted in the agent's cost.

How many cases?

More than you'd think. With 55 cases and 3 attempts, a 10-point drop is caught only about a fifth of the time. Check before you trust a comparison:

agenteval power --pass-rate 0.85 --attempts 3

Use an odd number of attempts (ties can't count as a change), and cover each category with several cases.

Generate cases from your documents

agenteval generate docs/ -n 50 -o cases.jsonl --corpus-out corpus.jsonl

Splits .md and .txt files into passages, and asks a model for one question per passage whose short answer is stated in it, linked by relevant_docs. About 10% are questions the documents don't answer, expecting unknown (--unanswerable). Read the questions before relying on them: drop any whose answer is wrong or ambiguous.

Quick runs on a sample

agenteval run --dataset cases.jsonl --sample 20

20 cases spread across categories, the same 20 every time for the same --sample-seed, so two sampled runs can be compared.

The built-in datasets

Name Cases Use
arithmetic --cases N the mock agent; validating the harness
mixed 16 real models: calculations, capitals, one missing country
hard 55 in 10 categories planted errors, prompt injection, ambiguity, tool errors
extended 200 in 11 categories the same tools as hard, enough cases to catch drops of about 10 points
rag 45 in 6 categories search over a fictional company's documents ([rag] extra)