引言:PostgreSQL 的 AI 时代转型

当 Milvus、Qdrant、Weaviate 等专用向量数据库在 AI 浪潮中崛起时,一个令人意外的事实是——全球开发者正在用 PostgreSQL 承载越来越多的 AI 工作负载。这不是技术怀旧,而是 pgvector 扩展带来的范式转移:2025 年,pgvector 0.8.0 引入的 HNSW 索引性能已经逼近专用向量数据库,同时保留了 PostgreSQL 生态数十年的可靠性、事务一致性和运维经验。

本文从生产环境出发,深入分析 PostgreSQL 在 AI 时代的多模态能力矩阵,结合真实场景给出完整的架构设计方案。

一、pgvector 核心原理:从 IVFFlat 到 HNSW

1.1 向量索引演进史

pgvector 经历三代索引算法的迭代:

索引类型时间算法精度速度内存
Flat (暴力搜索)2021无索引100%线性
IVFFlat2021倒排文件95-99%
HNSW2023分层小世界图99%+最快
HNSW + SQ82025量化压缩97-99%极快最低

HNSW(Hierarchical Navigable Small World)的核心思想是构建多层图结构:高层稀疏用于快速导航,底层稠密用于精确搜索。查询时从顶层开始,逐层细化,时间复杂度为 O(log n)。

1.2 创建向量列与索引

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE documents (
    id BIGSERIAL PRIMARY KEY,
    content TEXT NOT NULL,
    metadata JSONB DEFAULT '{}',
    embedding vector(1536),
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);

二、距离函数的正确选择

  • cosine(余弦距离):OpenAI、Cohere、大多数现代 Embedding 模型。使用 vector_cosine_ops
  • L2 / euclidean(欧氏距离):CLIP 图像向量、部分专用模型。使用 vector_l2_ops
  • inner product(内积):某些检索模型输出未归一化向量时使用。使用 vector_ip_ops
SELECT id, content,
       1 - (embedding <=> '[0.0123, -0.0456,...]'::vector) AS similarity
FROM documents
ORDER BY embedding <=> '[0.0123, -0.0456,...]'::vector
LIMIT 10;

三、混合检索:向量搜索 + 全文搜索 + 结构化过滤

pgvector 的真正威力在于它能与 PostgreSQL 的全文搜索、B-tree 索引、GIN 索引无缝组合——实现所谓"混合检索"(Hybrid Search)。

3.1 tsvector 全文索引 + 向量搜索

ALTER TABLE documents ADD COLUMN search_vec tsvector
GENERATED ALWAYS AS (to_tsvector('english', content)) STORED;

CREATE INDEX idx_search_vec ON documents USING GIN(search_vec);

WITH vector_scores AS (
    SELECT id, 1 - (embedding <=> query_vec) AS vec_score
    FROM documents ORDER BY embedding <=> query_vec LIMIT 100
),
text_scores AS (
    SELECT id, ts_rank(search_vec, query_tsquery) AS txt_score
    FROM documents WHERE search_vec @@ query_tsquery
)
SELECT d.id, d.content,
       (0.7 * v.vec_score + 0.3 * COALESCE(t.txt_score, 0)) AS combined_score
FROM vector_scores v
LEFT JOIN text_scores t USING (id)
JOIN documents d ON d.id = v.id
ORDER BY combined_score DESC LIMIT 10;

3.2 RRF(Reciprocal Rank Fusion)融合算法

WITH vector_recall AS (
    SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> query_vec) AS rank
    FROM documents ORDER BY embedding <=> query_vec LIMIT 50
),
text_recall AS (
    SELECT id,
           ROW_NUMBER() OVER (ORDER BY ts_rank(search_vec, query_tsquery) DESC) AS rank
    FROM documents WHERE search_vec @@ query_tsquery LIMIT 50
),
rrf_fusion AS (
    SELECT id, SUM(1.0 / (60 + rank)) AS rrf_score
    FROM (
        SELECT id, rank FROM vector_recall
        UNION ALL
        SELECT id, rank FROM text_recall
    ) combined GROUP BY id
)
SELECT d.*, r.rrf_score
FROM rrf_fusion r
JOIN documents d ON d.id = r.id
ORDER BY r.rrf_score DESC LIMIT 10;

公式中常数 k=60 防止头部排名权重过高,确保低排名结果也有被选中的机会。

四、PostgreSQL vs 专用向量数据库:选型决策框架

4.1 性能基准对比(1000 万向量,1536 维,99% recall)

指标pgvector HNSWMilvus 2.4QdrantWeaviate
P99 查询延迟12ms8ms6ms15ms
写入吞吐3K docs/s8K docs/s5K docs/s2K docs/s
最大向量数5000万+数十亿数十亿数亿
内存占用~90GB~60GB~55GB~120GB
混合检索原生 SQLDSLgRPC APIGraphQL

4.2 场景匹配

选择 PostgreSQL + pgvector 的场景:

  • 向量规模在 5000 万以内
  • 需要事务一致性(向量 + 业务数据在同一事务)
  • 团队已有 PostgreSQL 运维经验
  • 混合检索需求强(结构化过滤 + 向量 + 全文)

选择专用向量数据库的场景:

  • 向量规模超过 5000 万,需水平扩展
  • 纯向量检索,不需复杂关联查询
  • 对 P99 延迟要求极苛刻(低于 5ms)

五、实战:构建 LLM 长期记忆存储架构

5.1 记忆模型

  • Episodic Memory(情景记忆):用户对话历史和事件记录
  • Semantic Memory(语义记忆):知识库和文档理解
  • Procedural Memory(程序性记忆):工具使用的经验总结
CREATE TABLE memories (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id BIGINT NOT NULL,
    agent_id VARCHAR(100) NOT NULL,
    memory_type VARCHAR(20) CHECK (memory_type IN ('episodic','semantic','procedural')),
    content TEXT NOT NULL,
    embedding vector(1536),
    importance FLOAT DEFAULT 0.5,
    access_count INT DEFAULT 0,
    last_accessed_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    expires_at TIMESTAMPTZ,
    metadata JSONB DEFAULT '{}'
);

CREATE INDEX idx_memories_user_agent
ON memories (user_id, agent_id, memory_type);

CREATE INDEX idx_memories_embedding
ON memories USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

5.2 记忆写入(LLM 提取 + pgvector 存储)

import openai
from pgvector.psycopg2 import register_vector

async def store_memory(user_id, agent_id, conversation, conn):
    # 步骤 1: LLM 提取结构化记忆
    response = openai.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "从对话提取关键事实,JSON 格式"},
            {"role": "user", "content": conversation}
        ],
        response_format={"type": "json_object"}
    )
    memories = json.loads(response.choices[0].message.content)["memories"]

    # 步骤 2: 批量 embedding
    texts = [m["content"] for m in memories]
    embeddings = openai.embeddings.create(
        model="text-embedding-3-small", input=texts
    ).data

    # 步骤 3: 写入 PostgreSQL
    with conn.cursor() as cur:
        register_vector(cur)
        for mem, emb in zip(memories, embeddings):
            cur.execute(
                "INSERT INTO memories(user_id,agent_id,memory_type,content,embedding,importance) VALUES(%s,%s,%s,%s,%s,%s)",
                [user_id, agent_id, mem["type"], mem["content"], emb.embedding, mem["importance"]]
            )
    conn.commit()

5.3 记忆检索(向量 + 时间衰减 + 访问频率)

async def recall_memories(user_id, agent_id, query, top_k=5, conn):
    query_embedding = openai.embeddings.create(
        model="text-embedding-3-small", input=[query]
    ).data[0].embedding

    with conn.cursor() as cur:
        register_vector(cur)
        cur.execute("""
            SELECT id, content, importance,
                   1 - (embedding <=> %s::vector) AS similarity,
                   EXP(-0.01 * EXTRACT(DAY FROM NOW() - last_accessed_at)) AS time_decay,
                   LEAST(access_count::float / 10, 1.0) AS access_boost
            FROM memories
            WHERE user_id = %s AND agent_id = %s
              AND (expires_at IS NULL OR expires_at > NOW())
            ORDER BY (
                0.6 * (1 - (embedding <=> %s::vector)) +
                0.2 * importance +
                0.1 * EXP(-0.01 * EXTRACT(DAY FROM NOW() - last_accessed_at)) +
                0.1 * LEAST(access_count::float / 10, 1.0)
            ) DESC LIMIT %s
        """, [query_embedding, user_id, agent_id, query_embedding, top_k])

        results = cur.fetchall()
        # 更新访问统计
        for row in results:
            cur.execute(
                "UPDATE memories SET access_count = access_count + 1, last_accessed_at = NOW() WHERE id = %s",
                [row[0]]
            )
        conn.commit()
    return results

六、生产运维:pgvector 性能调优

6.1 PostgreSQL 核心参数

# postgresql.conf
work_mem = '256MB'
shared_buffers = '8GB'
max_parallel_workers_per_gather = 4
max_parallel_maintenance_workers = 4
max_connections = 200
max_wal_size = '4GB'

6.2 HNSW 运行时参数

-- ef_search 控制精度与速度权衡
SET hnsw.ef_search = 100;

-- 实时低延迟场景
SET LOCAL hnsw.ef_search = 64;

-- 离线高精度场景
SET LOCAL hnsw.ef_search = 200;

6.3 监控查询

-- 索引使用情况
SELECT schemaname, tablename,
       pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,
       idx_scan AS times_used
FROM pg_stat_user_indexes
WHERE indexrelname LIKE 'hnsw%';

-- 表膨胀监控
SELECT relname, n_live_tup, n_dead_tup,
       ROUND(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_ratio
FROM pg_stat_user_tables WHERE relname = 'memories';

七、AI Agent 完整多模态存储 Schema

-- 会话表
CREATE TABLE conversations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id BIGINT NOT NULL,
    messages JSONB NOT NULL DEFAULT '[]',
    summary_embedding vector(1536),
    created_at TIMESTAMPTZ DEFAULT NOW(),
    ended_at TIMESTAMPTZ
);

-- 工具调用记忆
CREATE TABLE tool_memories (
    id BIGSERIAL PRIMARY KEY,
    user_id BIGINT,
    agent_id VARCHAR(100),
    tool_name VARCHAR(100),
    input_params JSONB,
    output_result JSONB,
    success BOOLEAN,
    duration_ms INT,
    embedding vector(1536),
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Agent 配置
CREATE TABLE agent_configs (
    id SERIAL PRIMARY KEY,
    agent_name VARCHAR(100) UNIQUE NOT NULL,
    system_prompt TEXT,
    model VARCHAR(50) DEFAULT 'gpt-4o',
    temperature FLOAT DEFAULT 0.7,
    tools JSONB DEFAULT '[]',
    is_active BOOLEAN DEFAULT true,
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

八、2026 年展望

  • DiskANN 支持:打破内存限制,支持十亿级向量在 SSD 上运行
  • Sparse Vectors:原生稀疏向量,适配 Splade 等稀疏检索模型
  • Bitwise Quantization:二进制量化,内存占用降 32 倍
  • pg_bm25 扩展:纯 SQL 实现 BM25,与向量检索无缝融合
  • Citus 分布式向量:向量数据自动分片到多节点

结语

PostgreSQL + pgvector 并不是要替代专用向量数据库,而是提供了一个务实的、一体化的解决方案。当团队已运行 PostgreSQL 时,引入 pgvector 意味着零额外运维成本就能获得生产可用的向量检索能力。

PostgreSQL 在 AI 时代的多模态转型,本质上是用数据库的"连接"能力来整合企业数据——让向量不再是孤岛,让 AI 能触达企业每一个角落的数据。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部