π§© Advanced Chunking Strategies β The Architecture of Precision Retrieval
Dari Fixed-Size ke Parent-Child: Arsitektur Representasi Data untuk RAG Production-Grade
Chunking adalah tindakan pertama dan paling fundamental dalam pipeline Retrieval-Augmented Generation (RAG). Ini adalah keputusan arsitektural tentang bagaimana Anda mendefinisikan unit atomik makna yang akan di-retrieve. Pilihan yang salah menghasilkan jawaban yang terfragmentasi, kehilangan konteks, atau halusinasi. Pilihan yang tepat menghasilkan retrieval yang presisi, jawaban yang kaya konteks, dan efisiensi komputasi. Dokumen ini membedah spektrum strategi chunking, dari yang naif hingga production-grade, dengan fokus pada arsitektur Parent-Child Chunking yang menyelesaikan trade-off fundamental antara presisi pencarian dan kelengkapan konteks.
Hubungan ke Vault
Nota ini adalah fondasi teoretis dari implementasi RAG di project
vault-rag. Strategi chunking yang dijelaskan di sini diimplementasikan secara konkret di index_vault.py dengan arsitektur Parent-Child + Structure-Aware untuk vault markdown. Terkait dengan ai-engineering-stack-roadmap (Fase 2: Data Pipeline & Vector Infrastructure), cognitive-architecture-engineering (memori hierarkis), dan math-and-algorithms (cosine similarity untuk semantic chunking).
Implementasi di vault-rag
Di
scripts/index_vault.py, chunking sudah menggunakan Structure-Aware Parent-Child:
- Parent: H1/H2 section boundaries
- Child: H3/paragraph/sentence dengan CHILD_MAX_CHARS=600
- Section path: heading hierarchy ([βFoundationβ, βTechnical Deep-Diveβ])
- DB:
parentstable +chunkstable dengan FKparent_id
Daftar Isi
- 1. First Principles
- 2. Spektrum Strategi Chunking
- 3. Parent-Child Chunking β Production-Grade Architecture
- 4. Matriks Perbandingan
- 5. Rekomendasi untuk Vault Ini
- Koneksi ke Vault
- References
1. First Principles: Atom Makna dalam Ruang Vektor
1.1 The Semantic Atom Problem
Mengapa chunking begitu sulit? Karena makna tidak memiliki ukuran yang tetap. Sebuah kalimat tunggal bisa menjadi atom makna yang sempurna untuk sebuah fakta (βIbu kota Indonesia adalah Jakarta.β), tetapi tidak cukup untuk sebuah argumen atau penjelasan. Sebuah paragraf bisa menangkap sebuah konsep, tetapi bisa juga berisi beberapa ide yang berbeda.
Dalam RAG, kita menghadapi dua kebutuhan yang saling bertentangan:
- Presisi Pencarian (Search Precision): Chunk yang kecil dan fokus secara semantik akan menghasilkan kemiripan embedding yang tinggi dengan kueri. Ini meminimalkan noise dan memaksimalkan relevansi.
- Kelengkapan Konteks (Context Completeness): Chunk yang terlalu kecil kehilangan konteks sekitarnya, membuat LLM tidak dapat memahami nuansa, anteseden, atau implikasi dari teks yang di-retrieve.
Parent-Child Chunking adalah solusi arsitektural untuk kontradiksi ini.
1.2 Trade-off Fundamental
Presisi Konteks
ββββββββββββββββββββββββββββββββββββββββββββββββΊ
Fixed-Size Sentence Semantic Struct-Aware Parent-Child
(rendah) (rendah) (sedang) (tinggi) (sangat tinggi)
presisi konteks balanced presisi presisi +
tinggi konteks tertinggi
| Pendekatan | Presisi | Konteks | Trade-off |
|---|---|---|---|
| Fixed-Size kecil (100 token) | π’ Tinggi | π΄ Rendah | Dapet fakta tepat tapi gak paham konteks |
| Fixed-Size besar (1000 token) | π΄ Rendah | π’ Tinggi | Banyak noise, relevansi rendah |
| Parent-Child | π’ Sangat Tinggi | π’ Sangat Tinggi | Kompleksitas implementasi naik |
2. Spektrum Strategi Chunking
2.1 Fixed-Size Chunking β The Naive Baseline
Filosofi: βSaya tidak tahu apa-apa tentang data Anda, jadi saya potong rata.β
Input: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
[ABCDE][FGHIJ][KLMNO][PQRST][UVWXY][Z]
| Aspek | Detail |
|---|---|
| Mekanisme | Teks dipotong setiap N token/karakter, seringkali dengan overlap (N/10 token) |
| Keunggulan | Paling sederhana. Deterministis, prediktabel, mudah di-debug. Tidak perlu library NLP |
| Kelemahan | Boundary Problem. Kalimat terpotong, paragraf terputus, kode terbelah. Informasi terfragmentasi acak. Overlap sedikit membantu tapi gak solve masalah koherensi semantik |
| Kapan dipakai | Hanya prototype cepat, atau data homogen pendek (tweet, title) |
# Paling mentah β potong per karakter
def fixed_size_chunk(text, chunk_size=500, overlap=50):
chunks = []
start = 0
while start < len(text):
end = min(start + chunk_size, len(text))
chunks.append(text[start:end])
start += chunk_size - overlap
return chunks
# Lebih cerdas β RecursiveCharacterTextSplitter (LangChain)
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", ". ", " ", ""]
# Coba split di double-newline dulu, lalu newline, lalu titik, lalu spasi,
# baru kalau gak ada pilihan: potong karakter
)2.2 Sentence-Based Chunking β The Linguistic Baseline
Filosofi: βKalimat adalah unit linguistik alami. Hormati itu.β
| Aspek | Detail |
|---|---|
| Mekanisme | Sentence tokenizer (NLTK, spaCy) β pisah kalimat β grup 5-10 kalimat per chunk |
| Keunggulan | Gak ada kalimat terpotong. Setiap chunk punya integritas gramatikal |
| Kelemahan | Context Collapse. Kata ganti (βiniβ, βmerekaβ), singkatan, referensi implisit bikin kalimat tunggal ambigu. βIni adalah contoh yang baikβ gak berguna tanpa kalimat sebelumnya |
| Kapan dipakai | FAQ, chat logs, data QA pendek yang mandiri |
import spacy
nlp = spacy.load("en_core_web_sm")
def sentence_based_chunk(text, max_sentences=5):
doc = nlp(text)
sentences = [sent.text for sent in doc.sents]
chunks = []
for i in range(0, len(sentences), max_sentences):
chunks.append(" ".join(sentences[i:i+max_sentences]))
return chunks2.3 Semantic Chunking β The Embedding-Based Approach
Filosofi: βMakna adalah tentang koherensi, bukan panjang. Ikuti alur topik.β
Kalimat: [A] [B] [C] [D] [E] [F] [G] [H]
Similaritas: 0.95 0.93 0.89 0.45 0.91 0.94 0.88
ββββββ Chunk 1 βββββ βββ Chunk 2 βββ
^ threshold 0.7 β putus di sini
| Aspek | Detail |
|---|---|
| Mekanisme | Embed tiap kalimat β sliding window dengan threshold similaritas β grup kalimat mirip. Kalo similaritas turun drastis = ganti topik = chunk baru |
| Keunggulan | Chunk koheren secara topik. Adaptif: teks mudah β chunk panjang, teks kompleks β chunk pendek |
| Kelemahan | Hyperparameter sensitivity. Threshold tergantung domain. Terlalu tinggi β over-split (ratusan chunk kecil). Terlalu rendah β under-split (chunk raksasa campur aduk). Gak ada one-size-fits-all |
| Kapan dipakai | Dokumen tanpa struktur jelas (transkrip, esai, artikel). Kalo gak bisa andelin struktur Markdown |
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
def semantic_chunking(sentences, embeddings, threshold=0.7):
"""
sentences: list of strings
embeddings: list of embedding vectors (same order)
threshold: kalo similaritas < threshold, mulai chunk baru
"""
chunks = []
current_chunk = [sentences[0]]
for i in range(1, len(sentences)):
sim = cosine_similarity([embeddings[i-1]], [embeddings[i]])[0][0]
if sim < threshold:
chunks.append(" ".join(current_chunk))
current_chunk = [sentences[i]]
else:
current_chunk.append(sentences[i])
if current_chunk:
chunks.append(" ".join(current_chunk))
return chunks2.4 Structure-Aware Chunking β The Production Baseline
Filosofi: βSaya tahu strukturnya (Markdown, HTML, JSON). Gunakan itu sebagai panduan.β
| Aspek | Detail |
|---|---|
| Mekanisme | Parser Markdown β ekstrak judul, sub-judul, paragraf, tabel, kode β chunk dengan hormati batas elemen |
| Keunggulan | Untuk vault markdown, ini paling presisi. Heading = ringkasan topik alami. Metadata (judul, heading) bisa disimpan per chunk |
| Kelemahan | Fragility. Bergantung kualitas markup. PDF OCR, plain text, HTML kacau gak punya struktur |
| Kapan dipakai | Default untuk vault ini. Setiap .md adalah kanvas sempurna |
from langchain.text_splitter import MarkdownHeaderTextSplitter
headers_to_split_on = [
("##", "H2"),
("###", "H3"),
("####", "H4"),
]
splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
chunks = splitter.split_text(markdown_text)
# Setiap chunk punya metadata:
# {"H2": "2. Spektrum Strategi Chunking", "H3": "2.1 Fixed-Size"}3. Parent-Child Chunking β Production-Grade Architecture
Filosofi: βSaya butuh presisi pencarian dari chunk kecil, dan kelengkapan konteks dari chunk besar. Saya akan menyimpan keduanya.β
Parent-Child bukan teknik tunggal β ini arsitektur data dual-layer.
3.1 Arsitektur Dual-Layer
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LAYER PEMANFAATAN β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β PARENT LAYER (Konteks β disimpan, TIDAK di-embed) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β [## Foundation] [Teks lengkap section Foundation β β
β β dari awal "TCP three-way..." sampai "sequence n."] β β
β β id: "p_abc123" β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β² β² β² (dilihat via FK) β
β β β β β
β CHILD LAYER (Presisi β di-embed + di-search) β
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ β
β β Child 1 β β Child 2 β β Child 3 β β Child 4 β β
β β 140 char β β 150 char β β 70 char β β 130 char β β
β β parent: β β parent: β β parent: β β parent: β β
β β p_abc123 β β p_abc123 β β p_abc123 β β p_abc123 β β
β ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ β
β β β β β β
β βΌ βΌ βΌ βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β VECTOR STORE (Embedded) β β
β β Query β cosine similarity β Top-3 children β β
β β β resolve parent β context utuh β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
3.2 Alur Kerja Arsitektural
Ingestion Pipeline
[Dokumen Markdown]
β
βΌ
βββββββββββββββββββ
β Structure β
β -Aware Parser β Ekstrak struktur: H2, H3, Paragraf, List
ββββββββββ¬βββββββββ
β
βΌ
ββββββββββββββββββββββββββββββ
β Parent-Child Splitter β
β β’ Parent = H1/H2 section β
β β’ Child = pecahan parent β
β (H3 β paragraph β sntnc) β
β β’ Each child stores β
β parent_id as FK β
ββββββββββββββββ¬ββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββ
β DB: parents table + chunks table β
β β’ chunks: text, embedding, parent_id β
β β’ parents: id, text (full section) β
ββββββββββββββββββββββββββββββββββββββββββ
Retrieval Pipeline
[Query: "gimana SYN flood bekerja?"]
β
βΌ
ββββββββββββββββββββββββ
β 1. Vector Search β
β β Top-5 child chunks β
β β Dapet chunk ttg β
β "SYN flood attack" β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β 2. Resolve Parent β
β β child.parent_id β
β β SELECT FROM parentsβ
β β Dapet FULL section β
β "Foundation" β
β (mencakup SYN β
β flood, SYN cookies,β
β sequence pred.) β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β 3. LLM Generation β
β Konteks = parent textβ
β (child + context) β
β Jawaban LEBIH LENGKAPβ
ββββββββββββββββββββββββ
3.3 Implementasi di vault-rag
Implementasi konkret ada di scripts/index_vault.py dan scripts/query.py:
Indexer β chunking:
# Parent: H1/H2 section (full text)
parent_boundaries = []
h1h2_positions = [i for i, m in enumerate(headings) if len(m.group(1)) <= 2]
for idx, heading_idx in enumerate(h1h2_positions):
m = headings[heading_idx]
start = m.start()
# End at next H1/H2, or EOF
if idx + 1 < len(h1h2_positions):
end = headings[h1h2_positions[idx + 1]].start()
else:
end = len(text)
parent_boundaries.append((start, end, m.group(2).strip()))
# Child: within each parent, split at H3 β paragraph β sentence
# _split_by_headings β _split_by_paragraphs β _split_by_sentences
# CHILD_MAX_CHARS = 600, CHILD_MIN_CHARS = 40Query β parent context:
# 1. Search child chunks (dense cosine + BM25 FTS5)
# 2. For each hit, look up parent_id:
SELECT text FROM parents WHERE id = ?
# 3. Attach parent_text to each result
# 4. LLM prompt includes parent_text as full context3.4 Kapan Parent-Child Wajib
| Skenario | Kenapa Wajib | Contoh |
|---|---|---|
| Dokumen teknis panjang | Konteks section dibutuhkan untuk interpretasi | Vault note 30KB |
| Pertanyaan multi-hop | βJelaskan hubungan X dan Yβ butuh konteks luas | βApa hubungan SYN flood dan sequence prediction?β |
| Detil penting untuk akurasi | Latar belakang diperlukan | βKenapa SYN cookies mengorbankan window scaling?β |
| Menghindari halusinasi | Chunk kecil bikin LLM nebak-nebak | βJelaskan cara kerja TCP state machineβ β tanpa parent, LLM gak tau ini tentang detection |
4. Matriks Perbandingan
| Strategi | Presisi Pencarian | Kelengkapan Konteks | Kompleksitas | Ketahanan Noise | Terbaik Untuk |
|---|---|---|---|---|---|
| Fixed-Size | π΄ Rendah | π΄ Rendah | π’ Sangat Rendah | π΄ Rendah | Prototipe, data homogen |
| Sentence-Based | π‘ Sedang | π΄ Rendah | π’ Rendah | π‘ Sedang | FAQ, chat logs |
| Semantic | π’ Tinggi | π‘ Sedang | π‘ Tinggi (threshold tuning) | π’ Tinggi | Dokumen tanpa struktur |
| Structure-Aware | π’ Tinggi | π’ Tinggi | π‘ Sedang | π΄ Rendah (bergantung markup) | Vault markdown |
| Parent-Child | π’ Sangat Tinggi | π’ Sangat Tinggi | π΄ Tinggi | π’ Tinggi | Production-grade |
Cost Analysis
| Strategi | Embedding Cost | Storage Cost | Retrieval Latency | Context Quality |
|---|---|---|---|---|
| Fixed-Size (256 token) | 1x per chunk | 1x vector | Rendah | Rendah |
| Fixed-Size (1024 token) | 1x per chunk | 1x vector | Rendah | Sedang |
| Semantic | 1x per kalimat + per chunk | 1x vector per chunk | Sedang (ekstra embed) | Tinggi |
| Structure-Aware | 1x per chunk | 1x vector per chunk | Rendah | Tinggi |
| Parent-Child | 1x per child (parent gak di-embed) | 1x vector per child (parent cuma text) | Rendah (search child β lookup parent) | Sangat Tinggi |
Cost Efficiency Parent-Child
Parent-Child tidak menggandakan embedding cost. Hanya child yang di-embed. Parent cuma disimpan sebagai text biasa. Jadi biaya embedding β Structure-Aware dengan chunk_size = CHILD_MAX_CHARS.
5. Rekomendasi untuk Vault Ini
Berdasarkan analisis struktur vault β semua file markdown dengan heading hierarchy (## Foundation β ### Technical Deep-Dive β #### Case Studies):
Cetak Biru
- Parser: H1/H2 boundaries β Parent Chunks (full section context)
- Child Splitter: H3 β paragraph β sentence β Child Chunks (~600 chars)
- Metadata:
section_path(heading hierarchy),parent_id,filepath,chunk_index - Retrieval: Vector search on children β deduplicate parents β send parent text as context
Status Implementasi
| Komponen | Status | Lokasi |
|---|---|---|
| Structure-Aware Parser | β Done | scripts/index_vault.py |
| Parent-Child Splitter | β Done | scripts/index_vault.py:make_parents_and_children() |
| DB Schema (parents + chunks) | β Done | scripts/index_vault.py:init_db() |
| Parent Context Augmentation | β Done | scripts/query.py:augment_with_parent() |
| Re-index command | β Done | ./vault-rag.sh index --reindex |
| FTS5 BM25 on children | β Done | scripts/index_vault.py |
| Heading hierarchy metadata | β Done | section_path column |
| Semantic Chunking (embed-based) | β Not implemented | Fallback ke Structure-Aware sudah cukup untuk vault |
| LangChain/Qdrant integration | β Not implemented | Pakai sqlite-vec + API sendiri (lebih ringan) |
Koneksi ke Vault
- CLAUDE.md β Dokumentasi implementasi RAG di vault-rag project
- index_vault.py β Implementasi konkret parent-child chunking
- query.py β Implementasi parent context augmentation di retrieval
- ai-engineering-stack-roadmap β Peta jalan: Fase 2 (Data Pipeline & Vector Infrastructure)
- cognitive-architecture-engineering β Memori hierarkis: child = working memory, parent = long-term memory
- math-and-algorithms β Cosine similarity di semantic chunking
- encoding-serialization-compression-deepdive β Representasi data: dari teks ke vektor
- http-protocol-deepdive β Latency retrieval: HTTP/3 > HTTP/2 > HTTP/1.1 untuk API RAG
- linux-fundamentals-deepdive β filesystem RAG: parent path, symlink, hardlink
References
- LangChain. Text Splitters. https://python.langchain.com/docs/modules/data_connection/document_transformers/
- Qdrant. Parent-Child Document Retrieval. https://qdrant.tech/articles/parent-child/
- Pinecone. Chunking Strategies. https://www.pinecone.io/learn/chunking-strategies/
- Anthropic. Contextual Retrieval. https://www.anthropic.com/news/contextual-retrieval
- LlamaIndex. Node Parser. https://docs.llamaindex.ai/en/stable/module_guides/loading/node_parsers/
- UnstructuredIO. Chunking Strategies. https://docs.unstructured.io/open-source/core-functionality/chunking
- Cohere. Chunking for RAG. https://docs.cohere.com/docs/chunking-for-rag
- McInnes, L. et al. UMAP: Uniform Manifold Approximation and Projection. 2018. β Fondasi untuk visualisasi embedding chunk.
- Reimers, N. & Gurevych, I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. 2019. β Fondasi sentence embedding untuk semantic chunking.
- Karpathy, A. Tokenization in LLMs. https://github.com/karpathy/minbpe β Prinsip tokenisasi yang memengaruhi chunk_size.
- sqlite-vec. Vector Search in SQLite. https://github.com/asg017/sqlite-vec
- SQLite FTS5. Full-Text Search. https://www.sqlite.org/fts5.html
Bottom Line
Chunking bukan preprocessing β ini keputusan arsitektural. Fixed-size chunking adalah βmagic numberβ yang paling berbahaya di RAG karena memberikan ilusi presisi sambil merusak koherensi semantik. Parent-Child Chunking menyelesaikan trade-off fundamental: search di child (presisi tinggi), context dari parent (konteks lengkap). Untuk vault markdown, Structure-Aware (hormati heading) adalah langkah pertama yang wajib. Implementasi di vault-rag sudah menggunakan arsitektur ini β jalankan
./vault-rag.sh index --reindexuntuk mengaktifkannya.