Critic judge design
Skill Victoriakaey/build-reliable-agents/skills/critic-judge-design
Use when designing any LLM-as-Judge, Critic, or Evaluator node. Covers input structure, output schema, chain-of-thought ordering, single-pass vs multi-stage tradeoffs, and known failure modes. Prevents the most common design mistakes that cause Critic nodes to be unreliable.From its SKILL.md
npx -y skills add Victoriakaey/build-reliable-agents --skill critic-judge-designAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.3 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it
Critic / Judge Node Design
Terminology: This skill uses "Critic", "Judge", and "Evaluator" interchangeably. Pick one term for your codebase and use it consistently.
The Core Problem
A Critic node has one primary job: judge whether something meets a quality bar. Everything else it does (generating feedback, planning follow-ups, classifying failure types) is secondary.
The most common mistake: making the Critic do too much in one pass. When a node simultaneously judges quality AND generates structured follow-up plans, the two tasks interfere with each other — and the primary job (judgment) suffers.
Step 1: Define the Primary Job
Before designing any Critic, answer:
- What exactly is being judged? (evidence sufficiency, answer quality, SQL correctness, etc.)
- What is the binary output? (sufficient/insufficient, pass/fail, correct/incorrect)
- What does the Critic need to see to make this judgment?
- What does NOT need to be in the Critic's input?
The cleaner the primary job definition, the more reliable the Critic.
Step 2: Design the Input Structure
This is the most important decision. Input structure determines reasoning patterns more than prompt instructions do.
The Organization Problem
If you organize input by sub-question:
SQ1: "Find the patch window" → 0 rows
SQ2: "Find rollback artifacts" → 3 rows (contains patch window)
The LLM will evaluate each sub-question against its own text. It will conclude "patch window not found" from SQ1 even if SQ2's rows contain the answer. The input structure primes per-sub-question reasoning.
Flat Evidence Pool (preferred for sufficiency judgment)
Instead of organizing by sub-question, present evidence as a flat pool:
Original question: ...
Retrieved evidence:
- Row 1: {customer: "X", patch_window: "2026-03-24 02:00", ...}
- Row 2: {customer: "X", rollback_cmd: "orchestrator rollback ...", ...}
This forces the LLM to evaluate evidence against the original question, not against sub-question text.
Tradeoff: You lose sub-question provenance (can't say "SQ2 found this"). Gain: correct holistic reasoning.
When to keep sub-question organization
Keep it when the Critic's job IS per-sub-question evaluation (e.g., "did this specific sub-question return useful data?"). Remove it when the Critic's job is holistic sufficiency (e.g., "can the original question be answered from all evidence combined?").
Step 3: Design the Output Schema
Chain-of-thought ordering matters. The LLM reasons in the order it writes. Put the reasoning BEFORE the judgment, not after.
Wrong order (judgment first):
{
"is_sufficient": true,
"judgment": "Evidence covers the patch window...",
"confidence": 90
}
The LLM commits to is_sufficient before writing the reasoning. The reasoning becomes post-hoc justification.
Right order (reasoning first):
{
"evidence_present": ["patch_window", "rollback_cmd"],
"evidence_missing": [],
"judgment": "Both required fields are present in the retrieved rows...",
"confidence": 90,
"is_sufficient": true
}
The LLM must enumerate what's present and missing before committing to a verdict. This produces more accurate judgments.
Coverage score vs binary
Use an intermediate score (1-4) and derive a binary from it:
coverage_score: 1-4
is_sufficient: derived from coverage_score >= 3
This gives the LLM room to express nuance, and keeps the routing logic deterministic (binary derived from score, not LLM-generated bool).
Step 4: Decide Single-Pass vs Multi-Stage
Single-pass Critic (judgment + feedback in one call)
Use when:
- Feedback is simple (just a list of missing sub-questions)
- Prompt stays under ~60 lines
- The feedback doesn't require complex reasoning beyond what judgment already does
Risk: As feedback complexity grows, judgment quality degrades. The two tasks interfere.
Multi-stage Critic (judgment in one call, feedback in another)
Use when:
- Feedback requires detailed planning (gap types, dependency hints, suggested tables, strategy notes)
- Prompt is already long (>80 lines)
- Judgment quality is inconsistent
Structure:
Stage 1 — Judgment node:
Input: original question + flat evidence pool
Output: is_sufficient, retrieval_outcome, confidence, brief judgment
Stage 2 — Feedback Planner node (only runs if insufficient):
Input: original question + evidence + judgment from Stage 1
Output: gap_type, missing_evidence, suggested_tables, dependency_hints
Stage 3 — Follow-up Parser (already exists):
Input: feedback from Stage 2
Output: new sub-questions
Cost: One extra LLM call on insufficient cases only. No cost on sufficient cases.
Step 5: Routing Logic
Always derive routing from deterministic fields, not LLM-generated booleans.
# Fragile — LLM generates the bool
if state["is_sufficient"]:
route to synthesizer
# Robust — derive from score
if state["coverage_score"] >= 3:
route to synthesizer
For multi-outcome routing (sufficient / needs_more / no_evidence / unsupported_premise):
match state["retrieval_outcome"]:
case "sufficient": route to synthesizer
case "needs_more_retrieval": route to follow-up parser (if rounds < max)
case "no_evidence": route directly to synthesizer
case "unsupported_premise": route directly to synthesizer
case _: route to synthesizer (safe default)
Always have a safe default that prevents infinite loops.
Step 6: Loop Guard
Every Critic-driven loop MUST have a hard exit condition independent of Critic judgment:
if critic_round_count >= max_rounds:
force route to synthesizer
# regardless of what Critic says
Also add sub-question deduplication to prevent the Critic from requesting the same sub-question repeatedly:
seen_signatures = set()
# before adding to pending queue, check signature
if sub_question_signature not in seen_signatures:
pending_queue.append(sub_question)
seen_signatures.add(sub_question_signature)
Known Failure Modes
| Failure | Symptom | Root Cause | Fix |
|---|---|---|---|
| Per-sub-question reasoning | Critic judges each SQ independently, misses cross-SQ evidence | Input organized by sub-question | Flatten evidence pool |
| Post-hoc justification | Critic commits to verdict, writes reasoning to match | Judgment field before reasoning in schema | Reorder: reasoning → score → verdict |
| Ghost rules | Critical rules ignored, less important rules followed | Prompt too long, rules competing for attention | Simplify prompt, put critical rules first |
| Infinite loops | Critic keeps saying insufficient even when evidence exists | No loop guard, or loop guard too permissive | Hard exit at max_rounds regardless of Critic |
| Follow-up drift | Follow-up sub-questions drift from original question scope | Critic generates follow-ups without entity anchoring | Require Critic to scan completed SQs for entity names before generating feedback |
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most design frontend skills give in ~1.6k tokens
Counted across 1,179 of the 2,086 authors here whose files we hold, read 2026-09-06
- Commit to a bold aesthetic directionin 31 of 1179, across 24 files
- Prefer component composition over inheritancein 28 of 1179, across 14 files
- Animate only transform and opacity propertiesin 27 of 1179, across 22 files
- Memoize expensive computations with useMemoin 26 of 1179, across 13 files
- Use semantic HTML elementsin 24 of 1179, across 23 files
- Virtualize long lists for performancein 21 of 1179, across 10 files
- Use CSS variables for design tokensin 20 of 1179, across 14 files
- Implement loading, empty, and error statesin 20 of 1179
- Lazy load heavy components with Suspensein 19 of 1179, across 8 files
- Respect prefers-reduced-motion media queriesin 18 of 1179, across 10 files
- Prioritize CSS-only animations for HTMLin 18 of 1179, across 16 files
- Use compound components for related UI elementsin 18 of 1179, across 7 files
Said here and by no other author read
- define the primary judgment task clearly
- use a flat evidence pool for sufficiency judgments
- place reasoning fields before the judgment field in schema
- derive binary outputs from intermediate scores
- separate judgment and feedback into multi-stage nodes
- derive routing logic from deterministic fields
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.