RAG Chunking skill for Codex

Chunks that follow structure and stand alone. Codex picks a chunking strategy per document type, splits on headings, clauses, rows or functions instead of fixed character windows, and keeps tables and code blocks whole. Each chunk carries its heading path and source location, sizes are measured in tokens, and two or three configurations are compared on a retrieval eval before re-embedding.

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- rag-chunking

Installs to ~/.agents/skills/rag-chunking/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $rag-chunking, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Chooses and implements chunking for retrieval by document type — structure-aware splitting for Markdown, HTML and docs sites, page and section handling for PDFs, row and table handling for spreadsheets, function-level splits for code, token-based sizes and overlap, contextual headers, parent-child chunks and metadata. Use when building or re-tuning a RAG index, when retrieval returns fragments without context, when tables or code get split mid-structure, or when choosing chunk size and overlap.

Use it when

  • Building or re-tuning a retrieval index
  • Retrieved snippets lack context or start mid-sentence
  • Tables, lists or code are cut across chunks
  • Mixed document types need different splitting

Use something else when

  • Text extraction from scans or complex layouts is the problem: use document-ai-ocr
  • You need the whole pipeline from ingest to cited answers: use rag-pipelines

How to check the result

Chunk statistics sit within target token ranges, twenty random chunks read cleanly on their own, and the chosen configuration matches or beats alternatives on recall@k.

The mistake it prevents

Using fixed character windows with large overlap to paper over bad boundaries, which bloats the index and still cuts tables in half.

~/.agents/skills/rag-chunking/SKILL.md

--- name: rag-chunking description: Chooses and implements chunking for retrieval by document type — structure-aware splitting for Markdown, HTML and docs sites, page and section… license: MIT ---

Done well means: chunks follow the document’s own structure, each one makes sense on its own (with a contextual header), sizes are measured in tokens, tables and code blocks are never cut in half, metadata points back to the exact source location, and the chosen settings beat alternatives on a retrieval eval — not on intuition.

Defaults to start from

Document typeSplit onTarget sizeOverlapExtra
Markdown / docs sitesHeadings (H1–H3), then paragraphs300–600 tokens0–10%Heading path as header
HTML pagesSemantic tags (h*, section, article) after boilerplate removal300–6000–10%Drop nav, footer, cookie banners
PDFs (reports, manuals)Detected headings; fall back to page + paragraph400–80010–15%Page number metadata; strip running headers/footers
Contracts / policiesClause numbering (1.2, 1.2.3)One clause, merge tiny ones0Clause id as citation anchor
FAQs / ticketsOne Q&A or one ticket per chunkas-is up to ~8000Question text in header
Spreadsheets / CSVRow groups with header row repeated20–50 rows0Or convert rows to sentences; or query with SQL instead
CodeFunction/class (AST or tree-sitter)one symbol, cap ~8000File path + signature in header
Chat / transcriptsSpeaker turns grouped by topic or time window300–6001–2 turnsSpeaker and timestamp metadata

These are starting points. The right size depends on query type: short factual lookups favor smaller chunks; “explain how X works” favors larger ones or parent-child.

Workflow

  • 1. Sample 20 documents per type and look at the extracted text (not the original)
  • 2. Pick a strategy per type from the table
  • 3. Implement token-aware splitting with structure boundaries
  • 4. Add contextual headers and metadata
  • 5. Inspect chunk statistics and 20 random chunks
  • 6. Compare 2–3 configurations on the retrieval eval

3. Structure-aware, token-sized splitting

import re

def count_tokens(text: str) -> int:
    try:
        import tiktoken                       # optional dependency
        return len(tiktoken.get_encoding("cl100k_base").encode(text))
    except ImportError:
        return max(1, len(text) // 4)         # rough fallback

HEADING = re.compile(r"^(#{1,3})\s+(.*)$", re.M)

def split_markdown(doc_id: str, text: str, max_tokens=500, min_tokens=80):
    sections, path = [], []
    parts = HEADING.split(text)               # [pre, hashes, title, body, hashes, title, body...]
    if parts[0].strip():
        sections.append(([], parts[0]))
    for i in range(1, len(parts), 3):
        level, title, body = len(parts[i]), parts[i + 1].strip(), parts[i + 2]
        path = path[: level - 1] + [title]
        sections.append((list(path), body))

    chunks = []
    for heading_path, body in sections:
        buf = ""
        for block in split_blocks(body):      # paragraphs, but fenced code and tables whole
            if buf and count_tokens(buf + block) > max_tokens:
                chunks.append((heading_path, buf.strip()))
                buf = ""
            buf += block + "\n\n"
        if buf.strip():
            chunks.append((heading_path, buf.strip()))
    return merge_small(chunks, min_tokens)    # fold tiny chunks into their neighbor

def split_blocks(body: str):
    blocks, cur, in_fence = [], [], False
    for line in body.splitlines():
        if line.strip().startswith("```"):
            in_fence = not in_fence
        if not line.strip() and not in_fence and cur:
            blocks.append("\n".join(cur)); cur = []
        else:
            cur.append(line)
    if cur:
        blocks.append("\n".join(cur))
    return blocks

A single block larger than max_tokens (huge table, long code) gets its own chunk or is split by rows/lines with the table header or function signature repeated — never mid-row or mid-statement.

Libraries that already do this (LangChain MarkdownHeaderTextSplitter / RecursiveCharacterTextSplitter.from_tiktoken_encoder, LlamaIndex node parsers, unstructured partitioning) are fine if the repo already depends on them; installing new ones needs network and possibly approval.

4. Contextual headers and metadata

Prepend a short header to the text that gets embedded, so a chunk saying “this applies only to annual plans” carries what “this” is:

Document: Refund policy (v12) > Eligibility > Annual plans
<chunk text>

Optionally generate a one-sentence LLM summary of the chunk’s role in the document and prepend it (“contextual retrieval”). It improves recall on ambiguous chunks but costs one model call per chunk at ingest — measure before adopting, and run it through Batch.

Metadata per chunk: doc_id, chunk_index, heading_path, page (PDF), char_start/char_end or anchor for citation, doc_type, lang, updated_at, ACL. Store the raw text separately from the embedded text so citations show clean content.

Parent-child (small-to-big)

Embed small child chunks (100–200 tokens) for precise matching; at answer time return their parent section (800–1,500 tokens) for context. Deduplicate when several children share a parent. Use when users ask detailed questions whose answers need surrounding context.

5. Inspect

import statistics
sizes = [count_tokens(c.text) for c in chunks]
print(len(sizes), statistics.median(sizes), max(sizes), sum(s < 50 for s in sizes))

Red flags: many chunks under 50 tokens (headers alone, nav fragments), max far above the target (an unsplit table), identical chunks across documents (boilerplate), chunks starting mid-sentence.

6. Compare on retrieval eval

Run the same question set (see rag-evaluation) for 2–3 configurations, e.g. 300/0, 500/50 and parent-child. Choose by recall@k at the k you actually prompt with, then by index size and cost. Re-chunking requires re-embedding — budget it.

Gotchas

  • Splitting by characters, not tokens: sizes vary 2–3× across languages and code.
  • Fixed-size windows that cut tables, lists and code blocks in half.
  • Overlap as a fix for bad boundaries: it inflates the index and duplicates hits; fix boundaries instead.
  • PDF running headers/footers (“Confidential — Page 7”) repeated in every chunk dominate embeddings; strip them.
  • Losing the heading path: chunk text alone often omits the subject.
  • Changing chunking without bumping a prep_version leaves a mixed index.

Verification

  • Chunk stats within target ranges; zero chunks over the embedding model’s input limit.
  • 20 random chunks read by eye: each understandable alone, no broken tables or code.
  • Retrieval eval of the chosen config ≥ the alternatives tested.

Output contract

| Doc type | Strategy | Size / overlap | Chunks | Median tokens |
|---|---|---|---|---|
| Docs site | headings → paragraphs, header path prefix | 500 / 0 | 18,240 | 312 |
| PDF manuals | headings, page metadata, footers stripped | 700 / 80 | 9,880 | 540 |
| Pricing CSV | 25-row groups, header repeated | n/a | 140 | 410 |

**Eval:** recall@8 300/0 = 0.79, 500/0 = 0.84, parent-child = 0.86 → chose parent-child
**prep_version:** 3 (re-embed scheduled via Batch)

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "RAG Chunking"
  short_description: "Chunks that follow structure and stand alone"
  default_prompt: "$rag-chunking Our RAG index splits tables in half — rework chunking for the PDF manuals"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem