Confidence and review routing
- Lesson 15
- 55 min
- Module 5: Validate and review results
Lesson 15 of 19 · Document and Multimodal AIConfidence and review routing
1. Understand document inputs
2. Prepare and extract content
3. Recover structure and tables
4. Use multimodal models deliberately
5. Validate and review results
6. Evaluate and deliver the pipeline
7. Case studies
What you will be able to do
Decide which documents a person must check, with signals that you can measure. They are validation errors, model values that are not on the page, and the reading scores of the evidence. Measure the model’s own confidence before you trust it, and choose the review threshold from the results.
Before you startWhat to know and to have ready
What you need to know
- The errors and warnings of Business validation.
- Conflicts, grounded and unverified values from Combine methods, and the reading score of an OCR line from Text and OCR paths.
- A threshold on a score, from Thresholds and calibration in Machine Learning: From Problem to Reliable Model, if you took it. This lesson uses the same two ideas on a review queue.
What you need on your computer
- Your project at the end of the previous lesson:
python -m pytest -qprints90 passed.
Tested with Python 3.12.13 on macOS (Apple silicon). The Windows and Linux commands are not tested. Every model answer in this lesson is a recording of a real answer, replayed: no key, no cost.
Get the lesson’s files
Section titled “Get the lesson’s files”-
In the project folder, copy the module, its design note and its test from the snapshot:
Terminal window S=../nextia-learning-labs/C13/snapshots/end-of-m05cp $S/doc_extract/review.py doc_extract/cp $S/docs/review-queue.md docs/cp $S/tests/test_review.py tests/Terminal window $S = "..\nextia-learning-labs\C13\snapshots\end-of-m05"Copy-Item "$S\doc_extract\review.py" doc_extract\Copy-Item "$S\docs\review-queue.md" docs\Copy-Item "$S\tests\test_review.py" tests\ -
Run the tests:
Terminal window python -m pytest -qThe last line is
94 passed.
A new colleague who is always sure
Section titled “A new colleague who is always sure”A new colleague joins your team. You ask about each piece of work: “Are you sure?” The answer is always “Yes, completely.” After a month, you notice that most of their work is right, and some is wrong. Their “completely sure” came with both. So their answer tells you nothing about which piece to check.
You learn to look at other things. A sum that does not add up. A smudged number on a scan. A value that you cannot find on the page. These signals you can see for yourself, and they do tell you where the mistakes are likely to be.
Grace asked for the same thing at the start of the course: “Send us only the ones it is not sure about.” The question is: whose “sure”?
The problem: the model is sure of its mistakes
Section titled “The problem: the model is sure of its mistakes”Tomás has an easy answer: “The vision model gives a confidence for every field. Prompt v2 asks for it. Send a person every document where it is under 0.90.” The course tested this on the model’s recorded answers for all 54 receipt documents. First, every answer where the model is wrong:
D009 vendor model LARKEFIELD (found ) confidence 1 | true LarkfieldD045 tax model None (absent cut_off) confidence 0.96 | true 46.66D046 vendor model None (absent unreadable) confidence 1.0 | true LarkfieldD046 order_number model None (absent unreadable) confidence 1.0 | true LK-838601D046 date model None (absent unreadable) confidence 1.0 | true 2026-08-16D046 currency model None (absent unreadable) confidence 1.0 | true CADD046 subtotal model None (absent unreadable) confidence 1.0 | true 249.00D046 tax model None (absent unreadable) confidence 1.0 | true 32.37D046 total model None (absent unreadable) confidence 1.0 | true 281.37D046 payment_method model None (absent unreadable) confidence 1.0 | true Cash10 wrong answersTen wrong answers. Nine of them say confidence 1.0, the highest possible. Eight are D046, the photo that nobody can read: the model said “absent, unreadable” for every field, and it was completely sure. That answer is honest, because the photo is unreadable. But the receipt’s values are lost, and the confidence does not warn anyone. The ninth is D009’s vendor LARKEFIELD, a misread, also at 1.0.
Now route each document by the lowest confidence that the model gave on it:
D009 lowest confidence 0.98 wrong: vendorD045 lowest confidence 0.96 wrong: taxD046 lowest confidence 1.0 wrong: vendor, order_number, date, currency, subtotal, tax, total, payment_methodthreshold 0.90: 0 reviewed; documents with a wrong value to agents: D009, D045, D046threshold 0.96: 0 reviewed; documents with a wrong value to agents: D009, D045, D046threshold 0.97: 1 reviewed; documents with a wrong value to agents: D009, D046threshold 0.99: 8 reviewed; documents with a wrong value to agents: D046threshold 1.00: 28 reviewed; documents with a wrong value to agents: D046At Tomás’s threshold of 0.90, nobody checks anything, and three documents with wrong values go straight to the agents. Even at 1.0, which sends to a person every document that is not “completely sure”, D046 still goes to the agents. Its wrong answers all say 1.0.
The idea, part 1: is a confidence calibrated?
Section titled “The idea, part 1: is a confidence calibrated?”A calibrated confidence is one that matches reality over many answers. A weather forecaster who says “70% chance of rain” on 100 days is calibrated if it rains on about 70 of them. If it rains on all 100 days, or on 30, the number is wrong, even if each single forecast sounds reasonable.
So you check a confidence by grouping the answers by what they said, and counting how many were right in each group. A tiny example with every value visible: a reader gives 10 answers. It says 0.9 for five of them, and four are right (80%). It says 0.6 for five, and three are right (60%). The 0.9 group is right more often, so the numbers carry some information, but 0.9 overstates 80%.
The project does this for the model:
python -m doc_extract.review --calibrationThe model's own confidence (vision path, prompt v2) against the true values: confidence 1.0 341 answers, 332 correct (97.4%) confidence 0.95-0.99 145 answers, 144 correct (99.3%) confidence 0.80-0.94 0 answers, 0 correct (-) confidence under 0.80 0 answers, 0 correct (-)Text description of the chart
The vision model gave 486 field answers. Its lowest confidence was 0.96. At confidence 1.0: 341 answers, 332 right (97.4%), so 9 wrong. At 0.95 to 0.99: 145 answers, 144 right (99.3%), so 1 wrong. No answer had a confidence under 0.95. The answers that said 1.0 were right less often than the answers that said less.
Every answer says 0.96 or more, so the number cannot separate anything. And the answers at “1.0” are wrong more often (2.6%) than the answers at 0.95–0.99 (0.7%). This model’s confidence is not calibrated on this task. The measure took no new model call: the confidences were in the recorded answers.
Tomás objects: “Then ask the model to be more careful with its numbers.” The course does not try that. A different prompt may give different numbers, and you would have to measure them again in the same way. A signal that you measure yourself does not need the model’s cooperation.
The idea, part 2: certainty from things you can measure
Section titled “The idea, part 2: certainty from things you can measure”The project’s review decision uses two kinds of signals. Neither of them is the model’s confidence.
- Reasons, which always send a document to a person. A reason is a validation error, a conflict between the rules and the model, or an unverified value.
- Certainty, a number from 0 to 1 that the project measures. It is the lowest reading score of the evidence of the required fields. A value from the PDF text layer scores 1.0, because the text is stored in the file. A value from OCR has the score that the OCR engine gave its line. A value that only the model gave has no line of its own, so it scores 0.5.
A document goes to review when it has a reason, or when its certainty is under the threshold. The minimum is used because one bad field is enough to make a receipt wrong. Here are three receipts, each field with its score. Start python and type:
from doc_extract import combine, documents, reviewfor doc in ("D011", "D024", "D017"): ex, dec = combine.combine(documents.get(doc)) scores = {n: (round(ex.fields[n].score, 2) if dec.source[n] in ("rules", "both") else 0.5) for n in combine.REQUIRED if ex.fields[n].value is not None} print(doc, scores, "-> certainty", review.certainty(ex, dec))D011 {'vendor': 1.0, 'date': 1.0, 'currency': 1.0, 'subtotal': 1.0, 'tax': 1.0, 'total': 1.0} -> certainty 1.0D024 {'vendor': 1.0, 'date': 0.99, 'currency': 0.99, 'subtotal': 1.0, 'tax': 0.91, 'total': 0.99} -> certainty 0.9076D017 {'vendor': 1.0, 'date': 1.0, 'currency': 1.0, 'subtotal': 0.5, 'tax': 1.0, 'total': 0.5} -> certainty 0.5D011 is a digital PDF: every value comes from the text layer, so its certainty is 1.0. D024 is a scan at a low resolution: the tax line has an OCR score of 0.91, and that is the certainty. D017 is the partner shop’s receipt. Its subtotal and total came from the model, so they score 0.5. The receipt goes to a person at any threshold over 0.5. The model’s values were grounded on the page, but the rules did not read them, so a person confirms them.
The review queue
Section titled “The review queue”-
Build the queue at the project’s threshold of 0.90:
Terminal window python -m doc_extract.reviewD017 maple-pine-0534-560.pdf certainty 0.50 is under 0.9D018 maple-pine-0727-251.pdf certainty 0.50 is under 0.9D033 scan-partner-1.jpg certainty 0.50 is under 0.9D034 scan-partner-2.jpg certainty 0.50 is under 0.9D045 IMG_4880.jpg total: total is missing; currency: unverified: the model's value cannot be found on the pageD046 IMG_4887.jpg vendor: vendor is missing; date: date is missing; currency: currency is missing; subtotal: subtotal is missing; tax: taxD047 IMG_4894.jpg vendor: vendor is missing; date: date is missingD048 IMG_5100.jpg certainty 0.50 is under 0.9D049 IMG_5101.jpg certainty 0.50 is under 0.9D050 receipt-lk-762786.pdf total: subtotal 449.00 + shipping 0 + tax 58.37 = 507.37, but the total is 517.37 (difference +10.00)D051 scan0099.jpg total: subtotal 528.00 + shipping 0 + tax 68.64 = 596.64, but the total is 595.74 (difference -0.90)11 of 54 documents to review (20%); wrong totals caught 1, wrong totals sent to agents 0queue written to out/review-queue.jsonEleven of the 54 receipt documents go to a person. Five have a reason: the three damaged photos and the two totals that do not add up. Six have a certainty of 0.50: the partner shop’s receipts, whose money values came from the model.
-
Read one entry of the queue file. In
python:import jsonqueue = json.load(open("out/review-queue.json", encoding="utf-8"))print(len(queue), "documents in the queue")print(json.dumps(queue[-1], indent=1, ensure_ascii=False))11 documents in the queue{"doc_id": "D051","file": "scan0099.jpg","certainty": 0.9746,"reasons": ["total: subtotal 528.00 + shipping 0 + tax 68.64 = 596.64, but the total is 595.74 (difference -0.90)"],"values": {"vendor": "Larkfield","order_number": "LK-444410","date": "2026-07-25","currency": "CAD","subtotal": "528.00","shipping": null,"tax": "68.64","total": "595.74","payment_method": "Mastercard"}}Each entry has the reasons in plain words and the values, so the reviewer does not need to run anything.
-
Open
docs/review-queue.md. It is the design of the queue: what goes into it, what the reviewer sees, and what the reviewer can do. You use it in the module practice.
The last line of the queue says wrong totals caught 1. That is D046’s total: no reader saw it, so it is missing, and “missing” counts as wrong against the truth. The totals of D050 and D051 are not wrong readings: the receipts print them. They go to review because they do not add up.
What does a reviewer see? The queue gives the reasons, and the evidence gives the place of each value. For the cut-off photo D045:

Text description of the diagram
The photo D045 shows a Larkfield receipt cut off just below the tax line. Green boxes mark the values that the rules read: the shop name, the date, the order number, the subtotal 358.95 and the tax 46.66. A red line marks the cut edge. Next to it, the card gives the certainty, 0.50, and two reasons. The total is missing. The currency CAD came from the model, and it is not on the page. The values list shows each field with its status: the total and the payment are absent with the reason cut_off.
Choose the threshold
Section titled “Choose the threshold”The threshold decides how many documents a person checks. A higher threshold sends more documents to review. So does a higher threshold catch more mistakes? Run the sweep:
python -m doc_extract.review --sweepthreshold reviewed wrong totals caught wrong totals to agents 0.00 5 (9%) 1 0 0.50 5 (9%) 1 0 0.80 11 (20%) 1 0 0.90 11 (20%) 1 0 0.95 18 (33%) 1 0 0.97 25 (46%) 1 0 0.99 33 (61%) 1 0 1.00 36 (67%) 1 0The number of reviewed documents climbs from 5 to 36. The number of wrong totals sent to the agents stays at 0 on every line. Predict before you move the slider: what does the line of wrong values look like when you review fewer documents?
Explore
How many documents does a person check?
Predict first: If we review fewer documents, how many wrong totals reach the agents?
Solid line: the project. Dashed line: the model alone, routed by its own confidence. The 54 receipt documents of the course.
At 0.9: the project sends 11 of 54 documents to a person, and 0 with a wrong value to the agents. Routed by its own confidence, the model alone sends 0 to a person, and 3 with a wrong value to the agents.
| threshold 0.9 | Certainty and checks (the project) | The model's own confidence (model alone) |
|---|---|---|
| Documents a person checks | 11 (20%) | 0 (0%) |
| Wrong totals sent to the agents | 0 | 1 |
| Documents with a wrong value sent to the agents | 0 | 3 |
| Which ones | none | D009 (vendor); D045 (tax); D046 (every printed field) |
Text description of the explorer
One control sets the review threshold from 0 to 1. The top chart shows how many of the 54 documents a person checks. With the project’s routing, 5 documents at any threshold up to 0.50, then 11 from 0.51 to 0.90, then more as the threshold climbs: 18 at 0.95, 33 at 0.99 and 36 at 1.00. With the model alone, routed by its own confidence, nobody checks anything up to 0.96, then 1 document at 0.97, 3 at 0.98, 8 at 0.99 and 28 at 1.00. The bottom chart shows the documents with a wrong value that go straight to the agents. With the project’s routing, the line is flat at 0 for every threshold. The only document with wrong values, D046, has validation errors, and errors always go to a person. With the model’s confidence, 3 documents go to the agents up to 0.96 (D009 vendor, D045 tax, D046 every printed field), 2 at 0.97 and 0.98, and 1 (D046) at 0.99 and 1.00. The answer: with the project’s routing, no wrong total reaches the agents at any threshold. The errors set the floor, not the threshold.
The flat line at 0 is the good news. Errors and unverified values go to review at every threshold, so the only wrong document, D046, never reaches the agents. The threshold does not decide whether a wrong total is caught. Validation does.
So what does the threshold buy? On this set, nothing more: every document that it adds is right. The course checked every value that the rules read on a scan or a photo, grouped by its OCR reading score:
reading score under 0.90 1 values, 0 wrong (0.0%)reading score 0.90-0.95 10 values, 0 wrong (0.0%)reading score 0.95-0.99 77 values, 0 wrong (0.0%)reading score 0.99-1.00 145 values, 0 wrong (0.0%)None of the 233 values is wrong, at any score. The OCR mistakes of this set are values that the rules missed, and a missing value is a validation error. So the threshold is insurance for errors that validation cannot see, like a wrong amount that still adds up. You do not know the size of that risk until you see such errors. Two practical rules follow:
- Start with a threshold that sends the documents you do not trust yet: here 0.90, which sends every receipt where the model filled a gap.
- Measure again on your own labelled documents when the documents change: a new shop, a new phone camera, a new OCR version. Then move the threshold if the errors move.
Mei sums it up for Omar: “Eleven documents in 54 go to a person, about one in five. Five of them must go. The other six are the partner shop, until we write rules for its layout.”
When to route by certainty, and what it costs
Section titled “When to route by certainty, and what it costs”- Route by reasons first. An error or an unverified value is a fact about the document, not a guess. It goes to a person at any threshold.
- Use a certainty that you can measure and explain: a reading score, the reader that found the value, a check that passed. Measure the model’s own confidence before you use it. Here it was not calibrated, so the project does not use it.
- What it costs: every reviewed document costs a person’s time. At 0.90, 11 of 54 (20%); at 1.00, 36 (67%), with no extra error caught on this set.
- When not to use a threshold: when you have no labelled documents to measure it on. Then review a fixed sample first, label it, and set the threshold from that.
Common mistake
Review everything that is not perfect
Symptom: After a bad week, a manager asks for “maximum safety”: the threshold goes to 1.00. The queue grows from 11 to 36 documents out of 54. The reviewers fall behind, and they start to accept cards after a quick look.
Cause: The higher threshold adds 25 documents, and on this set all 25 are right. The sweep shows it: wrong totals to the agents is 0 at 0.90 and still 0 at 1.00. A queue full of right documents teaches reviewers that the queue is usually wrong. That is the risk: a real error then gets a quick “accept” too. The tired reviewers are a constructed story; the counts are the course’s measurement.
Diagnose: For each threshold, count the reviewed documents and the errors they catch, on documents that you labelled. If the errors caught do not grow with the queue, the extra reviews buy nothing.
Fix: Keep the threshold where the measurement puts it (0.90 here), and keep the reasons that always route. Spend the reviewers’ time on the documents that have a reason. If you want more safety, add a check that catches a known kind of error, such as the vendor list in the module practice.
Consequence in AI work: human review is a limited resource. A review step only protects you if the people in it have time to look carefully.
Your turn: a stricter threshold
Section titled “Your turn: a stricter threshold”Change one condition: the threshold 0.95 instead of 0.90. Predict which kinds of documents join the queue: digital PDFs, scans or photos? Then run:
python -m doc_extract.review --threshold 0.95Show the answer
D017 maple-pine-0534-560.pdf certainty 0.50 is under 0.95D018 maple-pine-0727-251.pdf certainty 0.50 is under 0.95D020 scan0002.jpg certainty 0.94 is under 0.95D024 scan0006.jpg certainty 0.91 is under 0.95D033 scan-partner-1.jpg certainty 0.50 is under 0.95D034 scan-partner-2.jpg certainty 0.50 is under 0.95D040 IMG_4845.jpg certainty 0.92 is under 0.95D042 IMG_4859.jpg certainty 0.95 is under 0.95D043 IMG_4866.jpg certainty 0.94 is under 0.95D044 IMG_4873.jpg certainty 0.94 is under 0.95D045 IMG_4880.jpg total: total is missing; currency: unverified: the model's value cannot be found on the pageD046 IMG_4887.jpg vendor: vendor is missing; date: date is missing; currency: currency is missing; subtotal: subtotal is missing; tax: taxD047 IMG_4894.jpg vendor: vendor is missing; date: date is missingD048 IMG_5100.jpg certainty 0.50 is under 0.95D049 IMG_5101.jpg certainty 0.50 is under 0.95D050 receipt-lk-762786.pdf total: subtotal 449.00 + shipping 0 + tax 58.37 = 507.37, but the total is 517.37 (difference +10.00)D051 scan0099.jpg total: subtotal 528.00 + shipping 0 + tax 68.64 = 596.64, but the total is 595.74 (difference -0.90)D058 warranty-claim-wc-2026-0418.pdf certainty 0.92 is under 0.9518 of 54 documents to review (33%); wrong totals caught 1, wrong totals sent to agents 0queue written to out/review-queue.jsonSeven documents join. They are the scans D020 and D024, the photos D040, D042, D043 and D044, and the warranty claim D058, whose receipt page is a scan. Their certainty is from 0.91 to 0.95, because OCR read a field of each at that score. No digital PDF joins: the text layer scores 1.0. D042 has a certainty of 0.9485, which prints as 0.95, and it is under the threshold. Every added document is right, so the queue grows by seven reviews and catches no new error.
Knowledge check
Section titled “Knowledge check”Check your understanding
Explain why the project does not route by the model's confidence, with two numbers from the course.
Show the answer
The model’s confidence is not calibrated on this task. First, its lowest confidence on 486 answers is 0.96, so a threshold can hardly separate anything. Second, 9 of its 10 wrong answers say 1.0, the highest value. The answers at 1.0 are right 97.4% of the time, and the answers at 0.95–0.99 are right 99.3%. Routed by that confidence at 0.90, three documents with wrong values, D009, D045 and D046, go straight to the agents.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
Predict: a receipt from the text layer has a total that does not add up. Its certainty is 1.0. Does it go to review at the threshold 0.50?
Show the answer
Yes. A total that does not add up is a validation error, and an error is a reason. A document with a reason goes to review at any threshold, even when its certainty is 1.0. This is D050: a digital PDF, certainty 1.0, and in the queue at every threshold, as review --threshold 0.5 shows with its 5 documents.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
Practical check: run python -m doc_extract.review --threshold 0.5. How many documents are in the queue, which ones leave it compared with 0.90, and why?
Show the answer
Five documents: D045, D046, D047, D050 and D051, each with a reason. The six partner-shop receipts (D017, D018, D033, D034, D048, D049) leave the queue. Their subtotal and total came from the model, so their certainty is 0.50. And 0.50 is not under the threshold 0.50. The wrong totals sent to the agents stay at 0. Grace decides if the partner-shop values can go straight to the agents. They are grounded on the page, but no rule read them.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Recap
- A confidence is calibrated when it matches how often answers are right. The model’s is not: lowest 0.96, and 9 of its 10 wrong answers say 1.0.
- The project’s review decision: a reason (validation error, conflict, unverified value) always goes to a person. Otherwise, the certainty is compared with the threshold.
- At 0.90: 11 of 54 documents to review (20%), 0 wrong totals to the agents. The line is flat at every threshold: the errors set the floor.
- On this set, a higher threshold adds only right documents. Measure the threshold on your own labelled documents, and again when they change.
- A reviewer sees the page with the boxes, the reasons and the values with their status.
Next, the reviewer acts: a correction is recorded next to the original value, with a name, a note and a version, and nothing is erased.
Next: Correction workflow