Study guides / CCAR-F / Domain 5

Context Management & Reliability · Lesson 5 of 6

5.5 - Human Review & Confidence Calibration

Design human review workflows and confidence calibration, including the aggregate metrics trap, stratified random sampling, field-level confidence scores, and reviewer capacity prioritisation.

Human review is the safety net for automated extraction and classification systems. The exam tests your understanding of when and how to deploy human reviewers effectively. The core challenge is not whether to use human review, but how to allocate limited reviewer capacity to maximise accuracy while minimising cost. This requires understanding confidence calibration, the trap of aggregate metrics, and stratified sampling strategies.

The Aggregate Metrics Trap

This is the most dangerous misconception in production extraction systems. A system reports 97% overall accuracy. The team celebrates. Management approves full automation for all high-confidence extractions.

The problem: that 97% hides catastrophic failure rates on specific document types. The system extracts dates from standard invoices at 99.5% accuracy. But handwritten receipts? 60%. Scanned PDFs with poor OCR? 72%. International documents with non-standard formatting? 45%.

The aggregate masks the segments where the system fails most. And those segments are often the ones where errors have the highest business impact - handwritten receipts from field staff, international invoices from new suppliers, scanned historical documents for compliance audits.

The rule: always validate accuracy by document type AND field segment before automating. Never make automation decisions based on aggregate metrics alone.

Document Type Date Accuracy Amount Accuracy Name Accuracy
Standard invoices 99.5% 98.2% 97.8%
Handwritten receipts 60.1% 55.3% 71.2%
Scanned PDFs 72.4% 69.8% 80.1%
International formats 45.2% 52.1% 63.4%
Aggregate 97.0% 96.1% 95.8%

The aggregate looks excellent because standard invoices dominate the volume. But three document types have unacceptable accuracy, hidden by the volume-weighted average.

Stratified Random Sampling

Even after validating by document type and field, you need ongoing verification. Stratified random sampling means selecting a representative sample from each stratum (document type, confidence band, field type) and having humans verify it.

The critical insight is that you must sample high-confidence extractions, not just low-confidence ones. Low-confidence items are already routed to human review. High-confidence items are automated. If the model develops a novel error pattern that affects high-confidence extractions, only stratified sampling will catch it.

Stratified sampling serves two purposes:

  1. Ongoing accuracy measurement - confirm that each segment maintains its validated accuracy rate.
  2. Novel error pattern detection - discover new failure modes that did not exist in the original validation set.

Without stratified sampling, you're flying blind on your automated extractions. The system could develop a systematic error on a new document format and you wouldn't know until downstream business processes fail.

Field-Level Confidence Calibration

The model can output confidence scores per field. For an invoice extraction, it might report:

{
  "vendorName": {"value": "Acme Corp", "confidence": 0.98},
  "invoiceDate": {"value": "2024-03-15", "confidence": 0.95},
  "totalAmount": {"value": "$1,247.83", "confidence": 0.72},
  "lineItems": {"value": [...], "confidence": 0.61}
}

But raw model confidence scores are not calibrated. A model that reports 0.95 confidence might actually be correct 88% of the time on certain field types. Or 99% of the time on others. The confidence score is relative, not absolute.

Calibration requires labelled validation sets (ground truth data). You take a set of documents with known correct extractions, run the model, compare its confidence scores to actual accuracy, and build a calibration curve. This tells you: "When the model reports 0.90 confidence on date fields, it's actually correct 94% of the time. When it reports 0.90 on amount fields, it's actually correct 82% of the time."

Calibrated thresholds then drive routing:

Reviewer Capacity Prioritisation

Human reviewers are expensive and limited. The exam tests whether you understand how to allocate their capacity effectively.

Route the highest-uncertainty items to reviewers first. This means:

Do NOT spread reviewer capacity evenly across all extractions. An even distribution wastes time reviewing high-confidence items that the model handles well while leaving insufficient capacity for the uncertain items that actually need human judgement.

The prioritisation should be dynamic, not static. As the system processes documents, the queue of items awaiting human review should be ordered by uncertainty. When a reviewer finishes one item, the next item in their queue should be the highest-uncertainty item remaining, not simply the next in chronological order.

Validation Before Automation

The sequence matters:

  1. Measure accuracy by document type and field segment - not aggregate.
  2. Calibrate confidence scores using labelled validation sets.
  3. Set calibrated thresholds for automation versus human review.
  4. Implement stratified random sampling for ongoing verification of automated extractions.
  5. Only then reduce human review on segments that demonstrate consistent, validated accuracy.

Skipping to step 5 based on aggregate metrics is the trap. Every step in this sequence exists to prevent a specific failure mode.

Key Concept

97% aggregate accuracy can hide 40% error rates on specific document types. Validate accuracy by document type AND field segment. Calibrate confidence scores using labelled validation sets. Sample high-confidence extractions through stratified sampling. Prioritise limited reviewer capacity on the highest-uncertainty items.

Exam traps

Practice question

A structured data extraction system achieves 97% overall accuracy across all document types. The team proposes automating all extractions where model confidence exceeds 95% to reduce human review costs. What is the critical risk in this approach?

  • A The 95% confidence threshold is too low for automation and should be raised to 99% before any extractions bypass human review

    The threshold value is not the core issue - the problem is that aggregate metrics hide per-type performance disparities regardless of where the threshold is set.

  • B Aggregate accuracy may mask poor performance on specific document types or fields, and confidence scores need calibration against labelled validation sets before use Correct

    97% overall can hide 40% error rates on specific document types. Without stratified validation and confidence calibration, automation will silently fail on certain inputs.

  • C The model will become overconfident as it processes more documents over time, so the system will require regular retraining to stay calibrated

    LLMs do not train during inference. The issue is existing calibration gaps in different document types, not model drift.

  • D Automated extractions should always receive human review regardless of the confidence score, which makes the proposal fundamentally flawed no matter where the threshold is set

    Automation is valid when properly validated by document type and field segment. The issue is making the decision based on uncalibrated aggregate metrics, not the concept of automation itself.

Build exercise: Build a Confidence-Calibrated Review Router

Advanced · 50 minutes

You'll practice:

  1. Create a mock extraction system that outputs field-level confidence scores for different document types (invoices, receipts, scanned PDFs, international documents)

    Field-level confidence scores are the foundation of intelligent review routing. The exam tests that raw model confidence is not calibrated and must be validated against ground truth before use. Building the mock system gives you data to calibrate against.

    You should see: An extraction function that returns each field with its value and a confidence score between 0.0 and 1.0. The system should process at least 4 document types with noticeably different confidence distributions per type.

    Hints
    1. Generate realistic confidence distributions: high confidence on standard invoices, lower on handwritten receipts and scanned PDFs, lowest on international documents.
    2. Include at least 3 fields per extraction (vendor name, date, amount) with independent confidence scores for each field.
    3. function mockExtraction(document) {
        const confidenceByType = {
          invoice: { vendorName: 0.98, date: 0.95, amount: 0.97 },
          receipt: { vendorName: 0.71, date: 0.60, amount: 0.55 },
          scannedPdf: { vendorName: 0.80, date: 0.72, amount: 0.69 },
          international: { vendorName: 0.63, date: 0.45, amount: 0.52 }
        };
        
        const base = confidenceByType[document.type];
        return {
          vendorName: { value: document.vendor, confidence: base.vendorName + (Math.random() * 0.1 - 0.05) },
          date: { value: document.date, confidence: base.date + (Math.random() * 0.1 - 0.05) },
          amount: { value: document.amount, confidence: base.amount + (Math.random() * 0.1 - 0.05) }
        };
      }
  2. Implement accuracy tracking broken down by document type and field segment - not just aggregate metrics

    The aggregate metrics trap is the most dangerous misconception in production extraction systems. 97% overall accuracy can hide catastrophic failure rates on specific document types because standard invoices dominate the volume. The exam tests that you must validate by document type AND field segment before automating.

    You should see: An accuracy table showing each document type and field combination separately. Standard invoices should show 95%+ accuracy while handwritten receipts and international documents show 40-70%. The aggregate should look excellent (90%+) despite the poor per-type numbers.

    Hints
    1. Calculate accuracy per document type per field, not just overall. The revealing comparison is between the aggregate and the worst-performing segment.
    2. Display the results as a table with document types as rows and fields as columns. Add an aggregate row at the bottom to show how it masks the problems.
    3. function trackAccuracy(extractions, groundTruth) {
        const stats = {};
        for (const ext of extractions) {
          const key = ext.documentType;
          if (!stats[key]) stats[key] = { vendorName: { correct: 0, total: 0 }, date: { correct: 0, total: 0 }, amount: { correct: 0, total: 0 } };
          for (const field of ["vendorName", "date", "amount"]) {
            stats[key][field].total++;
            if (ext[field].value === groundTruth[ext.id][field]) stats[key][field].correct++;
          }
        }
        // Calculate per-type accuracy
        for (const [type, fields] of Object.entries(stats)) {
          for (const [field, counts] of Object.entries(fields)) {
            counts.accuracy = (counts.correct / counts.total * 100).toFixed(1) + "%";
          }
        }
        return stats;
      }
  3. Build a calibration module that takes a labelled validation set (ground truth) and produces calibrated confidence thresholds per field type per document type

    Raw model confidence scores are not calibrated. A model reporting 0.90 confidence might actually be correct 94% of the time on date fields but only 82% on amount fields. Calibration using labelled validation sets is required before confidence scores can drive automated routing decisions.

    You should see: A calibration curve for each field type per document type, mapping reported confidence ranges to actual accuracy percentages. The curve should reveal that the same confidence score means different things for different field-document combinations.

    Hints
    1. Group extractions into confidence bands (0.5-0.6, 0.6-0.7, etc.) and calculate actual accuracy within each band for each field-document combination.
    2. The calibration output should be a lookup table: given document type, field type, and reported confidence, what is the actual expected accuracy?
    3. function buildCalibrationCurve(extractions, groundTruth) {
        const bands = {};
        for (const ext of extractions) {
          for (const field of ["vendorName", "date", "amount"]) {
            const key = `${ext.documentType}-${field}`;
            const band = Math.floor(ext[field].confidence * 10) / 10; // 0.0, 0.1, ..., 0.9
            if (!bands[key]) bands[key] = {};
            if (!bands[key][band]) bands[key][band] = { correct: 0, total: 0 };
            bands[key][band].total++;
            if (ext[field].value === groundTruth[ext.id][field]) bands[key][band].correct++;
          }
        }
        // Convert to calibrated thresholds
        const thresholds = {};
        for (const [key, bandData] of Object.entries(bands)) {
          thresholds[key] = Object.fromEntries(
            Object.entries(bandData).map(([band, counts]) => [band, counts.correct / counts.total])
          );
        }
        return thresholds;
      }
  4. Implement stratified random sampling that selects high-confidence extractions for ongoing verification, sampling proportionally across all document types

    High-confidence extractions are automated and not reviewed. If the model develops a novel error pattern affecting high-confidence items, only stratified sampling will catch it. Sampling only low-confidence items leaves you blind to systematic errors in automated extractions.

    You should see: A sampling function that selects a representative subset from each stratum (document type and confidence band), including samples from the high-confidence automated extractions. The sample should be proportional to the volume in each stratum.

    Hints
    1. The critical insight is sampling high-confidence items, not just low-confidence ones. Low-confidence items already go to human review. High-confidence items are the blind spot.
    2. Sample proportionally across all document types. If 80% of volume is standard invoices, 80% of your sample should be standard invoices, but ensure every document type has minimum representation.
    3. function stratifiedSample(extractions, sampleRate = 0.05) {
        // Group by document type and confidence band
        const strata = {};
        for (const ext of extractions) {
          const key = `${ext.documentType}-${ext.overallConfidence >= 0.80 ? "high" : "low"}`;
          if (!strata[key]) strata[key] = [];
          strata[key].push(ext);
        }
        
        // Sample from EVERY stratum, including high-confidence
        const sample = [];
        for (const [stratum, items] of Object.entries(strata)) {
          const n = Math.max(1, Math.ceil(items.length * sampleRate));
          const selected = shuffleAndTake(items, n);
          sample.push(...selected.map(s => ({ ...s, stratum })));
        }
        return sample;
      }
  5. Build a review router that prioritises limited reviewer capacity on the highest-uncertainty items, dynamically reordering the review queue as new extractions arrive

    Human reviewers are expensive and limited. Spreading capacity evenly across all extractions wastes time on high-confidence items while leaving insufficient capacity for uncertain items that need human judgement. Dynamic priority ordering ensures the most uncertain items are always reviewed first.

    You should see: A priority queue that orders items by uncertainty (lowest confidence first), dynamically reorders as new extractions arrive, and serves the next-highest-uncertainty item to each available reviewer. The queue should never serve items in chronological order.

    Hints
    1. Use a priority queue or sorted insertion to maintain uncertainty ordering. When a reviewer finishes one item, the next should be the highest-uncertainty item remaining, not the next in arrival order.
    2. Consider using calibrated confidence (from step 3) rather than raw confidence for prioritisation. A raw 0.80 on international documents is actually less reliable than a raw 0.70 on standard invoices.
    3. class ReviewQueue {
        constructor(calibrationData) {
          this.queue = [];
          this.calibration = calibrationData;
        }
        
        add(extraction) {
          const calibratedConfidence = this.getCalibratedConfidence(extraction);
          this.queue.push({ ...extraction, calibratedConfidence });
          this.queue.sort((a, b) => a.calibratedConfidence - b.calibratedConfidence);
        }
        
        getNext() {
          return this.queue.shift(); // Highest uncertainty first
        }
        
        getCalibratedConfidence(ext) {
          const key = `${ext.documentType}-${ext.fieldType}`;
          const band = Math.floor(ext.rawConfidence * 10) / 10;
          return this.calibration[key]?.[band] || ext.rawConfidence;
        }
      }

Sources