---
name: eval-llm-judge
description: >
  Write LLM-as-judge evaluator prompts for AI products. Use this skill whenever you need to
  evaluate semantic properties of AI outputs that cannot be checked programmatically —
  output quality, tone, factual accuracy, edge case handling, reference alignment. Also use
  when auditing existing judge prompts for bias, vagueness, or compound criteria. Output is a
  judge prompt string and a Python runner function, saved to the project's evaluators file.
---

# LLM Judge Evaluator Skill

An LLM-as-judge evaluator uses a language model to score another model's output against a
defined criterion. It handles semantic properties that code-based checks cannot: is the ranking
defensible? Did the agent handle ambiguity gracefully? Does the output align with the reference?

The tradeoff: judges are slower, more expensive, and non-deterministic. They require calibration
against human labels before you can trust their scores. Use them only after code-based checks have
covered the structural requirements.

## When to use this skill

Write an LLM judge when the property requires reasoning that a human reviewer would apply:
- **Output correctness**: are the results correct and in a defensible order or format?
- **Edge case handling**: did the agent appropriately flag ambiguity or decline gracefully?
- **Reference alignment**: does the output agree with a gold-standard expected response?
- **Tone and framing**: is the hedging appropriate for a speculative or high-stakes claim?
- **Faithfulness**: does the output stay within what the cited sources actually say?

Do NOT write an LLM judge for:
- Properties a code check can handle (count, format, presence) — these are cheaper and faster
- Compound criteria — write one judge per criterion, not one judge that checks everything

---

## Workflow

### Step 1 — Define exactly ONE criterion

The most common failure in judge design is compound criteria: "Is the output accurate, well-formatted,
and appropriately hedged?" This produces judges that are hard to calibrate and whose scores are
uninterpretable — a 0 could mean any of three things.

Write the criterion as one sentence that completes: *"A good output [verb phrase]."*

Good: "A good output ranks the top 3 companies in defensible order based on the stated metrics."
Good: "A good output explicitly states its interpretation when the query is ambiguous."
Bad: "A good output is accurate, well-cited, and appropriately hedged." (three criteria)

If you're tempted to write a compound criterion, split it into two judges.

### Step 2 — Write binary pass/fail definitions

**Binary (PASS/FAIL)** forces the judge to commit and produces clean metrics.

Do not use a 1–5 Likert scale. It collapses to 3s and 4s and tells you nothing. Do not add a
BORDERLINE tier — it becomes a crutch and makes TPR/TNR harder to interpret.

Write definitions for each level that are:
- **Specific**: reference the criterion, not generic quality
- **Mutually exclusive**: a judge should not reasonably assign two levels to the same output
- **Grounded**: name what counts as evidence, not just "good" vs "bad"

### Step 3 — Write 3 few-shot examples

The examples are the most important part of the prompt. They calibrate the judge's interpretation
of the criterion and tell it what evidence to look for.

Required: **one clear PASS, two clear FAILs** covering different failure modes.

Rules for good examples:
1. **Use two distinct FAIL examples.** Different failure modes teach the judge where the line is better than a single example. Spend more time on these than on the PASS.
2. **Use real-looking outputs**, not toy ones. The output in the example should look like what your
   model actually produces — same formatting, similar length, same hedging patterns.
3. **Write the critique before the score.** The critique teaches the judge *why* to assign a score,
   not just *what* score to assign. The judge should be able to derive the score from the critique.
4. **Don't use inputs from your eval dataset.** Example inputs and outputs should be entirely
   distinct from the queries in your test set, to avoid contaminating judge behavior.
5. **Match the domain.** Use examples from your actual product — same output format, same hedging
   patterns, same vocabulary. Don't use generic QA examples; the judge will pattern-match to the
   wrong signals.

### Step 4 — Require structured output

The judge's output should be a JSON object, with the critique *before* the score:

```json
{"critique": "2-3 sentence explanation of why this output passes/fails", "score": "PASS"}
```

Critique-first ordering matters: it forces the judge to reason before committing to a score,
which produces more reliable results than score-first or score-only formats.

### Step 5 — Validate the judge against human labels

Before using a judge in production evals, validate it:

1. Label 20–40 outputs by hand (10–20 PASS, 10–20 FAIL)
2. Run the judge on the same outputs
3. Calculate **TPR** (when human says PASS, judge also says PASS) and **TNR** (when human says FAIL,
   judge also says FAIL)
4. Target: TPR > 90% and TNR > 90%. Minimum viable: both > 80%
5. If failing, read the mismatches — they reveal criterion ambiguity or missing example coverage

If a judge consistently fails on a specific sub-type of output, add a few-shot example covering
that sub-type. If the criterion itself is ambiguous, rewrite it before adding more examples.

---

## Prompt Template

```python
JUDGE_PROMPT = """\
You are evaluating [AI product description].

## Evaluation Criterion
**[Criterion Name]**: [One-sentence definition of what a good output does.]

## Scoring
- **PASS**: [Specific description of what qualifies as a pass, grounded in the criterion.]
- **FAIL**: [Specific description of what qualifies as a fail.]

## Few-Shot Examples

### Example 1 — PASS
Input: "[example input]"
Output excerpt: "[example output]"
Result: {{"critique": "[why this passes]", "score": "PASS"}}

### Example 2 — FAIL
Input: "[example input]"
Output excerpt: "[example output]"
Result: {{"critique": "[why this fails]", "score": "FAIL"}}

### Example 3 — FAIL
Input: "[example input]"
Output excerpt: "[example output showing a different failure mode]"
Result: {{"critique": "[why this fails]", "score": "FAIL"}}

---

Now evaluate:

Input: {input}
Output: {output}

Respond with JSON only:
{{"critique": "<2-3 sentence explanation>", "score": "PASS" | "FAIL"}}
"""
```

---

## Runner Function Template

```python
def my_judge(output: str, input: str, expected: str = None) -> dict:
    """
    LLM judge for [criterion name].
    Returns: {"score": 0.0|0.5|1.0, "metadata": {"critique": str, "label": str}}
    """
    import anthropic, json, re

    client = anthropic.Anthropic()
    prompt = JUDGE_PROMPT.format(input=input, output=output)

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=512,
        messages=[{"role": "user", "content": prompt}]
    )
    text = response.content[0].text.strip()

    # Strip markdown fences if present
    text = re.sub(r'^```(?:json)?\n?', '', text)
    text = re.sub(r'\n?```$', '', text)

    try:
        result = json.loads(text)
    except json.JSONDecodeError:
        return {"score": 0.0, "metadata": {"critique": "Failed to parse response", "raw": text}}

    score_map = {"PASS": 1.0, "FAIL": 0.0}
    return {
        "score": score_map.get(result.get("score", "FAIL"), 0.0),
        "metadata": {
            "critique": result.get("critique", ""),
            "label": result.get("score", "FAIL")
        }
    }
```

---

## Working Notes

- **One criterion per judge, always.** This is the rule most often violated. A judge that checks
  two things at once cannot be improved — you don't know which criterion is failing. Split compound
  criteria before anything else.

- **The FAIL examples are more valuable than the PASS example.** Most judges default to PASS
  on ambiguous cases unless trained otherwise. Use two distinct FAIL examples covering different
  failure modes to teach the judge where the line is.

- **Don't use your eval dataset inputs as judge examples.** This is data leakage — the judge has
  effectively been pre-told the answer for those inputs. Use different examples that illustrate
  the same criterion.

- **Critique before score, always.** Putting the critique first in the JSON output forces the model
  to reason before committing. Putting the score first produces post-hoc rationalization, which
  is less reliable.

- **Validate TPR and TNR separately.** A judge can have 90% overall accuracy but 70% TNR — it's
  too lenient, marking real failures as passes. Track both rates. If TNR is low, make the FAIL
  definition more specific and add a clear FAIL example.

- **Model choice matters for judge quality.** Use the strongest model you can afford for judges.
  A weaker judge model introduces noise that looks like model variation. claude-sonnet-4-6 is
  the minimum; claude-opus-4-6 is preferable for nuanced semantic judgments.

- **Re-validate judges when the prompt changes.** If you update the system prompt (e.g., adding
  few-shot examples), re-run your human-labeled validation set. Prompt changes shift output
  distributions, which can invalidate a previously calibrated judge.

- **For reference judges specifically**: the gold-standard expected answer should be written by
  a domain expert, not generated by the same model you're evaluating. Using model-generated
  expected outputs as the reference creates circular evaluation — the judge will reward outputs
  that sound like the model, not outputs that are correct.
