From one happy-path case to N parameterized runs — same multi-stage evaluation, driven by a .jsonl dataset.
So far we've run a single case with hardcoded data. But with AI agents you need more: what about a broken item? A wrong item? A cracked screen? Each with its own expected response and evaluation criteria.
ABS answers this with datasets. Turn your session into a template with {{placeholders}} and run it against a data file.
Instead of hardcoding every message, use {{cases.column}}:
session: Damaged item → refund (parametrized)
dataset:
id: cases
path: refund-cases.jsonl
behaviors:
# ── Turn 1 ──
- actor: user
action: says
content: "{{cases.userMessage}}"
- actor: assistant
action: clarifies
content: "{{cases.expectedClarification}}"
evaluations:
- type: llm_judge
criteria: "{{cases.clarificationCriteria}}"
# ── Turn 2 ──
- actor: user
action: says
content: "{{cases.followUp}}"
- actor: assistant
action: informs
content: "{{cases.expectedResolution}}"
capture:
refundId: "{{cases.expectedRefundId}}"
evaluations:
- type: contains
value: "{{cases.expectedRefundId}}"
- type: llm_judge
criteria: "{{cases.resolutionCriteria}}"
- type: Relevance
query: user
response: self
# ── Turn 3 ──
- actor: user
action: says
content: "{{cases.closing}}"
- actor: assistant
action: confirms
content: "{{cases.expectedClosing}}"
evaluations:
- type: llm_judge
criteria: "{{cases.closingCriteria}}"
evaluations:
- type: sequence
order:
- { actor: assistant, action: clarifies }
- { actor: assistant, action: informs }
- { actor: assistant, action: confirms }
- type: variable_consistency
variable: refundId
Everything can be parametrized: user messages, expected responses, even the evaluation criteria themselves. Each scenario can define its own quality bar.
A .jsonl file — one JSON object per row, one row per test scenario:
{"userMessage":"I received a damaged item, I want my money back. Order #8291.","expectedClarification":"I understand your order #8291 arrived damaged. I'll help you get a refund.","clarificationCriteria":"1. Classifies as refund\n2. References #8291\n3. Acknowledges damage\n4. Takes ownership","followUp":"Yes please, how long?","expectedResolution":"Refund of €47.50 approved. Reference: R-5512. 3-5 days.","expectedRefundId":"R-5512","resolutionCriteria":"1. States amount €47.50\n2. Reference R-5512\n3. Timeline 3-5 days\n4. Professional tone","closing":"Great, thanks.","expectedClosing":"You're welcome! Anything else?","closingCriteria":"1. Offers further help\n2. Doesn't reopen refund\n3. Concise"}
{"userMessage":"My order #3412 arrived broken, refund please.","expectedClarification":"Sorry about order #3412 arriving broken...","clarificationCriteria":"1. Classifies as refund\n2. References #3412\n3. Acknowledges breakage\n4. Takes ownership","followUp":"How fast is the refund?","expectedResolution":"Refund of €32.00 approved. Reference: R-7811. 5-7 business days.","expectedRefundId":"R-7811","resolutionCriteria":"1. States amount €32.00\n2. Reference R-7811\n3. Timeline 5-7 days\n4. Professional tone","closing":"Thanks!","expectedClosing":"You're welcome! Anything else I can do?","closingCriteria":"1. Offers further help\n2. Doesn't reopen refund\n3. Concise"}
{"userMessage":"Wrong item in my box for order #5567, I want a refund.","expectedClarification":"I see order #5567 had the wrong item...","clarificationCriteria":"1. Classifies as refund\n2. References #5567\n3. Acknowledges wrong item\n4. Takes ownership","followUp":"Yes, go ahead.","expectedResolution":"Refund of €89.99 approved. Reference: R-3394. 3-5 days.","expectedRefundId":"R-3394","resolutionCriteria":"1. States amount €89.99\n2. Reference R-3394\n3. Timeline 3-5 days\n4. Professional tone","closing":"Perfect, thanks.","expectedClosing":"Glad to help! Anything else?","closingCriteria":"1. Offers further help\n2. Doesn't reopen refund\n3. Concise"}
{"userMessage":"Order #1234 came with a cracked screen, refund please.","expectedClarification":"That's not acceptable — your order #1234 arrived with a cracked screen...","clarificationCriteria":"1. Classifies as refund\n2. References #1234\n3. Acknowledges cracked screen\n4. Takes ownership","followUp":"How many days?","expectedResolution":"Refund of €215.00 approved. Reference: R-9904. 3-5 days.","expectedRefundId":"R-9904","resolutionCriteria":"1. States amount €215.00\n2. Reference R-9904\n3. Timeline 3-5 days\n4. Professional tone","closing":"Awesome, thank you.","expectedClosing":"You're welcome! Anything else I can help with?","closingCriteria":"1. Offers further help\n2. Doesn't reopen refund\n3. Concise"}
Four scenarios — damaged, broken, wrong item, cracked screen — each with its own messages, expected responses, and evaluation criteria. Same session structure, different data.
abslang run refund-parametrized.abs.yaml \
--agent $URL \
--dataset refund-cases.jsonl
Four rows, four runs. Each row gets the full multi-stage evaluation — five llm_judge calls across three stages, plus the two chain evaluations. 28 evaluations total across the suite.
abslang run refund-parametrized.abs.yaml \
--agent $URL \
--dataset refund-cases.jsonl \
--filter "expectedRefundId:R-5512"
This is what you want as QA: the same spec, run against diverse real data, catching regressions scenario by scenario. The session is the what. The dataset is the with what data. The multi-stage evaluation is the how well — at every stage, for every case.
Next: Common mistakes →