Skip to content
diegokoes edited this page Jul 5, 2026 · 1 revision

Hybrid and semantic search

search_knowledge and get_context are hybrid: keyword (Postgres FTS plus pg_trgm trigram) blended with semantic similarity over a local embedding, so paraphrases surface even with no shared keywords, while exact identifiers (error codes, filenames) still hit precisely.

The three signals

  1. Full-text (FTS). search_tsv is a generated tsvector over the entry's text fields (issue_summary, root_cause, resolution, resolution_pattern, product_area, and the joined symptoms / signals / tags). Indexed with GIN. Good for word-level matches.
  2. Trigram. search_text is the same fields concatenated, indexed with gin_trgm_ops. Catches partial and fuzzy matches on raw identifiers, so a search for 023 matches 023 TOO_MANY_STRINGS. This is why signals are a promoted column.
  3. Vector. embedding vector(384), indexed with HNSW (vector_cosine_ops). Semantic similarity, so a differently-worded description of the same fault still ranks.

The three are blended into a single ranked result set; filters (product_slug, team_slug, component, tags, cloud, and so on) narrow it. Results can include deprecated entries, flagged as outdated for the agent to handle.

Local embeddings

Embeddings come from a local model (all-MiniLM-L6-v2, 384-dim, via fastembed). Nothing leaves the machine. The model (~90MB) downloads on first use into .fastembed-cache/ (baked into the Docker image; override the path with FASTEMBED_CACHE).

Entries are embedded on save. Backfill null embeddings (entries created before the model was available, or restored from an older dump) with:

npm run sync embed-backfill

Reference docs are chunked (packages/core/src/search/chunk.ts) and each chunk is embedded, so search_reference and get_context return the best-matching snippet per doc rather than the whole document.

Why local

Two reasons. First, privacy: the whole point of the customer-blind index and the optional redaction layer is that ticket content stays on your infrastructure; shipping it to a hosted embedding API would undercut that. Second, cost and determinism: no per-query API bill, no network dependency in the search path.

Clone this wiki locally