最近有个朋友问我:做 Agent 项目,向量搜索用什么方案?我毫不犹豫地说:PostgreSQL + VectorChord

他有点意外:“不用 Milvus、Pinecone 这些专用向量数据库?”

这让我意识到,很多人对 Agent 时代的向量存储需求有误解。今天我就聊聊为什么我在 Agent 项目里只认 PostgreSQL,以及 VectorChord 如何实现低成本亿级向量检索,对比传统 HNSW 方案的优势。

Agent 时代,向量存储需要什么?

在聊技术之前,先想想 Agent 项目的特点:

1. 知识库频繁更新

Agent 的知识库不是静态的。用户不断上传新文档、新对话历史,向量索引需要频繁重建。传统 HNSW 索引构建 1 亿向量需要数小时,这根本跟不上更新节奏。

2. 规模快速增长

Agent 应用的向量规模往往和用户活跃度、文档数量成正比。

从实际场景来看:

  • 个人知识库:个人笔记、文档、邮件等场景,向量规模通常在 10 万 ~ 100 万 级别。单个用户的文档总量有限,即使加上增量更新,百万级向量已经能覆盖绝大多数个人场景。

  • 企业 Agent 应用:这是向量规模真正爆发的场景。客服机器人需要存储所有产品文档、历史对话、FAQ;企业知识库问答需要覆盖内部 wiki、工单记录、会议纪要;员工助手需要访问邮件、文档、代码库。随着业务增长,向量规模很容易达到 千万甚至亿级。比如一个中型企业(1000-5000 人)的知识库,文档数量百万级,分块后向量规模轻松破千万。

  • Agent 长期记忆需求:随着 OpenClaw、Codex 等 Agent 框架的普及,Agent 作为"数字员工"需要持久化记忆的内容远超传统 RAG:

    • 完整对话历史:每次对话都需要向量化存储,用于上下文检索和一致性保持
    • 任务记忆:cron 任务、待办事项、长期目标及其执行状态
    • 用户偏好记忆:沟通风格、决策模式、关注重点需要被记住并复用
    • 知识更新:每次交互中学习的新信息都需要纳入知识库

    这类记忆型内容会随着 Agent 使用时间持续累积,一个活跃使用半年的 Agent,仅对话记录就可能产生数百万向量。

这给向量存储方案带来了可扩展性挑战。

3. 成本敏感

大多数 Agent 项目还是早期阶段,没有大规模盈利。为了向量搜索单独维护一套专用数据库,无论是成本还是运维都是负担。

4. 数据一致性

Agent 的向量数据往往和业务数据(用户、文档、对话记录)强相关。分开存储会带来一致性问题和数据同步的复杂性。

基于这些特点,我的选择标准很清晰:

  • 构建要快:能应对频繁更新
  • 成本要低:小团队也能用得起
  • 已在技术栈内:不引入额外组件
  • 够用就好:不追求极致性能

专用向量数据库的问题

先说说我为什么不选专用向量数据库。

在向量数据库选型时,很多人会第一时间考虑 Milvus、Pinecone、Weaviate 等专用方案。但实际使用中,这些方案在 Agent 场景下暴露出明显问题:

问题 具体表现
成本高 1 亿 768 维向量需要 >$1000/月,内存占用 >100GB
构建慢 HNSW 索引构建 1 亿向量需要 >10 小时
运维复杂 需要额外部署、监控、备份
数据分散 向量数据和业务数据分离,一致性问题

对于大多数 Agent 项目来说,这些问题都是不可接受的。这也是为什么我在 RAG 向量检索方案对比中,更倾向于 PostgreSQL 原生方案。

VectorChord:PostgreSQL 原生的答案

VectorChord 是我们团队(TensorChord)推出的 PostgreSQL 原生向量搜索扩展,是 pgvecto.rs 的继任者。

先看个对比:

方案 1 亿 768 维存储 构建时间 内存占用 成本(每月)
专用向量数据库 (HNSW) ~1000GB >10 小时 >100GB ~$1000+
VectorChord (IVF+RaBitQ8) ~100GB ~20 分钟 <1GB ~$247

成本只有 1/5,构建速度提升 30x。这才是 Agent 项目需要的。

核心技术:为什么能做到这么快这么省?

IVF + RaBitQ:放弃 HNSW 的理由

HNSW 的多层图结构确实查询速度快,但有几个致命问题:

  1. 内存占用巨大:每个节点需要存储多层邻居信息,内存占用是向量本身的数倍
  2. 构建慢:需要逐个插入节点并维护多层图结构
  3. 不适合磁盘:频繁的随机访问模式在磁盘上性能很差

VectorChord 采用 IVF(倒排索引)+ RaBitQ(随机化比特量化)的组合:

  • 存储压缩比高:1bit 量化压缩率 32x,8bit 量化压缩率 4x
  • 构建速度快:流式构建,不需要全量数据在内存中
  • 磁盘友好:按簇连续存储,顺序 IO 优化

存储布局:顺序 IO + 缓存友好的两层设计

这是 VectorChord 最核心的设计之一,直接借鉴了 列存数据库 的设计思路。

全局:按簇连续存储

整个索引的量化向量按簇顺序存储在 PostgreSQL 数据页中,结构如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
┌───────────────────────────────────────────────────────────────────────┐
│   Cluster 0 (簇0) 连续存储区域                                                 │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │ 所有属于簇0的向量的量化数据 [连续存放]                                  │   │
│   └─────────────────────────────────────────────────────────────────────┘   │
├───────────────────────────────────────────────────────────────────────┤
│   Cluster 1 (簇1) 连续存储区域                                                 │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │ 所有属于簇1的向量的量化数据 [连续存放]                                  │   │
│   └─────────────────────────────────────────────────────────────────────┘   │
└───────────────────────────────────────────────────────────────────────┘

查询时只需要读取候选簇对应的连续存储区域,是顺序 IO而非随机 IO。这种设计借鉴了 LSM-Tree 的顺序写入思路,磁盘吞吐量提升 5~10 倍。

簇内:类列存布局

传统行存(每个向量连续放所有维度):

1
内存顺序 → | v0d0 v0d1 v0d2 v0d3 | v1d0 v1d1 ... v1d3 | ...

VectorChord 簇内类列存:

1
内存顺序 → | d0v0 d0v1 d0v2 d0v3 | d1v0 d1v1 ... | d3v0 ... d3v3

同一维度的所有向量量化数据连续存放,计算距离时可以连续加载到 SIMD 寄存器。CPU 缓存命中率从 ~30% 提升到 ~95%。

内存磁盘分层

极致压缩内存占用:只有几千个聚类质心全精度存放在内存中,几千个 768 维质心只需要 ~12MB 内存。

所有量化向量都存放在磁盘上,查询时只加载候选簇的数据。1 亿向量索引,全程内存占用不到 1GB。

RaBitQ:带理论保证的低比特压缩

RaBitQ 来源于论文 RaBitQ: Quantizing High-Dimensional Vectors with a Theoretical Error Bound for Approximate Nearest Neighbor Search,核心创新是通过随机旋转让维度独立,量化后误差有严格理论上界,在相同压缩率下召回率比传统低比特量化高 5%~10%。

我们用一个 2 维向量的完整例子走一遍量化流程:

步骤 1:训练集中心化

对整个训练集的所有向量,每个维度减去该维度的均值,消除偏移。这是 Standardization 的常用技巧。

1
2
3
4
5
原始向量 x = [1.2, -0.5]
训练集均值 μ = [0.3, 0.1]

中心化后:
x_centered = x - μ = [0.9, -0.6]

步骤 2:随机正交旋转

生成一个随机正交矩阵 R(满足 R^T * R = I),对中心化后的向量做旋转。旋转后各个维度的分布会更接近独立同分布,方便后续量化和误差控制。

1
2
3
4
5
6
2 维随机正交矩阵 R 例子:
R = [[ 0.6,  0.8],
     [-0.8,  0.6]]

旋转后的向量:
x_rotated = R * x_centered = [0.06, -1.08]

步骤 3:量化到低比特

  • 1bit 量化:只存符号,≥0 存 +1,<0 存 -1
  • 4bit 量化:划分 16 个区间,每个维度存 4bit 区间索引
  • 8bit 量化:划分 256 个区间,每个维度存 8bit 区间索引

我们演示 1bit 量化:

1
2
3
4
5
6
7
x_rotated = [0.06, -1.08]
0.06 ≥ 0 → +1
-1.08 < 0 → -1

最终 q(x) = [+1, -1]
二进制存储:`10` → 只需要 2 bit!
原始 float32 需要 8B → 压缩率 32x
量化 压缩率 召回损失 适用场景
1bit RaBitQ 32x ~5%~8% 超大规模,成本极致敏感
RaBitQ4 8x ~2%~3% 大规模,平衡成本精度
RaBitQ8 4x <1% 大多数生产场景

流式构建:128GB 内存构建 10 亿级索引

VectorChord 最大的亮点:流式磁盘构建。

1
1. 采样聚类 → 2. 流式分配向量 → 3. 持久化索引
  • 采样 20%-30% 向量做双层 K-Means 聚类
  • 顺序扫描全量数据,边读边写,内存只保留当前批次
  • 1 亿 768 维向量索引构建仅需 20 分钟

为什么 PostgreSQL 原生这么重要?

完全兼容 pgvector 的数据类型和查询语法,现有业务可以零成本迁移。

更重要的是,向量数据和业务数据在同一套数据库里

  • 不需要数据同步
  • 事务一致性有保障
  • 运维成本不增加
  • 已经在用的技术栈,不需要学习新东西

对于小团队来说,这意味着可以快速上线,而不是花时间在基础设施上。

这也是 PostgreSQL 作为 Pinecone 替代方案的优势——无需引入新的技术栈,直接在现有数据库上扩展向量检索能力。

查询性能:够用就好

有人可能会问:PostgreSQL 原生能有多快?

VectorChord 的查询流水线:

1
2
3
4
5
1. 粗选(全内存):计算查询向量和所有质心的距离,选出 top-n 簇
2. 加载候选:顺序磁盘读取候选簇的量化数据
3. 粗排:[SIMD](https://en.wikipedia.org/wiki/SIMD) 批量计算所有候选向量的距离
4. 重排序:选出 top-k 候选,用更高精度重新计算
5. 返回结果

预过滤支持:解决过滤后空结果问题

VectorChord 支持业务非常常用的「先过滤条件后向量搜索」场景,并且有两种过滤模式:

模式 流程 优势 适用场景
预过滤 先执行 SQL 条件过滤,再对过滤后的向量做搜索 只计算满足条件的向量,大幅减少距离计算量 过滤条件严格,会过滤掉 90% 以上数据
后过滤 先做向量搜索取出远多于需求的候选,再做 SQL 过滤 保证能返回足够的结果,不会轻易空 过滤条件宽松,需要保证结果数量

通过分批扩展 + Fallback 机制,VectorChord 解决了「过滤后没有结果」的异常问题:不会一次只取固定 n 个候选簇就结束,而是按距离排序分批取出,如果当前批次过滤后结果不足,自动取出下一批候选继续处理。

对于大多数 Agent 应用来说,这个性能完全够用。而且成本只有专用方案的 1/5。

我的选型建议

基于实际经验,我给 Agent 项目的向量搜索选型建议:

规模 < 1000 万

  • 直接用 pgvector + HNSW
  • 单机 PostgreSQL 完全够用

规模 1000 万 ~ 1 亿

  • VectorChord + RaBitQ8
  • 单机或主从架构

规模 > 1 亿

  • VectorChord + RaBitQ4/RaBitQ1
  • 考虑分库分表或读写分离

技术细节:双层 K-Means 设计

VectorChord 当前采用双层 K-Means 聚类来构建 IVF 索引:

  1. 第一层先聚 √k 个中观簇
  2. 每个中观簇内部再聚类,得到最终 k 个簇

整体计算量从 O(n * k * d) 降到 O(n * d * √k)。比如 k=4096,计算量直接减少 64 倍。

这种分层结构本身就来自 Hierarchical SuperKMeans,VectorChord 已经落地了这部分。迭代内剪枝是 SuperKMeans 最大的创新,是未来的演进方向。

未来演进:集成 SuperKMeans + PDX

VectorChord 还在快速演进中,有几个值得期待的方向:

SuperKMeans:构建速度再快 30x

SuperKMeans 的核心创新是在每轮 K-Means 迭代内部做渐进式剪枝:

  1. 只计算前 d’(12%~25%)维度,用 BLAS GEMM 批量计算
  2. 基于部分维度距离剪掉 97% 以上不可能的候选
  3. 只对剩下 3% 的候选计算完整维度距离

性能预估:集成 SuperKMeans 后,1 亿 768 维向量构建时间可以从 20 分钟降到 2~3 分钟

PDX 布局:搜索 QPS 再提升 200%~300%

PDX 是 CWI 团队提出的列存布局优化,VectorChord 当前簇内列存已经有了类似思路:

  • 垂直块:前 25% 维度完全列存,专门用于快速剪枝
  • 水平块:剩余维度按 64 维分组列存

缓存命中率和剪枝效率进一步提升,搜索 QPS 预计还能再提升 200%~300%

Thoughts

够用主义 vs 极致性能

Agent 项目早期,最重要的是快速验证和迭代,而不是追求极致的查询性能。

VectorChord 的设计哲学就是"够用就好”:在保证可接受的精度和延迟的前提下,把存储和成本打到极低。

这种务实的态度,才是小团队生存之道。

PostgreSQL 是 Agent 时代的最佳数据底座

向量搜索只是 PostgreSQL 众多能力中的一个。

后面我还会聊聊:

  • 全文检索:PostgreSQL 的 FTS 为什么比 ES 更适合 Agent 项目
  • ACID 事务:为什么 Agent 状态管理离不开事务
  • JSONB:为什么我偏好 JSONB 而不是单独的 MongoDB
  • OLAP 场景:Postgres 在分析型场景下的探索
  • Supabase:为什么我推荐开发者从 Supabase 入手

Next Step

VectorChord 还在快速演进中:

  • 集成 SuperKMeans 迭代内剪枝,构建速度再快 30x
  • 集成 PDX 布局,搜索 QPS 再提升 200%~300%

但核心思路不会变:PostgreSQL 原生 + 低成本 + 够用就好


这是"为什么我在 Agent 项目里只认 PostgreSQL"系列的第一篇。下一篇聊聊全文检索。