A pairwise evaluator puts the model output head-to-head against a reference answer and returns a winner (or a tie). Use it to benchmark a new prompt or model against a known-good baseline that’s already in the dataset row.
The example below asks an LLM judge to pick the better candidate — but blinds the judge so it never sees which side is the model output and which is the reference. The presentation order is randomized per example using a seed derived from the inputs (so runs stay reproducible), and the judge’s positional choice is decoded back to the original labels after the call. This mitigates LLM position bias — the tendency of judges to systematically prefer whichever response they see first.
The judge never sees which side is the model output and which is the reference — only “Candidate 1” and “Candidate 2”. The seeded shuffle is deterministic per example, so the same inputs always produce the same presentation order.
Code#
Sandbox dependencies — paste into the sandbox configuration’s Dependencies field, one package per line:
The TypeScript version uses a small djb2-style string hash instead of SHA-256 to avoid a node:crypto import that may not resolve in every TS sandbox. It’s not cryptographically strong — it doesn’t need to be — but it’s deterministic and well-distributed enough to balance presentation orders across the dataset.
Sandbox dependencies — paste into the sandbox configuration’s Dependencies field, one package per line:
The flip decision is recorded in the explanation so you can audit the decoding from the trace.
Input mapping#
| Parameter | Bind to |
|---|---|
output | The model output you want to evaluate, usually output. |
reference | The competing answer to compare against — typically the baseline or known-good response stored on the example, usually reference. |
Output configuration#
Categorical. The function returns its own numeric score along with the label, so configure these label-to-score mappings to match what it produces:
| Label | Score | Meaning |
|---|---|---|
output | 1.0 | The model output beats the reference. |
reference | -1.0 | The reference beats the model output. |
tie | 0.0 | Judge could not separate them. |
missing | 0.0 | One of the inputs was empty. |
invalid | 0.0 | Judge returned malformed output. |
Optimization direction: maximize — you want output to win against the reference baseline.
Runtime requirements#
| Setting | Value |
|---|---|
| Sandbox | A hosted backend that matches your language. Python: E2B, Daytona — Python, Vercel Sandbox — Python, or Modal. TypeScript: Daytona — TypeScript or Vercel Sandbox — TypeScript (the local Deno sandbox is started with --no-npm and cannot install the openai package). |
| Dependencies | Python: openai. TypeScript: openai (npm). |
| Internet access | Required — toggle Allow Internet Access on. The sandbox must reach api.openai.com. |
| Environment variables | OPENAI_API_KEY — preferably set as a secret reference to a key in Settings → Secrets. |
Each evaluate(...) call makes one chat-completion request against gpt-4o-mini. At scale:
- Raise the sandbox configuration’s Timeout to comfortably cover the LLM round-trip plus a cold-start package install.
- Watch the judge’s per-token cost — every pairwise prompt includes both the output and the reference, so token counts grow with output length.
- For high-stakes evaluations, see the Swap-and-confirm variant below. It doubles the cost but removes position bias on every example, instead of relying on randomized order to cancel it out in aggregate.
Variants#
Swap-and-confirm (position-bias-free)#
The blinding above randomizes order per example, so position bias cancels out in expectation — but any single example can still be biased. To remove it per-example, call the judge twice (once in each order) and only return a winner when both calls agree:
Costs 2× the API calls, but each example’s verdict is independent of the underlying model’s position bias.
Other directions#
- Multi-criterion judging — ask the judge to score on several axes (correctness, conciseness, format) and combine the per-axis verdicts. Return a structured
explanationso the trace shows the breakdown. - Embedding-based pairwise — replace the LLM call with cosine similarity between
outputandreferenceusing OpenAI embeddings or scikit-learn. Cheaper and deterministic, but it won’t catch semantic equivalence the way an LLM judge can on free-text answers.

