Skip to content
AI agents · Data

Evals from real failures

Build LLM and agent evals the practitioner way: read real traces first, tally failure categories, then turn each into code-checked regression cases.

Verified install Not agent-tested v1.0.0 MIT updated pythonpytestuv
Equips
  • 1 1 Rules
  • 2 2 Skills
  • – 0 MCP

Teams that ship LLM features keep giving the same advice: look at your data before writing an eval, and test for the failures you actually have rather than generic scores. This loadout makes that the default. The agent reads 30 to 50 real traces, notes the first failure in each, groups and counts the notes with a small script, and only then writes evals: code checks first, a binary model judge only where code cannot check, validated against human labels. Every case links to the trace it came from and must be shown to fail on the original bad output.

What's in it

  • Error analysis before evals: one note per trace, categories after reading
  • tally.py ranks failure categories with shares and example trace ids
  • pytest regression runner with code checks; every case links to a real trace
  • Model judges binary only, checked against human labels
  • Each eval proven to fail on the original bad output

You'll need

  • Tools: python3, uv
Level 1 · automated

Verification

Verified install

Checked at commit 03c1b98 · 9 passed, 0 failed, 1 not applicable

CheckResultWhat we found
Listinglisting.yaml matches the schema and declares every file in the folder. Pass
Schema valid; 8 files declared, none missing or extra
  • tested.status: untested (agent tests are separate from Level 1)
Files parseJSON parses; SKILL.md and .mdc front matter is valid YAML with the fields each agent needs; code fences are closed. Pass All 8 files parse; front matter valid
AdaptersCLAUDE.md imports @AGENTS.md, GEMINI.md imports @./AGENTS.md, and both match the generator. Pass
CLAUDE.md and GEMINI.md import AGENTS.md and are up to date
  • CLAUDE.md: matches the generator
  • GEMINI.md: matches the generator
Install pathsEvery file lands where each agent documents it, with no collisions, and the laid-out project is re-discovered correctly. Pass
Correct paths for all 6 agents; layouts re-discovered
  • Claude Code: 7 files, 2 skills in .claude/skills/
  • Codex CLI: 6 files, 2 skills in .agents/skills/
  • Cursor: 6 files, 2 skills in .agents/skills/
  • Gemini CLI: 7 files, 2 skills in .agents/skills/
  • Antigravity CLI: 6 files, 2 skills in .agents/skills/
  • GitHub Copilot: 6 files, 2 skills in .agents/skills/
MCP configmcp.json is structurally valid, converts to each agent's format, declares no literal secrets, and referenced packages exist on npm/PyPI. Not applicable No MCP servers in this loadout
Safety scanThe intake scanner finds no credentials, risky shell, hidden Unicode or prompt-injection wording, and the listing's network flag is honest. Pass
Clean: no credentials, risky shell or hidden text
  • Scanner intake-precheck/1: verdict clean; network no (listing says no)
ReferencesLinks and file paths between the loadout's files resolve, including for each agent's installed layout. Pass 3 references between files resolve for every agent
Size limitsFile count and sizes are within the submission limits; SKILL.md bodies stay under 500 lines. Pass
8 files, 10.5 KB total; largest skills/failure-to-eval/references/test_regressions.py 2.3 KB
  • Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule
LicenceA recognised SPDX licence, with the licence text available. Pass MIT, by Agent Loadout; text in the repository LICENSE
Starter buildA minimal fixture project with the loadout applied installs and builds or type-checks, and the loadout's own code examples compile. Pass
tally.py ranks failure categories from a notes CSV and flags uncategorised failures; test_regressions.py fails on the original bad outputs and passes on the fix, and rejects cases without a source trace
  • Applied 13 loadout files at every agent's paths
  • ✓ tally failure categories: python3 .agents/skills/error-analysis/scripts/tally.py notes.csv
  • ✓ uncategorised failures are flagged: bash -c python3 .claude/skills/error-analysis/scripts/tally.py notes-incomplete.csv; test $? -eq 1 && echo flagged
  • ✓ copy the eval runner into evals/: cp .agents/skills/failure-to-eval/references/test_regressions.py evals/
  • ✓ evals fail on the original bad outputs: bash -c EVAL_TARGET=app:original PYTHONPATH=. uv run --no-project --with pytest==9.1.1 pytest evals -q -p no:cacheprovider; test $? -eq 1 && echo caught
  • ✓ evals pass on the fix: bash -c EVAL_TARGET=app:fixed PYTHONPATH=. uv run --no-project --with pytest==9.1.1 pytest evals -q -p no:cacheprovider
  • ✓ a case without a source trace is rejected: bash -c echo '{"id": "x", "input": {"question": "q", "today": "2026-10-17"}, "expect": {}}' >> evals/cases/internal-data.jsonl; EVAL_TARGET=app:fixed PYTHONPATH=. uv run --no-project --with pytest==9.1.1 pytest evals -q -p no:cacheprovider; test $? -eq 1 && echo rejected
  • Covers: Failure categories are counted from failing traces only, with shares and example trace ids
  • Covers: Failing rows without a category are listed and make the tally exit non-zero
  • Covers: Code checks (contains, not_contains, max_chars, json_keys) catch the original failures and pass on the fix
  • Covers: Every case must link to the real trace it came from

What this means: a script checked the files themselves: they parse, they install where each agent looks, any MCP config is sound, the safety scan is clean, references between files resolve, and, where a free and repeatable fixture exists, a small starter project builds with the loadout applied. No AI agent was run. Per-agent results ("tested with agent") are separate and stay untested until an agent actually runs.

Install

Choose your agent. Run the commands from your project root, or download a zip already laid out in the right folders.

Claude Code reads CLAUDE.md (imports AGENTS.md with @AGENTS.md) and skills from .claude/skills/.

Download zip
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/evals-from-real-failures/1.0.0/AGENTS.md
curl -fsSL --create-dirs -o CLAUDE.md https://loadout.jnw-it.com/raw/evals-from-real-failures/1.0.0/CLAUDE.md
curl -fsSL --create-dirs -o .claude/skills/error-analysis/SKILL.md https://loadout.jnw-it.com/raw/evals-from-real-failures/1.0.0/skills/error-analysis/SKILL.md
curl -fsSL --create-dirs -o .claude/skills/error-analysis/scripts/tally.py https://loadout.jnw-it.com/raw/evals-from-real-failures/1.0.0/skills/error-analysis/scripts/tally.py
curl -fsSL --create-dirs -o .claude/skills/failure-to-eval/SKILL.md https://loadout.jnw-it.com/raw/evals-from-real-failures/1.0.0/skills/failure-to-eval/SKILL.md
curl -fsSL --create-dirs -o .claude/skills/failure-to-eval/references/test_regressions.py https://loadout.jnw-it.com/raw/evals-from-real-failures/1.0.0/skills/failure-to-eval/references/test_regressions.py
curl -fsSL --create-dirs -o .claude/skills/failure-to-eval/references/judge-prompt.md https://loadout.jnw-it.com/raw/evals-from-real-failures/1.0.0/skills/failure-to-eval/references/judge-prompt.md
FileGoes to
AGENTS.mdAGENTS.md
CLAUDE.mdCLAUDE.md
skills/error-analysis/SKILL.md.claude/skills/error-analysis/SKILL.md
skills/error-analysis/scripts/tally.py.claude/skills/error-analysis/scripts/tally.py
skills/failure-to-eval/SKILL.md.claude/skills/failure-to-eval/SKILL.md
skills/failure-to-eval/references/test_regressions.py.claude/skills/failure-to-eval/references/test_regressions.py
skills/failure-to-eval/references/judge-prompt.md.claude/skills/failure-to-eval/references/judge-prompt.md

Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.

Files

Rules AGENTS.md Order of work and rules for evals built from real traces. 27 lines
Download Raw
# AGENTS.md: evals from real failures

This project has an LLM feature or agent. Evals here are built from what actually went wrong, not from a generic metric list.

## Order of work
1. Look at the data first. Before writing any eval, read real traces (inputs, tool calls, outputs). Start with 30 to 50.
2. Error analysis: write a short free-text note on every failing trace, then group the notes into failure categories.
   Use the `error-analysis` skill. Count them; fix or evaluate the biggest categories first.
3. Only then write evals, one per category that matters. Use the `failure-to-eval` skill.
4. Every bug found in production becomes a regression case that links to its trace id.

## Rules for evals
- Prefer code checks (exact fields, JSON shape, forbidden strings, length, tool called or not) over model judges.
- Model judges only for what code cannot check. Binary pass or fail with a one-line reason, never a 1 to 5 score.
- Validate every judge against human labels before trusting it: report how often it agrees on passes and on fails separately.
- No generic metrics ("helpfulness", "coherence") unless error analysis showed that failure.
- Keep a held-out set. Never tune a prompt against the same cases you report.
- An eval that has never failed tells you nothing. Show it fails on the original bad output before adding it.

## Data handling
- Traces can contain personal data. Work on exports in `evals/traces/` that are gitignored, and redact before committing a case.
- Do not send traces to a third-party service the human has not approved.

## Commands
- Tally failure notes: `python3 <skills dir>/error-analysis/scripts/tally.py evals/notes.csv`
- Run evals: `uv run --with pytest pytest evals -q` (or the project's own test command).
Adapter CLAUDE.md Imports AGENTS.md for Claude Code. 10 lines
Download Raw
# CLAUDE.md

@AGENTS.md

## Claude Code notes
- Skills from this loadout live in `.claude/skills/`: `error-analysis`, `failure-to-eval`. Use them when their description matches the task.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.

<!-- From the Agent Loadout "Evals from real failures" v1.0.0 — https://loadout.jnw-it.com/loadouts/evals-from-real-failures/ -->
Adapter GEMINI.md Imports AGENTS.md for Gemini CLI. 10 lines
Download Raw
# GEMINI.md

@./AGENTS.md

## Gemini CLI notes
- Skills from this loadout live in `.agents/skills/`: `error-analysis`, `failure-to-eval`. Use them when their description matches the task.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.

<!-- From the Agent Loadout "Evals from real failures" v1.0.0 — https://loadout.jnw-it.com/loadouts/evals-from-real-failures/ -->
Skill skills/error-analysis/SKILL.md Read traces, note failures, group and count them. 28 lines
Download Raw
---
name: error-analysis
description: Read real traces, note what went wrong in each, group notes into failure categories and count them. Use before writing any eval, after a model or prompt change, or when users report bad answers.
---

# Error analysis

Goal: a ranked list of real failure modes, each with example trace ids.

1. Export a sample of recent traces to `evals/traces/` (gitignored). Mix ordinary traffic with known complaints.
   30 to 50 traces is enough to start; stop adding when new traces stop producing new kinds of failure.
2. Create `evals/notes.csv` with the header `trace_id,pass,note,category`.
3. Read each trace end to end: input, retrieved context, tool calls, final output. For each one write:
   - `pass`: `1` if a domain expert would accept the output as is, else `0`.
   - `note`: the first thing that went wrong, in plain words ("ignored the date range", "called search twice").
   Leave `category` empty for now. Do not invent categories before reading.
4. Read all the notes together. Group them into 4 to 8 categories named after the failure, not the component
   ("wrong date range", not "retrieval"). Fill in `category` for every failing row.
5. Tally: `python3 scripts/tally.py evals/notes.csv` prints each category with its count, share of failures and example
   trace ids, and lists failing rows that still have no category.
6. Report the table to the human. Recommend: fix obvious bugs directly (a broken prompt line, a missing tool argument);
   write evals only for failures that will recur or are costly.

Rules:
- One note per trace, about the first failure. Downstream errors usually follow from it.
- Do not grade with a model at this stage. The point is for a person (or you, read carefully) to see the data.
- Re-run this after every model or prompt change; categories move.
Skill file skills/error-analysis/scripts/tally.py Ranks failure categories from a notes CSV. 46 lines
Download Raw
#!/usr/bin/env python3
"""Tally failure categories from an error-analysis notes CSV (trace_id,pass,note,category)."""

import argparse
import csv
import sys
from collections import Counter, defaultdict
from pathlib import Path


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("notes", type=Path)
    ap.add_argument("--examples", type=int, default=3, help="example trace ids per category")
    args = ap.parse_args()

    with args.notes.open(newline="", encoding="utf-8") as fh:
        rows = list(csv.DictReader(fh))
    missing = {"trace_id", "pass", "note", "category"} - set(rows[0].keys() if rows else [])
    if missing:
        print(f"error: missing columns: {', '.join(sorted(missing))}", file=sys.stderr)
        return 2

    failing = [r for r in rows if r["pass"].strip() == "0"]
    counts = Counter(r["category"].strip() for r in failing if r["category"].strip())
    examples = defaultdict(list)
    for r in failing:
        cat = r["category"].strip()
        if cat and len(examples[cat]) < args.examples:
            examples[cat].append(r["trace_id"])
    uncategorised = [r["trace_id"] for r in failing if not r["category"].strip()]

    print(f"traces: {len(rows)}, failing: {len(failing)} ({len(failing) / max(len(rows), 1):.0%})")
    print("| category | count | share of failures | examples |")
    print("| --- | --- | --- | --- |")
    for cat, n in counts.most_common():
        print(f"| {cat} | {n} | {n / len(failing):.0%} | {', '.join(examples[cat])} |")
    if uncategorised:
        print(f"uncategorised failures: {', '.join(uncategorised)}")
        return 1
    return 0


if __name__ == "__main__":
    sys.exit(main())
Skill skills/failure-to-eval/SKILL.md Turn a failure category into regression cases. 23 lines
Download Raw
---
name: failure-to-eval
description: Turn a failure category or a production bug into regression eval cases with code checks, and a model judge only when needed. Use after error analysis, or when a bad output is reported.
---

# Failure to eval

Inputs: a failure category from error analysis (or a reported bug) with real trace ids.

1. For each trace, add one case to `evals/cases/<category>.jsonl`:
   `{"id": "...", "source": "<trace id>", "input": {...}, "expect": {...}}`. Redact personal data first.
   `source` is required: every case points at the real trace it came from.
2. Write the expectation as code checks wherever possible. `references/test_regressions.py` supports:
   `contains`, `not_contains`, `max_chars`, `json_keys` (output must be JSON with these keys) and `matches` (regex).
3. Copy `references/test_regressions.py` to `evals/` and set `EVAL_TARGET=module:function` to the function that produces
   the output (it receives the case `input` dict and returns a string).
4. Prove the case bites: run it against the original bad output (or the old prompt) and confirm it fails. Then confirm it
   passes on the fix. A case that never failed is not a regression test.
5. Only if code cannot express the check, write a judge prompt from `references/judge-prompt.md`: one failure mode per
   judge, binary pass or fail, a one-line reason. Label 20 or more outputs by hand, run the judge on them and report its
   agreement on passes and on fails separately. Do not use the judge until both are acceptable to the human.
6. Run `uv run --with pytest pytest evals -q` and add the command to CI next to the unit tests.
Skill file skills/failure-to-eval/references/test_regressions.py pytest runner for JSONL cases with code checks. 70 lines
Download Raw
"""Regression evals built from real failures. Each case in evals/cases/*.jsonl links to the trace it came from.

Set EVAL_TARGET=module:function. The function takes the case's input dict and returns the output string.
"""

import importlib
import json
import os
import re
from pathlib import Path

import pytest

CASES_DIR = Path(os.environ.get("EVAL_CASES", Path(__file__).parent / "cases"))


def load_cases():
    cases = []
    for path in sorted(CASES_DIR.glob("*.jsonl")):
        for n, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
            if line.strip():
                case = json.loads(line)
                case["_where"] = f"{path.name}:{n}"
                cases.append(case)
    return cases


def target():
    module, _, func = os.environ["EVAL_TARGET"].partition(":")
    return getattr(importlib.import_module(module), func)


def check(output: str, expect: dict) -> list[str]:
    problems = []
    for s in expect.get("contains", []):
        if s not in output:
            problems.append(f"missing {s!r}")
    for s in expect.get("not_contains", []):
        if s in output:
            problems.append(f"contains forbidden {s!r}")
    if "max_chars" in expect and len(output) > expect["max_chars"]:
        problems.append(f"{len(output)} chars > {expect['max_chars']}")
    if "matches" in expect and not re.search(expect["matches"], output):
        problems.append(f"does not match /{expect['matches']}/")
    if "json_keys" in expect:
        try:
            data = json.loads(output)
            absent = [k for k in expect["json_keys"] if k not in data]
            if absent:
                problems.append(f"JSON missing keys {absent}")
        except (json.JSONDecodeError, TypeError):
            problems.append("output is not JSON")
    return problems


CASES = load_cases()


def test_every_case_links_to_a_real_trace():
    assert CASES, f"no cases in {CASES_DIR}"
    unlinked = [c["_where"] for c in CASES if not str(c.get("source", "")).strip()]
    assert not unlinked, f"cases without a source trace id: {unlinked}"


@pytest.mark.parametrize("case", CASES, ids=[c.get("id", c["_where"]) for c in CASES])
def test_case(case):
    output = target()(case["input"])
    problems = check(output, case["expect"])
    assert not problems, f"{case['_where']} (trace {case.get('source')}): {'; '.join(problems)}"
Skill file skills/failure-to-eval/references/judge-prompt.md Binary judge template with a validation step. 18 lines
Download Raw
# Judge prompt template (one failure mode per judge)

You are checking one thing only: <failure mode, in the words from error analysis>.

Pass if: <concrete description of acceptable output>.
Fail if: <concrete description of the failure, with one real example>.

Input:
<input>

Output to check:
<output>

Answer with JSON only: {"pass": true or false, "reason": "<one sentence>"}

Before use: label at least 20 outputs by hand, run this judge on them and report agreement on passes and on fails
separately. Rewrite the pass and fail lines, not the labels, until the human accepts both numbers.

All files: source zip · Licence MIT · By Agent Loadout · First listed

Installing and testing

How do I install Evals from real failures in Claude Code?

From your project root, put AGENTS.md, CLAUDE.md, skills/error-analysis/SKILL.md at .claude/skills/error-analysis/SKILL.md, skills/error-analysis/scripts/tally.py at .claude/skills/error-analysis/scripts/tally.py, skills/failure-to-eval/SKILL.md at .claude/skills/failure-to-eval/SKILL.md, skills/failure-to-eval/references/test_regressions.py at .claude/skills/failure-to-eval/references/test_regressions.py, skills/failure-to-eval/references/judge-prompt.md at .claude/skills/failure-to-eval/references/judge-prompt.md. The "Claude Code" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/evals-from-real-failures/claude-code.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.

How do I install Evals from real failures in Codex CLI?

From your project root, put AGENTS.md, skills/error-analysis/SKILL.md at .agents/skills/error-analysis/SKILL.md, skills/error-analysis/scripts/tally.py at .agents/skills/error-analysis/scripts/tally.py, skills/failure-to-eval/SKILL.md at .agents/skills/failure-to-eval/SKILL.md, skills/failure-to-eval/references/test_regressions.py at .agents/skills/failure-to-eval/references/test_regressions.py, skills/failure-to-eval/references/judge-prompt.md at .agents/skills/failure-to-eval/references/judge-prompt.md. The "Codex CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/evals-from-real-failures/codex.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.

How do I install Evals from real failures in Cursor?

From your project root, put AGENTS.md, skills/error-analysis/SKILL.md at .agents/skills/error-analysis/SKILL.md, skills/error-analysis/scripts/tally.py at .agents/skills/error-analysis/scripts/tally.py, skills/failure-to-eval/SKILL.md at .agents/skills/failure-to-eval/SKILL.md, skills/failure-to-eval/references/test_regressions.py at .agents/skills/failure-to-eval/references/test_regressions.py, skills/failure-to-eval/references/judge-prompt.md at .agents/skills/failure-to-eval/references/judge-prompt.md. The "Cursor" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/evals-from-real-failures/cursor.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.

How do I install Evals from real failures in Gemini CLI?

From your project root, put AGENTS.md, GEMINI.md, skills/error-analysis/SKILL.md at .agents/skills/error-analysis/SKILL.md, skills/error-analysis/scripts/tally.py at .agents/skills/error-analysis/scripts/tally.py, skills/failure-to-eval/SKILL.md at .agents/skills/failure-to-eval/SKILL.md, skills/failure-to-eval/references/test_regressions.py at .agents/skills/failure-to-eval/references/test_regressions.py, skills/failure-to-eval/references/judge-prompt.md at .agents/skills/failure-to-eval/references/judge-prompt.md. The "Gemini CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/evals-from-real-failures/gemini-cli.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.

How do I install Evals from real failures in Antigravity CLI?

From your project root, put AGENTS.md, skills/error-analysis/SKILL.md at .agents/skills/error-analysis/SKILL.md, skills/error-analysis/scripts/tally.py at .agents/skills/error-analysis/scripts/tally.py, skills/failure-to-eval/SKILL.md at .agents/skills/failure-to-eval/SKILL.md, skills/failure-to-eval/references/test_regressions.py at .agents/skills/failure-to-eval/references/test_regressions.py, skills/failure-to-eval/references/judge-prompt.md at .agents/skills/failure-to-eval/references/judge-prompt.md. The "Antigravity CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/evals-from-real-failures/antigravity-cli.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.

How do I install Evals from real failures in GitHub Copilot?

From your project root, put AGENTS.md, skills/error-analysis/SKILL.md at .agents/skills/error-analysis/SKILL.md, skills/error-analysis/scripts/tally.py at .agents/skills/error-analysis/scripts/tally.py, skills/failure-to-eval/SKILL.md at .agents/skills/failure-to-eval/SKILL.md, skills/failure-to-eval/references/test_regressions.py at .agents/skills/failure-to-eval/references/test_regressions.py, skills/failure-to-eval/references/judge-prompt.md at .agents/skills/failure-to-eval/references/judge-prompt.md. The "GitHub Copilot" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/evals-from-real-failures/copilot.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.

Has Evals from real failures been tested?

It passed the Level 1 automated install checks on 2026-10-17: the files parse, install to each agent's documented paths, and pass the safety scan. No AI agent has run it yet, so no agent pass or fail is claimed.

More for Claude Code, Codex, Cursor, Gemini CLI, Antigravity CLI and GitHub Copilot.