An agent can follow its instructions perfectly and still pay the wrong amount, because the parser misread a table in the invoice. The agent never sees the PDF, only the extraction. If that step is wrong, everything downstream is confidently wrong too.
Jerry Liu, co-founder and CEO of LlamaIndex, made this point in a June 2026 episode. A parser that represents a table wrong, or hallucinates at a meaningful rate, puts a workflow below the accuracy bar you can operate with, and the downstream agent workflow gets disrupted. His bar for the use cases he sees:
And so a lot of these require really high accuracy, 95% plus, especially in more sensitive use cases, with human-in-the-loop review on some of the extracted data.
Jerry Liu, co-founder and CEO of LlamaIndex, on Chain of Thought ep 61
He was describing what those customers need, so set your own bar per field from what a wrong value costs you. The practical move is to treat extraction as a pipeline stage with its own tests, measured on the fields your agent consumes.
Label a small set of real documents
Pick the document types you plan to support and hand-label a few dozen real examples of each. Include the ugly layouts along with the clean ones. Hold some back and don't look at them while you tune prompts or rules.
Label fields, not documents. For an invoice, the amount due and the due date may need a much higher bar than a free-text note, and a single overall score hides that. Decide the missing-value rules up front: a field that isn't in the document has an expected value of None, while a field the extractor fails to return counts as wrong.
Compare values in a canonical form. Normalize dates and currency, but be careful: stripping punctuation from an ID can turn two different IDs into the same string.
Illustrative sketch, not a production library or anyone's actual implementation. extract_fields(doc) is a hypothetical function that returns a dict.
from collections import defaultdict
def evaluate(cases, extract_fields):
scores = defaultdict(lambda: {"correct": 0, "tested": 0, "misses": []})
for case in cases:
predicted = extract_fields(case["document"])
for field, expected in case["gold"].items():
s = scores[(case["doc_type"], field)]
s["tested"] += 1
if field in predicted and predicted[field] == expected:
s["correct"] += 1
else:
s["misses"].append((case["id"], predicted.get(field)))
return {
key: {**s, "accuracy": s["correct"] / s["tested"]}
for key, s in scores.items()
}
Report accuracy per document type and field, with the tested count next to it. A perfect score on eight invoices says very little. Keep the misses, too. Seeing which cell got confused for which gives you something concrete to fix, and rerunning the held-out set after each change shows you what regressed.
Turn the scores into a review gate
An offline score tells you how the extractor does on labelled documents. It can't tell you whether a new document's values are right, because production documents arrive without labels. So use the eval to decide what may run unattended, then check each incoming document against that.
| Condition | What happens |
|---|---|
| Document type has no eval coverage | Hold the whole document for review |
| Field's measured accuracy is below its bar | Review that field for that document type |
| Required field missing or fails a format check | Block the actions that depend on it |
| Values break a business rule (line items don't sum to the total) | Show the values and the source region to a reviewer |
Scope the hold to what depends on the bad value, so one disputed total doesn't freeze unrelated work. Show reviewers the extracted value next to the source passage or image region. Store the correction and feed adjudicated examples back into the labelled set, keeping a separate held-out set. Sample some auto-accepted documents too, since errors that pass every rule are the ones you won't otherwise see.
Store extracted facts with their review status
Extraction output often ends up as memory, and a wrong value saved for later can outlive the run that produced it. Liu's take on memory is deliberately simple. He described it as a form of persistent context and pointed to files as a starting point:
And so this concept of having memory just be a pool of files on your file system is kind of nice to think about because it's pretty easy to represent and store.
Jerry Liu, co-founder and CEO of LlamaIndex, on Chain of Thought ep 61
He was clear that he hasn't spent a ton of time building more complicated memory systems, so treat it as a place to start. For an extraction pipeline, it suggests a simple rule: keep candidates and accepted facts in separate places. Each record carries its source reference, its review status, and the extractor version that produced it. An agent can write candidates but can't promote them to accepted. When a reviewer corrects a value, mark the old one superseded instead of overwriting it, so you can trace where it came from.
Takeaway
Before an agent acts on extracted data:
- Label real documents per type and define comparison rules per field.
- Report per-field accuracy with sample counts and keep the misses.
- Route uncovered document types and weak fields to a human.
- Store extracted facts with their source, status and extractor version.
The full conversation, with the transcript, is on Chain of Thought.
Drafted with AI assistance from the episode transcripts, then edited by me.
Top comments (0)