A correctly read number can still be the wrong financial fact
Suppose the parser reads 1,240 perfectly. The characters are correct. The answer can still be wrong if the value belonged to the prior year, was expressed in thousands rather than millions, or referred to a segment rather than the consolidated company.
This is the central difficulty in parsing financial documents. Character recognition is only one layer. The system must preserve the relationships that make a number meaningful.
The initial LlamaParse approach for the client delivered high parsing accuracy. That mattered because the corpus included financial filings and reports where tables, headings and footnotes carry as much meaning as prose. The challenge was retaining enough of that accuracy while bringing the full parsing iteration within budget.
Define the parser’s output contract
The output should contain more than a large Markdown string. Markdown is convenient for an LLM, but a retrieval system also needs stable references back to the source.
A useful block representation contains:
{
"document_version": "example-2024-v1",
"block_id": "page-42-table-2-row-3",
"page_index": 41,
"kind": "table_row",
"section_path": ["Financial statements", "Operating results"],
"text": "Operating income | FY2024: 186 | FY2023: 150",
"table_id": "page-42-table-2",
"headers": ["USD millions", "FY2024", "FY2023"],
"source_regions": [{"x": 62, "y": 210, "w": 480, "h": 28}],
"coordinate_space": "top-left-page-points",
"page_width": 612,
"page_height": 792,
"parser_version": "financial-parser-v2"
}
The numbers in this schema are a worked example. The important design choice is the combination of semantic structure and physical location. Retrieval uses the first; the viewer uses the second.
A multi-page table may need several regions. A paragraph split across columns may need several boxes. A single rectangle is not always a complete representation of a source span.
Preserve the table’s question, not only its values
Consider this fictional table:
| USD millions, except margins | FY2024 | FY2023 |
|---|---|---|
| Revenue | 1,240 | 1,000 |
| Operating income | 186 | 150 |
| Operating margin | 15.0% | 15.0% |
A naive linearisation can become:
Revenue 1,240 1,000 Operating income 186 150 Operating margin 15.0 15.0
That representation makes the model reconstruct the header alignment. It also drops the percentage signs and unit declaration. A stronger representation repeats the minimum context needed by each retrieval unit:
ExampleCo | Consolidated operating results | USD millions
Metric: Operating income | FY2024: 186 | FY2023: 150
Source: annual report, financial statements, table 2, row 3
Repeating headers consumes tokens. It is often a good exchange because the alternative is retrieving a compact but ambiguous row. For complex tables, preserve a structured grid with row and column spans, and generate the retrieval text from that grid.
The evidence displayed to the user should remain the original source. Normalised text is a retrieval aid; it should not quietly become a replacement document.
The financial errors that deserve their own checks
Several errors have a much larger effect than their character count suggests:
(240)becomes240, reversing the sign of an expense or loss.0.8becomes8, changing the magnitude by ten times.- A dash is treated as zero when it means not applicable or not disclosed.
- A footnote explaining a discontinued operation is separated from the reported figure.
- A subtotal is confused with a total because indentation was lost.
- A fiscal-year label is read as a calendar year.
- A “restated” column heading disappears during normalisation.
A parser review should therefore include exact numeric tokens, sign, scale, row/column alignment and footnote attachment. Word error rate or a general text similarity score cannot tell the whole story.
For a field-level review, represent the target as a tuple:
(issuer, metric, period, value, sign, currency, scale, scope, source_span)
A value is correct only when the relevant context agrees. If the intended answer is consolidated revenue in USD millions, retrieving a business-unit figure in local currency is a failure even if the number happens to match.
Document types need different structural priorities
The client’s ingestion code had explicit dispatch for different document classes. This is the right place to isolate parsing and chunking decisions, even where early implementations share settings.
| Document | Structure to preserve | Common failure |
|---|---|---|
| 10-K / annual report | Item headings, notes, statements and table continuity | Footnotes detached from the statement |
| 10-Q | Quarter versus year-to-date columns | Three months confused with nine months |
| 8-K | Event item and attached exhibits | Press release separated from its filing identity |
| Earnings call | Speaker, role, prepared remarks and Q&A turns | Analyst question attributed to management |
| DEF-14A | Compensation tables and ownership footnotes | Beneficial ownership confused with voting control |
| ESG report | Metric definitions, reporting scope and units | A revised methodology treated as a performance change |
The existing type dispatcher can support these policies without forcing every parser to understand every business rule. Classification selects a policy; parsing extracts structure; validation checks the policy’s invariants.
A practical parsing cascade
A design today should avoid sending every page through the most expensive path. Start by inspecting the document:
- If reliable native text and layout are available, extract them and validate reading order.
- If a page is image-based, perform OCR with layout preservation.
- If a page contains a complex table, preserve the grid and associated notes.
- If critical checks fail, send the affected page or region through a stronger route.
- If the routes disagree on a material figure, quarantine it for review or require source inspection before using it in an answer.
The routing decision should be based on observable signals: text coverage, malformed table structure, missing headers, implausible numeric shifts and mismatches against independently extracted structured values. Provider confidence can help prioritise review, but it is not a probability that the financial interpretation is correct.
A clean scan can still contain a misleading table layout. A low-confidence word may be an irrelevant footer. The routing policy needs business context as well as optical confidence.
Evaluate the intermediate artifact
Take a small, stratified slice of the corpus before parsing everything. Include difficult pages intentionally: dense tables, landscape pages, split tables, scans, footnotes, nonstandard fonts and amended filings.
For each parser, retain the page image, raw output, normalised blocks, configuration, latency and billed units. Review disagreements at the page and field level. Then pass both outputs through the same retrieval and answer configuration to see which differences affect the analyst task.
This distinction matters. A prettier Markdown table may make no difference to retrieval. A single missing unit can make every downstream calculation wrong. The parser evaluation should expose both.
What changed by 2026
Modern parsing services increasingly expose the structure needed by this contract. Mistral’s current OCR API supports extracted tables and blocks, with paragraph regions and confidence at selectable granularities; these options depend on the model generation. Pin the model and extraction settings rather than relying on a moving latest alias. Mistral OCR processor documentation
The architectural opportunity is to preserve these fields end to end. If the ingestion adapter flattens the response to plain text, the additional citation and quality signals are lost before retrieval begins.
That is the bridge to the next article: parser selection changes both cost and the shape of the evidence available to the rest of the product.