PostgreSQL Field Guide

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 列或新表并双写重建,不要把两种向量混在一个索引里。

检索顺序

  1. 在 SQL 中过滤 tenant_id、权限、文档状态和时间范围。
  2. 用全文和向量分别产生有界候选集。
  3. 使用 rank fusion 或应用侧重排合并。
  4. 取少量相邻切块补足上下文。
  5. 返回 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

On this page