reference

Vector Store and Embeddings

Managed local Qdrant, embeddings providers, and the sqlite-vec fallback.

Updated

What ships with Docmancer

A base install gives you SQLite FTS5 (lexical) and the MCP runtime. To enable dense and sparse vectors, install the [vector] extra:

pip install 'docmancer[vector]'

That pulls in the Qdrant client and the sqlite-vec fallback. The Qdrant binary itself is downloaded automatically on first ingest from the pinned v1.14.1 GitHub release.

Managed Qdrant lifecycle

docmancer qdrant owns the local Qdrant process:

docmancer qdrant up
docmancer qdrant down
docmancer qdrant status
docmancer qdrant upgrade
docmancer qdrant logs
CommandWhat it does
upStarts the pinned Qdrant binary. Downloads on first run. Port selection guarded by filelock.
downStops the running Docmancer-owned Qdrant cleanly.
statusReports running state, port, storage path, and version.
upgradePulls the binary for the currently pinned release version.
logsTails the Qdrant log file at ~/.docmancer/qdrant/logs/.

Telemetry is disabled. PID and runtime files live under ~/.docmancer/qdrant/.

Foreign collection protection

QdrantStore.ensure_collection refuses to claim a pre-existing Qdrant collection that lacks the Docmancer ownership sentinel. If you point vector_store.url at a shared cluster or a collection that already exists, you'll get a clear PermissionError instead of silently writing into it.

delete_collection will only operate on collections that carry the sentinel. This makes accidental deletion of foreign data impossible.

Configuring the vector store

Add a vector_store: block to your YAML to opt into hybrid retrieval. The dispatcher auto-flips retrieval.default_mode to hybrid when this block is present and non-empty.

vector_store:
  provider: qdrant
  collection: docmancer
  managed: true

Override URL or use an existing Qdrant cluster (the ownership sentinel still applies):

vector_store:
  provider: qdrant
  url: http://localhost:6333
  collection: docmancer
  managed: false

Or set the env var:

export DOCMANCER_QDRANT_URL=http://localhost:6333

SqliteVecStore fallback

When your platform has no matching Qdrant binary, Docmancer transparently falls back to sqlite-vec — a vector extension that lives inside the same SQLite database as FTS5. You can also force this backend:

vector_store:
  provider: sqlite_vec

Tradeoffs: dense queries work, sparse (SPLADE) is unavailable, performance is slower than Qdrant for large corpora. Good enough for small-to-medium projects.

Embeddings providers

ProviderExtraModelsNotes
FastEmbed (default)noneBAAI/bge-base-en-v1.5 dense + prithivida/Splade_PP_en_v1 sparseLocal, no API key, ~500 MB model download on first run
OpenAI[embeddings-openai]text-embedding-3-small, text-embedding-3-largeRequires OPENAI_API_KEY
Voyage[embeddings-voyage]voyage-3, voyage-3-liteRequires VOYAGE_API_KEY
Cohere[embeddings-cohere]embed-english-v3.0Requires COHERE_API_KEY

Cloud providers retry on 429 / 5xx with bounded exponential backoff. If a configured cloud provider has no API key in env, ingest falls back to FTS5-only with a warning rather than aborting.

Configure in YAML:

embeddings:
  provider: openai
  dense_model: text-embedding-3-large

Content-hash cache

Every section is hashed by content. The cache at ~/.docmancer/embeddings-cache/ records (hash, model, vector). On re-ingest, sections whose hash matches the cache skip the embedding call entirely.

This means:

  • Re-ingesting an unchanged source is fast.
  • Swapping embeddings providers re-embeds everything (different model = different cache key).
  • ingest --recreate blows away SQLite and re-runs embeddings from scratch — but cache hits still apply if content hashes match.

Stale vector cleanup

sync_vector_store reconciles Qdrant against SQLite on every ingest:

  • New sections are embedded and upserted.
  • Sections whose content hash matches embedding_upserts bookkeeping skip the embedding pass.
  • Points whose chunk ids have vanished from SQLite are pruned (reported as pruned=N in the agent log).

This means ingest --recreate cannot leave stale dense hits behind, and removing a source via docmancer remove actually removes its vector points too.

Inspecting drift

docmancer inspect

Section counts are grouped by format and warnings flag any divergence between SQLite section counts and Qdrant point counts. Re-run ingest to reconcile.

Disabling vectors

To keep everything lexical-only:

docmancer ingest ./docs --no-vectors

Or globally:

export DOCMANCER_AUTO_VECTORS=0

A bare YAML without vector_store: also keeps the index lexical-only. The managed Qdrant lifecycle code only runs when explicitly opted in.