Pick the best of several options

Judge several candidates head-to-head and pick the strongest, with a transparent scorecard.

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-reply

The 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

View this page as Markdown

Predictable workflows from unpredictable AI