UGLYPEAR AI completes its business upgrade: High-Performance Document Compression × RAG Data Engineering PlatformLearn about the New Business →

Customer Cases

Document AI deployments in production. Customer names are withheld under NDA and replaced by industry and scale descriptors. Every number on this page was measured on a live index we operate — not estimated, not rounded up.

25 years of listed-company filings in one queryable index

Capital markets · equity research and investor-relations workflows · production deployment

Engagement at a glance

Industry Capital markets — equity research and investor relations
Coverage 2 listed issuers · 25 reporting years (June 2001 – August 2026)
Documents 859 — 632 issuer disclosures, 227 brokerage research notes
Formats PDF 673 · HTML 112 · DOCX 74
Index size 36,395 chunks · ~35.2 million characters
Research coverage 227 notes from 25 brokerages · 112 investor-relations transcripts
Structured output 457 financial metrics normalised into a queryable store, each traceable to its source filing
Deployment Fully on-premises · source corpus mounted read-only · Qwen3-Embedding-4B vectors, 158 MB on-disk cache

The problem

Analysts needed answers that span a quarter century: a 2008 balance-sheet footnote read against a 2026 interim report, a brokerage view read against the issuer's own disclosure. Doing that by hand meant opening dozens of PDFs per question, the oldest filings frequently failed to parse at all, and numeric answers had to be retyped out of tables with no way to trace them back to source.

What we built

  • Idempotent ingestion keyed on content hash — re-runs skip unchanged files, failed files retry without duplicating registry entries, and the source corpus is never modified.
  • Layer routing that treats issuer disclosures, brokerage research and investor-relations records under separate rules instead of one flat document pile.
  • Hybrid retrieval: BM25 plus dense vectors fused by reciprocal rank fusion, then cross-encoder reranking.
  • A recency window with organisation anchoring and year-aware query parsing, so "what did this brokerage say" still reaches notes published outside the default window.
  • Table extraction into a structured metric store, so revenue and net-profit questions are answered from normalised numbers rather than from a paragraph of prose.
  • Adaptive generation: direct numeric lookups receive top-1 context, analytical questions receive top-3, and composite questions are split and merged.

Measured results

Metric Value
Ingestion success rate 859 / 859 documents, 0 failures
Retrieval hit rate, 10-question smoke test (issuer A, 17,590 chunks) 10 / 10
Retrieval hit rate, 10-question smoke test (issuer B, 18,805 chunks) 10 / 10
Wall clock for a full 10-question smoke run (issuer B, keyword mode, including index load) 4.6 s
Candidate set after time filtering (issuer A) 829 of 17,590 chunks — 95.3% of the index excluded as out-of-window noise
Candidate set after time filtering (issuer B) 827 of 18,805 chunks — 95.6% excluded
Legacy PDF recovery (2001–2010 filings) ~5% parsed to 0 pages from trailing cross-reference damage; auto-repaired, no manual handling
Financial metrics extracted into the structured store 457

How these were measured: document and chunk counts come from the ingestion registry; hit rate and candidate-set sizes come from the 10-question retrieval smoke test shipped with the deployment, run in keyword-only mode with vector retrieval switched off. Timing covers process start, index load and all 10 queries on a local workstation.

What broke, and what we fixed

Long-horizon corpora fail in ways a demo corpus never does. These five issues were found in production and each fix is covered by a regression test.

Symptom Root cause Fix
Brokerage notes older than the recency window never surfaced A 400-day default window anchored to the newest report in the library excluded older coverage Organisation anchoring: naming a brokerage pulls all of its notes into the candidate set regardless of date
Investor-relations transcripts were never indexed The filename classifier did not recognise the document category and skipped the files Classifier extended to route investor-relations records into the disclosure layer, with a regression test
Colloquial questions returned irrelevant sections Lexical gap between spoken wording ("how much did they earn", "dividends") and filing wording ("net profit", "profit distribution") Synonym expansion appended to the query rather than substituted, so the original terms still match
Retrieval quality dropped with no error raised Vector retrieval silently fell back to keyword-only when the embedding cache was incomplete or the flag was missing at restart Cache completeness check at load, plus a model-consistency key that invalidates the cache when the embedding model changes
Balance-sheet figures arrived glued together PDF table cells merged during extraction, producing strings such as 3,294,053.003,320,386.00 Number splitting and table-markup stripping applied at generation time; the retrieval layer and vector cache were left untouched so no re-embedding was needed

Where else this pattern applies

The pipeline above is not industry-specific — it is what long-horizon, format-mixed, table-heavy corpora require

If your archive spans years, mixes scanned PDFs with office formats, and hides its most valuable numbers inside tables, the same ingestion, retrieval and extraction pattern applies. Sector pages describe the document types we handle in each industry.

Financial services · Legal · Healthcare · Manufacturing · Government · Academic & research · E-commerce & retail · General enterprise

Public benchmark reference

Numbers measured on our own pipeline — see the full method on the benchmarks page

Metric Value
443-page OFD, end-to-end 124 s
Three-layer parsing QC score 0.924
Table structure mis-detection reduced by 64%
Image token cost saving (GPT-4o basis) 85.9%

See the full test method and sample →

Run a test on your own documents

Bring the batch that troubles you most and we will show you the results with real test data.

Request a trial run