Paper · Study · 14 min · September 2026
Vectors without a vector database
Memory needs similarity search. It does not need a second database. The vector lives on the same row as the text. Pinecone is a product. The index is a column.
A vector database is a product category. Pinecone, Weaviate, Milvus, Chroma, Qdrant: stores built to hold embeddings and to answer nearest-neighbour queries fast. The pitch is clean. Your chatbot has documents. You chunk them, you embed them, you retrieve by meaning. The language model then writes from those chunks. That is retrieval augmented generation, and it is a real architecture. It is not how memory works here.
Glorics writes memories on purpose: observations, decisions, fragments of a brief. The source of truth is text a person can still open. Similarity search is an index over that text. The index is a column. It is not a second system of record.
768d
all-mpnet-base-v2, local, L2-normalised
One row
Text, lexical index, embedding, hash
Zero extra store
sqlite-vec on the monitor, pgvector on the engine
01What the vector is for
An embedding model is not a language model. A language model writes tokens. An embedding model writes a list of numbers. Similar passages produce similar lists. That is the whole trick. You do not need the linear algebra to use it. You do need to keep the two jobs apart.
Language model
Reads context. Writes text. Does not search your archive.
Embedding model
Reads a passage. Writes a vector. Does not explain anything.
The store
Keeps the vectors, and answers which ones sit close to a query.
On Glorics the encoder is sentence-transformers all-mpnet-base-v2: 768 dimensions, mean pooling, L2-normalised, English, 384-token window. It runs on localhost:4001. The default path does not leave the machine. If the process is down we do not fake meaning with a hash of the string. Cosine is switched off. Ranking becomes lexical only.
Memory also holds identifiers: a client name, an error code, a score, a date, a dotted token. Dense search is weak on those. Lexical search is built for them. A store that only does nearest neighbour is a store with a blind spot. We run both indexes on the same row. Fusion is the other article.
02One row, two indexes
The memory is a row. The text is on it. The lexical projection is on it. The embedding is on it. A SHA-256 of the content is on it, so unchanged text is never re-embedded. Dedup is (content_hash, project_id). There is no export step into another product. There is no sync job that can drift.
memory
│
├── content the text you can still open
├── tsvector / FTS5 lexical index, words
├── embedding(768) dense index, meaning
└── content_hash SHA-256, skip if unchanged
query
│
├── lexical retriever → list A
└── dense retriever → list B
│
└── fusion (the other study)
│
└── ranked memoriesChunking, here, is not a PDF pipeline. We do not ingest a wiki and hope the splitter was kind. An observation is written as an observation. If it is long, the encoder sees the first 384 tokens. The archive still holds the whole note. The vector is a window, not a licence to throw the rest away.
| Column | Job | Fails if |
|---|---|---|
| content | Source of truth | You cannot open it as text |
| tsvector / FTS5 | Find the words | You ask for a synonym and not the token |
| embedding(768) | Find the meaning | You ask for ECONNREFUSED, a score, a name |
| content_hash | Do not re-embed the same sentence | You change a comma and think it is new |
03A database you already have
A dedicated vector database solves a real problem: billions of vectors, a service boundary, teams that do not want to touch Postgres. That is not this lab. The monitor is a SQLite file. The engine is Postgres. Both already run. Both already have an extension that stores a vector and answers nearest neighbour.
| Surface | Extension | Index | Shape |
|---|---|---|---|
| Monitor | sqlite-vec | Cosine on the file | One file. No vector server. Fine at project scale. |
| Engine | pgvector | HNSW, cosine (vector_cosine_ops) | Approximate. O(log n), not a full scan. |
| Not used | Pinecone, Weaviate, Chroma, Qdrant, Milvus | Their own ANN | Another moving part. Another source of truth. |
Postgres with pgvector, MongoDB Atlas Vector Search, Elasticsearch: the operational stores grew a vector column. That is the correct direction. It is also the reason a separate product is the wrong default here. You do not buy a second catalog for a library you already walk through.
dedicated vector DB extension on the store
[ memories ] [ memories ]
│ │
├── export / sync ├── content
▼ ├── lexical
[ vector service ] └── embedding
│ │
▼ ▼
ANN query ANN query on the same row
two systems one row
two failures one backup
a sync that can lie the text is still thereThe failure mode of a dedicated store is not latency. It is split brain. The text moved. The vector did not. Or the vector moved and the text you show the model is a stale chunk. When both live on the same row, an update is a transaction. The hash decides whether the encoder runs again.
04Exact cosine, then HNSW
Similarity search, at small scale, is a scan: embed the query, compare it to every stored vector, keep the closest. On a normalised embedding, cosine similarity is a dot product. In pgvector, <=> is cosine distance. Similarity is 1 - (embedding <=> query). That conversion is correct for this operator.
A scan is honest. It is also linear. At a few thousand memories it is cheap. At hundreds of thousands it is not. HNSW (Malkov and Yashunin, IEEE TPAMI 2018) builds a graph so the search is logarithmic and approximate. You can miss a neighbour. You gain speed. pgvector’s HNSW on the engine is that trade. sqlite-vec on the monitor does not need it yet.
Project scale
Thousands of memories. Exact cosine on SQLite. The file is the backup.
Engine scale
The collection grows. HNSW on Postgres. Approximate, and fast enough to sit in the loop.
Billion-vector scale
A dedicated service starts to make sense. We are not there. Pretending we are is how you buy Pinecone for a notebook.
05Memory is not RAG over docs
RAG, in the product sense, ingests a corpus you did not write as memory: help centre, PDFs, tickets. You split, you embed, you retrieve, you generate. The corpus is someone else’s prose. The risk is a chunk that looks similar and is wrong, handed to a model that will sound sure.
Memory is the opposite authorship. We wrote the sentence. We kept it because it should be findable later. Retrieval is a catalog over our own notes, not a search engine over a dump. The language model still needs the passage. It should not be the only thing that has ever seen the file.
| RAG over docs | Memory, as we run it | |
|---|---|---|
| Authorship | Ingested. Someone else wrote it. | We wrote it. On purpose. |
| Unit | A chunk from a splitter. | An observation, with a hash. |
| Store | Often a vector service. | The same database as the text. |
| Failure | Plausible wrong chunk. | Missed identifier, if dense-only. |
| Fix | Better split, better rerank. | Lexical plus dense, then fusion. |
The same embedding model can serve both. The architecture should not. If you treat memory as a chatbot over your own diary, you will shard the diary into a service you cannot grep. That is the move this lab refuses.
06What this is not
- It is not a claim that vector databases are fake. They are good at a scale we do not have.
- It is not a claim that SQL is semantic search. SQL finds the row you named. The vector finds the row you meant.
- It is not a replacement for the hybrid study. Dense retrieval without lexical retrieval is the blind spot that study exists to close.
- It is not a local-only religion. The encoder is local because the default path should not leave the machine. The idea still holds if the encoder is remote. The store still should not.
Your SQL or NoSQL database remains the source of truth. The vector works alongside it.
What to remember
An embedding turns a passage into a point. A vector index finds nearby points. That is how meaning is searched. On Glorics those points sit on the memory row, next to the words, next to the hash. sqlite-vec on the monitor. pgvector HNSW on the engine. No extra catalog. No sync. The file is still the file.
Use a dedicated vector database when the collection is large enough that an extension is the wrong machine, and when the text does not have to live in the same transaction. Until then, the column is enough. The hard part is not storing the vector. The hard part is ranking what comes back. That is the other study.