Chunk financial documents along their structure, not by token count. Split on section and clause boundaries, keep each table with its header and units, and attach the surrounding context (heading path, filing period, entity, currency) to every chunk. Fixed-window splitting is what breaks retrieval: it cuts a covenant from its definition and a number from the row label that gives it meaning.
The failure is easy to reproduce. Take a 10-K, run a 512-token sliding window over the extracted text, and search for the interest coverage covenant. The retriever returns a chunk that contains the threshold ratio but not the definition of the term it applies to, because the definition sat two pages earlier under a different heading. The model then answers with a number that is real and a meaning it invented. That is the whole problem with naive chunking on finance content. The pieces retrieve cleanly, and they mislead with total confidence.
Structure is the signal, so parse it before you split
Financial documents are not free text with occasional formatting. A balance sheet is a grid where the row label and the column period together determine what a cell means. A credit agreement is a tree of defined terms, sections and schedules that reference each other. A prospectus repeats the same figure under three presentations. If your pipeline starts from a flat text dump, you have already lost the coordinates you need to chunk well.
So the first stage is layout parsing, not chunking. You want reading order that respects columns, a heading hierarchy, table cell geometry, and the boundaries of footnotes and cross-references. From that you build chunks. The order matters because the parse determines what a valid boundary even is.
- For prose sections (business overview, risk factors, MD&A), split on the heading tree and keep each leaf section together up to a size ceiling. When a section runs long, split on paragraph boundaries and carry the heading path into every child.
- For tables, keep the table whole when it fits in a chunk. When it does not, split by row groups and repeat the header row and any unit line in each piece. A financial table without its header and its scale (“in thousands, except per share”) is close to useless in retrieval.
- For contracts, the clause is the atom. Split on the numbered clause or sub-clause, never mid-sentence, and keep the clause number so downstream reasoning can cite it.
Carry context into the chunk, because the retriever only sees the chunk
At query time the retriever scores an isolated chunk. It does not see the document around it. So whatever a reader needed from the surrounding pages to understand that chunk, you have to fold in before indexing.
Concretely, we prepend a compact context header to each chunk: the document type and title, the reporting period, the reporting entity, the currency and scale, and the full heading path down to the chunk. A row of numbers from a cash flow statement becomes retrievable as “Acme Corp, consolidated cash flow, FY2025, USD thousands, Financing activities” rather than a bare list of figures that could belong to any company in any year. This directly protects point-in-time correctness. Without the period stamped on the chunk, a retriever will happily mix a Q1 figure into an answer about the full year, and no downstream check will catch it because the number itself is genuine.
Two more things belong in the chunk metadata, kept out of the embedded text but stored alongside:
- Lineage: source document id, page, and the byte or cell range the chunk came from. When an answer is wrong, you need to walk back to the exact origin, and for anything touching an audit trail this is not optional.
- Structural role: is this a heading, a table, a footnote, a definition, a signature block. Retrieval quality improves when you can filter or boost by role, and a hybrid retriever can weight a definitions chunk differently from a narrative one.
Definitions deserve special handling. In contracts, defined terms are the joints of the whole document. We extract the definitions section separately and, where a clause leans on a term, either inline a short gloss or index the clause and its definitions as a linked pair. This is the single change that most reduces the covenant-without-its-definition failure.
Split where meaning ends, then measure whether it worked
The unifying rule is dull and it holds: cut at a boundary a human editor would recognize, and never in the middle of a semantic unit. A sentence, a table, a clause, a footnote, a defined term. Fixed sizes are a fallback for when structure genuinely runs out, not the primary mechanism. Overlap between chunks is a crude patch for boundary errors; if you find yourself pushing overlap to 30 percent to recover coherence, the real fix is a better boundary, not more redundancy.
None of this is worth doing on faith, so build an eval set from real queries against real documents. Ours pairs a question with the specific span that answers it, and we score whether the retrieved chunks actually contain that span with its necessary context. A chunk that returns the right number without the row label or the period counts as a miss, because it will produce a wrong answer downstream even though the retrieval metric looks fine. Track that separately from raw hit rate.
Watch two things over time. First, coverage of tables and footnotes, which is where most extraction pipelines quietly degrade when a new document template shows up at quarter-end. Second, chunk-boundary drift: when a vendor changes a filing layout or you swap the layout parser, boundaries move and retrieval that used to work starts returning fragments. Re-run the eval set on each ingestion batch and both show up before they reach a user. Spot checks miss them. Treat chunking as a live part of the data layer that earns the same monitoring as anything else feeding a decision, not as a preprocessing step you configure once and forget.
FAQ
Why not just use a fixed token window for chunking?
A fixed window ignores document structure, so it routinely cuts a table away from its header or a defined term away from its definition. For filings, contracts and statements the layout carries meaning, and splitting on token count alone throws that meaning away.
How big should a chunk be for financial documents?
There is no single size. Prose sections retrieve well at roughly 400 to 800 tokens, tables should stay whole when they fit and split by row groups with the header repeated when they do not, and contract clauses should follow the clause boundary regardless of length.
Do I need OCR and layout parsing before chunking?
For scanned statements and older filings, yes. You need the reading order, table cell coordinates and heading hierarchy before you can chunk sensibly, because a plain text dump collapses columns and destroys the structure your chunker depends on.