Overview
A dataset is a standalone, reusable set of test cases — structured inputs, an optional expected output, and metadata. Build one by hand, import a spreadsheet, curate from production traces, or push rows from the SDK, then run any prompt against it as an experiment.
A dataset is a fixed set of test cases you re-run as you iterate. Where the playground answers "how does this prompt do on this input?", a dataset is the question set behind "how does it do across all my cases — and did my change make it better or worse?".
Open Datasets in the sidebar. A dataset is not owned by a prompt — it's a reusable question set that any prompt (or any task) can run against. That decoupling is the point: the same qa-golden-set can grade three different prompts, or the same prompt across two datasets.
What a dataset row is
Every row is a test case with three parts:
| Part | What it is | Sent to the task? |
|---|---|---|
| Scenario id | A permanent identifier, assigned once and never reused. It is how results from different experiments are lined up against the same case. | No — see Why a scenario keeps its identity. |
| Input | A JSON object of { variable: value }. Each key binds to a prompt's matching {{variable}} by name. | Yes — this is what runs. |
| Messages (alternative to Input) | The scenario as a conversation — [{ "role": "user", "content": "…" }] — for systems that take turns rather than template variables. | Yes. |
| Expected output | The ground-truth reference (optional). A string, or a structured rubric. | No — used only by scoring / evaluators. |
| Metadata | Free-form JSON for slicing and filtering (e.g. {"channel":"email"}). | No. |
| Origin / Difficulty / Category (optional) | Where the case came from, how hard it is, what kind it is. Queryable, so you can run an experiment against just the hard cases. | No. |
| Tags (optional) | Simple labels for grouping and filtering rows (e.g. billing, regression). | No. |
The table shows input columns first, then Expected, Tags, and Metadata, so it's always clear what feeds the model versus what only grades it. Expected output is never passed to the task as a variable — that would be leaking the answer.
Input is real JSON. A value can be a string, a number, an array ([1, 2, 3]), or an object ({"a": 1}) — typed values are stored as-is, not flattened to text. Click a row to open the Fields panel and edit the full input object as YAML or JSON. Each key binds to the prompt's {{variable}} of the same name.
Why a scenario keeps its identity
Every row gets a scenario id the moment it is created, and keeps it for life. That id is what makes two experiments comparable: when you diff last week's run against today's, results are matched by scenario id, not by row position. Match by position and inserting one row at the top shifts every comparison by one — silently, with the numbers still rendering.
So editing a dataset is safe:
- Editing a row updates it in place. The id is unchanged, and every past result that scored it still points at it.
- Removing a row archives it. It leaves the working set but remains resolvable, so an experiment that ran against it is still readable.
- Adding a row mints a new id. Runs from before it existed simply have no
result for that scenario, which a comparison reports as
newrather than pretending it regressed.
Open any row to see its id in the panel header — click to copy it. You will see the same id on that row's results inside an experiment.
Editing a dataset does not rewrite history. A completed experiment is pinned to the snapshot it ran against, so its results and scores stay exactly as they were. Your edits apply to future runs. The dataset header shows Used by N experiments so you know when a test set is load-bearing.
When a scenario is a conversation
Some systems take a question; others take a conversation. A row can hold messages instead of flat input:
{
"messages": [
{ "role": "system", "content": "Be concise." },
{ "role": "user", "content": "I bought a jacket last week." },
{ "role": "assistant", "content": "Thanks, noted." },
{ "role": "user", "content": "Can I still return it?" }
]
}When the experiment runs without a managed prompt, the last user turn is the question, a system turn becomes the system instruction, and the turns in between are carried as context — so the model answers the final question knowing what came before.
Expected output can be a rubric
Expected output is optional, and it does not have to be a single correct string. Many scenarios are graded against criteria rather than an exact answer — the PRD calls these reference-free. A structured expected output is stored and passed to evaluators as an object, not flattened to text:
{
"sample_reply": "Refunds are accepted within 30 days of purchase.",
"must_mention": ["30 days"],
"should_defer": false
}Use a plain string when there is one right answer; use an object when the grader needs more than that; leave it empty when a reference-free evaluator (valid JSON, not toxic, stayed on topic) is doing the judging.
How variables bind (datasets aren't tied to a prompt)
Because a dataset isn't owned by a prompt, matching happens by name, at run time. When you run an experiment you pick the prompt, and each input key binds to the prompt's declared {{variable}} of the same name:
- a key that matches a declared variable → bound (and coerced to that variable's type);
- a key the prompt doesn't declare → ignored (so extra columns never reach the model);
- a declared variable with no matching key → renders empty.
That's what makes one dataset reusable across prompts: it just carries named inputs plus ground truth, and each prompt decides which names it consumes.
Ways to build a dataset
Import files
Edit in dashboard
Ingest from SDK
Curate from production
Save persists your changes: edited rows are updated in place and keep their scenario id, new rows get one, and rows you removed are archived rather than deleted. Snapshots freeze the current rows as a numbered, immutable version.
Why snapshots
A dataset is a live, editable draft. A snapshot is a frozen version of it. When you run an experiment you can pin it to a snapshot, so "experiment #4 scored 0.92" stays reproducible even after you later add or edit rows. Without it, comparing two experiments run weeks apart is meaningless — the test set moved underneath them. Snapshots are optional per run (you can run against the live draft); reach for one when you want a stable baseline.
How this dataset is graded
A dataset can carry its own evaluators. Attach them once and every experiment run against this dataset inherits them — pre-selected in the New experiment dialog, and removable for one run if you need to.
This is worth setting up. Scoring chosen per run means a run created in a hurry has none, and a run with no scorers produces outputs rather than a verdict. How a test set should be judged is a property of the test set, not of whoever happened to launch the run.
Runs on this dataset form a series
Every experiment on a dataset joins that dataset's series — the set of runs it makes sense to line up, because the dataset is what is being held fixed while a prompt, a model, or an agent build varies.
That is what lets a new run pick a baseline automatically (the most recent completed run) and report which scenarios improved or regressed against it, without you having to open a comparison. See every run is measured against a baseline.
Run it — Evaluate
From a dataset (or a prompt) click Evaluate:
- Run in Experiment — batch every row through a prompt + model(s), scored and comparable. This is offline evaluation; see Experiments.
- Run in Playground — open the interactive playground prefilled with this dataset (or prompt) to spot-check a single case.
Whichever object you launch from is prefilled; you only supply the other half.
Next
- Import files — CSV, Excel, JSON, JSONL
- Edit in dashboard — manual row editing
- Ingest from SDK — append from code
- Curate from production — traces and experiments
- Experiments — run a prompt + models across a dataset
- Evaluations — reusable evaluators that also grade experiments offline
Playground
An interactive bench to test one input across models side by side, score the outputs, and tune prompts — including specific managed-prompt versions — before you ship them.
Import files
Upload CSV, Excel (.xlsx/.xls), JSON, or JSONL (.jsonl/.ndjson) into a dataset. Trodo previews the file, lets you map each column to input variables, Expected output, Metadata, or Tags, then appends the rows.