Open by saying plainly that this section is adapted from Amélie Chatelain's field guide, and that the original goes much deeper. Then get to the puzzle fast.
Orientation
Resetting the baseline
Single-vector dense search uses one fixed-size embedding for the query,
and one for each indexed unit.
queryq
q1q2q3q4
Encoder
pool
documentd
d1d2d3d4d5d6
Encoder
pool
one similaritypool(q) · pool(d)
Encoded independently
Query and document never meet during encoding — so the whole document side is prepared
offline, and query time is one similarity search.
But it makes one strong bet
That pool step preserved every clue a future query might care about — decided
before anyone knew what would be asked.
Late interaction · a primer
Keep this tight. It is a shared-vocabulary slide, not an argument. The argument starts next.
The puzzle
Why does Claude Code use grep?
If we have strong language models and strong embedding models, why does a
frontier coding agent still reach for lexical search?
$ grep -rn "refresh_token" auth/
"Grep is all you need" came back into fashion
for a reason. It is worth understanding which reason.
Late interaction · a primer
Ask the room to guess before advancing. Most people answer "infra" — which is half right, and the other half is the interesting half.
The puzzle
Grep is hard to beat — and not only on infra
The boring infra reasons
Zero infrastructure — works on any machine
No model to deploy
No index to maintain
Privacy by default
But also: why it sticks
Lexical matching is extremely strong for code.
Granularity is non-negotiable — exact symbols, paths, functions and
error strings all matter.
Code is highly formatted data, which grep can exploit.
So the bar for a learned retriever is high
It has to earn its keep by adding semantics without losing exact local clues —
and justify deploying a model and maintaining an index on top.
Late interaction · a primer
The right-hand column is the part people underrate. Spend your time there.
The bottleneck
One vector has to carry every clue
A single query can hold several independent relevance constraints.
"What's the backoff for OAuth
token refreshes after a 429?"
RetryPolicyHTTP 429refresh_token()auth/session.py
↓ compressed into
[ single vector · dim d ]
→ diluted signal
The pooled vector must decide what to preserve before it knows which detail
the query will care about.
Late interaction · a primer
This is not "a semantic question about authentication" — it is five constraints that all have to survive. This is the bottleneck to keep referring back to.
Capacity isn't the fix
Just increase the dimension?
The obvious instinct — and a clean result showing why it doesn't resolve the structural
problem.
LIMIT builds a toy corpus from random attributes and names; documents
hold combinations of them, and queries ask for those combinations.
It looks artificial — and it isn't. Product search has exactly this
shape.
The question it poses: can one point encode every useful combination of
evidence?
gaming monitor27"140 Hz
Three constraints that must all survive the encoding.
Read the heatmap row by row: each row is a query token, each column a document token. For each row MaxSim keeps only the best cell, then sums. Under mean pooling, a rare high-signal term like OAuth contributes far less than it should.
Where this lands
The useful overlap
Multi-vector search sits where the two things we wanted actually meet.
Lexical search
Exact granularity
Local clues survive intact. Semantics do not.
Dense search
Soft semantic matching
Meaning survives pooling. Local clues may not.
Multi-vector search
Both, at a price
Late interaction tries to keep exact local evidence and soft
semantic matching — for more storage and more scoring work.
Where the original lecture goes next
Code retrieval makes granularity impossible to ignore ·
multimodal document retrieval makes compression impossible to ignore · then the
production objections: storage, latency, updates and the model ecosystem.