ADR 0003 — sqlite-vec for vector search
Status: Accepted Date: 2026-02
Context
Agents benefit from semantic recall — surface a memory whose text doesn't share keywords with the query but shares meaning. The usual playbook: run a dedicated vector database.
Requirements:
- Zero extra infrastructure for single-machine deployments
- Same durability and transactional model as the rest of memory
- Embedding-dimension sanity checks at startup
- Hybrid retrieval (keyword ⊔ vector) without a separate query plane
Alternatives considered:
- Qdrant / Weaviate / Milvus — all excellent; all require an extra service, network hop, and ops surface
- pgvector — would force Postgres everywhere, abandoning SQLite for long-term memory
- Simple numpy file + linear scan — works for small datasets, falls over past ~10k memories per agent
Decision
Use sqlite-vec: a SQLite extension that adds a vec0 virtual
table in the same DB file as long-term memory.
- One SQLite file holds
memories,memories_fts, andvec_memories— a singleJOINreturns content + tags alongside similarity - Dimension is checked at schema init; mismatch between config and existing rows aborts startup with an explicit message
sqlite3_auto_extensionregisters once per process- Hybrid retrieval uses Reciprocal Rank Fusion (K=60) over the keyword FTS5 hits and the vector neighbors
Consequences
Positive
- Zero-infra single-machine deploys keep working — no extra service to run
- Backups, replication, export are all just "copy the
.dbfile" - Transactional writes:
INSERTintomemories+vec_memoriesin one statement; no dual-write races - Hybrid retrieval is easy (see vector docs)
Negative
- sqlite-vec is newer than Qdrant; its indexing algorithm improves over time. Large indexes may need re-sorting periodically
- Changing embedding models (even same-dimension ones) produces a stale index — the ADR doesn't solve this, users must reindex
- The
sqlite3_auto_extensionregistration happens once per process and has caught test suites that spawn many short-lived connections off-guard
Swap-out path
EmbeddingProvider is a trait and the recall_mode = vector branch
is a single code path. Replacing sqlite-vec with Qdrant is a
day's work, not a rewrite.