Careeroza
Parent Document and Contextual Retrieval
Small Chunks for Search, Big Chunks for the Answer
Small chunks match queries precisely, but they lack context. Parent-document retrieval searches small child chunks, then sends the whole parent section to the model.
Example: parent-document retrieval
import re
SECTIONS = {
"Returns > Electronics": "Electronics have a 7 day replacement only policy. They must be unused and in the "
"original box. Earbuds and phones need the IMEI or serial number on the invoice.",
"Returns > Fashion": "Clothes and shoes can be returned within 10 days. Tags must be attached.",
}
children = [] # (child_text, parent_id)
for parent, text in SECTIONS.items():
for sentence in re.split(r"(?<=\.)\s+", text):
children.append((sentence, parent))
def words(t): return set(re.findall(r"[a-z]+", t.lower()))
query = "serial number for earbuds"
best_child, parent = max(children, key=lambda c: len(words(query) & words(c[0])))
print("matched child :", best_child)
print("sent to model :", parent, "->", SECTIONS[parent])Output
matched child : Earbuds and phones need the IMEI or serial number on the invoice. sent to model : Returns > Electronics -> Electronics have a 7 day replacement only policy. They must be unused and in the original box. Earbuds and phones need the IMEI or serial number on the invoice.
Contextual Retrieval
A chunk like "They can be replaced within 7 days." is useless on its own: what is "they"? Contextual retrieval (described by Anthropic in 2024) asks a model to write one or two sentences of context for each chunk, and indexes context + chunk for both BM25 and embeddings.
Example: adding context to a chunk
import re
def words(t): return set(re.findall(r"[a-z]+", t.lower()))
chunk = "They can be replaced within 7 days if unused."
context = "From the ShopEase returns policy, section on electronics replacement for earbuds and phones." # model-written
query = "earbuds replacement days"
print("plain chunk overlap :", sorted(words(query) & words(chunk)))
print("contextual chunk overlap :", sorted(words(query) & words(context + " " + chunk)))Output
plain chunk overlap : ['days'] contextual chunk overlap : ['days', 'earbuds', 'replacement']
Generating the Context Cheaply
def chunk_context(full_doc: str, chunk: str) -> str:
resp = client.messages.create(
model="claude-haiku-5-5",
max_tokens=150,
system=[{"type": "text", "text": f"<document>\n{full_doc}\n</document>",
"cache_control": {"type": "ephemeral"}}], # same document for every chunk -> cached
messages=[{"role": "user", "content":
f"<chunk>{chunk}</chunk>\nGive a short context that situates this chunk in the document. "
"Answer only with the context."}],
)
return next(b.text for b in resp.content if b.type == "text")Tip: Because the whole document is the same for every chunk, prompt caching makes this step much cheaper: only the first call pays full price for the document.