Skip to content

evaluator-optimizer

Generate → evaluate against explicit criteria → refine with the feedback → repeat until it passes or a round cap is hit. Use it when two things are both true: there’s a clear bar (tests, a lint rule, a stated requirement — not “make it better”) and refinement demonstrably helps (a model can act on concrete feedback better than it produced the first draft blind). If either is missing — no checkable criteria, or one attempt is already as good as five — skip the loop; it just burns rounds for no gain.

This is a different shape from advisor-checkpoint: that skill is one strategic consult before you commit to an approach. This is a loop that converges one artifact against a bar you can actually check.

Whenever the task has one, run the real check — a test suite, go vet/swiftlint/a build — rather than asking a model to judge. A test result is ground truth; a model’s opinion about whether code “looks correct” is not. Reserve a model-as-evaluator for criteria that genuinely can’t be mechanically checked (architecture quality, whether a document actually answers the stated question, prose clarity).

  1. Generate. Produce a candidate against the task.
  2. Evaluate. Run the objective check, or — if there isn’t one — dispatch an evaluation only against explicit criteria you state up front (not “is this good,” but “does it satisfy: correctness, no new lint violations, handles the empty-input case”). The evaluator’s job is to grade, not to fix — keep the roles separated so the feedback is a clean signal, not a silent rewrite.
  3. Refine. If it fails, feed the concrete failure (the test output, the lint error, the evaluator’s specific complaint) back into the next generation — not “try again,” but “this failed because X.”
  4. Repeat, capped at 3-5 rounds. If it hasn’t converged by then, stop and surface the failure rather than keep spinning — a persistent failure after several rounds usually means the criteria are wrong, the task is underspecified, or the approach needs to change, not that round 6 will suddenly pass.

The build/test/fix cycle you already run is this loop with an objective evaluator. Make it explicit when the criteria are less obvious than “tests pass” — e.g., state the acceptance bar before generating, so the evaluation step (yours or a dispatched Agent call) has something concrete to check against instead of re-deriving what “done” means each round.

For a single session converging toward one checkable condition, /goal <condition> is the native version of this loop — it re-evaluates the condition after every turn and keeps the session going until it holds, without hand-rolling the round-and-check bookkeeping yourself. Reach for it when the loop lives entirely in one conversation; reach for the Workflow script below when you need custom evaluator logic, multiple candidates, or the loop spans more than this one session.

const CRITERIA = 'Correctness, no new lint violations, handles empty input.'
let candidate = await agent(`Implement <task>. Must satisfy: ${CRITERIA}`)
for (let round = 0; round < 4; round++) {
const verdict = await agent(
`Evaluate ONLY — do not fix. Criteria: ${CRITERIA}\n\nCandidate:\n${candidate}\n\n` +
`Reply PASS, or FAIL with the specific failure.`,
{ label: `evaluate-r${round}` }
)
if (verdict.includes('PASS')) break
candidate = await agent(
`Refine to fix this specific failure (keep everything else): ${verdict}\n\nCandidate:\n${candidate}`,
{ label: `refine-r${round}` }
)
}

Don’t confuse this with the workflow tool’s loop-until-dry / loop-until-count patterns — those accumulate a growing set of findings across rounds (discovery). This converges a single artifact toward a fixed bar (refinement). Reach for whichever matches what the round is actually doing.