Use this when a step produced a handful of candidates (draft replies, plan
options, name ideas, competing approaches) and you want the workflow to pick
the strongest one reliably, rather than trust a single model's one-shot
ranking. A round_robin judges every pair head-to-head against your
criterion, tallies a crosstable and standings, and routes decided (a clear
winner) or tie (the top options are genuinely level). Pairwise comparison
is easier for a model than ranking a whole list, and the full scorecard
shows exactly why one won.
Best-of-N: draft, judge, act#
The signature use is best-of-N: have the node generate several candidates in one
call, judge them, then act on the winner.
name: best-reply
version: 1
description: |
Draft four replies to a tricky customer email, judge them head-to-head, and
present the strongest one ready to send.
initial: draft
states:
draft:
type: round_robin
label: Draft and rank four replies
model: claude-haiku-4-5
criterion: |
A long-time customer is upset and asking for a refund just outside our
30-day window. Which reply best keeps them as a customer — genuinely
empathetic, honest about the policy, and offering a fair next step?
generate:
prompt: |
A loyal customer forgot to cancel and was charged for another year three
days after renewal — just past our 30-day refund window. Draft four
genuinely different complete replies a teammate could send (vary the tone
and the concrete offer). Each ready to send as-is.
count: 4
swapMode: contested
on:
decided: finalize
tie: tie-end
finalize:
type: ai_agent
label: Present the winning reply
model: claude-haiku-4-5
prompt: |
A round-robin ranked four candidate replies.
Replies: {{ draft.ideas }}
Standings: {{ draft.standings }}
Winning index: {{ draft.winnerIdx }}
Output the winning reply exactly as the team should send it, then add one
line — "Why this one:" — on what made it beat the runner-up.
on:
done: done-end
done-end:
type: end
label: Reply ready
outcome: success
message: The strongest reply was selected and prepared to send.
tie-end:
type: end
label: Too close to call
outcome: failure
message: Two replies were judged equally strong — a teammate should pick between them.Running it#
roscoe run best-replyThe web UI shows the scorecard: a winner banner, a crosstable (who beat whom,
by how much), standings, and "highlights" (undefeated, closest call, biggest
upset). When the top two finish level on points, the winner is settled on a
tie-break, and the "Won by tie-break" callout names which criterion broke it
(winning margin, head-to-head, or strength of opposition) with the two leaders'
values, so the decision is auditable. A genuine tie means the options were
inseparable; route it to a human (see Variations).
round_robin runs key-free on your Claude subscription when driven from Claude
Code / Cowork (the pairwise judging hands back to the host). Standalone or from
Claude desktop chat it runs server-side and needs ANTHROPIC_API_KEY or the
claude CLI.
Where the candidates come from#
Exactly one source per node:
| Source | Use it when… |
|---|---|
generate |
the node should brainstorm the options itself (best-of-N, as above). |
candidatesFrom |
an upstream node produced them (an ai_agent that brainstormed, a json input, a subworkflow). Any text output works: the node auto-extracts a clean list, no special formatting required. |
candidates |
the options are fixed at authoring time. |
Tuning swapMode (position bias)#
Models can be swayed by which option is shown first. swapMode trades cost
for resistance to that bias:
| Mode | Behaviour |
|---|---|
off |
judge each pair once (C(N,2) calls). Cheapest. |
contested |
judge once, re-judge only the close/cyclic pairs in reverse. Recommended default. |
all |
judge every pair in both orders (N·(N-1) calls). Least bias, ~2× the cost. |
When a pair is judged both ways and the two orders disagree, it's scored a draw: neither position can be trusted.
When to reach for it#
This covers best-of-N drafts (generate several replies, summaries, or specs and ship the strongest), picking an approach (brainstorm options for a goal, judge them, then expand the winner into a plan), and choosing copy (rank candidate headlines, subject lines, or names).
For a yes/no gate or a single fact check, use ai_judge. For "do N agents agree on
one question?", use consensus. round_robin is for "which of these several is
best?".
Variations#
To draw candidates from an upstream brainstorm, drop generate and add an
ai_agent that brainstorms options first, then
candidatesFrom: '{{ ideate.response }}'. A single input then drives both
the options and the judging, so it never goes stale. To route tie to a
human instead of a failure end, send tie to a human node so a person
breaks the genuinely close calls while AI handles the clear ones. To act on
the winner, have the downstream ai_agent do real work with
{{ draft.winnerIdx }} and {{ draft.ideas }}: write it up, ship it, draft
a plan.
See also#
/docs/nodes/round-robin— full reference for the node type/docs/nodes/ai-judge— single decision, cheaper/docs/nodes/consensus— N agents vote on one question