Skip to content
datarekha

LlamaParse — document parsing

A PDF can look perfect to a human and still poison RAG. LlamaParse uses layout-aware parsing and OCR to turn tables, columns, and scans into usable markdown.

12 min read Beginner Agentic AI Lesson 40 of 78

What you'll learn

  • Why a PDF's visual layout is missing from ordinary text extraction
  • How LlamaParse uses layout-aware parsing, OCR, and markdown output
  • How a broken table becomes a wrong RAG answer, with real numbers
  • How to validate parsing and diagnose failures before tuning retrieval

Before you start

At 3:07 a.m., a customer asks your support agent, “What is the Enterprise refund window?”

The answer is plainly printed in your pricing PDF: 30 days. Your embeddings are good. The retriever returns the right page. The language model confidently answers “no refund,” because the parser separated the table’s labels from its values before retrieval ever started.

This is the unglamorous part of RAG: a document can look perfectly readable in a browser and be nearly useless as machine input. LlamaParse is a document parser designed to preserve the structure that ordinary PDF extraction loses.

A PDF is not a text document

A PDF is closer to a set of painting instructions than to a Word document. It stores glyphs, images, fonts, and coordinates: put this character at this position, draw that line there, place this image on top. It may contain no explicit statement that words form a heading or that a number belongs in a table’s third column.

A text extractor reconstructs a text sequence from those instructions. It can work well on a simple, born-digital page, meaning one containing text characters rather than a scan. On a complicated page, it must guess:

  • A table is two-dimensional, but extracted text is usually one-dimensional.
  • Two columns need an invented reading order.
  • A visually separate footer may be mixed into the article.
  • A scanned page contains pixels, not characters, so it needs OCR, or optical character recognition.

Those guesses change the facts available to the model.

The pricing-table example

Suppose the visible table on page 4 is:

PlanPriceRefund
Enterprise$499/month30 days
Starter$99/monthNone

The values get their meaning from position. “$499/month” means something different beside Enterprise than beside Starter.

A naive extractor might produce:

Plan Price Refund Enterprise Starter 99 499 30 days None

The exact order varies by PDF and extractor. That variability is the problem: the output still contains the right words, but the associations are gone. A downstream system can no longer reliably tell whether Enterprise costs 99 or 499 dollars, or whether its refund window is 30 days or none.

Chunking can make the damage worse. If it splits the sequence between “Enterprise” and “499,” one chunk contains the plan and another contains the price. Retrieval may find the page but fail to return both pieces together. Generation then fills the gap with a plausible number.

RAG often fails this way—not with an obviously broken sentence, but with a fluent answer assembled from relationships that ingestion quietly destroyed.

Where the damage enters the RAG pipeline

The pipeline is often described as “load, split, embed, retrieve, generate.” The load step determines what information later stages are allowed to see.

PDF pagevisual layoutraw streamorder guessedlayout parserelations keptmarkdownchunks and index
Parsing is the first transformation in RAG. Once a relationship is lost there, later stages cannot reliably recreate it.

The stages form a chain:

  1. The page contains visual relationships.
  2. Parsing turns the page into text and structure.
  3. Chunking chooses what travels together.
  4. Embeddings represent those pieces for search.
  5. Retrieval selects context for the prompt.
  6. The model writes from that context.

If step 2 separates “Enterprise” and “30 days,” a better embedding model cannot restore the missing edge. An embedding represents the text it receives, and a reranker can choose only among relationships present in retrieved chunks. Parsing quality is therefore a ceiling on retrieval quality.

What LlamaParse does

LlamaParse is a managed parser in the Llama ecosystem. You submit a document and receive content that can be loaded into LlamaIndex. For text-based RAG, you usually request markdown, which supports headings, lists, links, and table syntax.

Depending on the document and parsing mode, it can combine native PDF extraction, page rendering, OCR, and vision-language-model assistance. A vision-language model, or VLM, interprets images and language together. The useful mental model is visual: identify headings, columns, table boundaries, captions, and reading order before emitting text. The exact processing path can change, so do not assume every page uses the same route.

For the pricing example, the desired output is:

## Plans

| Plan | Price | Refund |
| --- | --- | --- |
| Enterprise | $499/month | 30 days |
| Starter | $99/month | None |

This is still text, not a relational database, and it cannot infer missing information. But it makes relationships explicit enough for a chunker and language model to use.

LlamaParse is the loader, not the embedding model, vector database, retriever, or answer generator. You still need to split, index, retrieve, and evaluate.

A minimal LlamaIndex pipeline

The Python package is available through llama_cloud_services. Make the embedding and language-model providers explicit: LLAMA_CLOUD_API_KEY alone does not configure the rest of this example.

python -m pip install \
  "llama-cloud-services>=0.6,<0.7" \
  "llama-index-core>=0.12,<0.13" \
  "llama-index-llms-openai>=0.3,<0.4" \
  "llama-index-embeddings-openai>=0.3,<0.4"

Set both secrets in your environment or deployment secret manager:

export LLAMA_CLOUD_API_KEY="your-llama-cloud-key"
export OPENAI_API_KEY="your-openai-key"
import os

from llama_cloud_services import LlamaParse
from llama_index.core import Settings, VectorStoreIndex
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI

Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
Settings.llm = OpenAI(model="gpt-4o-mini")

parser = LlamaParse(
    api_key=os.environ["LLAMA_CLOUD_API_KEY"],
    result_type="markdown",
)

documents = parser.load_data("pricing.pdf")

# Inspect parsed text before indexing it.
for document in documents:
    print(document.get_content()[:800])

index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(similarity_top_k=3)

response = query_engine.query("What is the Enterprise refund window?")
print(response)

The inspection loop belongs before indexing. If the output has interleaved columns or a mangled table, stop there. Indexing bad text only makes it harder to diagnose.

Validate before tuning retrieval

Treat parsing as an observable ingestion step. Start with representative files: a clean text PDF, a multi-column report, a table with merged cells, a scan, and a form. Save each original file beside its markdown result, with document and page metadata when available. This supports review, citations, and re-indexing without reparsing unchanged files.

Inspect hard structures rather than only the first paragraph:

  • Do headings remain headings?
  • Does each table row keep its values together?
  • Are columns in the intended order?
  • Are headers and footers being repeated into chunks?
  • Do minus signs, decimals, currency symbols, and superscripts survive?

Only then chunk. Markdown provides useful boundaries but does not guarantee them. Keep a table’s title and header row with its data; for a large table, repeat the headers in each group of rows or store the table as a dedicated retrievable unit. For the pricing table, a useful unit contains the heading, header row, and both plan rows.

Build a small evaluation set of 10–30 questions with known answers. Include a table row, a footnote, a scanned page, and a value from a second column. For the running example, the expected answer is Enterprise, $499 per month, with a 30-day refund window. Check both the retrieved chunks and the final answer.

Debug in order: parsed markdown, indexed chunks, retrieval, then generation. If the markdown is wrong, fix parsing or the source. If it is right but the row is not retrieved, investigate chunking, embeddings, query construction, metadata filters, top_k, or reranking. Changing the language model cannot repair an association that disappeared during ingestion.

When LlamaParse is the wrong first choice

LlamaParse is not automatically better for every file.

A simple, born-digital, single-column PDF may be handled adequately by a local parser. A managed layout-aware service adds network latency, processing cost, an external data path, and an operational dependency. For a private corpus, confirm where documents are sent, how they are retained, and which contractual controls apply.

Use the heavier parser when layout carries meaning: financial tables, scanned contracts, scientific papers, forms, brochures, slide exports, or multi-column reports. Use a local path when the source is clean, the corpus is huge, latency matters, and tests show no meaningful parsing loss. Routing documents by type is often better than forcing every file through one parser.

There is a quality trade-off too. A vision-language model may infer a table boundary but misread a tiny character or merged cell. Markdown is a convenient interchange format, not proof that the interpretation is correct. For invoices, compliance records, or payment data, preserve the original and validate critical fields.

Failure modes you can see in production

A plausible value comes from the wrong row

Inspect the parsed markdown for the source page. Flattened rows or interleaved columns indicate a parsing or layout problem. If the markdown is correct but the retrieved chunk lacks the table heading, fix chunking and repeat headers. Temperature cannot restore a missing association.

OCR introduces small, expensive errors

Scans may turn O into zero, lose minus signs, or drop a digit from an account number. Compare parsed text with the exact source page, improve the scan when possible, and validate fields with domain rules. High-stakes values should require citations and human confirmation.

Columns, sidebars, or tables are retrieved incorrectly

A sidebar can appear inside the main article when reading order is wrong. A correct table can still be split so that its rows lack headers. Inspect the markdown and the exact indexed chunks; separate sidebars when they cannot be represented reliably in one passage, and keep table headings with rows.

Ingestion silently fails

Password protection, damaged PDFs, rate limits, and transient network failures can produce an empty or partial result. Open the source independently, record the file and page that failed, retry transient errors with backoff, and cache successful results by document version. Never index an empty result as a successful parse.

In one breath

A PDF stores positioned drawing instructions, not dependable semantic structure. Ordinary extraction must guess reading order, so tables flatten, columns interleave, and scans need OCR. LlamaParse uses a layout-aware, often vision-assisted process to turn difficult pages into markdown that keeps headings, rows, and relationships visible. That helps LlamaIndex create better chunks and retrieve the right evidence.

But parsing is not retrieval, and markdown is not ground truth. Inspect parsed output, keep source metadata, chunk tables with their headers, cache results, and test real questions before tuning embeddings or prompts. If a relationship is lost during parsing, the rest of the RAG pipeline cannot reliably invent it back.

Quick check

Quick check

0/3
Q1Why can a visually perfect PDF produce a wrong RAG answer?
Q2What is LlamaParse responsible for in a LlamaIndex RAG pipeline?
Q3A scanned two-column safety manual produces answers that mix the main instructions with a warning sidebar. What should you investigate first?

Next

For the larger indexing and retrieval picture, continue with LlamaIndex: indexes, query engines, and retrievers. For an agent framework with a different handoff and guardrail model, see the OpenAI Agents SDK.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions
In LlamaIndex, what are nodes and query engines, and how is RAG exposed as a tool to an agent?

Nodes are the retrievable pieces of source content, carrying text, metadata, and relationships. A query engine retrieves relevant nodes and synthesizes an answer; wrapping it in QueryEngineTool lets an agent choose that RAG pipeline as a tool.

How do you evaluate the quality of an LLM or RAG system?

Evaluation splits into retrieval quality (did we fetch the right chunks?) and generation quality (did the model use them correctly?). Key metrics are context precision/recall for retrieval and faithfulness plus answer relevance for generation. Frameworks like RAGAS automate LLM-as-judge scoring; human annotation anchors the ground truth.

What is Retrieval-Augmented Generation (RAG) and how does a basic RAG pipeline work?

RAG retrieves relevant external documents at query time and gives them to an LLM as context before generation. A basic pipeline ingests and chunks documents, embeds the chunks into a searchable store, retrieves the best matches for a question, and asks the LLM to answer from that evidence.

What is Retrieval-Augmented Generation (RAG) and why is it used?

RAG couples a retrieval step — fetching relevant documents from an external store — with a generative model so the LLM can answer questions about knowledge it was never trained on. It solves the stale-knowledge and hallucination problems without retraining. The pattern is preferred when the knowledge base changes frequently or contains proprietary data.

Related lessons

Explore further