agentsope/agentsop-multiscale-chunking
>- Enhancement-overlay (C5) for RAG over long documents — the chunk-paradox resolution. Activate when a single fixed chunk size cannot satisfy both retrieval precision (small large chunks dilute embedding relevance into "topic averages". Encodes the core flip — large for synthesis context — and the SOP to pick a base chunk size, choose a horizontal (sentence-window) vs vertical (auto-merging / parent-child) expansion strategy, and measure the lift. Cross-links [[llamaindex]] for the full RAG SOP; this overlay supplies the missing "chunk-paradox-resolution" recipe that the framework docs (HierarchicalNodeParser, SentenceWindow) only describe in fragments. Medium-frequency for chunking strategy, parent document retriever, sentence window, small-to-big retrieval, hierarchical chunking, optimal chunk size.
npx skills add https://github.com/agentsope/SkillAlchemy --skill agentsop-multiscale-chunking
> Overlay on top of [[llamaindex]]. The base skill teaches the 5-layer RAG
> pipeline and lists DecoupleChunkScope as one optimization knob among many.
> This overlay zooms in on that single knob and turns it into a standalone
> recipe: **how to resolve the chunk paradox when one chunk size is provably
> not enough.** Third-person analytical view for an agent writing / reviewing
> RAG ingestion code — not an end-user tutorial.
Activate this overlay when all three RAG preconditions hold and the chunk
paradox has actually surfaced:
contracts, research papers, codebases — where a single answer-bearing fact
sits inside a larger context that the LLM needs to interpret it.
precision but the LLM answers from fragments; large chunks (1024–2048) give
rich context but recall on specific queries drops because the embedding
becomes a "topic average". The official failure-mode checklist documents
both poles as *separate* failures — #2 (wrong chunk from too-small) and #6
(context overflow / dilution from too-large) (cited in [[llamaindex]] R3).
chunk_size only moves the failure from one pole to the other.
Concrete triggers:
SentenceSplitter(chunk_size=4096) shipped as the fix for"incomplete answers" (this is anti-pattern A1 in [[llamaindex]]).
Do not activate when:
> **Decouple the embed-unit from the return-unit. Embed small for retrieval
> precision; return large for generation context.**
The naive assumption is that the unit you index *is* the unit you feed the LLM.
That single identity is the source of the paradox: it forces one chunk size to
serve two opposing jobs.
NAIVE (one unit, two jobs) MULTI-SCALE (two units, one job each)
───────────────────────── ─────────────────────────────────────
[ chunk ] embed unit → small (precision job)
/ \ │
embed it feed it match
(wants (wants │
small) large) return unit → large (context job)
↓ ↓ ▲
CONFLICT — pick one, expand from
lose the other match → parent / window
Three load-bearing sub-principles:
Node carriesrelationships (PREV/NEXT/PARENT/CHILD). Those links are exactly what let
you store a small node for matching and *resolve* it to a larger node for
return ([[llamaindex]] Principle 2). Multi-scale chunking is "build a
chunk-graph", not "split into chunks".
the small match expands into the large return:
(sentence-window). The expansion is *positional*.
(auto-merging / parent-child). The expansion is *hierarchical*.
horizontal. Documents with real structure (headings, sections, tables of
contents) → vertical. This is the central dilemma case (§5.1).
The overlay's promise: this *strictly dominates* a compromise chunk size when
the sweep frontier is non-flat — you no longer average two bad sizes.
A three-gate protocol. Do not skip Gate 0 — multi-scale chunking is only
justified once a single chunk size has been proven insufficient.
Run the canonical sweep from [[llamaindex]] OP-02 *before* reaching for any
multi-scale machinery:
from llama_index.core.evaluation import (
FaithfulnessEvaluator, RelevancyEvaluator,
)
# 1. ~20 eval QA pairs via DatasetGenerator.from_documents(docs)
# 2. sweep:
for cs in (128, 256, 512, 1024, 2048): # overlap = 0.1–0.2 × cs
idx = build_index(docs, chunk_size=cs, overlap=int(0.15 * cs))
record(cs, faithfulness=eval_f(idx), relevancy=eval_r(idx), p95=latency(idx))
and stop. LlamaIndex's own Uber 10-K study peaked at 1024 for prose;
code lands at 80–160 tokens (§5.2).
no single winner) → proceed to Gate 1. *Do not compromise on a middle size.*
| Document shape | Strategy | Geometry |
|---|---|---|
| Flat prose, no clear sectioning | Sentence-Window | horizontal |
| Clear hierarchy (headings, sections, ToC) | Auto-Merging (Hierarchical / parent-child) | vertical |
| Bursty multi-chunk relevance ("this whole section matters") | Auto-Merging | vertical |
| Point-fact needing surrounding paragraph | Sentence-Window | horizontal |
| Unknown structure / lowest setup cost | Start Sentence-Window | horizontal |
Set the embed-unit small (single sentence, or 128–256-token leaf) and the
return-unit large (the window, or the parent/root chunk).
SentenceWindowNodeParser must be paired withMetadataReplacementPostProcessor — otherwise the metadata-stuffed matched
sentence (not the window) reaches the LLM, defeating the entire point (§6 A2).
HierarchicalNodeParser builds leaf+parent nodes; store allleaves in the docstore and AutoMergingRetriever merges children → parent
when ≥ threshold siblings match.
top-k on faithfulness; if neither does, the bottleneck is elsewhere (revert).
index metadata, exactly as Gate 0's chunk size would have been pinned.
Each operation: Trigger / Action / Output / Evidence. These refine
[[llamaindex]] OP-02 and OP-05 into executable sub-steps.
chunk_size ∈ {128,256,512,1024,2048},overlap = 10–20%; build one VectorStoreIndex per config; record
faithfulness + relevancy + p95 latency.
that *authorizes* multi-scale chunking.
llamaindex.ai/blog/evaluating-the-ideal-chunk-size-for-a-rag-system-using-llamaindex-6207e5d3fec5; [[llamaindex]] OP-02.SentenceWindowNodeParser(window_size=3) to embed singlesentences with N-neighbor windows in metadata; at query time apply
MetadataReplacementPostProcessor(target_metadata_key="window") so the LLM
receives the window, not the lone sentence.
developers.llamaindex.ai SentenceWindow / MetadataReplacement docs; [[llamaindex]] R3 Dilemma 5.HierarchicalNodeParser.from_defaults(chunk_sizes=[2048,512,128])to build a leaf→parent→root tree; index leaf nodes in a
VectorStoreIndex, keep all nodes in a docstore; retrieve with
AutoMergingRetriever, which returns the parent once a configured fraction of
its children are in the hit set.
developers.llamaindex.ai/python/framework/integrations/retrievers/auto_merging_retriever/; [[llamaindex]] OP-05.structured/bursty → MSC-03; unknown → start MSC-02 (cheaper), escalate to MSC-03 if it underperforms on multi-chunk queries.
medium.com/@harsh_77214/beyond-naive-rag-comparing-basic-sentence-window-and-auto-merging-retrieval-....same object once the sweep is non-flat. Verify by inspecting *what text the
retriever actually sends to the synthesizer* (must be the large unit).
strip/shorten metadata before shrinking chunks (GitHub #12200, #13792).
github.com/run-llama/llama_index/issues/12200, #13792; [[llamaindex]] A7.over the best single chunk size; if none, revert to the pinned single size.
困境: Both patterns implement the same core flip. They are *not*
interchangeable — choosing wrong wastes setup cost and underperforms.
约束:
决策步骤:
结果: Both consistently beat naive top-k on faithfulness in published
comparisons. Auto-Merging is more *principled* for structured docs;
Sentence-Window is more *robust* for unstructured prose. The decision is driven
by document structure, not theoretical elegance.
(Source: [[llamaindex]] R3 Dilemma 5;
developers.llamaindex.ai/.../auto_merging_retriever/;
medium.com/@harsh_77214/beyond-naive-rag-comparing-basic-sentence-window-and-auto-merging-retrieval-...)
可提取的操作: MSC-02, MSC-03, MSC-04.
困境: At chunk_size=256 embeddings are precise but the LLM gets fragments;
at chunk_size=2048 context is rich but the embedding becomes a "topic
average" and recall on specific queries drops. Where to set chunk_size — and
what to do when no single value wins?
约束:
#12200, #13792).决策步骤:
chunk_size ∈ {128,256,512,1024,2048}, overlap 10–20%.VectorStoreIndex per config; record faithfulness + relevancy + latency.结果: LlamaIndex's own published evaluation on Uber's 10-K found
faithfulness peaked at chunk_size 1024 and relevancy maxed at 1024,
with only mild latency growth — so **1024 became the framework default for
prose** (code lands at 80–160 tokens). But on corpora where the curve does not
converge, the multi-scale decoupling pattern wins; never average two bad chunk
sizes into one mediocre one.
(Source: [[llamaindex]] R3 Dilemma 1;
llamaindex.ai/blog/evaluating-the-ideal-chunk-size-for-a-rag-system-using-llamaindex-6207e5d3fec5;
statsig.com/perspectives/llamaindex-rag-retrieval)
可提取的操作: MSC-01, MSC-05, MSC-06.
| # | Anti-pattern | Correct move |
|---|---|---|
| A1 | Bump chunk_size (e.g. → 4096) when answers feel incomplete | Decouple embed-scope from return-scope (MSC-02/03); do not enlarge the embed-unit |
| A2 | Use SentenceWindowNodeParser without MetadataReplacementPostProcessor | Always pair them — else the lone sentence, not the window, reaches the LLM |
| A3 | Index *parent* nodes in the vector store for auto-merging | Index leaf nodes; keep parents in the docstore for merge-on-retrieval |
| A4 | Pick a single compromise chunk size on a non-flat frontier | Refuse the compromise; switch to multi-scale (MSC-05) |
| A5 | Reach for multi-scale chunking on a short/static corpus | Prompt-stuff with caching; multi-scale is over-engineering (B1) |
| A6 | Ship multi-scale config without re-running the eval set | MSC-07: measure the lift or revert |
| A7 | Shrink the embed-unit while metadata still dominates the payload | MSC-06: budget metadata <50% before shrinking |
chunk paradox does not arise. (Mirrors [[llamaindex]] B1.)
it and stop; multi-scale adds complexity with no payoff.
model, the reranker, or the synthesizer (lost-in-the-middle), fix that first —
multi-scale chunking only resolves the *precision-vs-context* axis.
and window expansion add latency; a raw vector store may be the right tool.
SentenceSplitter(chunk_size=4096) introduced as a fix for "incomplete answers" → A1.SentenceWindowNodeParser present but no MetadataReplacementPostProcessor in the query engine → A2.AutoMergingRetriever over an index built from parent nodes (no leaf docstore) → A3.The "embed small, return large" pattern is framework-agnostic; the primitives differ.
| Concept | LlamaIndex | LangChain | Notes |
|---|---|---|---|
| Horizontal (sentence-window) | SentenceWindowNodeParser + MetadataReplacementPostProcessor | (no direct equivalent; emulate with custom retriever returning neighbor windows) | LlamaIndex's is the cleanest first-class implementation |
| Vertical (parent-child / auto-merging) | HierarchicalNodeParser + AutoMergingRetriever | ParentDocumentRetriever (child splitter + parent splitter + docstore) | Same idea: embed children, return parents |
| Small-embed unit store | VectorStoreIndex over leaf nodes | child vectorstore | both index the small unit |
| Large-return unit store | docstore (nodes with PARENT/CHILD relationships) | InMemoryStore / byte-store for parent docs | the return-unit lives outside the vector index |
Mapping rule: LlamaIndex AutoMergingRetriever/HierarchicalNodeParser ≈
LangChain ParentDocumentRetriever. LlamaIndex additionally offers the
horizontal SentenceWindowNodeParser, which LangChain has no first-class
analogue for. For a coder agent already inside the LlamaIndex stack, prefer the
native parsers; the [[llamaindex]] base skill governs the surrounding pipeline
(ingestion, eval loop, reranking, routing).
> This overlay does not replace [[llamaindex]] — it deepens the single
> DecoupleChunkScope knob into a full recipe. For everything around it
> (baseline, eval, hybrid, rerank, routing, production hardening), defer to the
> base skill.
references/R1-source-evidence.md — citations and provenance for every claim above.intermediate/operation_candidates.json — machine-readable MSC operation list.SKILL.md + references/R3-dilemma-cases.md Dilemmas 1 & 5).llamaindex.ai/blog/evaluating-the-ideal-chunk-size-for-a-rag-system-using-llamaindex-6207e5d3fec5 (Uber 10-K, 1024 optimum)developers.llamaindex.ai/python/framework/integrations/retrievers/auto_merging_retriever/developers.llamaindex.ai SentenceWindowNodeParser / MetadataReplacementPostProcessor / HierarchicalNodeParser docsdevelopers.llamaindex.ai/python/framework/optimizing/rag_failure_mode_checklist/ (failures #2, #6)medium.com/@harsh_77214/beyond-naive-rag-comparing-basic-sentence-window-and-auto-merging-retrieval-with-llamaindex-f778173bed98statsig.com/perspectives/llamaindex-rag-retrieval (code chunk size 80–160)github.com/run-llama/llama_index/issues/12200, #13792 (metadata-dominates-chunk)ParentDocumentRetriever docs (python.langchain.com/docs/how_to/parent_document_retriever/)Take agentsope/agentsop-multiscale-chunking from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.