LLMsChoosing

Picking a model

Start from your task and your own cases, run more than once, not from a leaderboard.

WHY THIS, FOR YOU

You test two models on twelve of your own tickets, one run each. They tie, so you ship the cheaper one. A week later an invoice question gets labelled as a bug, though the same ticket passed in your test. Nothing changed except the draw: a model can answer the same input differently on different runs, and one pass per case cannot see that. A leaderboard could not have helped either, because it never saw your tickets. This lesson is the method for after the shortlist: a small set of your own cases, graded pass or fail, run more than once, and a routing rule so the cheap model does the easy work.

This lesson makes the decision: build a small test from your own inputs, then use a routing pattern so you do not have to pick only one model.

1.First check that the model is the problem

Public rankings do one job well: they cut a long list to about three (Reading a leaderboard covers how). Price, context window, rate limits and region (What you actually pay for, Rate limits and reliability) cut it further. Then stop reading rankings.

Before you compare, ask whether a different model would even help. The authors of an evals FAQ (Sep 2026) advise against treating a model swap as the main lever: read real failing outputs first. They sell a paid course, but the logic holds. If the failures come from an unclear prompt, poor retrieval or missing context, a new model gives you the same failures at a different price.

When this does not apply. If the product is new and has no failing outputs, a comparison is premature. Pick a mid-tier model, ship behind a flag, and collect real inputs. Invented cases are shaped by what you already expect.

2.Your own cases decide it

The prompt-engineering course builds a test like this in Versioning and testing prompts; the Evals course goes deeper in Building an eval set from your own traffic and How to trust a judge. The short version, pointed at one question, which model to buy:

  1. Read first. Look at 20 to 50 real outputs from your current setup, note each failure, and group the notes into a few named failure types.
  2. Pull cases from real traffic. Take them from logs or tickets: as a rule of thumb, about half ordinary and half awkward (long inputs, missing fields, known edge cases).
  3. Write the rubric after the reading. The FAQ authors say a rubric written first gets in the way of seeing what actually fails.
  4. Grade pass or fail. The FAQ authors prefer yes or no to a 1 to 5 score, noting that adjacent points on a scale get judged inconsistently. Use an equality check where one exists. Where a model must judge, check it against one domain expert's labels first.
  5. Run every candidate on the same cases with the settings you would ship, next to cost and p95 latency.

Twenty is a floor, not a verdict. As of Sep 2026 the same FAQ says to start with 20 to 50 reviewed outputs, review until failures stop appearing (at least 100 traces), and label 100 to 200 examples to validate a model judge. It names no number for trusting a result. My rule of thumb: 100 or more cases before you believe a small gap; a three-case gap on twenty is a coin toss.

Twenty cases, one run each
Enough to expose a plainly wrong model, not to separate two decent ones.
  1. cases20
  2. runs per case1
  3. you can saythis one is broken
One test set growing in three stages. The 20 to 50 start and 100-plus review follow the evals FAQ (Sep 2026); the 100-case bar and the runs per case are my rules of thumb.

Run each case more than once. A model samples its answer, so one run is one draw. A preprint from August 2026 ran each of 900 benchmark items 50 times against three OpenAI model versions, and found that single runs recovered none of the 457 item-level changes the repeats showed reliably. It covers one vendor and three public benchmarks, so its percentages are not rates for your app. The narrow lesson: ask which cases flipped, not only what the average did.

One run eachOne mark per case

One mark per case per model, and one total. Two models that both score 14 of 20 look equal.

Several runs eachA pass rate per case

A rate per case: 5 of 5, 0 of 5, 2 of 5. Settled cases drop out, flaky ones are listed by name, and a case that goes from 5 of 5 to 1 of 5 is a regression even when the totals tie.

The same twenty cases, run two ways. A tie on the left is a limit of the method, not a finding; the single-run claim comes from one preprint (Aug 2026) on OpenAI models.

A failure case (invented, to show the shape). A team runs a cheap and a strong model on twenty tickets, once each: 17 and 17. They ship the cheap one. Run five times each, the cheap model passes the invoice tickets only some of the time, the strong one nearly always.

Hold the settings fixed, since thinking effort changes quality and cost (Thinking and effort, model by model). Record output tokens too: the same preprint saw one model pair's mean output on a benchmark roughly halve, so cost can move with no price change.

Keep the set. It makes Switching models safely and When a model is retired routine.

3.Cost, latency, quality, and the routing pattern

For a given task you get two of these three. Deciding which to give up is a product decision.

TaskWhat it needsWhat it can give up
Live chat with a userLatency, qualityCost — a slow reply loses the user
Nightly bulk classificationCost, qualityLatency — use a batch endpoint and its discount
Autocomplete, inline suggestionsLatency, costQuality — the user judges instantly and can ignore it
Legal, medical, financial outputQuality, above everythingCost and latency, without argument

A starting point by task type, to test rather than to believe:

Task typeStart withBecause
Classification, extraction, routingThe smallest model that passes your casesThe work is pattern-shaped; Anthropic's model page (Oct 2026) says its smallest current model targets this
Summarising, rewriting, toneA mid-tier modelA human sees the quality, but the task is not hard
Code generation and reviewA strong modelMy experience (Oct 2026): small models often write code that looks right and is not
Multi-step agents and tool useThe strongest one with reliable tool callingErrors compound across steps
Long-document question answeringJudge by recall at your real lengthThe limit is Context windows and their limits, not raw capability

The routing pattern is how you stop choosing. Send every request to the small model first. Escalate to the strong one only when a signal you can compute says the cheap answer is not good enough: a failed schema or business-rule check, an input-length or complexity score over a threshold, or a customer tier that pays for the better answer. A model's own "I am confident" flag is the weakest of these, so check it against your cases before you rely on it.

When not to route. Skip it if you have no computable signal, because then you are guessing. Skip it for the high-stakes rows of the first table, which go straight to the strong model. An escalated request pays for and waits on both calls, so routing helps only if most requests stay cheap.

Request arrivesEvery request, not just the hard ones
Small model answers firstThe default path for the easy majority
A computable signal
  1. Check passed, model confident: ship it
  2. Check failed, not confident, or over a threshold: escalate
Strong modelPays only for what the small one could not do
The routing pattern. Every request starts at the small model, and only a signal you can compute, never a guess or the user's choice, sends it on to the strong one.
# Python 3.10+; pip install openai   (run on SDK 3.33.0, 2026-10-17)
import os
import json
from openai import OpenAI

client = OpenAI()
CHEAP = os.environ["CHEAP_MODEL"]
STRONG = os.environ["STRONG_MODEL"]

SCHEMA = {
    "type": "object",
    "properties": {
        "label": {"type": "string", "enum": ["billing", "bug", "other"]},
        "confident": {"type": "boolean"},
    },
    "required": ["label", "confident"],
    "additionalProperties": False,
}


def classify(text: str, model: str) -> dict | None:
    r = client.responses.create(
        model=model,
        instructions="Label the support ticket. Set confident to false if unsure.",
        input=text,
        max_output_tokens=2000,  # room for hidden thinking as well as the answer
        text={"format": {"type": "json_schema", "name": "ticket", "strict": True, "schema": SCHEMA}},
    )
    if r.status != "completed":  # cut off at max_output_tokens: a failure
        return None
    try:
        out = json.loads(r.output_text)
    except json.JSONDecodeError:  # a refusal is "completed" but has no JSON
        return None
    out["model"] = r.model  # log which model really answered
    return out


def route(text: str) -> dict:
    cheap = classify(text, CHEAP)
    if cheap and cheap["confident"]:  # a computable signal, not a guess
        return cheap
    return classify(text, STRONG) or {"label": "other", "model": None}


print(route("I was charged twice for the same invoice last Tuesday."))
print(route("It. Does the thing. Again. Maybe."))  # vague, so expect escalation
The pattern as code. route() holds the whole decision and confident is the signal. Model names come from the environment.

Set CHEAP_MODEL and STRONG_MODEL to two ids from GET /v1/models. I ran it on 2026-10-17 with two small models from one OpenAI family. The first ticket stayed with the cheap model; the vague one was answered by the stronger one. The returned model was a dated snapshot name, not the short id I sent, so log it per request, or you cannot tell whether your escalation rate is 5 percent or 60.

A refusal is completed with no JSON, so classify() returns None; if both models refuse, the caller gets {"label": "other", "model": None}.

Three choices are dated. OpenAI's text guide (Oct 2026) recommends the Responses API "over the older Chat Completions API"; Chat Completions was not on the deprecations page on 2026-10-17. As of Oct 2026 the structured-outputs guide says to always use Structured Outputs instead of JSON mode when possible. The sample sends no temperature, because some models reject it; check each model's page (as of Oct 2026) and see Sampling, and the knobs providers are taking away.

WHAT YOU TAKE AWAY

  1. Check that the model is the problem before you shop for a different one.
  2. Use public rankings to cut the list, then close the tab and let your own cases decide.
  3. Grade each case pass or fail, and read a few failures before you write the rubric.
  4. Run each case several times and list the cases that flip, not just the average.
  5. Route the easy majority to a small model and escalate on a signal you can compute.

RECALL NO SCROLLING BACK

  1. Your set has twenty cases. How should you treat it?

  2. Why grade each case pass or fail instead of 1 to 5?

  3. Two models both score 14 of 20 on one run each. What can that still hide?

  4. A nightly bulk job has no user waiting. Which corner of cost, latency and quality should it give up?

  5. What should send a request from the small model to the strong one?

GO DEEPER