Build
Vectors and search
pgvector columns and HNSW indexes, Postgres full-text search, and ranked text search over a table.
SoonThese pages describe Chronos Cloud, which opens from the waitlist. Running Chronos yourself? Self-host docs
Vectors (pgvector)
Bring your own embeddings. Chronos speaks pgvector: vector(n) columns, the distance operators, and HNSW indexes, so code written for pgvector (LangChain, LlamaIndex, Mem0, your own) runs as it is. CREATE EXTENSION vector is accepted; it’s built in.
create table docs (id bigint primary key, tenant int, body text, embedding vector(1536));create index on docs using hnsw (embedding vector_cosine_ops);select id, body, embedding <=> $1 as distancefrom docs where tenant = $2order by embedding <=> $1limit 5;| Operator | Distance | Index opclass |
|---|---|---|
<=> | cosine | vector_cosine_ops |
<-> | Euclidean (L2) | vector_l2_ops |
<#> | negative inner product | vector_ip_ops |
Every vector in a column has the column’s dimensions; a vector of another length is 22000. Normalize embeddings if you want cosine and inner product to agree.
Indexes and recall
- Small tables, or narrow filters, are compared exactly: every candidate, full precision.
- Larger ones scan 1-bit codes of every vector on the free cores, then rescore the best at full precision: on 76,000 real 1,536-dimension embeddings that finds 99.98% of the true top 10 in about 0.6 ms.
- Past that, the HNSW graph is used once it has tested itself (99% or better of the scan’s top 10 on its own vectors); a weaker graph waits until the scan would cost more. Recall comes first.
SET hnsw.ef_search = 200widens the graph’s beam for more recall at some speed.- Graphs build in the background the first time they’re needed; no query waits for one.
With filters
Filter in SQL as usual: where tenant = $2 is applied first, so a filter keeping a few thousand rows is searched exactly. Index the columns you filter on (create index on docs (tenant)).
Full-text search
Postgres’s full-text search works as in Postgres: its parser, stop words and Snowball stemmer for english and simple, checked word for word against Postgres.
create index on docs using gin (to_tsvector('english', body));select id, ts_rank(to_tsvector('english', body), q) as rankfrom docs, websearch_to_tsquery('english', 'refund "late delivery" -test') qwhere to_tsvector('english', body) @@ qorder by rank desc limit 10;to_tsvector, to_tsquery, plainto_tsquery, phraseto_tsquery, websearch_to_tsquery, @@, ts_rank, setweight, and tsvector columns work. The index’s expression must match the query’s, config included. Not yet: ts_headline, ts_rank_cd.
Ranked search: search()
Chronos also keeps its own text index per table: BM25 over every text field, with typo tolerance (a word one edit away matches at half weight). Use it as a table in FROM:
select d.title, s.scorefrom search('docs', 'billing eror', 10) s -- the typo still finds "error"join docs d on d.id::text = s.idorder by s.score desc;It returns id, score and row (jsonb), best first, on the session’s world. Words under 4 letters and over 32 aren’t corrected, and exact spellings always rank first.
Fuzzy matching
pg_trgm, fuzzystrmatch and unaccent are built in: similarity, %, word_similarity, the trigram distances, levenshtein, soundex, metaphone, unaccent. A gin or gist index with gin_trgm_ops serves %, LIKE and ILIKE.
create index on customers using gin (name gin_trgm_ops);select name from customers where name % 'jon smyth' order by similarity(name, 'jon smyth') desc limit 5;Search in worlds
A world sees its own changes in search the moment they’re written. Indexes are shared between worlds, so a fork searches about as fast as main: an agent can re-embed a table in a world and compare results before merging.
From an agent
The find tool
MCP’s find takes where (exact filters), text (BM25), and vector with vector_field (dot product), and fuses text and vector rankings when both are given. Most agents use the sql tool with the pgvector queries above instead.