Most RAG tutorials assume you'll pair your documents with a dedicated vector database — Pinecone, Weaviate, Qdrant, or similar. But if the goal is a genuinely private, fully local pipeline, running a whole separate database service is often more infrastructure than the job needs. sqlite-vec is a real, actively-maintained SQLite extension (successor to the now-deprecated sqlite-vss) that adds vector similarity search as ordinary SQL — meaning your document chunks, their embeddings, and your metadata can all live in one .sqlite file.
How vector search works inside SQLite
1. Load the extension and create a vec0 virtual table. This defines a table whose column holds fixed-length float vectors — you specify the dimension to match your embedding model's output size.
2. Insert chunk embeddings as rows. Each row's rowid can map back to a normal SQLite table holding the chunk's actual text and metadata — the vector table and your regular schema live side by side in the same file.
3. Query with a MATCH clause, ordered by distance. A nearest-neighbor search is just a SQL query: match the table against a query vector, order by the built-in distance column, and limit to your top-K.
# Local RAG store using sqlite-vec -- one file, no external service
import sqlite3
import sqlite_vec
db = sqlite3.connect("local_rag.sqlite")
db.enable_load_extension(True)
sqlite_vec.load(db)
# 1. Create a virtual table sized to your embedding model's output
db.execute("""
create virtual table if not exists chunk_vectors using vec0(
embedding float[384]
)
""")
# 2. Insert a chunk's embedding (rowid links back to your chunks table)
db.execute(
"insert into chunk_vectors(rowid, embedding) values (?, ?)",
[chunk_id, embedding_bytes]
)
# 3. Nearest-neighbor search is a plain SQL query
results = db.execute("""
select rowid, distance from chunk_vectors
where embedding match ?
order by distance limit 5
""", [query_embedding_bytes]).fetchall()
| Approach | Infrastructure required | Data stays local? |
|---|---|---|
| Managed vector database (Pinecone, Weaviate, etc.) | External service, account, network calls | No — embeddings and metadata leave the machine |
SQLite + sqlite-vec |
None — a single file on disk | Yes — everything stays in the local .sqlite file |
This isn't a claim that SQLite will out-scale a purpose-built vector database on a billion-row index — it won't, and that's not the point. For a personal knowledge base, an internal document set, or any project where "private and local" is the actual requirement, keeping the whole retrieval layer inside one SQLite file you fully control is a genuine, verifiable option, not a toy.
Practical Challenge
Install sqlite-vec (pip install sqlite-vec), create a vec0 table sized for a small embedding model's output, insert a handful of embedded sentences, and run a MATCH query to confirm the nearest-neighbor ordering makes sense.
Concept Check
Sources & Further Reading
- sqlite-vec (GitHub) — the open-source SQLite extension used in this article's example, including the vec0 virtual table syntax and language bindings.
AI