Explainers · updated July 2026
You typed an exact symbol. Semantic search returned a near-miss.
Ask an embedding index for a function by name and it does exactly what it was built to do: return the nearest points in meaning-space. For a fuzzy question (“where do we handle refunds?”), that is the feature. For an exact one (“find every call to getUserById”), the nearest point is often the wrong one, and the real ones scatter below the cut. Here is the mechanism, in one picture, and the case for keeping a literal lane next to the semantic one, the way Cursor and grep have always split the work.
One exact symbol, two answers
The nearest neighbour is not the exact match.
Embeddings place code by meaning, so ideas that read alike land near each other. That is the whole trick, and it is exactly why an exact symbol gets blurred. getUserById, getUserByEmail, and fetchUserById mean almost the same thing, so they collapse to almost the same point. A search returns that neighbourhood ranked by similarity: the scores barely separate the one you meant from its siblings, and a genuine call site can rank below the top-k cut and never come back. The exact lane, on the right, asks a different question and gives a different shape of answer.
Left is what a vector index does (Cursor builds one for the repo you open). Right is an exact literal lookup (grep’s guarantee, at index speed). They answer different questions, which is why the near-miss is not a bug in the embeddings, it is a mismatch between the tool and the task.
Why the miss happens
Three reasons a symbol slips through.
This is not a takedown of embeddings: for the questions they are built for, they are the right tool, and Cursor’s index is good at them. The point is narrower: when the token is known, ranking-by-similarity has three structural ways of handing back the wrong thing.
Top-k returns the k closest chunks: a ranked list, not a complete set. Ask “every call site” and you get the most similar-looking ones. The rest fall below the cut with no flag, so you can't tell a clean sweep from a partial one.
getUserById and getUserByEmail differ by a few characters and mean nearly the same thing, so their vectors nearly coincide. The score that should single out the exact symbol barely separates it from its siblings: the near-miss ranks first.
Vectors are a snapshot. Rename or move a symbol and the index keeps pointing at the old shape until it's re-embedded. The function you touched thirty seconds ago isn't findable by its new name yet.
None of these is a defect: they are what “search by meaning” costs. The trouble is only that a very common developer question needs the opposite guarantee: not the closest match, but this exact one, and all of them.
When a near-miss is a bug, not a ranking
“Close enough” is a wrong answer here.
Miss two of fourteen and the build breaks. Or worse, it doesn't, and the two stragglers ship.
You need every use, not the ten that ranked highest. The eleventh is the one that pages you at 3am.
A user pastes a log line. You want the one place that literal string is emitted, not code that's “about” errors.
Security questions are set questions. “Probably all of them” is not an answer you can sign off on.
Each of these is a set question (return all of X, exactly) and a ranked list can’t promise a set. This is the job the exact tools were made for: grep, find, ripgrep. Every literal match, deterministically, no model in the loop. The only thing they get wrong is speed.
The exact lane · Ryzen 9 9950X3D · 4.47M files
Right shape, wrong speed. Fix the speed.
grep and ripgrep give you the exact-match guarantee, and they get it by walking the disk on every query, reading files from the top each time. That is documented behaviour, and on a real machine it is slow: bound to the folder you started in, and blind to the files git ignores. A purpose-built indexed engine keeps the same literal guarantee and answers from a live index instead. Measured on the machine below:
Methodology: Ryzen 9 9950X3D · 64 GB DDR5 · NVMe · Windows 11 · 4,470,000 files. Content queries run 7 to 9 ms typical (rare symbols ~7 ms, common words ~9 ms); the proof line is one query, 93.8 s on ripgrep versus 16 ms on the index. Filename figures are medians (139 μs across 20 queries, 85 μs single-file). The same lookup is 412,000× a VS Code file search and ~480,000× Windows Search. At rest the engine sits near 44 MB in Task Manager, and the on-disk index stays under 1% of the drive (~24 GB on a 4 TB machine).
The thesis
Keep Cursor’s semantic lane. Run an exact lane under it.
The near-miss isn’t a reason to drop embeddings: it’s a reason to stop asking them the exact question. Point the vectors at meaning, where fuzzy recall is the feature. Point a live literal index at the exact symbol, string, or error message: whole-machine, across every repo and the files git ignores, fresh to the last save. No model in the loop, so every hit is a literal match you can verify and there is nothing to re-embed and go stale. Interlinked is that exact lane. It replaces neither your model nor Cursor’s index; it is the deterministic floor they stand on.
SEMANTIC FOR “WHAT’S NEAR?” · EXACT FOR “WHERE IS IT, ALL OF IT?”
Give every agent the exact lane
Stop asking the fuzzy tool the exact question.
One signed installer indexes every file on your machine (names and contents) and wires up the AI clients you already use, with no JSON to edit, no cloud account, and no keys. Keep Cursor’s index for the questions with no single literal answer; every agent also gets a fast, literal, whole-machine lookup for the ones that need every exact match. It stays fresh on its own: a save is searchable in about a millisecond, with nothing to re-embed.
Everything local is free forever, on all your devices, no card. Hosting starts at $5.99/mo. Download for Windows.
Related: What embeddings do for code · How Cursor indexes your codebase