Vector Store Backends
FFAI’s RAG system uses a pluggable vector store architecture. All backends
implement the same VectorStoreBase interface,
so you can swap backends without changing your indexing, search, or query code.
Available backends
Backend |
Install |
Mode |
|---|---|---|
ChromaDB |
Included with |
Local files (persistent client) |
Qdrant |
|
Local, server, or cloud |
pgvector |
|
Server (Docker or managed PostgreSQL) |
SQLite-vss |
|
Local files (SQLite extension) |
Check which backends are available in your environment:
from ffai.rag.stores import list_stores, list_available_stores
print(list_stores())
# ['chroma', 'pgvector', 'qdrant', 'sqlite_vss']
print(list_available_stores())
# ['chroma', 'qdrant']
list_stores() returns all known backends. list_available_stores()
returns only those whose dependencies are installed.
Choosing a backend
ChromaDB — best for getting started. Zero configuration, persistent local storage, included with
ffai[rag].Qdrant — best for production. Supports local mode (no server), dedicated server, and Qdrant Cloud. High performance with filtering.
pgvector — best when you already have PostgreSQL. Uses the pgvector extension for vector similarity search. Requires a running Postgres instance.
SQLite-vss — best for embedded and edge deployments. Zero infrastructure, single-file database.
Using get_store()
The get_store() factory creates a store instance by
backend name. Each backend accepts different constructor arguments:
from ffai.rag.stores import get_store
# ChromaDB (default)
store = get_store("chroma", collection_name="my_kb", dir="./chroma_db")
# Qdrant local mode (no server needed)
store = get_store("qdrant", path="./qdrant_db", embedding_dim=1024)
# Qdrant server
store = get_store("qdrant", host="localhost", port=6333, embedding_dim=1024)
# Qdrant Cloud
store = get_store("qdrant", url="https://cluster.qdrant.io", api_key="...", embedding_dim=1024)
# pgvector (requires running PostgreSQL)
store = get_store("pgvector",
connection_string="postgresql://ffai:ffai@localhost:5432/ffai",
embedding_dim=1024)
# SQLite-vss
store = get_store("sqlite_vss", db_path="./vss.db", embedding_dim=1024)
All stores implement the same interface. After construction, the rest of your code stays the same:
from ffai.rag.embed import Embeddings
from ffai.rag import RAG
embed = Embeddings("mistral/mistral-embed", api_key="your-key")
rag = RAG(embed=embed, store=store, chunk_size=500, chunk_overlap=100)
rag.index("Python is a programming language...", source="python_intro")
hits = rag.search("programming")
Configuration via YAML
Set the backend in config/main.yaml under the rag: key:
rag:
enabled: true
store_backend: qdrant
store_config:
path: "./qdrant_db"
embedding_dim: 1024
embedding_model: "mistral/mistral-embed"
chunker: recursive
chunk_size: 1000
chunk_overlap: 200
store_config passes backend-specific constructor arguments directly to the
backend. Omit it (or pass {}) to use defaults.
Then use from_config():
from ffai.rag import RAG
rag = RAG.from_config() # reads store_backend + store_config from YAML
Backend-specific notes
ChromaDB
The default backend. No additional installation beyond ffai[rag].
store = get_store("chroma", collection_name="my_kb", dir="./chroma_db")
Constructor arguments:
collection_name(str) — Collection name. Default:"ffai_kb"dir(str) — Filesystem path for persistent storage. Default:"./chroma_db"
Qdrant
Supports four modes: local (file-based, no server), server (connect to a running Qdrant instance), cloud (Qdrant Cloud), and in-memory (ephemeral, data lost on exit).
Local mode uses asyncio.to_thread internally to avoid file-lock conflicts
between sync and async clients. Non-UUID string IDs are automatically converted
to deterministic UUID5 values. Payload indexes for source and
chunking_strategy are created automatically when a new collection is
initialized (required for filtering on Qdrant Cloud).
# Local mode (no server)
store = get_store("qdrant", path="./qdrant_db", embedding_dim=1024)
# In-memory mode (ephemeral)
store = get_store("qdrant", location=":memory:", embedding_dim=1024)
# Server mode
store = get_store("qdrant", host="localhost", port=6333, embedding_dim=1024)
# Cloud mode
store = get_store("qdrant", url="https://your-cluster.qdrant.io",
api_key="your-key", embedding_dim=1024)
Constructor arguments:
collection_name(str) — Default:"ffai_kb"embedding_dim(int) — Vector dimensionality. Default:1024path(str or None) — Local storage path. Set this for local mode.location(str or None) —":memory:"for in-memory mode.host(str) — Server host. Default:"localhost"port(int) — Server port. Default:6333url(str or None) — Cloud URL.api_key(str or None) — Cloud API key.
pgvector
Requires a running PostgreSQL instance with the pgvector extension. Use
docker-compose.dev.yaml to start a local instance:
docker compose -f docker-compose.dev.yaml up -d
store = get_store("pgvector",
connection_string="postgresql://ffai:ffai@localhost:5432/ffai",
embedding_dim=1024)
Constructor arguments:
connection_string(str) — PostgreSQL connection string.collection_name(str) — Table name prefix. Default:"ffai_kb"embedding_dim(int) — Default:1024
SQLite-vss
Zero-infrastructure option. Uses the SQLite vss0 extension for vector
similarity search. Good for embedded deployments and prototyping.
store = get_store("sqlite_vss", db_path="./vss.db", embedding_dim=1024)
Constructor arguments:
db_path(str) — Path to the SQLite database file. Default:"./vss.db"collection_name(str) — Table name. Default:"ffai_kb"embedding_dim(int) — Default:1024
VectorStoreBase interface
All backends implement these methods:
Method |
Description |
|---|---|
|
Backend identifier ( |
|
Add documents with embeddings (async) |
|
Search by vector similarity (async) |
|
Delete all chunks for a source |
|
Delete chunks matching source + chunking strategy |
|
|
|
Total number of stored chunks |
|
Delete all data and recreate |
|
Sorted list of indexed source names |
|
All stored documents as dicts |
|
Check if source needs re-indexing (checksum) |
Docker setup for pgvector and Qdrant
A docker-compose.dev.yaml is provided for local development:
# Start both pgvector and Qdrant
docker compose -f docker-compose.dev.yaml up -d
# Stop
docker compose -f docker-compose.dev.yaml down
Example notebooks
The examples/vector_stores/ directory contains runnable Jupyter notebooks
that demonstrate each backend and mode. All notebooks clean up after themselves
— no data left on disk or in the cloud after execution.
Notebook |
What it demonstrates |
Cleanup |
|---|---|---|
|
In-memory mode: add, search, filter, delete |
Data vanishes with kernel |
|
Local file mode: persistence, re-open verify |
|
|
Server mode: auto-starts Docker, search |
Clears collection, stops container |
|
Cloud mode: indexes to remote cluster, queries |
|
|
ChromaDB vs Qdrant side-by-side via factory |
Clears both stores + removes temp dir |
All notebooks use synthetic embeddings (random unit vectors via NumPy) so no
embedding API key is required. The qdrant_cloud notebook requires
QDRANT_CLUSTER_ENDPOINT and QDRANT_KEY in your .env file. The
qdrant_server notebook requires Docker.
See also
RAG Search Strategies — search strategies (vector, BM25, hybrid, rerankers)
Building a RAG Pipeline with FFAI — end-to-end RAG tutorial
Installation — installation and setup