PostgreSQL RAG 管道
用关系过滤、全文与 pgvector 组合可审计的混合检索
数据模型
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
tenant_id bigint NOT NULL,
source_uri text NOT NULL,
source_version text NOT NULL,
title text NOT NULL,
access_scope text[] NOT NULL DEFAULT '{}',
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (tenant_id, source_uri, source_version)
);
CREATE TABLE document_chunks (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
document_id bigint NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
ordinal integer NOT NULL CHECK (ordinal >= 0),
content text NOT NULL,
token_count integer NOT NULL CHECK (token_count > 0),
embedding vector(1536) NOT NULL,
embedding_model text NOT NULL,
search_vector tsvector GENERATED ALWAYS AS
(to_tsvector('simple', content)) STORED,
UNIQUE (document_id, ordinal)
);摄取必须可重放
保存 source version、切块算法版本、embedding 模型和维度。对相同输入生成确定的文档/切块键,支持幂等 upsert。模型变更时创建新 embedding 列或新表并双写重建,不要把两种向量混在一个索引里。
检索顺序
- 在 SQL 中过滤
tenant_id、权限、文档状态和时间范围。 - 用全文和向量分别产生有界候选集。
- 使用 rank fusion 或应用侧重排合并。
- 取少量相邻切块补足上下文。
- 返回
source_uri、版本、chunk id 与原文片段供引用。
示意性的向量候选查询:
SELECT
c.id,
c.document_id,
c.ordinal,
c.content,
c.embedding <=> $1::vector AS distance
FROM document_chunks AS c
JOIN documents AS d ON d.id = c.document_id
WHERE d.tenant_id = $2
AND d.access_scope && $3::text[]
ORDER BY c.embedding <=> $1::vector
LIMIT 40;索引类型与参数取决于数据量、召回率、延迟和写入模式。先建立 exact-search 基线,再评估 HNSW 或 IVFFlat;不要只用演示数据判断。
近似索引与过滤
HNSW/IVFFlat 的 tenant、ACL 等条件通常在索引产生候选后应用,因此可能返回少于 LIMIT 的结果。这不是放宽权限过滤的理由。pgvector 0.8.0+ 可用 iterative scans 扩大候选扫描;高频 tenant 还可评估分区、部分索引或独立表。每种方案都要按真实 tenant/ACL 分桶测量 recall@k。
具体索引 DDL、参数和评测方法见向量检索生产化与 pgvector 官方文档。
安全与引用
权限条件必须存在于 SQL/RLS 内,由数据库在结果离开前执行;不能先向应用返回全局候选再过滤,否则无权内容可能通过日志、缓存或模型上下文泄露。最终回答携带可验证引用;找不到足够证据时明确说“不足以回答”。
向量相似不等于事实正确
相近文本可能过期、互相矛盾或属于错误租户。RAG 需要版本、权限、来源优先级和答案评估,而不只是最近邻。
Last updated on