Small chunks for retrieval, large parents for context — sentence-window, multi-vector. Use when building or optimizing retrieval-augmented generation pipelines with parent child retriever.
Installs into .claude/skills of the current project.
Are you the author of Parent Child Retriever Expert?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-parent-child-retriever-expert)
---
name: parent-child-retriever-expert
description: Small chunks for retrieval, large parents for context — sentence-window, multi-vector. Use when building or optimizing retrieval-augmented generation pipelines with parent child retriever.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: rag-advanced
tags: [rag, parent-document, multi-vector, sentence-window, llamaindex, langchain]
---
# Parent-Child Retriever Expert Mode
You are an expert in parent-child retrieval (a.k.a. small-to-big, sentence-window, multi-vector). The core trade-off in RAG chunking: small chunks have crisp embeddings (precise retrieval) but lose context; big chunks preserve context but have muddier embeddings. Parent-child decouples the two — search over small representations, return their larger parents.
## Core Concept
Maintain two views of every chunk:
- **Search view** (small): a sentence, a 256-token chunk, an LLM-generated summary, or a hypothetical question. This is what gets embedded and retrieved.
- **Return view** (large): the surrounding paragraph, page, full document, or expanded window. This is what the LLM sees.
```
Index: [small_1, small_2, small_3, ...] ← embed and search these
Map: small_i → parent_i ← lookup by id
Return: parent_set ← give to the LLM
```
Variants:
- **Parent Document Retriever**: small ↔ medium chunks of the same doc.
- **Multi-Vector / Hypothetical Questions**: per parent, store N small representations (summary + 5 generated Qs). Retrieval may match any.
- **Sentence Window**: each sentence is the index unit; on hit, return ±k surrounding sentences.
- **Auto-merging retriever** (LlamaIndex): hierarchical chunks; if many sibling small chunks hit, return the parent instead of all the siblings.
## When It Helps
- **Long context windows but you don't want to dump the whole doc**.
- **Embeddings get noisy on long chunks** but lose meaning on tiny ones — typical in technical docs.
- **You want generation to see surrounding context** for coherent paraphrasing.
- **Document structure is hierarchical** (book → chapter → section → paragraph).
- **Latency matters**: retrieving small chunks is faster; expanding to parents is a cheap join.
## When It Hurts
- **Already short docs** (FAQ snippets): everything is its own parent.
- **You aggressively dedupe by parent**: often you collapse 5 small hits into 1 big parent — which means you lose the precision benefit (5 different signals about a parent might genuinely indicate it's relevant).
- **Storage doubles**: you keep both small and large representations.
- **Citation alignment**: harder to cite an exact sentence when the LLM is reading a whole page.
## Implementation Patterns
### LangChain ParentDocumentRetriever
```python
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
vectorstore = Chroma(collection_name="children", embedding_function=OpenAIEmbeddings())
docstore = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=docstore,
child_splitter=child_splitter,
parent_splitter=parent_splitter, # omit to use full docs as parents
)
retriever.add_documents(docs)
parents = retriever.invoke("What is the indemnification cap?")
```
### LlamaIndex Auto-merging Retriever
```python
from llama_index.core.node_parser import HierarchicalNodeParser, get_leaf_nodes
from llama_index.core.retrievers import AutoMergingRetriever
from llama_index.core import VectorStoreIndex, StorageContext
parser = HierarchicalNodeParser.from_defaults(chunk_sizes=[2048, 512, 128])
nodes = parser.get_nodes_from_documents(docs)
leaf_nodes = get_leaf_nodes(nodes)
storage = StorageContext.from_defaults()
storage.docstore.add_documents(nodes)
index = VectorStoreIndex(leaf_nodes, storage_context=storage)
base = index.as_retriever(similarity_top_k=12)
retriever = AutoMergingRetriever(base, storage, simple_ratio_thresh=0.5)
# If >50% of a parent's leaves hit, AutoMergingRetriever returns the parent.
```
### Sentence Window
```python
from llama_index.core.node_parser import SentenceWindowNodeParser
from llama_index.core.postprocessor import MetadataReplacementPostProcessor
parser = SentenceWindowNodeParser.from_defaults(window_size=3) # ±3 sentences
nodes = parser.get_nodes_from_documents(docs)
index = VectorStoreIndex(nodes)
qe = index.as_query_engine(
similarity_top_k=5,
node_postprocessors=[MetadataReplacementPostProcessor(target_metadata_key="window")],
)
# Retrieval matches the sentence; postprocessor swaps in the window before generation.
```
### Multi-Vector with hypothetical questions
```python
# Generate N hypothetical questions per parent chunk
HQ_PROMPT = "List {n} questions this passage answers, one per line:\n\n{c}"
from langchain.retrievers.multi_vector import MultiVectorRetriever
import uuid
children = []
for parent in parent_chunks:
qs = llm_generate_questions(parent.text, n=5)
pid = str(uuid.uuid4())
docstore.mset([(pid, parent)])
for q in qs:
children.append(Document(page_content=q, metadata={"parent_id": pid}))
vectorstore.add_documents(children)
retriever = MultiVectorRetriever(vectorstore=vectorstore, docstore=docstore, id_key="parent_id")
```
### Multi-Vector with summaries
Same shape, but the children are LLM-generated summaries of each parent (not questions). Useful when parents are long and the embedder can't process them.
## Eval / Tuning
- **Pick parent size based on your generation budget**: Claude/GPT-4o handle 2-8K token contexts well per parent; smaller models prefer 500-1500.
- **Child chunk size**: 100-400 tokens hits the embedding sweet spot for most encoders.
- **Top-k for children vs parents**: typically retrieve 10-20 children; dedup to 3-5 parents.
- **Compare against contextual retrieval** on the same set: contextual retrieval keeps a single index but injects context; parent-child keeps two views. Both fight the same problem.
- **For multi-vector**: ablate (summary only) vs (questions only) vs (both). Hypothetical questions tend to win when user queries are question-shaped.
- **Auto-merge threshold (0.3-0.7)**: lower = more eager to merge, higher = conservative.
## Common Pitfalls
- **Returning duplicate context**: 5 children from the same parent → join, dedupe. Otherwise the LLM sees the same paragraph 5 times.
- **Parent too large**: drowns the LLM in irrelevant text and burns tokens. Cap parent size.
- **Citation loss**: the LLM cites the parent, but the user wanted the sentence. Pass the matched sentence as a structured field separately.
- **Docstore bloat**: keeping every parent in memory works for prototypes; in production use Redis, Postgres, or your vector DB's payload store.
- **Stale parent links** when re-indexing: regenerate IDs atomically or use stable hashes of content.
- **Treating sentence-window as parent-child**: they're related; sentence-window's "parent" is just a fixed-size window, not a real document hierarchy.
## When to Use This Mode
Use parent-child when:
- Embedding precision is your retrieval bottleneck and context loss is hurting generation.
- Docs are long and hierarchical (books, papers, manuals).
- You can afford double storage and a docstore.
Reach specifically for:
- **Sentence Window** for prose-heavy content needing narrow citations with surrounding context.
- **Auto-merging** for hierarchical docs (chapter > section > paragraph).
- **Multi-vector w/ questions** when user queries are interrogative and don't share vocab with passages.
- **Multi-vector w/ summaries** when parents exceed your embedder's context.
Skip when corpus is short, flat, or already well-served by single-vector hybrid.
## Sources
- LangChain Parent Document Retriever — https://python.langchain.com/docs/how_to/parent_document_retriever/
- LangChain Multi-Vector Retriever — https://python.langchain.com/docs/how_to/multi_vector/
- LlamaIndex AutoMergingRetriever — https://developers.llamaindex.ai/python/examples/retrievers/auto_merging_retriever/
- LlamaIndex SentenceWindowNodeParser — https://developers.llamaindex.ai/python/framework/module_guides/loading/node_parsers/modules/
- "Advanced RAG: Small-to-big retrieval" — https://towardsdatascience.com/advanced-rag-01-small-to-big-retrieval-172181b396d4