Intermediate7 min read34 of 40

Auto-Merging Retrieval - Reconstruct Context from Overlapping Chunks

When enough child chunks from the same parent are retrieved, automatically merge them back into the parent chunk for richer, coherent context.

Overview

Auto-Merging Retrieval detects when multiple small child chunks from the same parent section are all retrieved for the same query, then replaces them with the full parent chunk. This produces richer, coherent context instead of a collection of fragmented snippets.

Pattern #23 (Parent-Child RAG) in the 37 RAG Patterns Collection

flowchart TD
  DOC --> L1["Leaf chunks ~128 chars — indexed"]
  L1 --> L2["Parent chunks ~512 chars — stored"]
  L2 --> L3["Root chunks ~1024 chars — stored"]
  Q[Query] --> SR[Search leaf chunks top-K]
  SR --> AM{"Count: how many retrieved leaves share the same parent?"}
  AM --> |majority from same parent| MG[Merge: use parent chunk instead of leaves]
  AM --> |leaves from different parents| KL[Keep individual leaves]
  MG & KL --> LLM[Generate — richer context when content concentrates]

The Problem

Retrieving many small overlapping chunks from the same document section creates fragmented, redundant context:

Query: "What are the side effects of Drug X?" Retrieved (5 × 100-token child chunks from Section 3): Chunk 3a: "...nausea was reported in 12% of patients..." Chunk 3b: "...nausea and headache were the most common..." Chunk 3c: "...headache affected 8% of trial participants..." Chunk 3d: "...dizziness was observed in rare cases..." Chunk 3e: "...rare cases included dizziness and fatigue..." Problems: - Overlapping content wastes ~40% of the token budget on repetition - Sentence fragments lose inter-sentence reasoning chains - The LLM sees shards instead of the coherent paragraph it needs

Solution

When a sufficient number of child chunks from the same parent are retrieved, swap them out for the full parent:

Retrieve child chunks ↓ Group by parent_id ↓ For each group: count(children) >= M? ├── YES → fetch full parent chunk (coherent, ~400 tokens) └── NO → keep individual children (precise, ~100 tokens each) ↓ Send merged context to LLM

The merge threshold M is tunable: lower M merges aggressively (more coherence), higher M stays granular (more precision).

Implementation

1. Index Structure with Parent-Child Linking

python
def build_hierarchical_index(documents, vectorstore, docstore,
                              child_size=100, parent_size=400):
    """
    Index child chunks into the vector store for retrieval.
    Store parent chunks in a separate docstore for lookup.
    Each child carries a parent_id reference.
    """
    for doc in documents:
        # Split into parent chunks
        parents = split_text(doc["content"], chunk_size=parent_size)

        for p_idx, parent_text in enumerate(parents):
            parent_id = f"{doc['id']}::parent::{p_idx}"
            docstore.store(parent_id, {"content": parent_text, "source": doc["id"]})

            # Split each parent into children
            children = split_text(parent_text, chunk_size=child_size)
            for c_idx, child_text in enumerate(children):
                child_id = f"{parent_id}::child::{c_idx}"
                vectorstore.add({
                    "id": child_id,
                    "content": child_text,
                    "parent_id": parent_id,
                })

2. Auto-Merge Logic

python
from collections import defaultdict

MERGE_THRESHOLD = 2  # merge when >= 2 children from same parent are retrieved

def auto_merge_results(retrieved_children, docstore, threshold=MERGE_THRESHOLD):
    """
    Replace groups of children with their parent when count >= threshold.
    """
    # Group retrieved children by their parent
    groups = defaultdict(list)
    for child in retrieved_children:
        groups[child["parent_id"]].append(child)

    merged_context = []
    for parent_id, children in groups.items():
        if len(children) >= threshold:
            # Enough children hit — fetch the coherent parent
            parent = docstore.get(parent_id)
            merged_context.append({
                "content": parent["content"],
                "source": parent_id,
                "merge_type": "parent",
                "children_merged": len(children),
            })
        else:
            # Too few children — keep them as retrieved
            merged_context.extend([
                {**c, "merge_type": "child"}
                for c in children
            ])

    return merged_context

3. Full Pipeline

python
def auto_merging_rag(query, vectorstore, docstore, reranker, llm,
                     top_k=10, merge_threshold=2):
    # Step 1: Retrieve child chunks by similarity
    children = vectorstore.search(query, top_k=top_k)

    # Step 2: Apply auto-merge — replace qualifying groups with parents
    context_docs = auto_merge_results(children, docstore, merge_threshold)

    # Step 3: Optionally rerank the merged context
    reranked = reranker.rerank(query, context_docs, top_k=5)

    # Step 4: Generate
    context = "\n\n".join([d["content"] for d in reranked])
    response = llm.generate(
        f"Context: {context}\n\nQuestion: {query}"
    )

    merged_count = sum(1 for d in context_docs if d["merge_type"] == "parent")
    return response, {
        "children_retrieved": len(children),
        "after_merge": len(context_docs),
        "parents_substituted": merged_count,
        "final_context": len(reranked),
    }

Complete Notebook

Run the full implementation:

Parent-Child RAG AWS Notebook

Includes:

  • Hierarchical index construction with parent-child linking
  • Auto-merge threshold tuning experiments
  • Coherence scoring comparisons
  • LlamaIndex vs. manual implementation notes

Performance

MetricAuto-MergingRaw Children
Coherence score0.910.73
Answer completeness85%68%
Token efficiencyHigh (fewer repeats)Low (overlapping fragments)
Latency overhead~50ms (docstore lookup)Baseline

Auto-merging improves coherence score from 0.73 to 0.91 — a 25% gain — with minimal latency cost.

When to Use

Best For

  • Long-form documents — books, technical reports, legal contracts where section coherence matters
  • Dense reference material — API docs, manuals where surrounding context aids comprehension
  • Narrative text — articles, case studies where argument flow spans multiple sentences
  • Reducing redundancy — any corpus where small chunks frequently overlap

Combine With

  • Parent Document Retrieval (#23) — the underlying architecture that auto-merging builds on; use it when you always want the parent regardless of child count
  • Reranking (#4) — apply cross-encoder reranking after the merge decision to select the best final context chunks
  • Contextual Compression (#6) — if merged parents are still too large, compress them after merging

Related Papers

Next Steps

  1. Try it: Run the Parent-Child RAG notebook
  2. Tune: Experiment with merge threshold M — start at 2, adjust based on your document structure
  3. Combine: Add Reranking (#4) after the merge step to tighten the final context
  4. Compare: Measure coherence scores with and without merging on your own corpus

Part of: 37 RAG Patterns Collection See also: Complete Pattern Index

Go Deeper With Live Instruction

Covered in depth in our llm engineering program.