Custom no-code evaluations
Custom no-code evaluations let you evaluate AI systems on datasets and tasks specific to your use case without writing or maintaining custom evaluation code. Define the evaluation in a quantiles.toml or .quantiles.toml configuration file, then run, analyze, and compare it using the standard Quantiles workflow.
For highly specialized evaluations such as multi-step workflows, LLM judge-based evaluations, or agent evaluations, use a custom code evaluation instead.
Custom no-code evaluation styles
Custom no-code evaluations have two styles, defined by the style.type field:
exact_match: an evaluation that compares the model’s response with an expected answer.multiple_choice: an evaluation that extracts and scores the model’s selection from a configured set of answer choices.
The following required and optional fields apply to both exact-match and multiple-choice evaluations:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | Selects the no-code custom evaluation runner. Must be "custom_nocode". |
style | table | yes | Selects the scoring style and contains the fields required by that style. |
dataset | table | yes | Identifies the Hugging Face dataset to evaluate, including its optional subset, split, and revision. |
dataset.name | string | yes | Hugging Face dataset ID, such as "quantiles/dataset_name" |
prompt_template_file | string | yes | Path to an existing Jinja prompt template. |
dataset.config_name | string | no | Dataset configuration or subset to load. If omitted, Quantiles infers the configuration. |
dataset.split | string | no | Dataset split to evaluate. If omitted, Quantiles prefers test, validation, eval, or train, in that order, then uses the first available split. |
dataset.revision | string | no | Dataset branch, tag, or commit to load. If omitted, Hugging Face uses the dataset’s default revision. |
model | string or table | no | Model used to generate evaluation responses, such as an OpenAI or Anthropic model. If omitted, Quantiles uses its built-in demo model, which is intended only for testing and examples. |
limit | integer | no | Maximum number of dataset rows to evaluate. Defaults to all available rows and must be greater than zero. |
max_workers | integer | no | Maximum number of dataset rows evaluated concurrently. If omitted, Quantiles uses its configured runtime default. |
The built-in demo model (
model = "random") is for workflow validation only. It generates random responses forexact_matchevaluations and selects uniformly fromstyle.choice_labelsfor multiple_choice evaluations. Do not use its results to assess model quality.
Exact match
Use the exact_match evaluation style when each dataset row has a single expected answer that should match the model response exactly. Quantiles scores the model response based on whether it matches that answer exactly.
In addition to the fields above, this evaluation style requires:
| Field | Type | Required | Description |
|---|---|---|---|
style.type | string | yes | Selects exact-match scoring. Must be "exact_match". |
style.golden_column | string | yes | Dataset column containing the expected answer. |
The style.golden_column field identifies the dataset column containing the expected responses. Each value in that column must be a string, number, or boolean, which Quantiles converts to text. Before comparison, Quantiles removes leading and trailing whitespace from both the expected response and the model response. All other text is compared case-sensitively.
Example
The following example configures an exact-match evaluation in quantiles.toml. It assumes that each dataset row contains a prompt column with the model input and an answer column with the expected response.
1. Define the evaluation in quantiles.toml
# Example of an exact-match custom no-code evaluation.
[benchmarks.exact_match_nocode_eval]
# Type must be set to "custom_nocode" or the section defaults to "builtin".
type = "custom_nocode"
# Style sets how responses are parsed and scored.
style = { type = "exact_match", golden_column = "answer" }
# Identifies the Hugging Face dataset to evaluate.
dataset = { name = "your-org/your-dataset" }
# This example uses the built-in demo model. Replace it with a
# provider-prefixed model (e.g., OpenAI or Anthropic) for real inference.
# Provider-backed models require provider-specific credentials and may
# incur usage charges.
model = "random"
# Path to an existing Jinja prompt template.
prompt_template_file = "prompts/exact-match.txt"
# Limit the number of samples to be evaluated. Omit this if you
# want to run all samples in that evaluation.
limit = 1002. Create the Jinja prompt template
Create prompts/exact-match.txt relative to the directory containing quantiles.toml or .quantiles.toml:
{{ row.prompt }}Dataset fields are available through
row. Use dot notation, such as{{ row.prompt }}, for simple field names. Use bracket notation, such as{{ row["Prompt Text"] }}, for field names containing spaces or other characters that do not work cleanly with dot notation.
3. Run the evaluation
Run the configured evaluation by name:
qt run exact_match_nocode_evalAfter the evaluation finishes, Quantiles prints a run_id that can be used to inspect aggregate metrics and sample-level results:
qt show <run_id> --jsonMultiple choice
Use the multiple_choice evaluation style when each dataset row contains a set of answer choices and identifies the correct choice. Quantiles scores the model response based on the choice it selects.
In addition to the fields above, configure the following required and optional fields:
| Field | Type | Required | Description |
|---|---|---|---|
style.type | string | yes | Selects multiple-choice scoring. Must be "multiple_choice". |
style.choices | table | yes | Defines the dataset fields that contain the answer choices. Configure exactly one of the following: • style.choices.column: Use when one dataset column contains the choices as an ordered array or an object keyed by style.choice_labels. An object must contain a scalar value for every configured label.• style.choices.columns: Use when each choice is stored in a separate scalar dataset column. The number of columns must equal the number of style.choice_labels. Each column maps to the label at the same position.Quantiles normalizes the choices as { label, text } entries and exposes them to the prompt template as choices. |
style.choice_labels | array of strings | yes | Unique labels assigned to choices in order, such as ["A", "B", "C", "D"]. |
style.answer | table | yes | Defines how Quantiles identifies the correct answer. Configure exactly one of the following options: • style.answer.label_column: Use when a dataset column contains the correct choice label. Values are trimmed and matched case-insensitively against style.choice_labels.• style.answer.index_column: Use when a dataset column contains the numeric position of the correct choice before optional shuffling. Set style.answer.index_base to 0 for zero-based indexes or 1 for one-based indexes. It defaults to 0.• style.answer.correct_choice_column: Use when one entry in style.choices.columns always contains the correct answer. This option is only supported with column-backed choices. |
style.shuffle | table | no | Enables deterministic per-row shuffling before labels are assigned. When configured, style.shuffle.seed_column is required. It identifies a dataset column containing a stable string, number, or boolean used to reproduce the same shuffled order. |
metrics | array | no | Optional derived metric families. Supports f1 and confusion. String entries are shown in JSON output by default; inline tables can set show = "json" or show = "all". Duplicate metric families are not allowed. |
Example
The following example configures a multiple-choice evaluation in quantiles.toml. It assumes that each dataset row contains a question column with the prompt, an options column with the answer choices, and an answer_label column with the correct choice label. Each answer_label value must match one of the configured style.choice_labels.
1. Define the evaluation in quantiles.toml
# Example of a multiple-choice custom no-code evaluation.
[benchmarks.multiple_choice_nocode_eval]
# Type must be set to "custom_nocode" or the section defaults to "builtin".
type = "custom_nocode"
# Identifies the Hugging Face dataset to evaluate.
dataset = { name = "your-org/your-multiple-choice-dataset" }
# This example uses the built-in demo model. Replace it with a
# provider-prefixed model (e.g., OpenAI or Anthropic) for real inference.
# Provider-backed models require provider-specific credentials and may
# incur usage charges.
model = "random"
# Path to an existing Jinja prompt template.
prompt_template_file = "prompts/multiple-choice.txt"
# Limit the number of samples to be evaluated. Omit this if you
# want to run all samples in that evaluation.
limit = 10
# Style sets how responses are parsed and scored.
[benchmarks.multiple_choice_nocode_eval.style]
type = "multiple_choice"
# Read the answer choices from the "options" dataset column.
choices = { column = "options" }
# Assign these labels to the choices in order.
choice_labels = ["A", "B", "C", "D"]
# Read the expected label from the "answer_label" dataset column.
answer = { label_column = "answer_label" }2. Create the Jinja prompt template
Create prompts/multiple-choice.txt relative to the directory containing quantiles.toml or .quantiles.toml:
{{ row.question }}
{% for choice in choices %}
{{ choice.label }}. {{ choice.text }}
{% endfor %}
Answer with only the letter of the correct choice.Reminder: Dataset fields are available through
row. Use dot notation, such as{{ row.question }}, for simple field names. Use bracket notation, such as{{ row["Question Text"] }}, for field names containing spaces or other characters that do not work cleanly with dot notation. Multiple-choice templates also receive the normalized answer choices aschoices.
Explicitly ask the model to return one of the labels in style.choice_labels. Quantiles first compares the complete trimmed response with each configured label, case-insensitively. If the complete response does not match, Quantiles checks up to the last eight whitespace-separated tokens, ignoring non-alphanumeric punctuation around each token. If no configured label is found, the response is recorded as unparsed.
3. Run the evaluation
Run the configured evaluation by name:
qt run multiple_choice_nocode_evalFor more prompt template examples, see the custom no-code prompt templates in the Quantiles GitHub repository.
See the sample custom_nocode configurations for examples of common benchmarks implemented through configuration.
Dataset
Choose or publish a Hugging Face dataset whose rows contain the fields needed to render your prompt and score the model’s response. As described above, the evaluation style determines which additional fields are required:
style = { type = "exact_match", ... }evaluations need a golden-answer columnstyle = { type = "multiple_choice", ... }evaluations need fields describing the choices and the correct answer
The Quantiles datasets on Hugging Face include the datasets referenced by the custom no-code examples on GitHub.
Loading a Hugging Face dataset may require network access, such as when downloading uncached data.
Metrics and results
For each dataset row processed by a custom no-code evaluation, Quantiles calculates and records the following sample-level metrics:
is_correctindicates whether the parsed response matches the expected answer.response_parsedindicates whether Quantiles could parse the response. Exact-match responses are always considered parsed; for multiple-choice evaluations, this value is0when the response cannot be parsed as a configured choice label.latency_msrecords the sample’s execution latency, including any LLM provider latency, in milliseconds.
Each run persists the following aggregate metrics:
accuracy- the fraction of evaluated examples assigned the correct class label.mean_latency_ms,median_latency_ms,p95_latency_ms,p99_latency_ms,min_latency_ms, andmax_latency_ms- statistics describing the distribution oflatency_msvalues for each sample in the run
Optional metrics for multiple_choice evaluations
Multiple-choice evaluations can compute additional metrics from their recorded step outputs. Configure the metrics array in the benchmark table alongside fields such as dataset, model, and prompt_template_file, rather than the nested [benchmarks.<eval_name>.style] table. These metrics are computed when command output is rendered and are not persisted with the default aggregate metrics.
F1
The following example adds F1 metrics to the evaluation:
[benchmarks.multiple_choice_nocode_eval]
# Other benchmark-level fields...
metrics = ["f1"]
[benchmarks.multiple_choice_nocode_eval.style]
# Multiple-choice style fields...The F1 metric family provides:
f1_label_N: one-vs-rest F1 for the label at indexNinstyle.choice_labels.macro_f1: the arithmetic mean of the per-label F1 values.weighted_f1: the average of the per-label F1 values weighted by each label’s number of expected samples.
F1 is reported as 0 when its denominator is zero.
Confusion Matrix
To enable a confusion matrix, use the following value in the same benchmark-level location:
metrics = ["confusion"]The confusion-matrix metric family provides:
confusion_matrix_G_P: the number of samples whose expected label has indexGand whose parsed prediction has indexP.confusion_matrix_G_unparsed: the number of samples whose expected label has indexGbut whose response could not be parsed as a configured label.
In these metric names, N, G, and P are zero-based positions in style.choice_labels.
Expected labels form the matrix rows. Parsed labels and an additional unparsed bucket form the columns.
Metric display formats
F1 and confusion matrix values are shown only in JSON output by default. To include a metric family in both human-readable and JSON output, set show = "all" in the same benchmark-level location, for example:
metrics = [{ name = "confusion", show = "all" }]Runtime overrides
Use --input to apply one-time overrides, such as model to use a different AI model or limit to evaluate a smaller number of samples, without modifying quantiles.toml. All other settings will continue to come from the configuration file, and subsequent runs will use just the saved configuration unchanged. This is useful for smoke tests, model comparisons, and temporary prompt experiments. For example:
qt run <eval_name> --input '{"model":"<provider>:<model>","limit":<count>}'
--inputoverrides apply only to the current run, or when that run is resumed. The next time you run the same evaluation without--input, Quantiles uses the values defined in the configuration file.
Validation and common failures
Quantiles validates the configuration and reports row-specific errors when it cannot prepare or score the dataset. Use the following fixes for common failures:
| Error | Solution |
|---|---|
prompt_template_file does not exist or contains invalid Jinja syntax. | Confirm that the configured path points to an existing file from the directory where you run qt, then correct any invalid Jinja expressions or control blocks. |
| A configured golden-answer, choice, answer, or shuffle-seed column is absent from a dataset row. | Inspect the dataset schema and update the corresponding configuration field. Every evaluated row must contain the required fields. |
style.choice_labels is empty, contains duplicates, or does not match the length of style.choices.columns. | Provide a nonempty list of unique labels. When using style.choices.columns, provide exactly one label for each choice column. |
| An answer label or index does not identify one of the configured choices. | Ensure label values match style.choice_labels, or set index_base correctly and keep every answer index within the available choice range. |
limit is 0. | Set limit to a positive integer or omit it to evaluate every available row. |
| The configuration contains an unknown or style-incompatible field. | Remove unsupported fields and place style-specific fields under the correct style or style.answer table. |
Use coding agents to create custom no-code evaluations
Use the Quantiles agent skill to have a coding agent build, run, and analyze custom no-code evaluations with the qt CLI. Before proceeding, install the skill. Then customize one of the prompts below based on the custom no-code evaluation style you want to create.
Exact-match agent prompt
Create and run a custom no-code exact-match evaluation named <evaluation_name> using the Hugging Face dataset <dataset_id>. Use <prompt_column> as the prompt column, <answer_column> as the golden-answer column, and the following Jinja prompt template: <jinja_prompt_template>. Inspect the run and summarize the results.Multiple-choice agent prompt
Create and run a custom no-code multiple-choice evaluation named <evaluation_name> using the Hugging Face dataset <dataset_id>. Use <prompt_column> as the prompt column, <choice_source> as the choice source, <choice_labels> as the choice labels, <answer_source> as the correct-answer source, and the following Jinja prompt template: <jinja_prompt_template>. Inspect the run and summarize the results.See Agents Overview for more detail on using agents with Quantiles.