When does query expansion help context assembly, and when does it just inflate noise?
Same topic, related formats. Practice these next.
Same topic, related formats. Practice these next.
Expansion paraphrases the query into a small fan and unions the results to lift recall on short under-specified queries; the cost is broader noise unless reranking and trimming clean up after the union.
Imagine you ask a librarian for help with one search term and they only check that one term. If the book uses different words, they miss it. Query expansion is asking the librarian to also try two or three closely related phrases just in case. You will find more relevant books, but you will also find some only loosely related ones. The fix is to look at the whole pile and let a better filter pick the actual best ones. Expansion alone is half a solution. Expansion plus a strong second-pass filter is the full move.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example.
Everything important, quickly.
4 minutes: define query expansion, state the recall vs precision tradeoff, walk through the union rerank trim pipeline, mention HyDE as a sibling variant, name two workloads where expansion is the wrong default.
def expand_query(original: str, n: int = 3) -> list[str]:
prompt = (
f"Generate {n} different ways to phrase this query, each a single line, "
f"covering different vocabulary the corpus might use. Original: {original}"
)
paraphrases = llm.complete(prompt).strip().split("\n")
return [original] + paraphrases[:n]
def retrieve_with_expansion(user_query: str, k1: int = 20) -> list[Chunk]:
queries = expand_query(user_query, n=3)
# union and dedupe across all retrievals
seen: dict[str, Chunk] = {}
for q in queries:
for c in vector_db.search(q, top_k=k1):
seen.setdefault(c.id, c)
# rerank against the ORIGINAL query, not the paraphrases
candidates = list(seen.values())
return reranker.rerank(user_query, candidates, top_n=5)Real products, models, and research that use this idea.
What an interviewer would ask next. Try answering before peeking at the approach.
Red flags and common mistakes that signal junior thinking. Click to expand.
Running expansion without a reranker after the union. The expanded result set is broader and the original problem (the right chunk is buried) is now the new problem (too many loosely relevant chunks).
The night-before-the-interview bullets. Scan these on the way to the call.
Primary sources. Skim if you want the original framing.