Semantic Search
Searching the knowledge base by meaning, not by keyword.
Semantic Search
Semantic search finds articles by what they mean, not by what words they contain. Ask a question in natural language and get relevant results even if they share no vocabulary with your query.
Keyword search vs semantic search
Keyword search — matches the exact words in your query against the words in the article.
Semantic search — matches the meaning of your query against the meaning of the article.
Example query: "how do attackers move laterally in Active Directory"
Keyword search finds articles containing those exact words. If your AD playbook says "kerberoasting" and "delegation abuse" but never "move laterally," keyword search misses it.
Semantic search finds the AD playbook, the Kerberoasting article, the delegation article — because they're about the same thing.
How it works
Semantic search uses embeddings — numeric vectors that represent the meaning of a piece of text.
The pipeline:
- Every KB article is converted to a 768-dimensional vector using a
local embedding model (
nomic-embed-text) - Vectors are stored in Postgres using the pgvector extension
- When you search, your query is converted to a vector the same way
- ATHELOS computes cosine similarity between your query vector and every article vector
- Articles are ranked by similarity, top matches returned
Everything runs locally. The embedding model runs in Ollama. The vector search runs in Postgres.
The embedding model
ATHELOS uses nomic-embed-text by default:
- 768 dimensions per vector
- 8,192 token context window
- Trained specifically for retrieval
- Fast enough to embed articles in hundreds of milliseconds
Pull it if you haven't:
ollama pull nomic-embed-textVerify:
ollama list | grep nomicThe pgvector extension
Pgvector is a Postgres extension that adds a vector column type and
similarity operators.
ATHELOS installs it automatically when you run migrations. To verify:
docker exec soc_postgres psql -U soc -d soc_solo -c "\\dx" | grep vectorShould show vector in the extensions list.
The embeddings table
Articles are embedded in kb_article_embeddings:
| Column | Purpose |
|---|---|
| article_id | Foreign key to kb_articles |
| model | Which embedding model was used |
| dimensions | Vector size (768 for nomic) |
| content_hash | SHA-256 of the source text |
| embedding | The actual vector |
The content hash is how ATHELOS knows when an article needs re-embedding. If the article's text changes, the hash changes, and the embedding is stale.
Automatic embedding on write
When you create or edit a KB article:
- ATHELOS composes the text to embed (title + summary + body)
- Computes its SHA-256 hash
- Compares to the stored hash
- If different, generates a new embedding and updates the row
- If identical, skips — no wasted work
This is idempotent — editing an article without changing the text doesn't re-embed it.
Reindexing
If you change the embedding model, or want to force a full re-embed:
Settings → AI → Embeddings → Reindex all
Or via CLI:
pnpm reindex:embeddingsThis walks every article, recomputes its embedding, and updates the database. Progress is shown in real time.
Reindexing is safe — it doesn't delete anything, just updates embeddings.
Using semantic search
Two ways to access it:
1. From the knowledge base
Open Knowledge. The search box has a mode toggle:
- Keyword — Meilisearch (fast, typo-tolerant)
- Semantic — pgvector (meaning-based)
Type your query in either mode. In semantic mode, use natural language — full sentences work best.
2. From the command palette
Press Ctrl+K. Type your query. Results include both keyword and semantic matches, ranked together.
Semantic matches are marked with a small icon so you know how they were found.
Query tips
Semantic search rewards natural language. Examples:
Good queries:
"how do I exploit unconstrained delegation""techniques for bypassing EDR""lateral movement using WMI""remediating SQL injection in Node.js"
Poor queries:
"delegation"— too vague"mimikatz"— a keyword; use keyword search"T1550.002"— an ID; use keyword search
The embedding model understands concepts, not codes. Use it for concepts.
Similarity threshold
By default, ATHELOS shows the top 10 matches. Results below a similarity threshold are filtered out to avoid noise.
Adjust the threshold in Settings → AI:
- High (0.75) — only very close matches
- Medium (0.60) — balanced (default)
- Low (0.45) — broader, more noise
Start with the default. Lower it if you're not finding things. Raise it if results feel irrelevant.
Performance
Semantic search on a typical KB (a few hundred articles) returns in under 100ms. The bottleneck is usually the query embedding computation, which takes 20–50ms on CPU.
For very large KBs (thousands of articles), pgvector's HNSW index keeps search fast. ATHELOS creates the index during migration.
When semantic search isn't available
Semantic search requires:
- Ollama running
- The embedding model pulled
- Embeddings generated for your articles
If any of these is missing, semantic search falls back to keyword search with a small notice.
Check Settings → AI to see the state.
Embedding coverage
The embeddings panel shows:
- Total articles in the KB
- Articles with valid embeddings
- Articles that need re-embedding (stale hash)
- Coverage percentage
If coverage is below 100%, click Reindex to catch up.
Storage cost
Each embedding is 768 floats — about 3 KB per article. For a few hundred articles, that's under 1 MB. Negligible.
pgvector stores vectors efficiently and the index adds overhead proportional to the number of vectors. Still tiny.
Where to go next
- RAG chat — asking questions and getting answers
- Assistive actions — drafting report sections
- Using the knowledge base — the KB itself