如何以 PostgreSQL、pgvector 和 Ruby on Rails 建置 RAG
Building RAG on PostgreSQL with Pgvector and Ruby on rails
A.Talaat 示範如何在 Ruby on Rails 中以 PostgreSQL 的 pgvector 擴充功能建置 RAG,並透過 ActiveRecord 搜尋嵌入向量。
🧠 Your Database Is Already a Vector Database
Most RAG tutorials start the same way: "First, spin up a vector database."
But Stop.
If you're on Rails, you already have PostgreSQL.
With the pgvector extension it can store and search embeddings and for most applications, that's the whole answer.
🐘 Setup is one migration
`enable_extension "vector"
add_column :document_chunks, :embedding, :vector, limit: 1536`
💎 And one line in your model
Gemfile
gem "neighbor"
class DocumentChunk < ApplicationRecord
has_neighbors :embedding, dimensions: 1536, normalize: true
end
🔍 Searching is just ActiveRecord
DocumentChunk
.where(tenant: Current.tenant)
.nearest_neighbors(:embedding, query_vector, distance: "cosine")
.limit(6)
Scoped, chainable, joinable. A normal Rails query.
🚀 Why this beats a separate vector DB
1️⃣ No new infrastructure
Nothing extra to deploy, monitor, back up, or upgrade.
2️⃣ Transactional consistency
Embeddings commit in the same transaction as the record. Two systems can't drift apart.
3️⃣ Authorization you already trust
This is the big one. Retrieval almost always needs scoping by tenant, project, or permission. In Postgres that's a WHERE clause. In an external store it's metadata filtering you have to keep in sync with your auth model.
Get it wrong, and one customer's documents land in another customer's LLM prompt.
4️⃣ Real joins
Chunks plus their documents, authors, and timestamps in one query.
⚠️ Three things that will bite you
❌ Index opclass mismatch
vector_cosine_ops goes with cosine distance. Mismatch them and Postgres silently skips your index and sequential-scans the table.
❌ No similarity floor
Nearest neighbours are nearest, not relevant. Without a threshold, an unrelated question still returns chunks — which the model will happily use to hallucinate.
❌ Pure vector search
Embeddings are weak at exactly what users type literally: error codes, SKUs, invoice numbers. ERR_4012 and ERR_4013 are nearly identical in vector space.
Fix: hybrid search — pgvector + Postgres full-text, fused with Reciprocal Rank Fusion.
🎯 When to actually leave Postgres
Past roughly 5–10 million chunks, or when the vector workload starves your transactional workload for memory.
Until then, a dedicated vector database buys you one more service to operate and a second place for your authorization logic to be wrong.
Your database stays the source of truth. The vector index is a lookup layer on top of it.
💬 If you've shipped RAG in Rails — what pushed you off Postgres, if anything?
來源:DEV Community · dev.to
