# Draft, critique, revise, check

**AI Engineering Visualised** · Prompting · Film, 2:10 · Updated 2026-09-11

Draft, critique, revise is a prompting pattern where one model writes, a second scores the draft against a rubric, and the first revises against that score, with code checking whatever can be counted. It improves output on average and can make individual revisions worse.

> One outage update goes round a revision loop: a writer model drafts, a critic model scores it against a five-point rubric and returns JSON, your code re-checks what it can count, and the writer revises one target at a time. Every text and verdict is a real model output, including the revision that made things worse.

Watch the film: https://understandingdata.com/ai-engineering-visualised/draft-critique-revise/
Video file: https://assets.understandingdata.com/viz/draft-critique-revise/video/film.mp4?v=e565d9234a

Asking a model to "make it better" and then asking again tends to make a draft different rather than better. Each pass fixes something the last one noticed and quietly moves something else. The film shows the loop that keeps iteration honest, often called self-refine or evaluator-optimiser: a writer model drafts, a critic model scores the draft against a written rubric and returns a structured verdict, your code picks one target, the writer revises, and the loop stops when every check passes or its lap budget runs out.

The task is a customer-facing status update for a payments incident, written from an internal log that invites trouble on purpose: it names the on-call engineer, a paging mistake, and the vendor whose key rotation caused the outage. The rubric has five checks. The update must say who is affected and what they cannot do, say when it began (14:02 UTC), say when the next update comes (15:00 UTC), stay under 80 words, and blame no person, team or vendor. Start time, next update and length are checked in code. Impact and blame need judgement, so the critic decides them.

Every text and verdict in the film is real. Both parts were played by `google/gemini-3.8-flash` through OpenRouter at temperature 0 on 11 September 2026, the critic in its own call. It got the rubric and the draft under a strict JSON schema, a pass and a one-line note for every criterion plus a list of what fails, and it ran three times per version. The three runs agreed on every criterion of every version. One further reply arrived cut off mid-JSON and failed to parse, which is exactly why the loop reads the reply with `Verdict.parse(JSON.parse(raw))` and asks again rather than trusting whatever came back.

The first draft ran to 136 words, and the critic failed it on length. It passed the start time, in all three runs, with the note "Mentions orders placed between 14:02 and 14:31 UTC": the only 14:02 in the draft is about which orders might show as payment pending, not when the outage began. The code check needs 14:02 stated as a start, so it fails, and because the loop spreads the code checks over the critic's scores, code wins wherever code can count. The critic also miscounted words, 35 for a draft of 43 and 52 for one of 58. The loop then took one target, the first failing criterion in rubric order, and sent it to the writer with the whole rubric. Version two came back in 43 words, the loop re-scored all five criteria rather than only the target, every one passed, and the loop stopped and kept it.

Then the failure mode, which we set up on purpose and want to be plain about. An earlier recording, where each lap simply fixed the next failing criterion with the rubric in view, produced no regression at all. So the third version comes from the kind of request that often arrives mid-review: one narrow stakeholder ask, "make the impact clearer: tell customers exactly what went wrong and why", sent with the update but without the rubric. The model did what was asked. Version three is clearer, and it grew to 103 words and explained that the outage "was caused by an unannounced security key update from our third-party payment vendor". The word count failed in code and the critic voted three to nothing that it blamed the vendor. Two checks that version two passed turned red, and neither was what the edit aimed at. The fix sent the edit again with the rubric back in the prompt, version two in view as the last version that passed, and an instruction to lose nothing that had passed. Version four keeps the new detail about pending orders and duplicate charges, drops the blame, fits in 58 words and passes all five.

Two honest notes. The code checks are only as good as their patterns: a regular expression that matched 14:02 anywhere would have passed the first draft exactly as the critic did, so the check has to say what it means, a start. And the code on screen is the shape of what ran rather than every line of it; the lap budget of four never bit here, because the loop converged on its second lap, but a loop without one can spend forever on a rubric its writer cannot meet.

What to keep: let code decide whatever it can count, put the whole rubric in the prompt with every edit, re-score every criterion after it, keep the last version that passed so a bad edit can be undone, and cap the laps.

## The maths

### Every version gets the whole rubric

```latex
s(v) \;=\; \big(c_1(v),\, c_2(v),\, \dots,\, c_5(v)\big) \in \{0, 1\}^5, \qquad \text{accept } v \iff \textstyle\prod_{i} c_i(v) = 1
```

Each criterion is a check that passes or fails. The loop scores all five for every version, whichever one the edit aimed at, and a version is only kept when every check passes.

### A regression

```latex
\text{regressed}(v_k) \;=\; \{\, i \;:\; c_i(v_{k-1}) = 1 \;\wedge\; c_i(v_k) = 0 \,\}
```

A check the previous version passed and this one fails. Version three regressed on two: length and blame. Only re-scoring every criterion after every edit finds these, because neither was the target of the edit that broke it.

### Code checks overrule the critic

```latex
\text{score}_i(v) \;=\; \begin{cases} \text{code}_i(v) & i \in \{\text{start},\, \text{next},\, \text{length}\} \\ \big[\, \textstyle\sum_{r=1}^{3} p_{ir}(v) \ge 2 \,\big] & \text{otherwise} \end{cases}
```

Start time, next update and length are deterministic checks in code, two regular expressions and a word count, and where code can count, its answer replaces the critic's. Impact and blame need judgement, so the critic scored each version three times and the majority stands; p_ir is 1 when run r passes criterion i. The critic passed the first draft's start time in all three runs; the code check failed it.

### The loop stops on a pass or a budget

```latex
v_{k+1} \;=\; W\big(v_k,\ \text{fails}(v_k)_1,\ R\big) \quad \text{until} \quad \text{fails}(v_k) = \varnothing \ \text{ or } \ k = K
```

Each lap the writer W gets the current version, one target (the first failing criterion in rubric order) and the whole rubric R. The loop returns as soon as nothing fails, and throws after K = 4 laps (MAX_LAPS in the code) so a loop that never converges still stops. Here it converged on the second lap.

## Transcript

### 0:00 One rubric

Asking a model to keep improving a draft makes it different rather than better, so this loop scores every version of an outage update against one written rubric: five checks, three in code and two by a model.

### 0:15 First draft

Trace one update: the writer model turns the team's internal outage log into a first draft of 136 words, far over the 80-word limit.

### 0:26 The critic's JSON

Next, a second call, the critic, scores that draft against all five criteria and returns JSON, and it passes the start time, fooled by the only 14:02 in the text, which is about pending orders.

### 0:42 Code decides

So your loop re-checks, for free, the three things code can count, and where code can count, code decides: the start time fails, and the draft has two failures, not one.

### 0:55 One target

The writer gets one target, the start time, with the whole rubric in view, and version two comes back in 43 words.

### 1:03 Re-check all five

The part people miss is that the loop then re-scores all five criteria, not only the target, and version two passes every one, so the loop stops and keeps it.

### 1:16 A narrow ask

Here is the failure mode, set up on purpose: a stakeholder asks only for a clearer impact and the reason why, and that edit goes to the writer without the rubric.

### 1:28 Regression

Version three is clearer, but at 103 words it fails the length check, and the critic says it blames the vendor, so two passing checks turn red.

### 1:38 The fix

The fix sends the same ask with the rubric back and version two in view, and version four keeps the new detail, drops the blame and passes all five in 58 words.

### 1:51 What to keep

So let code decide whatever it can count, show the whole rubric with every edit, re-score every criterion after it, and cap the laps, since each lap costs two model calls and a loop that never converges must still stop.

## Related terms

- [Self-critique](https://understandingdata.com/ai-coding-dictionary/self-critique/)
- [Automated check](https://understandingdata.com/ai-coding-dictionary/automated-check/)
- [Automated review](https://understandingdata.com/ai-coding-dictionary/automated-review/)
- [Spec](https://understandingdata.com/ai-coding-dictionary/spec/)
- [Human review](https://understandingdata.com/ai-coding-dictionary/human-review/)

## More visualisations

- [The anatomy of a good prompt](https://understandingdata.com/ai-engineering-visualised/anatomy-of-a-prompt/)
- [Few-shot prompting: examples define the pattern](https://understandingdata.com/ai-engineering-visualised/few-shot-prompting/)
- [Tool calling is a round trip](https://understandingdata.com/ai-engineering-visualised/tool-calling-round-trip/)

---

Source: https://understandingdata.com/ai-engineering-visualised/draft-critique-revise/
From AI Engineering Visualised by James Phoenix, Understanding Data. Published 2026-09-11.
