ILLATE
← Benchmark

GPT-5.6 Luna vs a fine-tuned model for ticket and intent classification

We pre-registered a fair test of GPT-5.6 Luna, OpenAI's named replacement for fine-tuned gpt-4.1-nano, so nobody could say we handicapped it. On three public classification tests, GPT-5.6 Luna, given the 20 most similar past examples, tied a small fine-tuned model on two and trailed by 2 points on the third. A plain embeddings classifier tied on one of them. The cheapest option that passes differs by task, so we test them all on your data before you pay.

Updated 9 October 2026Measured 5–8 October 2026Luna tests pre-registered13,697 held-out test itemsPaired bootstrap, 10,000 resamplesOne run per setting
I tested OpenAI's replacement for my fine-tuned model The short version, on video. Plays from YouTube when you press play
01 Results

How far each alternative sits from the fine-tuned model

Each line is our model's accuracy minus the alternative's, scored on the same test items, with its paired 95% confidence interval. A line that crosses zero means the test can't tell the two apart.

Ours minus each alternative · accuracy points Ours aheadNo measurable difference
−2 0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 ours minus the alternative, points (paired 95% CI) BANKING77 · 77 CATEGORIES GPT-5.6 Luna · 20 similar+0.2 · tied bge-base embeddings+0.5 · tied GPT-6 Luna · 20 similar+0.9 GPT-5.6 Luna · one per category+6.8 Decisions API · GPT-6 Luna+17.2 CLINC150 · INTENTS + OFF-TOPIC GPT-5.6 Luna · 20 similar+0.5 · tied GPT-6 Luna · 20 similar+0.7 bge-base embeddings+4.5 GPT-5.6 Luna · list only+6.8 CFPB · LONG COMPLAINTS GPT-5.6 Luna · 20 similar+2.0 GPT-5.6 Luna · list only+2.9 bge-small embeddings+3.1 0 +4 +8 +12 +16 +20 ours minus the alternative, points (paired95% CI) BANKING77 · 77 CATEGORIES GPT-5.6 Luna · 20 similar+0.2 · tied bge-base embeddings+0.5 · tied GPT-6 Luna · 20 similar+0.9 GPT-5.6 Luna · one per category+6.8 Decisions API · GPT-6 Luna+17.2 CLINC150 · INTENTS + OFF-TOPIC GPT-5.6 Luna · 20 similar+0.5 · tied GPT-6 Luna · 20 similar+0.7 bge-base embeddings+4.5 GPT-5.6 Luna · list only+6.8 CFPB · LONG COMPLAINTS GPT-5.6 Luna · 20 similar+2.0 GPT-5.6 Luna · list only+2.9 bge-small embeddings+3.1
Shown: each Luna with the 20 most similar training examples (pre-registered, reasoning effort none) and the best embeddings classifier on each test set; GPT-6 Luna's run with examples on CFPB didn't fit the budget and wasn't run. For contrast: on Banking77, GPT-5.6 Luna with one fixed example of every category and the Decisions API; on CLINC150 and CFPB, GPT-5.6 Luna with the category list only (8 October). Every other row, including reasoning effort low, TF-IDF, GPT-4.1-mini and gpt-oss-120b, is in the tables below.
Banking77 · support triage

Luna with similar examples ties

Ours 94.0%. GPT-5.6 Luna, shown the 20 most similar training messages, 93.8%: ours +0.2 points, paired interval −0.6 to +0.9. bge-base embeddings on a CPU, 93.5%, also tied. With one fixed example of every category, Luna scored 87.2%.

CLINC150 · intents + off-topic

Luna with similar examples ties

Ours 95.1%. GPT-5.6 Luna, shown the 20 most similar training requests, 94.6%: ours +0.5 points, paired interval −0.04 to +1.0. GPT-6 Luna with the same examples, 94.4%. Off-topic requests are a trade-off: ours catches more, Luna wrongly rejects fewer real ones (section 06).

CFPB · long complaints

By product: one product carries the lead

Credit reporting, the largest product, is more than the whole gap: ours gets 117 more of its 3,266 complaints right, and Luna gets 13 more right on the other products together. Overall, ours 89.5% and GPT-5.6 Luna with examples 87.5%: ours +2.0 points (+1.2 to +2.9), a narrow lead. Renamed products merged after seeing the errors (section 07).

02 Fair test

The pre-registered test: Luna at its best

Our first Luna runs, on 8 October 2026, gave it a plain prompt: on Banking77, 85.4% with the category list and 87.2% with one example of every category. A critic could fairly say we handicapped it. So before running anything else, we wrote down every setting, the run order, a $20 spending cap and the rule for what we would publish, and committed it. Then we ran five of the seven pre-registered Banking77 settings once each, on 8 October from 21:24 to 22:17 UTC, and changed nothing afterwards; the two left out for the budget are named below.

When Luna tied on Banking77, we did the same for the other two test sets: a second pre-registration with the same prompt, the same rule and a $50 cap, committed at 22:54 UTC on 8 October. The runs started 54 seconds later and finished at 23:57 UTC.

  • The prompt. The category list, Structured Outputs holding every answer to the label list, and the 20 training examples most similar to each test item as worked examples (section 04 shows it).
  • The runs. On Banking77: GPT-5.6 Luna and GPT-6 Luna, each at reasoning effort none and low, plus OpenAI's Decisions API (beta) on GPT-6 Luna. On CLINC150 and CFPB: each Luna at reasoning effort none. GPT-6 Luna on CFPB didn't fit the $50 cap and wasn't run, as the pre-registration expected.
  • Luna's best. The highest accuracy among all complete Luna runs on each test set, including the ones from earlier on 8 October: 9 runs on Banking77, 4 on CLINC150 and 3 on CFPB. Taking the best flatters Luna slightly. That was deliberate.
  • The rule. If our model leads Luna's best by under 2 points, or their 95% intervals overlap, we publish and drop the accuracy headline. If it leads by 2 to 5 points, we lead with the per-category results, not the gap.
The result

Luna's best on every test set was GPT-5.6 Luna with the 20 similar examples. It tied our model on Banking77 and CLINC150 and trailed it by 2.0 points on CFPB.

  • Banking77: 93.8% (92.9–94.6) against our 94.0% (93.1–94.8). Ours minus it +0.2 points, paired 95% CI −0.6 to +0.9.
  • CLINC150: 94.6% (94.0–95.2) against our 95.1% (94.6–95.7). Ours minus it +0.5 points, CI −0.04 to +1.0.
  • CFPB: 87.5% (86.6–88.4) against our 89.5% (88.7–90.3). Ours minus it +2.0 points, CI +1.2 to +2.9.

Under the rule we set in advance, we no longer claim an accuracy lead over Luna on Banking77 or CLINC150. On CFPB, we lead with the per-product results (section 07).

What made the difference

  • The examples, not the model or the reasoning. On Banking77, GPT-5.6 Luna went from 87.2% with one fixed example of every category to 93.8% with the 20 most similar ones: +6.6 points (+5.6 to +7.6). GPT-6 Luna went from 82.2% to 93.1%. On CLINC150, GPT-5.6 Luna went from 88.4% with the category list only to 94.6%: +6.3 points (+5.5 to +7.1).
  • On long complaints, the examples barely moved the score. On CFPB, GPT-5.6 Luna went from 86.6% to 87.5%: +0.9 points (+0.1 to +1.7). On the 14 labels as filed they helped far more, from 51.2% to 62.3%. Most likely the examples carry the agency's old and new product names, which a category list can't teach.
  • Reasoning effort low didn't help. On GPT-5.6 Luna it made no measurable difference on Banking77: 93.5% against 93.8%. On GPT-6 Luna it cost 1.9 points (+1.2 to +2.7): 91.1% against 93.1%.
  • The newer model wasn't better. With the same 20 examples, GPT-5.6 Luna beat GPT-6 Luna by 0.7 points on Banking77 (+0.1 to +1.3). On CLINC150 the two were 0.2 points apart, which the test can't tell from zero (−0.2 to +0.6).
  • The Decisions API was fast and cheap, but less accurate: 76.8% (75.3–78.2) on Banking77, 120 ms at the median. It got the bare category names as choices, with no descriptions and no examples. 10 of its 3,080 answers were refusals, counted as wrong.

The comparisons between Luna runs in this list weren't pre-registered. They use the same paired bootstrap, so read them as post-hoc. Two pre-registered Banking77 runs, each Luna with the schema and no examples, were left out before any full run because the $20 cap didn't cover all seven. We budgeted both jobs at deliberately high prices: $16.38 of a $20 cap for Banking77 and $43.17 of a $50 cap for CLINC150 and CFPB, smoke tests included. From our OpenAI bill for 8-9 Oct, they cost about $3.70 and $9.40. The 20-example prompt also added Structured Outputs and a slightly different instruction line. The schema-only runs that would separate those changes were the two left out, so the 6.6 points are the examples plus those changes. On 8 October, without a schema, GPT-5.6 Luna gave no invalid answers on Banking77, so the schema had none to fix. One unplanned event: Modal restarted the container once during the CLINC150 run on GPT-6 Luna. It resumed from the answers already saved; a few requests (2 or 3, going by our bill) were sent twice, and only the second answer exists.

03 Why Luna

Why GPT-5.6 Luna is the comparison that matters

Fine-tuned gpt-4.1-nano, o4-mini, gpt-3.5-turbo and gpt-4 models stop answering on 23 October 2026, and for fine-tuned gpt-4.1-nano OpenAI names gpt-5.6-luna as the recommended replacement base model (OpenAI deprecations page, checked 8 October 2026).

Luna can't be fine-tuned: its model page lists fine-tuning as not supported (GPT-5.6 Luna model page, checked 8 October 2026). So a team that moves a nano fine-tune to Luna replaces a trained model with a prompt. This page measures how close a prompt gets on classification, next to a fine-tuned small model and a much smaller embeddings classifier.

The named replacement has moved before. As recorded by a public mirror of OpenAI's page, it listed gpt-5-nano (25 April 2026), then gpt-5.4-nano (13 May 2026), then gpt-5.6-luna (4 August 2026); the dates are when the mirror's copy changed (history of the page in a public mirror, checked 8 October 2026). gpt-5-nano itself shuts down on 11 December 2026 (OpenAI deprecations page, checked 8 October 2026).

On 8 October 2026 we read these pages through verbatim public GitHub mirrors of OpenAI's docs; the links go to the live pages, except the change history. What changes in the request when you switch, and every dated fact: replacing a fine-tuned gpt-4.1-nano. Every retiring fine-tune and what to do before 23 October 2026: the shutdown checklist.

04 Setup

Models, prompts, test sets and statistics

Every model saw the same training split (or none) and was scored on the same held-out test items. Nothing was tuned on the test sets.

ModelWhat it isWhere it ran
Qwen3-4B + LoRA (ours)Qwen3-4B-Instruct-2507 with a rank-32 LoRA adapter, fine-tuned on each training split. A short instruction and the text; outputs constrained to the label list, so it can't invent a category.One NVIDIA L4 (Banking77, CLINC150); one H100 for CFPB training
bge-base, bge-small and MiniLM embeddings + classifierA frozen sentence encoder (BAAI/bge-base-en-v1.5, BAAI/bge-small-en-v1.5, sentence-transformers/all-MiniLM-L6-v2) and a logistic regression trained on the same split, its strength picked on the dev split. No GPU needed to serve it.Classifier trained on a CPU
TF-IDF + classifierWord and two-word TF-IDF features with logistic regression. The floor.CPU
GPT-5.6 Luna and GPT-6 Lunagpt-5.6-luna and gpt-6-luna through OpenAI's Chat Completions API, default temperature. On 8 October: reasoning effort none, max_completion_tokens 16 (40 for CFPB), two prompts. In the pre-registered tests: Structured Outputs, reasoning effort none (on Banking77 also low), max_completion_tokens 64 (2,048 at low). Prompts below.OpenAI API, called from Modal
OpenAI Decisions API (beta)POST /v1/decisions on gpt-6-luna: one choice question per message, with the 77 category names as its choices and no descriptions. Pre-registered, Banking77 only.OpenAI API, called from Modal
GPT-4.1-minigpt-4.1-mini-2025-04-14, the two 8 October prompts, temperature 0. Banking77 only.OpenAI API, called from Modal
gpt-oss-120bThe two 8 October prompts, served by vLLM at low reasoning effort with outputs constrained to the label list (results page). Banking77 only.One NVIDIA H100

The prompts

Category list only (8 October). One system message: a task line, an instruction to answer with one category name, and every category name. Then the text to classify.

An example of every category (8 October). The same system message, then one worked example of every category from the training split, as a user message and the right answer, in label order: 77 examples for Banking77, 151 for CLINC150 and 14 for CFPB. Then the text to classify.

The 20 most similar training examples (pre-registered, all three test sets). A system message with the task line, an instruction to choose the one category that fits best, and every category name. Then the 20 training items most similar to the one being classified, each as a user message followed by its right answer as JSON, least similar first, so the closest sits just before the text to classify. Similarity is cosine over BAAI/bge-small-en-v1.5 embeddings, searched over the training split only: 9,000 messages for Banking77, 15,250 requests for CLINC150 and 17,905 complaints for CFPB. Those are the same rows our model trained on, never a dev or test row. A strict JSON schema whose enum is the test set's label list holds every answer to it: 77 labels, 151 with out-of-scope, and the 14 CFPB products as filed. Only the task line and the labels change between test sets.

8 October prompts
system  {task line}
        Reply with exactly one category name from the list below and nothing else.

        Categories:
        {every category name, one per line}
user    {the text to classify}

task lines
  Banking77  You classify customer messages for a bank's support team.
  CLINC150   You classify requests made to a virtual assistant. Use oos if the
             request fits none of the categories.
  CFPB       You classify consumer complaints filed with the US Consumer Financial
             Protection Bureau by the financial product they are about.

Pre-registered prompt (Banking77 shown; CLINC150 and CFPB change the task line and labels)
system     You classify customer messages for a bank's support team.
           Choose the one category from the list below that best fits the customer's message.

           Categories:
           {the 77 category names, one per line}
user       {the 20th most similar training message}
assistant  {"label": "{its category}"}
           ... 19 more pairs, the most similar last ...
user       {the message to classify}

response_format  strict JSON schema: {"label": one of the test set's category names}

On 8 October we didn't constrain the API models' output to the label list. Each answer was matched to a category generously (exact, then ignoring case and punctuation, then a close spelling match); an answer that matched nothing counts as invalid and wrong. Invalid answers were rare: at most 15 in 5,500. In the pre-registered runs, Structured Outputs held all 28,437 chat answers to the label list: 12,320 on Banking77 and 16,117 on CLINC150 and CFPB. Refusals from the Decisions API count as wrong.

Test setTaskCategoriesTrain / dev / testSource
Banking77Bank customer-support messages, one line each779,000 / 1,003 / 3,080PolyAI, CC BY 4.0; official test set
CLINC150Requests to a virtual assistant, including requests that fit no intent (out-of-scope)150 + out-of-scope15,250 / 3,100 / 5,500Clinc, CC BY 3.0; the dataset's own split, "plus" version
CFPB complaintsConsumer complaints about financial products, median 711 characters, names and numbers redacted14 as filed, scored as 1017,905 / 2,560 / 5,117US CFPB, public domain; filed since January 2023, duplicates removed, split by label

Statistics. Accuracy comes with a 95% bootstrap confidence interval from 10,000 resamples of the test set. "Ours minus it" is a paired bootstrap: both models are scored on the same resampled items, which removes the noise from item difficulty. If that interval includes zero, the test can't tell the two models apart. Macro-F1 averages F1 over categories, so a small category counts as much as a big one; an answer outside the label list counts as wrong, not as a category of its own. On CLINC150, the off-topic and wrongly-rejected rates each have a 95% bootstrap interval over their own requests (1,000 off-topic, 4,500 real).

05 Banking77

Support triage: 3,080 bank-support messages, 77 categories

ModelPrompt or setupAccuracy95% CIOurs minus it (paired 95% CI)Macro-F1Invalid or no answer
Qwen3-4B + LoRA (ours)fine-tuned, one L494.0%93.1–94.8–94.0%0
GPT-5.6 Lunathe 20 most similar training examples, Structured Outputs (pre-registered)93.8%92.9–94.6+0.2 (−0.6 to +0.9)93.8%0
bge-base embeddings + classifiertrained, CPU93.5%92.7–94.4+0.5 (−0.3 to +1.2)93.5%0
GPT-5.6 Lunathe same, reasoning effort low (pre-registered)93.5%92.6–94.4+0.5 (−0.3 to +1.3)93.5%0
bge-small embeddings + classifiertrained, CPU93.4%92.5–94.3+0.6 (−0.2 to +1.3)93.4%0
GPT-6 Lunathe 20 most similar training examples, Structured Outputs (pre-registered)93.1%92.1–94.0+0.9 (+0.1 to +1.7)93.1%0
MiniLM embeddings + classifiertrained, CPU92.8%91.9–93.7+1.2 (+0.4 to +2.0)92.8%0
GPT-6 Lunathe same, reasoning effort low (pre-registered)91.1%90.1–92.1+2.9 (+1.9 to +3.8)90.9%0
TF-IDF + classifiertrained, CPU89.3%88.1–90.3+4.7 (+3.7 to +5.8)89.3%0
GPT-5.6 Lunaone example of every category (8 October)87.2%86.0–88.4+6.8 (+5.7 to +7.9)86.9%0
gpt-oss-120ban example of every category, one H10085.6%84.3–86.8+8.4 (+7.2 to +9.6)85.0%1
GPT-5.6 Lunacategory list only (8 October)85.4%84.1–86.6+8.6 (+7.5 to +9.8)84.6%0
GPT-6 Lunaone example of every category (8 October)82.2%80.8–83.5+11.8 (+10.5 to +13.1)81.9%4
gpt-oss-120bcategory list only, one H10082.0%80.7–83.4+11.9 (+10.6 to +13.3)81.3%0
GPT-6 Lunacategory list only (8 October)79.7%78.3–81.1+14.3 (+12.9 to +15.6)78.9%0
GPT-4.1-minian example of every category78.7%77.2–80.1+15.3 (+13.9 to +16.8)77.9%0
OpenAI Decisions API (beta), GPT-6 Lunathe 77 category names as choices (pre-registered)76.8%75.3–78.2+17.2 (+15.8 to +18.7)76.3%10
GPT-4.1-minicategory list only74.9%73.3–76.4+19.1 (+17.6 to +20.6)73.8%0
GPT-5.6 Lunacategory list, Structured Outputs, no examples (pre-registered)not run––––
GPT-6 Lunacategory list, Structured Outputs, no examples (pre-registered)not run––––

The two "not run" rows were last in the pre-registered order and were left out before any full run, because the $20 cap didn't cover all seven runs. The 10 invalid Decisions answers are refusals, on messages such as "what is my card PIN" and "someone is using my account".

Three alternatives are statistically tied with ours. GPT-5.6 Luna with the 20 most similar examples (+0.2, −0.6 to +0.9), the bge-base embeddings classifier (+0.5, −0.3 to +1.2) and bge-small (+0.6, −0.2 to +1.3). GPT-5.6 Luna at reasoning effort low ties too. On this task, accuracy doesn't pick between them; cost at your volume, latency and control do (section 09).

With a fixed prompt, every prompted model scored 87.2% or less. The 20 most similar examples added 6.6 points to GPT-5.6 Luna and 10.9 to GPT-6 Luna. MiniLM trails ours by 1.2 points (+0.4 to +2.0).

06 CLINC150

Intents and off-topic requests: 5,500 assistant requests, 150 intents plus out-of-scope

With the 20 most similar training requests as worked examples, GPT-5.6 Luna scored 94.6% against our 95.1%: a statistical tie. Under the rule we set in advance, we publish it and drop the accuracy headline on CLINC150. With the category list only, on 8 October, both Lunas scored about 88%.

ModelPrompt or setupAccuracy95% CIOurs minus it (paired 95% CI)Macro-F1Invalid
Qwen3-4B + LoRA (ours)fine-tuned, one L495.1%94.6–95.7–96.2%0
GPT-5.6 Lunathe 20 most similar training examples, Structured Outputs (pre-registered)94.6%94.0–95.2+0.5 (−0.04 to +1.0)96.0%0
GPT-6 Lunathe 20 most similar training examples, Structured Outputs (pre-registered)94.4%93.8–95.0+0.7 (+0.2 to +1.3)95.8%0
bge-base embeddings + classifiertrained, CPU90.6%89.8–91.4+4.5 (+3.8 to +5.3)93.2%0
bge-small embeddings + classifiertrained, CPU89.3%88.5–90.2+5.8 (+5.0 to +6.6)92.2%0
MiniLM embeddings + classifiertrained, CPU88.9%88.1–89.7+6.3 (+5.5 to +7.0)91.6%0
GPT-5.6 Lunacategory list only (8 October)88.4%87.5–89.2+6.8 (+5.9 to +7.6)89.3%15
GPT-6 Lunacategory list only (8 October)88.3%87.4–89.2+6.8 (+6.0 to +7.7)88.5%6
TF-IDF + classifiertrained, CPU81.6%80.5–82.6+13.6 (+12.6 to +14.6)85.7%0
GPT-5.6 Lunaan example of every category (8 October)incomplete––––
GPT-6 Lunaan example of every category (8 October)incomplete––––

The example-of-every-category runs were stopped part-way and not completed, after 339 (GPT-5.6 Luna) and 92 (GPT-6 Luna) of 5,500 requests. We don't score a run on a subset. With the schema, the 15 and 6 invalid answers of the list-only runs went to 0.

Off-topic requests

1,000 of the 5,500 test requests fit none of the 150 intents, and the right answer is "out of scope". The training split has only 250 of them among 15,250, which likely limits how many off-topic examples Luna's search can find. The trained models learned out-of-scope as one more class; Luna was told to answer oos when nothing fits. Two rates matter: how many off-topic requests a model catches, and how many real requests it wrongly turns away.

ModelOff-topic caught (of 1,000), 95% CIReal requests wrongly rejected (of 4,500), 95% CIAccuracy
GPT-6 Luna, category list only90.0% (88.1–91.8)5.49% (4.84–6.18)88.3%
Qwen3-4B + LoRA (ours)83.9% (81.6–86.2)0.40% (0.22–0.60)95.1%
GPT-6 Luna, 20 most similar examples79.9% (77.4–82.3)0.42% (0.24–0.62)94.4%
GPT-5.6 Luna, 20 most similar examples79.1% (76.6–81.6)0.18% (0.07–0.31)94.6%
GPT-5.6 Luna, category list only77.8% (75.2–80.4)2.31% (1.89–2.76)88.4%
bge-base embeddings + classifier62.9% (59.9–65.9)0.36% (0.20–0.53)90.6%
MiniLM embeddings + classifier60.5% (57.5–63.5)0.49% (0.29–0.69)88.9%
bge-small embeddings + classifier58.6% (55.6–61.6)0.33% (0.18–0.51)89.3%
TF-IDF + classifier40.9% (37.9–44.0)1.24% (0.93–1.58)81.6%

Paired on the same requests, ours minus each Luna run, pre-registered: against GPT-5.6 Luna with examples, off-topic caught +4.8 points (+2.5 to +7.2) and wrongly rejected +0.22 (+0.04 to +0.42); against GPT-6 Luna with examples, +4.0 (+1.5 to +6.5) and −0.02 (−0.24 to +0.20); against GPT-5.6 Luna with the category list only, +6.1 (+3.4 to +8.8) and −1.9 (−2.4 to −1.5); against GPT-6 Luna with the category list only, −6.1 (−8.5 to −3.8) and −5.1 (−5.8 to −4.4).

Against Luna given similar examples, off-topic requests are a trade-off. Ours catches more of them: 83.9% against 79.1% for GPT-5.6 Luna and 79.9% for GPT-6 Luna. GPT-5.6 Luna with examples wrongly rejects slightly fewer real requests: 0.18% against our 0.40%, 8 of 4,500 against 18. GPT-6 Luna with the category list only catches the most, 90.0%, but wrongly rejects 5.5% of real requests.

On the 4,500 real requests alone, GPT-5.6 Luna with examples is slightly ahead: 98.1% against our 97.6% (ours −0.4 points, −0.8 to −0.04). Per intent, ours has the higher F1 on 55 intents, Luna on 54, and 41 are level. So the 0.5-point gap overall comes from the off-topic requests: ours catches 839 of the 1,000, Luna 791. Which side matters more depends on what a missed off-topic request costs you against a real customer turned away. The real-request and per-intent splits are post-hoc.

The embeddings classifiers turned away about as few real requests as ours (0.33% to 0.49%) but caught only 58.6% to 62.9% of off-topic ones. Luna with examples was 4.0 points more accurate than the bge-base classifier here (+3.3 to +4.7, post-hoc), mostly because it caught far more off-topic requests.

07 CFPB

Long real complaints: 5,117 consumer complaints, 10 products

The CFPB renamed three of its products on 25 August 2023, so the same complaint carries a different label depending on when it was filed, and the date isn't in the text. As on our results page, we score with each old name merged into its new one, which leaves 10 products, and show the score on the 14 labels as filed alongside. We chose the merge after seeing the confusions, so treat it as a diagnosis rather than a pre-registered result. Ours leads Luna's best here by 2 to 5 points, so under our rule the products come first.

Product (merged)Test complaintsOursGPT-5.6 Luna, 20 most similar examplesGPT-5.6 Luna, category list onlybge-base classifier
Credit reporting or other personal consumer reports3,26694.8% (95.4%)93.0% (91.9%)92.7% (92.9%)93.1% (94.1%)
Debt collection46774.3% (71.3%)69.7% (66.8%)63.8% (54.6%)66.4% (64.9%)
Credit card or prepaid card41880.2% (82.1%)80.8% (84.7%)77.5% (80.1%)73.5% (68.2%)
Checking or savings account39985.9% (87.0%)85.9% (86.2%)85.2% (85.2%)79.9% (86.7%)
Mortgage20290.1% (90.1%)90.4% (96.0%)91.2% (97.0%)89.2% (89.6%)
Money transfer, virtual currency, or money service11672.1% (68.1%)69.3% (69.0%)67.8% (66.4%)62.4% (58.6%)
Vehicle loan or lease10275.3% (71.6%)67.2% (78.4%)67.5% (82.4%)68.6% (69.6%)
Student loan8490.1% (91.7%)79.8% (91.7%)83.8% (95.2%)87.6% (88.1%)
Payday loan, title loan, personal loan, or advance loan5856.6% (51.7%)65.5% (62.1%)55.7% (55.2%)26.3% (17.2%)
Debt or credit management50.0% (0.0%)0.0% (0.0%)12.5% (20.0%)0.0% (0.0%)

F1 for each product, with recall in brackets: the share of that product's complaints answered correctly. Bold marks the higher F1 of ours and Luna with examples where they differ by more than a point; recall can point the other way, as on vehicle loans. Post-hoc, with no intervals; the small products move a lot with a handful of answers.

One product is more than the whole gap. On credit reporting, the largest product, ours answers 95.4% of 3,266 complaints correctly and GPT-5.6 Luna with examples 91.9%: 117 more complaints right, more than the whole gap of 104. Luna wins back 13 net on the other products. Counting complaints answered correctly, ours is ahead on debt collection by 21, and Luna on mortgages by 12, credit cards by 11, vehicle loans by 7 and payday loans by 6. By F1, which also counts complaints wrongly sent to a product, ours is also ahead on debt collection, vehicle loans, student loans and money transfer, Luna on payday and personal loans, and the two are level on credit cards, checking and savings, and mortgages.

ModelPrompt or setupAccuracy (10 products)95% CIOurs minus it (paired 95% CI)Macro-F1As filed (14 labels)Invalid
Qwen3-4B + LoRA (ours)fine-tuned, one H10089.5%88.7–90.3–71.9%67.0%0
GPT-5.6 Lunathe 20 most similar training examples, Structured Outputs (pre-registered)87.5%86.6–88.4+2.0 (+1.2 to +2.9)70.2%62.3%0
GPT-5.6 Lunacategory list only (8 October)86.6%85.7–87.6+2.9 (+2.1 to +3.7)69.8%51.2%0
TF-IDF + classifiertrained, CPU86.6%85.7–87.5+3.0 (+2.1 to +3.8)61.5%67.7%0
bge-small embeddings + classifiertrained, CPU86.4%85.5–87.3+3.1 (+2.3 to +3.9)62.6%63.7%0
bge-base embeddings + classifiertrained, CPU86.2%85.2–87.1+3.3 (+2.6 to +4.1)64.7%64.3%0
GPT-6 Lunacategory list only (8 October)86.0%85.1–87.0+3.5 (+2.6 to +4.4)68.2%55.0%2
MiniLM embeddings + classifiertrained, CPU85.7%84.7–86.6+3.9 (+3.0 to +4.7)63.3%63.0%0
GPT-5.6 Lunaan example of every category (8 October)incomplete–––––
GPT-6 Lunaan example of every category (8 October)incomplete–––––
GPT-6 Lunathe 20 most similar training examples, Structured Outputs (pre-registered)not run–––––

The example-of-every-category runs stopped after 484 (GPT-5.6 Luna) and 203 (GPT-6 Luna) of 5,117 complaints, for the same reason as on CLINC150. GPT-6 Luna's run with similar examples on CFPB was left out before it started: the $50 cap, set in advance for the CLINC150 and CFPB runs together, covered three of the four. On Banking77 and CLINC150, GPT-6 Luna with examples scored below GPT-5.6 Luna, so we don't expect it to change Luna's best here, but that is an expectation, not a measurement.

Overall, ours leads by 2.0 points (+1.2 to +2.9), and the lead is narrow. It is 104 of 5,117 complaints, and the 2-point line in our rule is 102.3: had Luna answered 2 more complaints correctly, the rule would have dropped the accuracy headline here too. On long complaints the examples barely helped Luna, from 86.6% with the category list only to 87.5%. Each call also got long: about 5,400 input tokens on average.

As filed, on the 14 labels, ours scored 67.0% against 62.3% for Luna with examples (ours +4.7 points, +3.1 to +6.3) and 51.2% for Luna with the list only. TF-IDF scored 67.7%, a statistical tie with ours (ours −0.7, −1.9 to +0.5), and bge-small 63.7%. Our reading: with two names for the same product and no date in the text, a prompt with the list alone can't tell which name the agency used. The examples carry both names, which is likely why they helped Luna as filed, and a trained model learns which name is more common.

On macro-F1, which weights small products as much as big ones, GPT-5.6 Luna with examples (70.2%) came closer to ours (71.9%) than any trained alternative. There's no interval on macro-F1, so treat that as a hint, not a result.

08 Latency

Time per request

The API rows time each request from a Modal container to OpenAI's API, so they include that network round trip. The 8 October list-only runs kept 8 requests in flight and the example runs 2; the pre-registered chat runs kept 4 and the Decisions run 8. The search for the 20 similar examples ran beforehand, so it isn't in these times. Our rows were measured on one L4 inside the GPU's own region, one request at a time, so add your network round trip to them. The two aren't measured the same way.

ModelTest setPrompt or setupp50p95
Qwen3-4B + LoRA (ours)Banking77one L4, one request at a time194 ms269 ms
Qwen3-4B + LoRA (ours)CFPBone L4, one request at a time317 ms551 ms
OpenAI Decisions API (beta), GPT-6 LunaBanking77the 77 category names as choices120 ms246 ms
GPT-5.6 LunaBanking77the 20 most similar training examples812 ms1,158 ms
GPT-5.6 LunaBanking77the same, reasoning effort low847 ms1,424 ms
GPT-5.6 LunaBanking77an example of every category978 ms1,368 ms
GPT-5.6 LunaBanking77category list only863 ms1,194 ms
GPT-5.6 LunaCLINC150the 20 most similar training examples830 ms1,190 ms
GPT-5.6 LunaCLINC150category list only825 ms1,135 ms
GPT-5.6 LunaCFPBthe 20 most similar training examples902 ms1,225 ms
GPT-5.6 LunaCFPBcategory list only851 ms1,121 ms
GPT-6 LunaBanking77the 20 most similar training examples764 ms1,554 ms
GPT-6 LunaBanking77the same, reasoning effort low983 ms2,021 ms
GPT-6 LunaBanking77an example of every category1,117 ms2,391 ms
GPT-6 LunaBanking77category list only989 ms2,414 ms
GPT-6 LunaCLINC150the 20 most similar training examples840 ms1,396 ms
GPT-6 LunaCLINC150category list only907 ms2,079 ms
GPT-6 LunaCFPBcategory list only944 ms1,867 ms
GPT-4.1-miniBanking77an example of every category735 ms1,912 ms
GPT-4.1-miniBanking77category list only552 ms1,718 ms

Our model's Banking77 latency, throughput at higher load and serving cost are on the results page; its CFPB latency is from a separate one-at-a-time test on 300 complaints. With the 20 examples, Luna's latency rose little despite much longer prompts. We timed the embeddings classifiers only on long CFPB complaints, one at a time on one CPU core: bge-small 232 ms and bge-base 507 ms at the median. We didn't time them on Banking77's short messages.

09 What it means

When accuracy ties: cost, latency and control

On Banking77, three options land within one point of each other. On CLINC150, Luna with examples ties ours. On long CFPB complaints, ours stays 2.0 points ahead. Where the test can't separate options, the choice comes down to what you pay at your volume, how fast you need the answer, and who controls the model.

OptionBanking77 / CLINC150 / CFPBWhat you payMedian latencyWhat you run, and who controls it
GPT-5.6 Luna with the 20 most similar examples93.8% / 94.6% / 87.5%Per call, from our OpenAI bill for 8-9 Oct: about $0.37 per 1,000 calls on Banking77, $0.39 on CLINC150 and $1.20 on long CFPB complaints, where the examples are long.812 to 902 ms over the API, plus the search for the examplesAn index of your labelled examples, searched before each call. OpenAI can change or retire the model.
bge-base embeddings + classifier, on a CPU93.5% / 90.6% / 86.2%Flat: about $58 a month for one always-on CPU server (1 core, 4 GiB, on Modal).Not timed on short messages; 507 ms on long CFPB complaints, on one coreA small classifier you host. It changes only when you retrain it.
Qwen3-4B + LoRA, fine-tuned and owned94.0% / 95.1% / 89.5%Flat: one always-on L4 GPU, about $584 a month for the GPU alone, about $815 with an assumed 4-core, 16 GiB container. It carries about 130 million short Banking77 calls a month at 50 requests a second.194 ms on one L4, one request at a time; 317 ms on CFPB complaintsOne GPU, yours or ours. You keep the weights, and it changes only when you retrain it.

From our OpenAI bill for 8-9 Oct, GPT-5.6 Luna with examples comes to about $370 per million calls on Banking77 and about $1,200 on CFPB. So on Banking77 the CPU server costs less than Luna above roughly 160,000 calls a month, and the L4 with its container above roughly 2.2 million. On CFPB the L4 costs less above roughly 681,000. GPT-6 Luna costs about half as much per call ($0.18 per 1,000 on Banking77) and scored 0.7 points lower there. These figures leave out the cost and time of finding the 20 examples for each live request.

Low volume, or you'd rather not host a classifier: Luna with retrieved examples

If it passes your own test set, use it. You still need a labelled pool to retrieve from, and a small embeddings model or embeddings API to find the nearest examples for each request: our tests drew on the full training splits, 9,000 to 17,905 labelled rows. On long texts every call gets long too: about 5,400 input tokens a call on CFPB complaints. Pin the exact model ID and keep your test set, because OpenAI can change the model behind a name, and has named three replacements for nano fine-tunes since April 2026.

A flat bill on short, simple texts: an embeddings classifier

On Banking77 it tied ours, needs no GPU, and an engineer can train it in-house. It fell further behind on CLINC150, where it caught at most 62.9% of off-topic requests and Luna with examples was 4.0 points ahead of it, and on long CFPB complaints.

Long or messy text, high volume, or the cheaper options miss your bar: a model you own

A fine-tuned small model costs the same at 1 million calls a month as at 100 million. On one L4 it answered in 194 ms at the median one request at a time, and in 310 ms at about 50 requests a second (results page). It doesn't move unless you move it. With similar examples, Luna tied it on CLINC150. On long CFPB complaints it stayed 2.0 points ahead, with renamed products merged after seeing the errors, and the largest product alone accounts for more than that lead. Each Luna call there cost about three times as much as on short messages.

On your data

Whoever runs it for you, these 12 questions show what the report should contain. What ours looks like: a sample decision report on the same public CFPB complaints.

  • We run the same bake-off on your labelled data at no charge: GPT-5.6 Luna with your most similar labelled examples, an embeddings classifier and a model you'd own, plus your current model's answers if you still have them.
  • The check runs inside your cloud account or on a machine you control, so your data and the answers stay with you. You run the Luna column in your own OpenAI organization, with your own key; we never hold your key. We train only on labels a person or your records confirmed.
  • If Luna with retrieved examples passes, the recommendation is Luna. We set it up and test it on your data for a fixed $750. If an embeddings classifier is enough, we say that too.
  • If a model you own is needed, the migration is $3,000 fixed for one classification or routing task; the first three clients pay nothing up front and owe nothing unless the final model meets the margin we agree.
10 Limits

What this comparison doesn't show

  • One run each. The intervals cover sampling noise in the test set, not run-to-run variation: a different training seed for ours, or a different day on the API for Luna. OpenAI can also change a model behind the same name.
  • Public datasets. Banking77 and CLINC150 are clean, English and single-label; the CFPB complaints are real but public. Any of these models, including the base model we fine-tune, may have seen them during pre-training, and we can't rule that out. Your data is the test that counts.
  • CFPB's result is on a knife-edge. Ours leads Luna's best by 104 of 5,117 complaints, and the 2-point line in our rule is 102.3. Two more right answers for Luna would have put CFPB in the "drop the accuracy headline" band too. The paired interval, +1.2 to +2.9, is above zero but includes values under 2.
  • GPT-6 Luna with examples on CFPB is untested. It didn't fit the $50 cap. Running it now would be a new decision made after these results.
  • Luna's best is the best of up to 9 runs per test set, which flatters it slightly. We chose that on purpose, before the results.
  • A large labelled pool. Luna drew its examples from the full training split of each test set, 9,000 to 17,905 labelled rows, the same rows our model trained on. How it does with fewer labelled examples is untested.
  • Post-hoc comparisons. The per-product table, the real-requests-only score on CLINC150 and the comparisons between Luna runs weren't pre-registered. Per-product F1 has no intervals.
  • Not tested: reasoning effort medium; each Luna with the schema and no examples on Banking77 (pre-registered, left out for the budget); GPT-6 Luna with examples on CFPB; the Decisions API on CLINC150 and CFPB, or with choice descriptions; a fine-tuned encoder such as ModernBERT; and fine-tuned OpenAI models, because OpenAI refused to start a fine-tuning job on our account on 7 October 2026.
  • Prices checked against our bill, not per model. The per-token prices come from mirrors of OpenAI's docs and a third-party article. Our OpenAI bill for 8-9 Oct came to about 9% more than those prices for the same tokens, across both Lunas, so the costs here include that 9%. The bill doesn't split by model, so each model's own price is still unverified. We priced every input token at the full rate; if OpenAI discounted the tokens it cached, the per-call cost on prompts that don't cache is higher than shown.
  • The CFPB merge was chosen after seeing the confusions. In a migration, we agree a label mapping like this with you before training. The complaints were cut to 2,000 characters for every system alike.
  • Latency was measured differently for the API and for our model (section 08).
11 Reproduce

How to rerun it

Every accuracy, interval, paired difference, macro-F1, invalid count, off-topic rate and API latency in the tables is recomputed from the saved answers of each run and published as bakeoff.json. The per-product F1, the paired off-topic differences and the real-requests-only score come from the same saved answers but aren't in bakeoff.json. Our serving latency is from the results page. These are the commands that produced the answers.

# our fine-tuned model on a Modal L4: training, prediction, scoring, paired test
modal run scripts/modal_run.py --stage full                      # Banking77
modal run scripts/modal_run.py --stage full --dataset clinc150   # CLINC150, out-of-scope kept

# embeddings + logistic regression: bge-base, bge-small, MiniLM on all three test sets
modal run scripts/modal_embed_baseline.py

# 8 October: GPT-5.6 Luna and GPT-6 Luna, both plain prompts, one test set per run
modal run --detach scripts/modal_openai_prompted.py --model gpt-5.6-luna --dataset banking77
modal run --detach scripts/modal_openai_prompted.py --model gpt-6-luna --dataset clinc150

# the pre-registered Luna test on Banking77: 20 similar examples, Decisions API
python scripts/modal_openai_fair.py smoke    # 20 items a setting, before any full run
python scripts/modal_openai_fair.py run      # the full runs, in the pre-registered order
python scripts/modal_openai_fair.py fetch    # scores, Luna's best and the rule

# the pre-registered Luna test on CLINC150 and CFPB: 20 similar examples, $50 cap
python scripts/modal_openai_fair_more.py estimate   # no API key, can't spend
python scripts/modal_openai_fair_more.py run        # the runs, in the pre-registered order
python scripts/modal_openai_fair_more.py fetch      # scores, Luna's best, bands, off-topic rule

# gpt-oss-120b on one H100 (Banking77)
modal run --detach scripts/modal_strongbase.py

# every number on this page, from the saved answers
modal run scripts/modal_bakeoff_data.py

To try the Luna prompt on your own data: embed your labelled examples and each incoming message with BAAI/bge-small-en-v1.5 (normalised, so cosine is a dot product), take the 20 nearest labelled examples, and send the prompt above with a strict JSON schema whose enum is your label list. Score it on a held-out set it never retrieves from.

The CFPB file is built by scripts/make_cfpb_client_file.py and goes through the same one-command check in scripts/modal_run.py that a client's file does. The 8 October prompts and answer matching are in src/illate/baselines/llm.py; the bootstrap and paired test in src/illate/bootstrap.py and src/illate/parity.py.

These scripts are in ILLATE's private repository; a migration hands yours over with the report. Public now: the parity check (github.com/illate/parity), the Banking77 adapter (illate/qwen3-4b-banking77-lora), the prompt templates above and bakeoff.json.

12 Questions

Common questions

Is GPT-5.6 Luna as accurate as a fine-tuned model for text classification?

On two of our three public tests, yes, when it is shown the right examples. In pre-registered tests, GPT-5.6 Luna given the 20 training items most similar to each request tied a fine-tuned Qwen3-4B on Banking77 (93.8% against 94.0%, paired difference +0.2 points, 95% CI −0.6 to +0.9) and on CLINC150 (94.6% against 95.1%, +0.5, CI −0.04 to +1.0). On long CFPB complaints, scored with renamed products merged (a choice we made after seeing the errors), it trailed by 2.0 points (87.5% against 89.5%, CI +1.2 to +2.9), and one product, credit reporting, accounts for more than that gap; on the 14 labels as filed it scored 62.3% against 67.0%. With a fixed prompt it scored far lower: 87.2% on Banking77 with one example of every category. Test it on your own labelled data before deciding.

How do I get the best classification accuracy from GPT-5.6 Luna?

In our tests, the examples mattered most. Put the category list in the system message, hold the answer to your labels with a strict JSON schema, and add the 20 labelled examples most similar to each request, found with an embeddings model such as bge-small-en-v1.5. That took GPT-5.6 Luna from 87.2% with one fixed example of every category to 93.8% on Banking77, and from 88.4% with the category list only to 94.6% on CLINC150. On long CFPB complaints, with renamed products merged after seeing the errors, it added only 0.9 points over the category list only. Reasoning effort low didn't help, and GPT-6 Luna wasn't better.

Can GPT-5.6 Luna be fine-tuned?

No. Its OpenAI model page lists fine-tuning as not supported (checked 8 October 2026). You can prompt it, and give it retrieved examples, but you can't train it on your examples.

Is an embeddings classifier enough instead of a fine-tuned LLM?

Sometimes. On Banking77, short bank-support messages with 77 categories, a bge-base embeddings classifier scored 93.5% against 94.0% for our fine-tuned 4B model, a difference the paired test can't tell from zero. It fell further behind on CLINC150 (90.6% against 95.1%), where it caught 62.9% of off-topic requests against 83.9%, and on long CFPB complaints (86.2% against 89.5%, renamed products merged after seeing the errors). On CLINC150, GPT-5.6 Luna with similar examples, at 94.6%, was also clearly ahead of it.

Is GPT-6 Luna better than GPT-5.6 Luna for classification?

Not in our tests. With the same 20 similar examples, GPT-5.6 Luna scored 93.8% and GPT-6 Luna 93.1% on Banking77, a gap of 0.7 points (paired 95% CI +0.1 to +1.3). On CLINC150 they scored 94.6% and 94.4%, a gap the test can't tell from zero (−0.2 to +0.6). With the category list only, GPT-6 Luna caught more off-topic requests (90.0% against 77.8%) but wrongly rejected more real ones (5.5% against 2.3%). We didn't run GPT-6 Luna with examples on CFPB. Test the exact model you plan to ship.

Does a fine-tuned model handle off-topic requests better than GPT-5.6 Luna?

It's a trade-off. On CLINC150, against GPT-5.6 Luna given the 20 most similar examples, our fine-tuned model caught more off-topic requests, 83.9% against 79.1% (paired +4.8 points, 95% CI +2.5 to +7.2), but wrongly rejected slightly more real requests, 0.40% against 0.18%: 18 of 4,500 against 8. Which matters more depends on what a missed off-topic request costs you against a real customer turned away.

Is a fine-tuned model more accurate than GPT-5.6 Luna on long, messy text?

On long CFPB consumer complaints, ours scored 89.5% and GPT-5.6 Luna with the 20 most similar examples 87.5%: ours +2.0 points (95% CI +1.2 to +2.9), a narrow lead. Credit reporting, the largest product, accounts for more than all of it: ours got 117 more of its 3,266 complaints right. On the other products together Luna got 13 more right, most on mortgages, credit cards, vehicle loans and payday loans. These scores merge renamed products, a choice we made after seeing the errors; on the 14 labels as filed, ours scored 67.0% and Luna 62.3%.

How accurate is OpenAI's Decisions API for classification?

On Banking77, 76.8% (95% CI 75.3–78.2), against 94.0% for our model and 93.8% for GPT-5.6 Luna with similar examples. We sent one choice question per message, with the 77 category names as choices, no descriptions and no examples; 10 answers were refusals, counted as wrong. It was the fastest option we measured: 120 ms at the median, and about $0.07 per 1,000 calls, from our OpenAI bill for 8-9 Oct. It has been in beta since 6 October 2026, on gpt-6-luna only.

What does GPT-5.6 Luna with retrieved examples cost per call?

It depends on the length of your texts. On short Banking77 messages each call sent about 1,600 input tokens: about $0.37 per 1,000 calls, from our OpenAI bill for 8-9 Oct. On long CFPB complaints each call sent about 5,400: about $1.20 per 1,000 calls. That bill came to about 9% more than the per-token prices we read in OpenAI's docs through public mirrors, $0.20 per million input tokens and $1.20 per million output, across both Lunas. It doesn't split by model. For comparison, one always-on L4 GPU for our model is about $584 a month for the GPU alone, about $815 with an assumed 4-core, 16 GiB container, and one always-on CPU server for an embeddings classifier about $58.

How fast is GPT-5.6 Luna compared with a self-hosted model?

Called over the API from a Modal container, GPT-5.6 Luna answered in 812 to 978 ms at the median and 1,121 to 1,424 ms at p95 across our runs; 812 to 902 ms with the 20 similar examples. Our fine-tuned 4B model on one NVIDIA L4, one request at a time, measured 194 ms at the median and 269 ms at p95 on Banking77 inside the GPU's own region, and 317 ms on long CFPB complaints. The two weren't measured the same way, so add your network round trip to ours before comparing.

Did you train your model on Luna's answers?

No. Luna's answers were used only to score Luna. We train only on labels a person or your records confirmed: here, the labels that come with each public dataset.

Can I check these numbers?

Yes, in part. The datasets are public, the prompt templates are on this page, bakeoff.json has every accuracy, interval, macro-F1, invalid count and API latency, including the pre-registered Luna runs on all three test sets (our serving latency is from the results page), and the parity check and our Banking77 adapter are public. The training and run scripts are in our private repository; a migration hands yours over with the report.

Run this bake-off on your own data

Start with a 20-minute call or a short email; no data needed. Then, at no charge, we score GPT-5.6 Luna with your most similar labelled examples (you run it with your own key), an embeddings classifier and a model you'd own on the same held-out rows, where your data already is: inside your cloud account or on a machine you control (how to prepare the file). Nothing leaves your infrastructure, and you get a straight answer within 2 business days of getting access. If Luna passes, we tell you to use it, and setting it up on your data is a fixed $750. If a model you own is needed, the migration is $3,000 fixed for one classification or routing task; the first three clients pay nothing up front and owe nothing unless the final model meets the margin we agree (pricing).