How openclawOS uses sqlite-vec for agent memory
An honest look at how openclawOS stores Pi's memory: sessions in normal SQLite tables, vector recall via sqlite-vec, no Pinecone or Weaviate required.
- memory
- sqlite-vec
- vectors
- deep dive
Long-term memory is what separates “a chatbot” from “an agent that remembers you”. openclawOS stores it in two places: structured facts in SQLite tables, and semantic chunks in a vector index. Both live in the same SQLite database, thanks to sqlite-vec.
This is how it works and the trade-offs we picked.
Why SQLite
openclawOS’s choice of “everything in one SQLite file” is intentional. It means:
- Zero ops. Backup is
cp openclaw.db openclaw.db.bak. - Atomic transactions across structured + vector data.
- No external service to monitor, restart, version-pin.
- Runs everywhere. Pi 4? Yes. Mac mini? Yes. $5 VPS? Yes.
For personal/small-team scale (low millions of vectors max), this is plenty. If you ever outgrow it, the storage layer is abstracted — swap for Postgres + pgvector with one config change.
What gets stored
Structured facts
A simple key-value table per identity:
CREATE TABLE identity_facts (
identity_id TEXT,
key TEXT,
value JSON,
source TEXT, -- which session emitted it
created_at INTEGER,
PRIMARY KEY (identity_id, key)
);
Pi writes via a memory.set_fact tool. Common entries: “user’s spouse name”, “user’s city”, “user’s vegetarian preference”, “user’s GitHub handle”.
The model decides what’s worth remembering — it’s not implicit on every message.
Semantic chunks
The harder, more powerful store. Pi writes via a memory.note tool:
memory.note({
identity: "dipankar",
text: "User prefers Postgres over MySQL for new projects. Reason: better JSON support, less foot-gun-y."
})
That call:
- Embeds the text using the configured embedding model (default: OpenAI text-embedding-3-small, cheap).
- Stores the chunk + embedding + metadata in the
memory_chunkstable via sqlite-vec. - Returns a chunk ID for later reference.
At recall time:
- Pi calls
memory.recall(query: string, k: int). - openclawOS embeds the query, does a vector ANN search in sqlite-vec.
- Top-k chunks come back, with their relevance scores and metadata.
The sqlite-vec setup
sqlite-vec is a SQLite extension that adds vector columns and ANN queries. It’s loaded once at Gateway start:
db.loadExtension("sqlite-vec");
db.exec(`
CREATE VIRTUAL TABLE memory_chunks USING vec0(
embedding float[1536],
identity TEXT,
text TEXT,
created_at INTEGER
);
`);
Queries are SQL — sqlite-vec adds a MATCH operator:
db.prepare(`
SELECT text, distance
FROM memory_chunks
WHERE embedding MATCH ?
AND identity = ?
ORDER BY distance ASC
LIMIT ?
`).all(queryEmbedding, identityId, k);
It’s fast (sub-10ms for 100k chunks on a laptop). It’s atomic with the rest of your data. It’s one file.
Identity scoping
Memory is namespaced by identity. A query from identity A cannot recall chunks written by identity B unless an explicit cross-identity rule is configured (e.g., a household “shared memory” identity).
This prevents the worst-case privacy failure: your bot accidentally recalling someone else’s preferences in your conversation.
Compaction interaction
Long sessions get compacted (older turns summarised to save tokens). Compacted summaries can optionally be written to long-term memory as chunks, so the agent retains gist beyond the session window.
This is opt-in per binding. Default is on for personal use, off for high-traffic public bots (where the noise-to-signal ratio of compacted summaries is poor).
Embedding model
The default is OpenAI’s text-embedding-3-small. It’s cheap (~$0.02 per million tokens) and good enough for personal-scale recall.
For full-air-gapped setups, openclawOS supports local embedding via an Ollama endpoint or sentence-transformers. Quality is slightly lower; latency is much lower.
Backup and migration
The whole database is one file. To backup: copy it. To migrate to a new machine: rsync the file. To start fresh: delete it.
For automated backups, run litestream against the SQLite file — point-in-time recovery, continuous backup to S3 or a remote SQLite. We recommend it for any non-toy deployment.
When you outgrow this
If you ever need to scale past a few million vectors or want shared multi-region memory, swap the storage layer for Postgres + pgvector. The interface is the same; the config changes one line:
memory:
driver: postgres
url: postgres://...
Most people never need this.
The point
Vector search is often presented as something that requires a whole separate service. For agent memory at personal/team scale, it doesn’t. A single SQLite file with sqlite-vec gives you everything you need, with zero ops and atomic transactions across all your data.
It’s one of those choices that looked unfashionable two years ago and now looks obvious.
Frequently asked
Because for personal and small-team use, sqlite-vec is enough. It's a single file, zero ops, and runs in the same process. We avoid an external service unless we need to.
Related reading
How Pi's tool use works (and how to add your own tools)
Pi, the agent bundled with openclawOS, has a tool loop with browser, shell, file system, vector memory, and more. Here's how it works and how to extend it.
How multi-channel AI agents actually work under the hood
A deep dive into the openclawOS kernel: routing, sessions, identity, memory and the tricky parts of making one agent feel coherent across WhatsApp, Telegram, Discord and the rest.