RAG Ingestion Engineering: Reliable Data, Sources, and Costs
How to optimize data ingestion for RAG: preserve context and sources, validate extractions, resume ingestion, and evaluate before publishing.
Does your search engine find the right report, link to the source, and still return the wrong figure? Data ingestion for RAG can preserve a number while losing its meaning. I would check that transformation before replacing the model that answers the question.
Imagine a fictional report: “35% of companies with more than 500 employees increased their training budget in 2025.” The search engine answers that companies increased that budget by 35%. It includes a link, but it has changed the population and turned a proportion of companies into the size of the increase.
RAG, retrieval-augmented generation, combines search and generation: we retrieve information and give it to an LLM to answer a question. Preparing that information well reduces some causes of hallucination. It also helps distinguish extraction failures from retrieval or interpretation failures, which need different fixes.
A data point needs more than a number and a URL
A citable data point needs evidence we can locate and enough context to interpret its scope. A URL identifies a source, but it does not explain what was measured or where to check it.
For our fictional report, I would preserve the original sentence, page, measured variable, period, and population. If the sample or country does not appear in the available evidence, I would leave it unknown. A title about European companies can provide a clue, but it does not establish that every figure in the document describes all of Europe.
This is an illustrative record, not the schema of a specific tool:
{
"id": "dato-001",
"fuenteId": "informe-ficticio",
"versionFuente": "2025-v1",
"paginaPdf": 12,
"origen": "texto",
"afirmacion": "El 35 % de las empresas de más de 500 empleados aumentó su presupuesto de formación en 2025.",
"evidencia": "El 35 % de las empresas de más de 500 empleados aumentó su presupuesto de formación en 2025.",
"variable": "empresas que aumentaron el presupuesto de formación",
"valor": 35,
"unidad": "porcentaje",
"poblacion": "empresas de más de 500 empleados",
"periodo": "2025",
"base": null,
"ambito": null,
"aproximado": false
}
The null prevents us from filling gaps with assumptions. It also helps to record which checks were performed and which extractor version was used, so we can review the data point later.
The page reference must have a stable meaning: its position in the PDF or its printed number. We can store both when they differ. For HTML, we need another locator, such as a section or an identified fragment, and a versioned copy if we want to reproduce the check.
This expands on knowledge preparation for a company brain. The transformation should let us trace an answer back to a data point and that data point back to the original evidence, even after updating the index.
Extract text, tables, and charts without losing relationships
Extraction must preserve the relationships between figures, labels, and context, as well as the document’s words. Converting a PDF to text can mix columns, separate a header from its rows, or omit a chart legend.
OCR, optical character recognition, recovers text from images, but it can misread signs, decimals, or units. A model with visual input can interpret the page layout and still match a color to the wrong series. Combining text and images gives us more evidence to check the extraction; it does not automatically make the result correct.
Docling’s documentation treats document structure, reading order, tables, and OCR as distinct capabilities, among others. [1] It is a useful reference for understanding what information we lose when we treat an entire document as a string of text.
| Origin | Evidence I would preserve | Required check |
|---|---|---|
| Text | Original passage and location | The sentence exists and supports the claim within its stated scope |
| Table | Relevant cells, headers, and notes | The figure belongs to that row and column, with the correct unit |
| Chart | Page or crop, title, series, and category | The value belongs to that series; distinguish a printed figure from a visual estimate |
A page is a convenient unit for analyzing visual layout. However, a table may continue on the next page, and the sample definition may appear earlier. We need to retrieve that context without indiscriminately assigning the entire report’s metadata to every data point.
Some of these decisions appear in Athena, a search engine for data and studies on the events industry: its ingestion pipeline combines text and images on each page and distinguishes data from text, tables, and charts, preserving the source and page reference.
I would use visual extraction when the layout carries information that the text does not preserve. For simple documents, it can add cost without improving the result, so that decision also needs evaluation.
Separate evidence, claims, and search text
The original evidence should remain separate from the normalized claim and the representation we use for search. Each serves a different purpose and may change at a different pace.
Evidence supports verification. The claim brings together what was measured, about whom, and when in a sentence that stands on its own. Search text can include normalized terms that make it easier to find, but those added terms do not become words from the source.
Embeddings represent text as numbers that let us compare similarity. pgvector, for example, provides vector similarity search in PostgreSQL. [2] That comparison helps retrieve candidates; it does not check whether evidence supports a conclusion.
If we search for “increase in training spending,” we might retrieve our 35%. The record must preserve the fact that it measures how many companies increased their budget, because a similar query does not change the meaning of the data point.
Extracting structured facts adds value when we want to answer questions about figures. Text passages remain useful for explanations, methodology, or conditions that do not fit into a record. We can retrieve a fact and add relevant context instead of forcing all documentation into numerical records.
The cost of normalization matters too: we can lose nuance or store a wrong interpretation that looks precise. That is why I would check the record before making it searchable knowledge.
What do we validate before indexing?
Schema validation shows that we can process an extraction, not that it faithfully represents the source. JSON containing an incorrect percentage can satisfy every type rule.
I would first apply deterministic checks: an existing source identifier, a page within the document, correct types, supported units, and required fields. Then I would check whether the claim is supported. In our example, we must distinguish “35% increased the budget” from “the budget increased by 35%.”
For a direct quotation, we can look for its presence in the extracted text. If we normalize line breaks or end-of-line hyphens, those rules need to stay limited, and we need to preserve the original. The more tolerant the comparison, the more care we need to avoid accepting misleading matches.
A matching quotation can still support a different claim. Its presence is a useful check, but the subject, unit, and period need semantic review.
For a chart with no equivalent sentence, the direct quotation can be null while the evidence remains verifiable. The reference must lead to the relevant page and chart. If we only estimate a bar’s height, the data point must be marked as approximate or excluded when the application requires exact figures.
An LLM audit can detect errors that rules miss, although it can also fail. I would use it as an additional layer, with human-reviewed examples, and preserve the reasons for rejection. For particularly sensitive data, the review threshold should reflect the cost of getting it wrong.
Resume ingestion and control cost
Efficient ingestion reuses valid work and repeats only the units that need processing. That requires persistence, clear states, and versioned artifacts.
Deduplicating by a hash of the bytes detects identical files. If PDF compression changes, the bytes can change even when the content is equivalent, so content comparison adds another layer. We need to define its scope: two editions with repeated paragraphs may contain different figures and should not simply be merged.
We also need to distinguish duplication from corroboration. The same figure copied into several documents does not establish that there were several independent measurements.
I would persist results by page or extraction unit, with states that distinguish a successfully processed page containing no data from a failed call. A cover page can correctly return zero facts; a provider error must not become an accepted empty extraction.
The reuse key should include the document version and the versions of the extractor, prompt, model, and schema. If we fix a prompt because it confused percentages with changes, reusing its previous extractions would preserve the failure. Changing only the search representation, by contrast, may let us recompute embeddings without extracting the PDF again.
Limited concurrency lets us process several pages without launching every call at once. I would add bounded retries for transient failures, along with spending and time limits. Recording costs by phase helps determine whether we are paying for useful extraction or repeated errors; speeding up calls we later reject does little to improve the system.
Evaluate a candidate corpus before activating it
A new ingestion should be tested in a candidate corpus separate from the one the search engine uses. That lets us compare versions and detect regressions before changing users’ answers.
I would prepare a human-reviewed set containing prose, tables, charts, difficult documents, and unanswerable questions. For our fictional report, I would include both “What proportion of companies increased their budget?” and “How much did the budget increase?” The sentence answers the first question, but not the second.
I would measure these separately:
- Extraction: correctly represented data points, lost context, and material errors, broken down by origin.
- Coverage: relevant evidence the extractor missed. Counting only accepted data points hides these omissions.
- Retrieval: whether the necessary evidence appears among the results and retains its context.
- Response: supported claims, correct references, and appropriate limits when data is missing.
I would add cost per accepted data point and ingestion duration. A cheap extraction that needs many corrections can work out worse than a more expensive one; we also need to know how much useful knowledge we are discarding.
Expected answers belong in the evaluator, not in the queried index. If we put them into retrievable material, we can pass a test without showing that the system understands the sources.
I would separate infrastructure failures from content errors. A timeout explains why a page is missing, but it is not the same as an incorrect interpretation. I would activate the candidate after it passes the defined checks and retain the previous version so we can roll back a regression.
The response still needs checks
Generation must limit its claims to the retrieved evidence and resolve references from existing identifiers. The model can indicate which data point it uses; the application must check that it exists and construct its link, preventing the model from inventing a URL.
We then need to check the support. The 35% record supports an answer about the proportion of companies, but it does not let us calculate how much their budget increased. Nor can we generalize it to small companies or remove the period to present it as the current situation.
If sources contradict each other, we should present those differences with their scope rather than fabricate a single figure. If evidence is missing, we can explain that and ask for a narrower query. A second LLM to review the answer is an option, with its own cost and errors, not a guarantee against hallucinations.
I would start optimizing this chain with a small corpus and questions that force us to preserve context. If we still return “the budget increased by 35%,” adding more documents only increases the work ahead. First, we need to preserve a data point’s meaning from its page to the answer.
Sources
- Docling documentation: document processing, structure, reading order, tables, and OCR.
- pgvector documentation: vector similarity search in PostgreSQL.
Frequently Asked Questions
Does RAG eliminate hallucinations?
No. Retrieving evidence reduces some causes of error, but the extractor can misinterpret it, the search engine can omit context, and the model can generate an unsupported conclusion.
Does a reference prove that an answer is correct?
It shows where to check it, provided the reference exists and is accessible. The claim must still be supported by that evidence and respect its population, period, and unit.
Should we index facts or passages?
It depends on the queries. For figures, structured facts let us preserve each value’s meaning; for explanations and methodology, passages retain context a schema may leave out.
We can combine both and evaluate whether retrieving additional context improves answers or just adds tokens.
How do we cite a data point from a chart without inventing a sentence?
With a reference to the document, page, and chart, preserving the visual evidence and the elements that identify the data point. The direct quotation can remain empty.