Semantic Search
Semantic search retrieves chunks by meaning rather than by shared words. Each chunk is turned into a vector embedding; at query time RCE Code embeds your query and returns the chunks whose vectors are nearest by cosine similarity. This finds “rate limiting” code when you search for “throttle requests”, even with no words in common.
Unlike lexical and grep, semantic search has a prerequisite: chunks must be embedded first. Until then it returns an empty list.
Two steps: embed, then search
Embedding is decoupled from indexing so you can index quickly and embed when you are ready. Call engine.embed() to embed every chunk that lacks a current embedding; it returns the number of chunks embedded. Embeddings are content-addressed, so re-running it after a small change only embeds what actually changed.
How the query is answered
search_semantic(query, top_k=10) embeds the query string with the same embedder used at index time, then runs a K-nearest-neighbour search over the vector index and loads the matching chunks. The returned SearchResult list is ordered by descending cosine similarity, so .score is larger-is-better. Each result also carries .chunk and .source = SearchSource.SEMANTIC.
Returns [] when nothing is embedded
search_semantic short-circuits and returns [] — it does not fall back to keywords. Run engine.embed() (or rce-code embed) first. If you want a search that works with or without embeddings, use hybrid, which degrades gracefully.The default embedder is offline
An embedder is anything satisfying a small protocol: it exposes model_id, model_version, and dim, and an embed(texts) method returning a float array. By default RCE Code uses the HashingEmbedder, a deterministic, dependency-free stub that needs no network and no model download.
The hashing embedder is good enough to exercise and test the whole semantic and hybrid path locally, but it does not capture real semantic similarity the way a trained model does. For production-quality results, swap in a real embedder — an OpenAI-compatible endpoint, a sentence-transformers model, or your own — by passing it to CodeEngine.open_or_create(..., embedder=...).
Embedder identity matters
model_id, model_version, and dim key the vector index. Changing embedders means re-embedding so the query and stored vectors live in the same space. See Embedders for swapping models and Vector Backends for where the vectors are stored.CLI
Embed first, then run search-semantic. It prints one line per hit with the file, line span, and similarity score.