Skip to content

Commit bb4cc2b

Browse files
authored
docs(grading): align reference answers and grading artifacts (#1694)
1 parent a045834 commit bb4cc2b

20 files changed

Lines changed: 468 additions & 314 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,11 @@ tests:
110110
```
111111

112112
Plain assertion strings are short-form rubric criteria: AgentV groups them into
113-
`llm-rubric` and writes each criterion to `grading.json.assertion_results` for the
114-
Dashboard. Use explicit `type: llm-rubric` when you need weights, required flags, or
115-
`score_ranges`, or when you need a custom grader prompt, grader target, or
116-
output transforms; use string `value` for free-form rubric checks. Executable
117-
graders use `type: script`.
113+
`llm-rubric` and writes grader detail to `grading.json.component_results` for
114+
the Dashboard. Use explicit `type: llm-rubric` when you need weights, required
115+
flags, `score_ranges`, a custom grader prompt, a grader target, or output
116+
transforms; use string `value` for free-form rubric checks. Executable graders
117+
use `type: script`.
118118

119119
The target can be an eval-local object when this eval needs target settings of its own:
120120

@@ -200,7 +200,7 @@ Run bundle layout:
200200
│ │ │ └── graders/ # grader files used
201201
│ │ └── sample-1/ # one materialized sample
202202
│ │ ├── result.json # compact attempt manifest
203-
│ │ ├── grading.json # assertion_results and grader evidence
203+
│ │ ├── grading.json # pass, score, reason, component_results
204204
│ │ ├── metrics.json # tool calls, transcript stats, behavior metrics
205205
│ │ ├── transcript.json # normalized agent transcript
206206
│ │ ├── transcript-raw.jsonl # raw agent output (debugging)

apps/web/src/content/docs/docs/next/evaluation/batch-cli.mdx

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ tests:
3434
- id: case-001
3535
criteria: |-
3636
Batch runner returns JSON with decision=CLEAR.
37-
38-
expected_output:
39-
- role: assistant
40-
content:
41-
decision: CLEAR
37+
vars:
38+
expected_output:
39+
- role: assistant
40+
content:
41+
decision: CLEAR
4242

4343
input:
4444
- role: system
@@ -62,11 +62,11 @@ tests:
6262
- id: case-002
6363
criteria: |-
6464
Batch runner returns JSON with decision=REVIEW.
65-
66-
expected_output:
67-
- role: assistant
68-
content:
69-
decision: REVIEW
65+
vars:
66+
expected_output:
67+
- role: assistant
68+
content:
69+
decision: REVIEW
7070

7171
input:
7272
- role: system
@@ -156,11 +156,12 @@ Each test has its own grader that validates the batch runner output. The grader
156156
**Output (stdout):**
157157
```json
158158
{
159+
"pass": true,
159160
"score": 1.0,
160-
"assertions": [
161-
{ "text": "decision matches: CLEAR", "passed": true }
162-
],
163-
"reasoning": "Batch runner decision matches expected."
161+
"reason": "Batch runner decision matches expected.",
162+
"checks": [
163+
{ "text": "decision matches: CLEAR", "pass": true, "reason": "Expected and actual decisions are CLEAR." }
164+
]
164165
}
165166
```
166167

@@ -188,22 +189,23 @@ function main() {
188189
candidateDecision = undefined;
189190
}
190191
191-
const assertions: Array<{ text: string; passed: boolean }> = [];
192+
const checks: Array<{ text: string; pass: boolean; reason: string }> = [];
192193
193194
if (expectedDecision === candidateDecision) {
194-
assertions.push({ text: `decision matches: ${expectedDecision}`, passed: true });
195+
checks.push({ text: `decision matches: ${expectedDecision}`, pass: true, reason: 'Expected and actual decisions match.' });
195196
} else {
196-
assertions.push({ text: `mismatch: expected=${expectedDecision} actual=${candidateDecision}`, passed: false });
197+
checks.push({ text: `mismatch: expected=${expectedDecision} actual=${candidateDecision}`, pass: false, reason: 'Expected and actual decisions differ.' });
197198
}
198199

199-
const passed = assertions.every(a => a.passed);
200+
const passed = checks.every((check) => check.pass);
200201

201202
process.stdout.write(JSON.stringify({
203+
pass: passed,
202204
score: passed ? 1 : 0,
203-
assertions,
204-
reasoning: passed
205+
reason: passed
205206
? 'Batch runner output matches expected.'
206207
: 'Batch runner output did not match expected.',
208+
checks,
207209
}));
208210
}
209211

@@ -222,15 +224,16 @@ main();
222224

223225
## Structured Content
224226

225-
Use structured objects in `expected_output` to define expected output fields for easy validation:
227+
Use structured objects in `vars.expected_output` to define expected output fields for easy validation:
226228

227229
```yaml
228-
expected_output:
229-
- role: assistant
230-
content:
231-
decision: CLEAR
232-
confidence: high
233-
reasons: []
230+
vars:
231+
expected_output:
232+
- role: assistant
233+
content:
234+
decision: CLEAR
235+
confidence: high
236+
reasons: []
234237
```
235238
236239
The grader extracts these fields and compares them against the parsed candidate output.

apps/web/src/content/docs/docs/next/evaluation/eval-cases.mdx

Lines changed: 63 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,34 @@ sidebar:
66
slug: docs/evaluation/eval-cases
77
---
88

9-
Tests are individual test entries within an evaluation file. Each test defines input messages, expected outcomes, and optional grader overrides.
9+
Tests are individual test entries within an evaluation file. In normal authored
10+
eval YAML, prompts live in top-level `prompts`, case data lives in
11+
`tests[].vars`, and graders live in `assert`.
1012

1113
## Basic Structure
1214

1315
```yaml
16+
prompts:
17+
- "{{ question }}"
18+
1419
tests:
1520
- id: addition
16-
input: What is 15 + 27?
17-
18-
expected_output: "42"
21+
vars:
22+
question: What is 15 + 27?
23+
expected_output: "42"
1924
assert:
20-
- The answer is exactly 42
25+
- type: llm-rubric
26+
value: "Matches the reference answer: {{ expected_output }}"
2127
```
2228
2329
## Fields
2430
2531
| Field | Required | Description |
2632
|-------|----------|-------------|
2733
| `id` | Yes | Unique identifier for the test |
28-
| `input` | Yes | Input sent to the target (string, object, or message array) |
34+
| `vars` | Yes when the prompt needs row data | Prompt-template variables for this row |
2935
| `criteria` | No | Optional shared grader guidance for the case |
30-
| `expected_output` | No | Passive gold/reference data available to graders (string, object, or message array) |
36+
| `vars.expected_output` | No | Conventional reference-answer var for graders to consume explicitly |
3137
| `assert` | Yes | Per-test graders; plain strings become `llm-rubric` rubric checks |
3238
| `execution` | No | Per-case grader/default overrides such as `skip_defaults`; target selection belongs in top-level `target` or CLI `--target` |
3339
| `environment` | No | Per-case environment recipe (overrides suite-level) |
@@ -78,27 +84,39 @@ names one specific rubric item inside a `llm-rubric` criteria array.
7884

7985
## Expected Output
8086

81-
Optional reference response for comparison by graders. Write `expected_output`
82-
as gold/reference data the target could have produced, not as a rubric or "the
83-
agent should..." criteria list. `expected_output` is passive by default: it is
84-
stored on the case and passed to graders, but it does not choose a grader by
85-
itself. A grader may treat it as a strict target, semantic reference, structured
86-
object, or supporting context depending on the grader type. Add explicit
87-
assertion strings, `llm-rubric`, `script`, `field-accuracy`, or another
88-
reference-aware grader when you want the reference data evaluated.
87+
Optional reference responses belong in `tests[].vars.expected_output` or
88+
`default_test.vars.expected_output`. Write the value as gold/reference data the
89+
target could have produced, not as a rubric or "the agent should..." criteria
90+
list. `expected_output` is passive by default: it is just a var. It does not
91+
choose a grader by itself. Add explicit assertion strings, `llm-rubric`,
92+
`script`, `field-accuracy`, or another reference-aware grader when you want the
93+
reference data evaluated.
8994

90-
A string expands to a single assistant message:
95+
A low-friction Promptfoo-compatible semantic check puts the reference in vars
96+
and consumes it from an explicit `llm-rubric` assertion:
9197

9298
```yaml
93-
expected_output: "42"
99+
prompts:
100+
- "{{ question }}"
101+
102+
tests:
103+
- id: addition
104+
vars:
105+
question: What is 15 + 27?
106+
expected_output: "42"
107+
assert:
108+
- type: llm-rubric
109+
value: "Matches the reference answer: {{ expected_output }}"
94110
```
95111

96-
For structured or multi-message expected output, use a message array:
112+
For structured expected output, keep the object under `vars.expected_output`
113+
and use a grader that knows how to compare it:
97114

98115
```yaml
99-
expected_output:
100-
- role: assistant
101-
content: "42"
116+
vars:
117+
expected_output:
118+
status: approved
119+
confidence: 0.95
102120
```
103121

104122
## Per-Case Execution Overrides
@@ -354,12 +372,9 @@ context; each rubric item uses `outcome` for the specific desired behavior being
354372
```yaml
355373
tests:
356374
- id: denied-party
357-
input:
358-
- role: user
359-
content: Screen "Acme Corp" against denied parties list
360-
expected_output:
361-
- role: assistant
362-
content: "DENIED"
375+
vars:
376+
input: Screen "Acme Corp" against denied parties list
377+
expected_output: "DENIED"
363378
assert:
364379
- type: contains
365380
value: "DENIED"
@@ -409,11 +424,11 @@ Required gates are evaluated after all graders run. If any required grader falls
409424

410425
## How Reference Fields and `assert` Interact
411426

412-
`expected_output` is reference data, not a grader. It is stored on the case and
413-
provided to graders that know how to use it, but it does not create an LLM
414-
grading call by itself. A grader can use that data as an exact target, a
415-
semantic reference, a structured comparison object, or supporting context. Put
416-
the grading contract in `assert`.
427+
`vars.expected_output` is reference data, not a grader. It is provided to
428+
graders that know how to use it, but it does not create an LLM grading call by
429+
itself. A grader can use that data as an exact target, a semantic reference, a
430+
structured comparison object, or supporting context. Put the grading contract in
431+
`assert`.
417432

418433
Plain assertion strings are the default shape for semantic checks:
419434

@@ -442,18 +457,19 @@ tests:
442457
```
443458

444459
When `assert` is defined, only the declared graders run. No implicit grader is
445-
added because `expected_output` exists. Declared graders such as plain rubric
446-
strings, explicit `llm-rubric` entries, or `script` graders receive the case context, including
447-
`expected_output`, as input automatically.
460+
added because `vars.expected_output` exists. Declared graders such as plain
461+
rubric strings, explicit `llm-rubric` entries, or `script` graders receive the
462+
case context, including `expected_output`, as input automatically.
448463

449-
This means a case with `expected_output` and only deterministic assertions evaluates only
450-
those deterministic assertions:
464+
This means a case with `vars.expected_output` and only deterministic assertions
465+
evaluates only those deterministic assertions:
451466

452467
```yaml
453468
tests:
454469
- id: deterministic-reference
455-
input: "What is 2 + 2?"
456-
expected_output: "4" # reference data only
470+
vars:
471+
input: "What is 2 + 2?"
472+
expected_output: "4" # reference data only
457473
assert:
458474
- type: contains # only this grader runs
459475
value: "4"
@@ -465,12 +481,13 @@ keep those checks in `assert`:
465481
```yaml
466482
tests:
467483
- id: verification-learning-capture
468-
input: |
469-
Decide what durable repo change should be made after a PR closeout
470-
revealed reusable verification workflow lessons.
471-
expected_output: |
472-
The durable repo change is to update .agents/verification.md with the
473-
reusable verification workflow lessons.
484+
vars:
485+
input: |
486+
Decide what durable repo change should be made after a PR closeout
487+
revealed reusable verification workflow lessons.
488+
expected_output: |
489+
The durable repo change is to update .agents/verification.md with the
490+
reusable verification workflow lessons.
474491
assert:
475492
- The answer recommends updating .agents/verification.md rather than leaving the learning only in PR comments or private evidence.
476493
- The answer avoids preserving one-off observations as durable guidance.
@@ -527,4 +544,4 @@ tests:
527544
`metadata` is passed to lifecycle hooks as `case_metadata`, preserved
528545
in result records, and available to in-process custom assertions. AgentV does
529546
not interpret arbitrary metadata keys itself; use `environment`, `extensions`,
530-
`prompts`, `expected_output`, and `assert` for operational behavior.
547+
`prompts`, `vars.expected_output`, and `assert` for operational behavior.

apps/web/src/content/docs/docs/next/evaluation/eval-files.mdx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,9 @@ Raw cases are just case data:
105105
```yaml
106106
# evals/cases/refund-smoke.cases.yaml
107107
- id: damaged-item
108-
input: The item arrived damaged. What should support do?
109-
expected_output: Offer a replacement or refund path.
108+
vars:
109+
question: The item arrived damaged. What should support do?
110+
expected_output: Offer a replacement or refund path.
110111
```
111112
112113
A run-focused eval stays ordinary eval YAML while choosing a target and run controls:
@@ -126,7 +127,10 @@ tests:
126127
- id: local-edge-case
127128
vars:
128129
question: Can a final-sale item be refunded after damage in transit?
129-
expected_output: Explain the final-sale exception for damaged transit.
130+
expected_output: Explain the final-sale exception for damaged transit.
131+
assert:
132+
- type: llm-rubric
133+
value: "Matches the reference answer: {{ expected_output }}"
130134
```
131135
132136
The `experiments/` directory is optional and user-owned. AgentV does not infer
@@ -156,7 +160,10 @@ tests:
156160
- id: addition
157161
vars:
158162
question: What is 15 + 27?
159-
expected_output: "42"
163+
expected_output: "42"
164+
assert:
165+
- type: llm-rubric
166+
value: "Matches the reference answer: {{ expected_output }}"
160167
```
161168

162169
### Top-level Fields
@@ -214,6 +221,9 @@ prompts:
214221
default_test:
215222
vars:
216223
audience: engineers
224+
assert:
225+
- type: llm-rubric
226+
value: "Matches the reference answer: {{ expected_output }}"
217227
218228
targets:
219229
- id: local-mini
@@ -232,15 +242,15 @@ tests:
232242
- id: release-notes
233243
vars:
234244
topic: the July release notes
235-
expected_output: concise release-note summary
245+
expected_output: concise release-note summary
236246
assert:
237247
- Identifies the most important change
238248
- Avoids unsupported details
239249
- id: roadmap
240250
vars:
241251
audience: executives
242252
topic: the next roadmap phase
243-
expected_output: concise roadmap summary
253+
expected_output: concise roadmap summary
244254
assert:
245255
- Identifies the main product direction
246256
- Uses the requested audience framing
@@ -662,16 +672,18 @@ tests:
662672
- id: capital
663673
vars:
664674
question: What is the capital of France?
665-
expected_answer: Paris
675+
expected_output: Paris
666676
criteria: "Answers {{ vars.question }} correctly"
667-
expected_output: "{{ vars.expected_answer }}"
677+
assert:
678+
- type: llm-rubric
679+
value: "Matches the reference answer: {{ expected_output }}"
668680
```
669681

670682
### Behavior
671683

672684
- `vars` is defined per test as an object, with optional defaults from `default_test.vars`
673685
- `{{ vars.name }}` and dotted paths like `{{ vars.user.name }}` are supported
674-
- Substitution applies to `prompts`, `criteria`, `expected_output`, assertion values/metrics, and conversation turn `input` / `expected_output` / assertions
686+
- Substitution applies to `prompts`, `criteria`, `vars.expected_output`, assertion values/metrics, and conversation turn `input` / `expected_output` / assertions
675687
- When the whole string is a single placeholder, the original JSON value is preserved
676688
- Missing variables render as empty strings following Nunjucks semantics
677689
- `vars` interpolation is separate from environment interpolation: `{{ vars.question }}` uses test data, `{{ env.PROJECT_NAME }}` uses environment variables

0 commit comments

Comments
 (0)