Skip to main content
A bank’s support inbox receives messages like “my card still hasn’t arrived” and “why was I charged a fee for this transfer?”. Each should go to the right place, and there are 77 places. The public BANKING77 dataset (PolyAI, CC BY 4.0) has messages like these with an intent label on each, which makes it a good place to ask how well a model routes them. The example is in prototype/examples/banking77/: 770 messages for developing the spec, and 385 held back to measure it.

Names are not enough

The first spec listed the 77 intent names and nothing else. It scored 82.3% on the holdout against the dataset’s labels. Reading the mistakes showed why. Some names mislead: get_physical_card is about PINs, not about getting a card. A model that sees only the name has no way to know. So the second step was to describe every option in one line:
intent.yml
With every option described, the holdout score rose to 88.3%: 28 rows fixed, 5 broken, p < 0.001. Describing only 29 of the 77 did not help significantly (22 fixed, 14 broken, p = 0.24). The described options pulled in rows that belonged to their bare neighbours. Describe all the options or none. To reproduce it from the repository root, at no cost:
diff scores against today’s gold, which includes the reviews described next, so its percentages are higher than the raw-label ones above.

The answer key was wrong more often than the model

At 88.3%, the model disagreed with the dataset’s label on 45 of the 385 holdout messages. The natural reading is 45 model mistakes. It was not. A panel of three AI reviewers checked every one of the 45. Each reviewer first chose the right intent from the message alone, then rated the two candidate labels without being told which was the key’s and which the model’s. The label was wrong 20 times. The model was wrong 5 times. For the other 20, both intents were reasonable answers to the message. If some labels are wrong where the model disagrees, some are probably wrong where it agrees too, and the model would be wrong along with them. So the panel also checked a random 60 of the 340 agreeing rows. With both sets of verdicts, hunch test estimates accuracy over all 385:
The model was right on an estimated 95.8% of messages, against 91.9% for the answer key itself. Two lessons followed and became part of hunch. Gold can be a set: where two intents are both acceptable, both count as right. And a review queue needs random spot checks as well as disagreements, or it can only move accuracy up.

When to trust an answer

The spec sets act: 0.90: below that confidence, a message goes to a person. On the holdout, 81% of messages were at or above it, and none of those were wrong on current gold. Calibration error was 0.043, so the model’s confidence is close to how often it is right.

A tree that lost

77 options make a long request. A tree seemed cheaper: first route each message to one of ten groups, then choose among that group’s intents. prototype/examples/banking77_tree/ builds it from the same descriptions. It was 48% cheaper. diff against the flat spec:
33 messages were sent to a group that did not contain their answer. Once in the wrong group, the second question had no right option left. After the panel reviewed the tree’s own disagreements, its estimated accuracy was 87.5%, against 95.8% for the flat question.

Also tried

  • hunch suggest rewrote the bare-label question automatically. It gained 2.4 points on the holdout, not significant; the hand-written descriptions gained 6.0. See Change a spec.
  • A second engine, DeepSeek V4.1 Flash, scored 92.3% on the same spec: fixed 4, broke 22, p = 0.001. See Engines.