Topic 4.7
Extraction and Classification
In one line
Extraction pulls fields out of text and classification picks one label from a list. Both go wrong the same way: a model asked to fill every field will invent a value. Give it an honest way out (null or unknown), and check values against the source.
Think of it like this
A form clerk copying details from a letter. The form has a phone number box, the letter has no phone number. A good clerk leaves the box empty. A clerk under pressure to "fill every box" writes a number that looks plausible. The second clerk's forms look complete and are quietly wrong. A model behaves like the second clerk unless you tell it, and check, otherwise.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Extraction
- Pulling specific fields (such as name or date) out of free text.
- Classification
- Choosing one label from a fixed list for a piece of text.
- Hallucination
- A confident, fluent statement from a model that is not true or not in the source.
- Null
- The JSON value meaning "no value". Different from an empty string or a missing key.
- Unknown option
- An allowed label such as
unknownthat the model can choose when no real label fits. - Evidence check
- Checking that an extracted value really appears in the source text.
- Confidence
- A number the model gives for how sure it is. Rough, often too high.
- Threshold
- The cut-off that decides which answers are accepted and which go to a person.
Step by step
01Step 1: the text with a missing field
The source email gives a name, a company and an invoice number, but no phone number. The task asks for four fields, one of which does not exist in the text. This is the situation that tempts a model to invent.
02Step 2: a model that fills every field
The first scripted reply writes phone: "555-0100". The JSON is valid, the schema is satisfied, a validator would pass it. Only a check against the source shows the problem.
03Step 3: the evidence check
keep_only_evidence looks at each non-null value and asks whether it appears in the source. 555-0100 does not, so it is set to null and reported. The second reply, from a model told to use null, passes without any removal.
04Step 4: classification with exits
The second example classifies tickets. The model may answer a label from the list, unknown, or something outside the list. The decide function turns each case into an outcome: a label, unknown (with the reason), or needs review when the confidence is below 70. It never passes on a label that is not in the list.
05Step 5: the lab
The lab asks a real model to extract the four fields from the same email, with instructions to use null for anything missing. Goals check the keys, that phone is null, and that the invoice is right. Try removing the "never guess" sentence and see whether the model invents a phone number.
Try it yourself
- 1
Predict the removal
Change the invoice in the first reply to
"4472". Predict whatinvented and removedsays, then run. - 2
Normalise before comparing
Change the source to say
phone 555 0100and the reply's phone to"555-0100". The check removes a real value. Fixkeep_only_evidenceto compare after removing spaces and dashes from both. - 3
Move the threshold
In the classification example change
THRESHOLDto 90. Which cases move toneeds review? Then change it to 50. What is the trade-off in reviewer workload? - 4
The lab
Run the lab at temperature 0 five times, then change it to 1.5 and run five more. Count how often
phonestays null.
Code & diagrams
A substring check is a crude evidence test. Real systems normalise spaces, case and number formats first.
Expected output
model that fills every field
name: Priya
company: Acme
phone: (missing)
invoice: 4471
invented and removed: phone
model told to use null
name: Priya
company: Acme
phone: (missing)
invoice: 4471
invented and removed: noneConfidence is an integer from 0 to 100, so the output is the same in every language.
Expected output
I was charged twice -> billing
The app crashes on start -> bug
Maybe dark mode, or fix login? -> needs review (feature, confidence 55)
hello -> unknown (the model said so)
Refund me -> unknown (label "refund" is not allowed)
??? -> unknown (reply was not JSON)Try: Send as it is. Does phone come back as null? Then delete the sentence "If a field is not in the text, use null. Never guess." from the system message and send a few times: does the model start inventing a number? Put the sentence back and make all four goals pass.
Goals
- It has the keys name, company, phone and invoice
- phone is null (nothing invented)
- invoice is 4471
- company is Acme
Sign in to send this request to a real model (free).
Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Skip the evidence check
In the Python extraction example change the test value not in source to False (so nothing is ever removed) and run it.
Myth vs fact
Myth
A model only invents facts when it doesn't know the topic.
Fact
It invents when the format demands an answer. A required field with no matching text invites a guess even on an easy task.
Myth
A confidence number from the model is a probability.
Fact
It is the model's guess about itself, often too high. Use it as a rough filter, tuned on real data.
Myth
Adding "unknown" lowers accuracy.
Fact
It lowers the number of wrong answers by turning them into honest ones. Accuracy on the answers you do give goes up.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Measure both sides: how often the model says unknown when a human would have answered (too cautious) and how often it answers wrongly when it should have said unknown (too bold). Tune the prompt and threshold against both.
- ▸
For long texts ask for the supporting quote next to each value (
phone_quote). The evidence check then becomes: is the quote in the source, and does the value appear inside the quote? - ▸
Keep a labelled sample of real inputs, including ones where the true answer is null or unknown, and run it after every prompt or model change (Phase 9). Extraction regressions are quiet.
Remember this
- 1
Why this exists. Extraction (pull the name, company, phone and invoice out of an email) and classification (billing, bug or feature request) are the two most common structured tasks. They are also where a model's urge to be helpful hurts most: if the schema says
phoneis a required string, the model must write a string, and it will make one up. The result is valid, well-formed and false. This is a hallucination (Topic 1.9) caused by the format, not by missing knowledge. - 2
Give the model an honest answer. For extraction, make the field nullable and say in the prompt: "If a field is not in the text, use null. Never guess." For classification, add an
unknown(orother) option to the enum and say when to use it. Without those exits, the schema forces a wrong answer whenever the truth is "not there". - 3
Null is information.
phone: nullmeans "the model looked and the text has no phone number". A missing key means "the model skipped it". Strict provider modes often require all keys, so null becomes your marker for "not found" (Topic 4.4). Downstream code can then ask the customer for the phone number instead of calling a made-up one. - 4
Check against the source: the evidence check. For extraction of facts that should appear in the text (names, ids, amounts), check that each value actually occurs in the source. The first example runs a model that invents
555-0100: the check finds it isn't in the email and replaces it with null. It's crude (a phone number written "555 0100" would be rejected), so normalise before comparing, but it catches the most dangerous failure for almost no cost. - 5
Confidence, used carefully. You can ask for a confidence number, as an integer from 0 to 100, and send low-confidence answers to a person. Models are often overconfident, so the number is a rough signal, not a probability. Pick the threshold from data: collect real examples, see what confidence the wrong answers had, and choose a cut-off that gets the errors without drowning your reviewers. The second example shows the decisions: allowed label, "unknown" from the model, label outside the list, low confidence, and a reply that isn't JSON.
- 6
Make the unsafe case the default. In a pipeline, the path with the least harm should be the one taken when anything is doubtful.
unknowngoes to a human queue.nullfields are asked for again. Nothing is invented to keep a form looking complete. Add counters for how oftenunknownand nulls occur. A sudden rise or fall both mean something changed, and a fall can mean the model has started guessing.
Explain it without notes
Why does a required field in a schema encourage hallucination?
What is an evidence check, and what are its limits?
How would you design the label list for a classifier?
How do you use a confidence value responsibly?
Practice
Write normalise(s) that lowercases a string and removes spaces and dashes, and supported(value, source) that returns True when the normalised value is inside the normalised source.
Write route(label, confidence) that returns "auto" if the label is not unknown and the confidence is at least 70, and "human" otherwise.
Trade-offs
- ↔
A low confidence threshold automates more and lets more errors through; a high one sends more work to people.
- ↔
Evidence checks add a little code and reject some legitimate reformatted values; they save you from invented facts.
- ↔
An
unknownoption makes the pipeline honest and needs a place for unknowns to go.
Done when you can
Done when you can make a schema field nullable and write the prompt sentence that uses it.
Done when you can add
unknownto a classification enum with a rule.Done when you can write an evidence check for extracted values.
Done when you can choose a confidence threshold from real examples.
Done when you can explain why a valid reply can still be false.