Ask a model for something once and you get exactly one pass: a first draft. It emits tokens left to right, never looking back, so what comes out is fluent, plausible, and almost always improvable. A function that forgets the empty-input case. A paragraph padded with "really great". An email with no deadline. The frustrating part is the model usually knows better — ask it to check and it spots the flaw instantly — but in a single forward pass there's no moment where it stops to look.
Humans don't ship first drafts either. We reread and revise. Self-Refine (Madaan et al., 2023) bakes that instinct into the prompt: the same model produces a draft, then switches hats and critiques its own output, then switches again and rewrites the draft to fix exactly the flaws it found. Critique the new version, refine again, and repeat until the self-critique stops finding anything worth changing.
The whole thing is three prompts and a loop.
Generate. Just do the task, one pass. Expect first-draft quality.
let draft = await llm(task);
Feedback. Point the model at its own finished output and give it the single narrow job of finding flaws — specific, actionable, quoted from the text.
const FEEDBACK = (draft) => `Here is an attempt at the task:
"""${draft}"""
List the SPECIFIC, ACTIONABLE problems with it as bullets —
correctness, missing cases, clarity, structure, tone.
Be concrete and quote the text you mean.
If it is already excellent, reply exactly: NO_ISSUES.`;
Refine. Hand back both the draft and the critique, and ask for a rewrite that addresses every point.
const REFINE = (draft, critique) => `Task attempt:
"""${draft}"""
Critique of that attempt:
"""${critique}"""
Rewrite the attempt so it fixes EVERY point in the critique.
Output only the improved version.`;
Run a scrappy average(nums) through that. The first draft divides by nums.length with no empty-array guard, does no validation, and has no doc. The critique names all three. v2 fixes them but writes a clumsy accumulator loop and lets Infinity slip through, so the next critique points that out, and v3 lands on a clean reduce with a finiteness check and a real doc comment. Then the critique of v3 comes back with nothing actionable — only matters of taste — and the loop halts. Same story for prose: a bloated product blurb tightens across two rounds into something concrete and benefit-first, and an over-hedged email gains a deadline, a reason, and a focus.
Why does this help when the knowledge was already in the weights? Because recognizing a flaw is an easier task than avoiding it while generating. A first draft juggles content, structure, and phrasing all at once under next-token pressure, and some things slip. But pointed at a finished draft with the single job "find what's wrong," the model can spend its whole pass on evaluation. The critique surfaces knowledge the draft didn't use; the refine step puts it to work.
A few things that make it work in practice:
The feedback prompt is load-bearing. "Make it better" yields a vague reshuffle. "The loop divides by length with no empty-array guard" drives a real fix. Demand concrete, itemized problems quoted from the text, and give a clean exit (reply NO_ISSUES) so a good draft can end the loop.
Give the loop a brake. Stop when the critique says it's done, when the marginal gain drops below a threshold, or at a hard cap of three or four rounds. Most of the lift is in the first one or two refinements — returns diminish fast, so a small cap costs little and captures nearly all the benefit.
Ground the critic. An open-ended "what's wrong?" invites the model to shrug and say "looks good" — an empty critique that wastes a round. Give it a rubric (correctness, completeness, clarity, structure) and make it justify any criterion below full marks.
Prefer an oracle when one exists. Self-critique is only as sharp as the model's own eyes; it can be blind to its own errors, or even refine a correct answer into a wrong one. Wherever a cheap, trustworthy signal exists — unit tests, a compiler, a type-checker — feed that back instead. That's its cousin Reflexion (Day 20): external feedback instead of self-feedback. Self-Refine is for the open-ended tasks where no such oracle exists.
The cost is real: a K-round loop is roughly 2K+1 model calls, all sequential. But on code, writing, and multi-constraint reasoning — anywhere a first draft is decent but a second look reliably makes it better — it's often the cheapest quality you can buy.
I built an interactive walkthrough — pick a task and step through the loop, watching each critique name real flaws and the next version fix exactly those while the quality meter climbs then plateaus: https://dev48v.infy.uk/prompt/day30-self-refine.html
Top comments (0)