> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meibel.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Preparing parsed content for RAG

> Use headings, elements, and provenance from a parse to build well-structured chunks for retrieval-augmented generation

Retrieval-augmented generation (RAG) answers a question by retrieving passages from your documents and handing them to a language model. The quality of those answers depends on the shape of what you indexed. Chunks split on a fixed character count cut across sentences and merge unrelated sections, which weakens both retrieval and the answers built on it. A parse gives you the document's real structure, so you can chunk on section boundaries, keep tables and their captions whole, and carry each chunk's page and position for citation. This guide turns a parse into retrieval-ready chunks.

For how the structure is produced, see [how parsing works](/document-parsing/concepts/how-parsing-works).

## Prerequisites

* A completed parse job.
* The structured result for element structure, or the `markdown` format when you want ready-to-index text.

## Chunk on section boundaries

Headings mark where one topic ends and the next begins, and their level records the section hierarchy. Walking the elements and starting a new chunk at each `Title` or `SectionHeader` keeps a section's content together and splits where the document itself splits. Carrying the current heading path onto each chunk gives every chunk a breadcrumb of where it sits.

<CodeGroup>
  ```python Python theme={null}
  from meibel import ParseLayoutLabel

  result = client.documents.get_structured_result(job_id=job_id)

  chunks, current, heading_path = [], [], []

  def flush():
      if current:
          chunks.append({"heading_path": list(heading_path), "text": "\n".join(current)})
          current.clear()

  for page in result.pages:
      for el in page.elements:
          if el.label in (ParseLayoutLabel.TITLE, ParseLayoutLabel.SECTIONHEADER):
              flush()
              level = el.heading_level or 1
              heading_path[:] = heading_path[: level - 1] + [el.text]
          elif el.text:
              current.append(el.text)
  flush()

  print(f"{len(chunks)} chunks")
  ```

  ```typescript TypeScript theme={null}
  const result = await client.documents.getStructuredResult(jobId);

  const chunks = [];
  let current = [];
  let headingPath = [];
  const flush = () => {
    if (current.length) {
      chunks.push({ headingPath: [...headingPath], text: current.join('\n') });
      current = [];
    }
  };

  for (const page of result.pages) {
    for (const el of page.elements) {
      if (el.label === 'Title' || el.label === 'SectionHeader') {
        flush();
        const level = el.headingLevel ?? 1;
        headingPath = [...headingPath.slice(0, level - 1), el.text];
      } else if (el.text) {
        current.push(el.text);
      }
    }
  }
  flush();
  ```
</CodeGroup>

## Keep tables whole

A table loses its meaning when a fixed-size splitter cuts it in half. Because a table is a single element, you can keep it intact as its own chunk, and pair it with a nearby caption for context. Serializing the grid to Markdown or to rows keeps the structure a language model can read.

```python Python theme={null}
for page in result.pages:
    for el in page.elements:
        if el.label == ParseLayoutLabel.TABLE and el.table:
            t = el.table
            rows = [["" for _ in range(t.num_cols)] for _ in range(t.num_rows)]
            for c in t.cells:
                rows[c.row][c.col] = c.text
            table_text = "\n".join(" | ".join(r) for r in rows)
            chunks.append({"heading_path": list(heading_path), "text": table_text, "kind": "table"})
```

## Carry provenance for citation

Each element carries its `bbox`, and the page it sits on is the page you are walking. Keeping these on a chunk lets an answer cite the page it came from and lets a reviewer find the exact region on the source. Attach them as metadata when you index a chunk.

```python Python theme={null}
def provenance(page, el):
    return {"page": page.page_number, "bbox": el.bbox.model_dump()}
```

<Note>
  For chunks you feed straight to a language model, the `markdown` format is often enough on its own, since it already renders headings, lists, and tables. Reach for the structured result when you need per-element control over chunk boundaries and metadata.
</Note>

## Related

<CardGroup cols={2}>
  <Card title="The parsed document" icon="diagram-project" href="/document-parsing/concepts/the-parsed-document">
    The element model these chunks are built from.
  </Card>

  <Card title="Datasources" icon="database" href="/concepts/datasources">
    How Meibel indexes and retrieves over prepared content.
  </Card>
</CardGroup>
