User guide
Semantic Search — Operator Guide
Looking for what it does rather than how to use it? Read the Semantic Search overview .
This guide covers how it is indexed, how it is scoped, and how to operate it.
Concepts
| Concept | What it means |
|---|---|
| Cosine distance | The <=> operator. Lower is closer. Similarity is 1 - distance. |
| Relevance floor | The minimum similarity a result must clear. Higher is stricter. |
| Iterative scan | Keeps pulling index candidates until the limit is met under a filter, instead of stopping early. |
Where vectors live
These three values must agree — a mismatch causes insert failures or silently wrong vectors.
The migration-safety test guards these indexes against being dropped.
Tenant scoping
Scoping is part of the SQL, evaluated before the result limit:
- Knowledge: the visibility cascade is compiled into the
WHEREclause per caller. Platform-level articles additionally require no owning organization. - Tickets:
"organizationId" = $nis in the query, and the source ticket is resolved inside the caller’s scope first. - Catalogue: deliberately unscoped. It is a shared global corpus and returns ids only; visibility, vendor and pricing filters are applied by the calling service afterwards.
Relevance thresholds
| Use | Threshold |
|---|---|
| General knowledge search (default) | 0.70 |
| Duplicate detection | 0.75 |
| Similar tickets | 0.60 |
| Portal knowledge search | 0.60 |
| Related articles | 0.50 |
RAG_MIN_RELEVANCE_SCORE sets the service default.
Operating the pipeline
Coverage
The counts are scoped to the caller’s organization; platform staff see the whole platform. Only published knowledge documents are counted as eligible.
Background sweep
- a new ticket becomes searchable within roughly one tick;
- a busy platform accumulates a backlog that the sweep works through at 100 rows per source per 15 minutes;
- an edited ticket is not re-embedded, because it already has one.
Catalogue backfill
Run it after a large import. The storefront’s smart-search toggle is offered only while coverage exceeds STORE_SEMANTIC_MIN_COVERAGE (default 0.8), so letting coverage decay silently removes the feature.
Configuration
| Variable | Default | Notes |
|---|---|---|
RAG_MIN_RELEVANCE_SCORE | 0.7 | Default relevance floor |
RAG_MAX_RESULTS | 10 | Default result cap |
STORE_SEMANTIC_MIN_COVERAGE | 0.8 | Below this, the storefront hides smart search |
Troubleshooting
| Symptom | Cause |
|---|---|
| A specific corpus returns nothing | Coverage is zero for it. Check /status and run a sync. |
| Newly created tickets never match | The sweep has not reached them yet, or is backlogged. |
403 on sync-all | It requires platform admin; use per-org sync instead. |
Limits and known behaviour
- No chunking: a document is embedded whole. Very long articles are represented by one vector and long tails contribute less.
- The sweep does not re-embed changed rows, only unembedded ones.
- A single failed batch marks the whole batch failed and writes nothing; because the sweep re-selects the same unembedded head, one persistently bad row can stall that source type.
lastSyncAtis reported as null rather than inferred from unrelated activity.