PostgreSQL Field Guide

AI Agent 长期记忆与 PostgreSQL

Agent 记忆层选型:专用框架、向量库,还是用 PostgreSQL(pgvector + jsonb + 全文检索 + SQL/PGQ)做记忆底座

对语言模型的每次调用都是无状态的:模型不会回写任何东西,它能“记住”的只有 prompt 里的内容。一旦应用需要知道用户是谁、上周的偏好是什么、哪些事实已经变化,这份状态就必须由应用自己持有。这就是“Agent 记忆”在工程上的含义——一个写入路径由 LLM 辅助完成的数据库问题,而不是模型功能。

记忆不等于 RAG

RAG 和 Agent 记忆经常被混为一谈,因为两者都以“把相关文本检索进 prompt”结束。区别在于写入路径:

RAGAgent 记忆
内容外部语料(文档、工单、代码)交互中产生的事实、偏好与经历
写入路径批量摄取,可重放,幂等对话过程中的在线写入,常由模型抽取
更新方式重新摄取新版本文档对单条事实进行更正、取代与遗忘
典型查询“找与 X 相关的段落”“我们当前对这个用户了解什么”
典型故障切块过期或无权限错误事实以与正确事实相同的权威写入

因此记忆存储必须支持定向的 UPDATEDELETE、按时间的有效期和冲突处理,而不只是最近邻搜索。把对话日志建成只读向量索引,是对聊天记录做 RAG,不是记忆。

记忆层的三种形态

  1. 专用记忆框架——SDK 与服务(Mem0、Cognee 等),在 add/search API 背后接管抽取、存储与检索。原型最快,但 schema 与检索策略留在框架内部。
  2. 只用向量库——embedding 加元数据过滤。简单,但用户画像、实体间关系、精确匹配查询都要在别处重建。
  3. 直接用 PostgreSQL——一个系统同时承载 embedding(pgvector)、结构化画像(jsonb)、关键词检索(全文检索)、实体关系(普通表,或 PostgreSQL 19 的 SQL/PGQ 属性图),外加事务与行级安全。写入策略留在你自己的代码和 SQL 里,而不是框架里。

三者并不互斥:第一类框架通常会把数据持久化到后两类存储上。真正的决策是“schema 的权威放在哪里、写入策略由谁控制”。

用 PostgreSQL 做记忆底座

一个可用的记忆 schema 把事实与它的 embedding 分开存放,并用显式的历史链代替原地覆盖:

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE agent_memory (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  tenant_id bigint NOT NULL,
  user_id text NOT NULL,
  kind text NOT NULL CHECK (kind IN ('profile', 'preference', 'episode', 'fact')),
  content text NOT NULL,
  attributes jsonb NOT NULL DEFAULT '{}',
  embedding vector(1536),
  search_vector tsvector GENERATED ALWAYS AS
    (to_tsvector('simple', content)) STORED,
  valid_from timestamptz NOT NULL DEFAULT now(),
  expires_at timestamptz,
  superseded_by bigint REFERENCES agent_memory(id),
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX ON agent_memory USING hnsw (embedding vector_cosine_ops);
CREATE INDEX ON agent_memory USING gin (search_vector);
  • pgvector 把 embedding 放在它描述的事实旁边;索引选择与带过滤条件的扫描行为遵循 RAG 管道安装 pgvector中的同一套规则。
  • jsonb 承载画像的结构化部分(时区、语言、套餐档位),这些字段必须能逐字段过滤和更新,而不是每次变更都重新 embedding。
  • 全文检索覆盖 embedding 处理不好的精确名字、ID 与错误串;两路候选集合并后融合,与混合 RAG 检索完全一致。
  • 实体关系就是普通表:entity 加一张以 PRIMARY KEY (src, dst, relation) 约束的边表。在 PostgreSQL 19 上还可以把它们声明为属性图并用 GRAPH_TABLE 查询,见 SQL/PGQ 图查询;更早的版本用 join 或 WITH RECURSIVE 查询同样的表。

检索是一条把租户与权限过滤推进数据库的 SQL,模式见 RAG 管道。这里没有任何东西需要一台“记忆专用服务器”。

候选框架对比

以下能力声明于 2026-08 对照官方仓库与文档核对。厂商发布的基准分数属于厂商自报数据,本站未独立验证。

Mem0Cognee
自我定位“记忆层” SDK、自托管 server 与托管云“AI 记忆平台”,从摄取的数据构建知识图谱
记忆模型按 user、session、agent 三个层级组织的记忆文档 → 图中的实体/关系加 embedding;remember / recall / forget API
存储后端可插拔向量库,支持列表包含 PGVector可插拔的关系、向量与图后端;文档给出 PostgreSQL + pgvector 配置
与 PostgreSQL 的关系PostgreSQL 是多种可选向量存储之一其 README 明确标注 Postgres 存储目前是 demo 特性,生产图负载建议使用图原生后端或其付费产品
抽取策略基于 LLM 的事实抽取与更新决策在框架内完成基于 LLM 的流水线(cognify)在框架内构建图谱

两个框架都可以把部分存储放在 PostgreSQL 之上,所以“框架 vs PostgreSQL”通常是“框架的写入策略跑在 PostgreSQL 上”与“你自己的写入策略跑在 PostgreSQL 上”之间的选择,而不是两个不同的数据库。

观点:框架是暂时的中间层

冯若航(vonng)在一篇 2026 年的文章中主张:记忆框架是被模型和数据库两头挤压的中间件——抽取策略会迁入模型(或一个简短的 skill 文件),存储会回到 PostgreSQL,持久的护城河在数据层。这是一位从业者的个人观点,不是可核实的事实;把它当作一个假设,对照你自己的写入路径复杂度去检验,再决定采用还是放弃框架。

生产关注点

记忆写入的一致性

最危险的写入是模型抽取的那一条。要给它加约束:记忆走固定 schema 和 CHECK 约束;执行抽取的模型使用不能触碰业务表的受限角色(安全 SQL 护栏);更正操作是 INSERTsuperseded_by 链接,而不是原地改写,这样“Agent 回答时我们相信什么”始终可审计。如果某条记忆派生自一笔业务事务,要么两者在同一事务中写入,要么显式记录来源版本。

遗忘与 TTL

没有过期机制的记忆会积累成噪声,再被检索放大。领域允许时给事实加 expires_at,定期清理过期行;用户主动发起的删除要执行硬 DELETE(连同 embedding 行),而不是打标记。pgvector 索引不会让已删除的行从备份里消失——PITR 保留期要与删除策略对齐。

用 RLS 做多租户隔离

记忆是带 prompt 注入爆炸半径的用户数据:在某个租户被投毒写入的记忆,绝不能被另一个租户检索到。隔离必须做在数据库里,而不是检索代码里:

ALTER TABLE agent_memory ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON agent_memory
  USING (tenant_id = current_setting('app.tenant_id')::bigint);

每个请求从已认证身份设置 app.tenant_id。这样即使 Agent 或 MCP 工具发出你未曾手写的查询,隔离保证依然成立。

评估记忆质量

检索召回率是必要条件而非充分条件:记忆层还可能因为抽取了错误事实、保留了互相矛盾的条目、返回了过期状态而失败。保留一组带版本的对话轨迹,标注它们应产生的记忆和检索应返回的答案;把抽取准确率与检索召回率分开度量;每次改 prompt、换模型、动 schema 都重跑。评测框架与数据库 Agent 评估相同;框架发布的基准是厂商自报数字,不能替代用你自己流量构建的测试集。

AI prompt:起草记忆 schema

在 PostgreSQL 上设计 Agent 记忆 schema
帮我在 PostgreSQL 19 上设计一个 Agent 长期记忆 schema。

1. 我的 Agent 需要记住什么:(如用户偏好、历史决策、未结问题)
2. 规模:(用户数、每用户记忆条数、每天写入量)
3. 请做到:
 - 事实(文本 + jsonb 属性)与 embedding(pgvector)、实体关系(边表)分表
 - 更正用 INSERT + superseded_by 链接,不做原地 UPDATE
 - 需要自动遗忘的地方加 expires_at
 - 包含 tenant_id 隔离的 RLS 策略
 - 写出混合检索查询(租户过滤 + 全文 + 向量候选)
4. 指出哪些内容应该留在业务表而不是记忆里。

相关页面

Last updated on

On this page