聊天记录或 Embedding 在进程重启后仍然存在,不代表 AI Agent 已经拥有可靠记忆。生产级记忆必须回答:什么信息可以影响未来行为、影响谁、在哪段时间有效、依据是什么、如何纠正和删除

这个区分直接关系用户体验。记住用户明确要求的代码格式可以改善后续回答;永久保存一次性的医疗信息、检索文本中的恶意指令,或已经作废的项目决定,则会持续制造隐私风险与错误。

因此,记忆系统不应优化“召回越多越好”或“全部保存”,而应优化有用途、有授权、仍有效、可归因的召回,并让过时和危险记忆容易被拒绝。关键结论最后核验于 2026 年 7 月 16 日。

核心结论

  • 分开管理线程状态、长期记忆、权威业务数据和不可变审计日志。
  • 长期记忆可包括语义事实、情景经验与程序性规则,三者需要不同写入和检索策略。
  • 向量数据库只是可选索引,不是事实源,更不等于完整记忆系统。
  • 少写入:要求用途、用户同意或其他有效依据、来源、置信度、有效期、敏感等级和删除策略。
  • 不静默覆盖旧事实,用版本或 supersedes 关系表达纠正。
  • 检索必须同时考虑范围、时间、权限、类型与相关性,不能只看向量相似度。
  • 召回的记忆属于不可信数据,不能被当作系统指令。
  • 分别评测抽取、召回、时态、更新、拒答、隐私、延迟和删除。

四种状态边界

“记忆”经常混淆四套不同所有权的系统:

状态 作用范围 示例 主要存储
线程/工作流状态 单个对话或运行 消息、工具结果、待审批节点 Checkpointer/状态库
Agent 长期记忆 跨线程复用 用户偏好、历史方案、经验流程 受治理的记忆存储
权威业务状态 领域事实源 订单状态、账户余额、访问角色 事实源系统(system of record)
审计/事件日志 取证与合规 谁在何时修改了什么 追加式(append-only)日志

不要把业务事实复制成一句“记忆”,之后就以它为准。Agent 查询订单状态时应调用订单服务。可以在用途明确且被允许时记住“用户经常询问某个订单”,但当前状态必须来自事实源。

当前 LangGraph 文档同样区分:Checkpointer 负责线程级持久化,Store 负责应用定义的跨线程长期记忆。Checkpoint 持久存在,也不等于其中每条消息都应该成为长期用户事实。

语义、情景与程序性记忆

认知心理学分类只有映射到明确应用行为时才有价值。

语义记忆:事实与偏好

例如:

  • “用户明确要求简洁回答。”
  • “Atlas 项目使用 PostgreSQL 17。”
  • “团队财年从四月开始。”

语义记忆需要实体身份、来源、时间有效性和冲突处理。“用户喜欢 Python”如果只是从单次任务间接推断,就不能被包装成永恒事实。

情景记忆:过去经验

一条情景记忆是对一次相关事件的结构化记录:

  • 任务与环境;
  • 动作和工具调用;
  • 观察结果;
  • 最终结果和验证器输出;
  • 失败原因;
  • 来源 Trace 与时间。

原始对话记录只是构建这条情景记录的证据,不会自动成为有用的情景记忆。保存每个 Token 会制造噪声、隐私暴露和检索成本。对编码 Agent 而言,通过测试的迁移过程可能值得复用,周边闲聊并不值得。

程序性记忆:应该怎样做

程序性记忆保存可复用规则与策略:

  • “修改支付 Schema 前必须运行向后兼容检查。”
  • “该租户退款超过 500 美元时转人工审批。”
  • “部署健康检查失败时停止,不允许无限重试。”

它可能与提示词、策略、操作手册(playbook)或代码重叠。高影响流程必须版本化和审查,并尽量由确定性代码强制执行,不能从聊天中静默“学会”。

完整记忆生命周期

可靠系统至少包含六个阶段:

text
观察 -> 提议 -> 校验/写入 -> 检索 -> 使用/验证 -> 修订/遗忘

1. 观察,但不自动升级

只采集保留策略允许的事件。临时模型上下文与持久存储分离。数据最小化应发生在抽取前,而不是数据库填满后。

2. 生成候选记忆

LLM 可以提出结构化候选,但不能拥有单方面写权限。候选至少应说明:

  • 主体(subject)与谓词(predicate);
  • 值(value);
  • 记忆类型;
  • 精确来源证据;
  • 用户明确陈述还是模型推断;
  • 置信度;
  • 有效时间与观察时间;
  • 敏感度与用途。

推断必须标为推断。“用户要求素食菜谱”不能证明“用户是素食者”。

3. 执行写入策略

以下情况应拒绝或要求确认:

  • 不清楚未来用途;
  • 信息只是临时状态;
  • 来源不可信或有歧义;
  • 信息敏感;
  • 缺少用户同意或其他有效依据;
  • 应从权威业务系统实时查询;
  • 与已有有效记忆重复;
  • 冲突无法安全解决。

适合个性化的产品应提供“记住这件事”和记忆管理页面。显式控制优于不可见用户画像。

4. 分离事实记录与索引

权威记忆记录存入事务数据库,词法、向量、图或时间索引只是派生视图。PostgreSQL 可以同时支持结构过滤、全文检索和 pgvector 等扩展提供的向量检索,“SQL 无法语义搜索”并不成立。

按访问模式选存储:

  • 精确实体与时态更新 -> 关系型/文档库;
  • 语义候选召回 -> 向量索引;
  • 实体连接与来源图 -> 确有必要时使用图表示;
  • 不可变原始事件 -> 追加式(append-only)事件或对象存储。

生产系统通常会组合使用,一次向量命中必须能够回到受治理的事实记录。

5. 检索、过滤与打包

正确顺序是:

  1. 认证调用者;
  2. 强制租户、主体、用途与敏感度范围;
  3. 过滤有效时间、删除状态;
  4. 使用精确键、时间、词法、向量或图关系召回;
  5. 按当前任务重排序(rerank);
  6. 去重并限制记忆的 Token 预算;
  7. 附上来源和不确定性。

全局 top_k=5 既不是安全策略,也不是质量策略。时态问题可能需要旧事实与更新事件;偏好问题可能只需要最新且已确认的值。

6. 验证后使用,再修订或遗忘

执行动作前,区分已确认记忆与推断,高影响事实应回到权威系统验证。记录哪些记忆 ID 实际影响了回答。

系统必须支持:

  • 不丢失审计历史的纠正;
  • 到期(expiry)与时间有效期;
  • 用户可见删除;
  • 按保留策略清理;
  • 删除传播到向量、摘要、缓存、副本和备份;
  • 模型或 Schema 变化后的重建索引。

“记忆衰减”不能简单等于按未访问时间删除。罕见事实可能仍然重要,最新事实也可能已经失效。

版本化记忆记录

真实记录不能只有 textembedding

json
{
  "memory_id": "mem_01J...",
  "tenant_id": "tenant_42",
  "subject_id": "user_123",
  "type": "semantic",
  "predicate": "preferred_programming_language",
  "value": "Rust",
  "source_event_id": "evt_987",
  "assertion": "explicit_user_statement",
  "confidence": 1.0,
  "observed_at": "2026-07-16T09:30:00Z",
  "valid_from": "2026-07-16T09:30:00Z",
  "valid_to": null,
  "supersedes": "mem_older_python",
  "purpose": "coding_assistance",
  "sensitivity": "low",
  "consent_basis": "user_requested_memory",
  "status": "active",
  "schema_version": 1
}

旧 Python 偏好应被标记为已取代,而不能与新事实合并成“用户喜欢编程语言”。这种有损总结会毁掉纠正的时态和含义。

可运行的时态解析

下面只使用 Python 标准库,在不假设“向量相似度能解决时间和鉴权”的前提下,选择指定时点有效的记忆:

python
from dataclasses import dataclass
from datetime import datetime, timezone


@dataclass(frozen=True)
class Memory:
    memory_id: str
    tenant_id: str
    subject_id: str
    predicate: str
    value: str
    valid_from: datetime
    valid_to: datetime | None = None
    status: str = "active"


def resolve_memory(
    memories: list[Memory],
    *,
    tenant_id: str,
    subject_id: str,
    predicate: str,
    at: datetime,
) -> Memory | None:
    if at.tzinfo is None:
        raise ValueError("at must be timezone-aware")

    candidates = [
        memory
        for memory in memories
        if memory.tenant_id == tenant_id
        and memory.subject_id == subject_id
        and memory.predicate == predicate
        and memory.status == "active"
        and memory.valid_from <= at
        and (memory.valid_to is None or at < memory.valid_to)
    ]
    if not candidates:
        return None
    return max(candidates, key=lambda memory: memory.valid_from)


utc = timezone.utc
history = [
    Memory(
        "mem_python", "tenant_42", "user_123",
        "preferred_language", "Python",
        datetime(2025, 1, 1, tzinfo=utc),
        datetime(2026, 7, 16, tzinfo=utc),
    ),
    Memory(
        "mem_rust", "tenant_42", "user_123",
        "preferred_language", "Rust",
        datetime(2026, 7, 16, tzinfo=utc),
    ),
]
print(resolve_memory(
    history,
    tenant_id="tenant_42",
    subject_id="user_123",
    predicate="preferred_language",
    at=datetime(2026, 7, 17, tzinfo=utc),
))

数据库应为单值谓词强制不重叠有效期,或把歧义交给冲突解决流程。应用层过滤不能替代行级安全(row-level security)或独立加密边界。

冲突、不确定性与时间

使用显式操作:

  • ADD:新增独立事实;
  • CONFIRM:新证据支持已有事实;
  • SUPERSEDE:新有效值取代旧值;
  • RETRACT:来源或用户声明旧事实错误;
  • EXPIRE:有效期结束;
  • DELETE:按用户请求或政策删除。

不能让“更新”自动获胜。最新模型推断不应覆盖较早的用户明确陈述。应先比较证据权威性,再比较时态适用性。

时间至少有两个维度:

  • 观察时间(observed time):系统什么时候获知;
  • 有效时间(valid time):信息在现实中什么时候为真。

“我下个月搬到柏林”现在被观察到,但下个月才生效。LongMemEval 单独评估时态推理与知识更新,正是因为纯语义检索经常丢掉这个区别。

安全与隐私

长期记忆会放大风险:一次恶意或错误写入可能影响未来许多会话。

  • 召回文本是不可信数据,必须与系统指令隔离。
  • 邮件、网页和工具输出中的指令未经验证不得写入程序性记忆。
  • 尽可能在向量检索前鉴权,并对每条返回结果再次校验。
  • 使用租户隔离、行级安全策略、独立密钥和受审计的服务鉴权;仅靠元数据过滤不够。
  • 对敏感度分类,除非架构明确支持,否则不把秘密和高度敏感信息做嵌入(embedding)。
  • 防止缓存、日志、评测集与调用链(trace)跨用户泄漏。
  • 在合适产品中提供查看、纠正、遗忘、导出和关闭记忆的能力。
  • 没有正当目的与治理时,不推断敏感属性用于个性化。

注入 Prompt 的记忆应标注来源、时间和可能过期,优先级不能高于开发者政策与当前工具结果。

评测完整生命周期

LongMemEval 把对话记忆拆为信息抽取、多会话推理、时间推理、知识更新和拒答。生产评测应在此基础上扩展。

写入质量

  • 有用候选的准确率(precision)与召回率(recall);
  • 推断与明确陈述分类;
  • 重复、敏感和临时信息写入率;
  • 主体与租户归属正确率;
  • 时间与来源提取正确率。

检索质量

  • 证据 Recall@k 与 nDCG;
  • 时间与实体过滤正确率;
  • 过期记忆召回率;
  • 跨用户/租户泄漏率;
  • 固定上下文预算下的唯一有效 Token。

使用质量

  • 有无记忆时的任务准确率;
  • 来源引用正确率;
  • 冲突处理与应当拒答时的拒答;
  • 个性化收益与无关“套近乎”比例;
  • 错误或注入记忆造成的伤害。

生命周期与运营

  • 更新、撤回、到期与删除正确率;
  • 向所有派生存储传播的延迟;
  • p50/p95 读写延迟;
  • 每条有效记忆的存储与 Embedding 成本;
  • 模型、索引或 Schema 迁移后的性能。

必须包含负例:未同意记忆的用户、重名用户、冲突偏好、未来事实、已删除事实、恶意记忆文本,以及根本不应使用记忆的问题。

如何评估框架

Mem0、Zep、Letta/MemGPT、LangGraph Store 与自研方案所处层次不同,而且 API 持续变化。应评估能力,不要复制一段短 SDK 示例:

  • Schema 与来源控制;
  • 同步写入和后台写入;
  • 冲突与时态语义;
  • 命名空间与鉴权;
  • 混合检索和重排序;
  • 用户纠正与删除 API;
  • 可观测性和评测钩子;
  • 导出与迁移能力;
  • 价格、部署和数据驻留。

MemGPT 的虚拟上下文管理是重要的分层存储范式,但不意味着可以跳过治理。模型自己决定记什么时,写入校验反而更重要。

生产检查清单

  • [ ] 每类记忆都有明确跨会话用途;
  • [ ] 线程状态、业务事实、审计日志与长期记忆分离;
  • [ ] 写入包含来源、时间、用途、敏感度和有效授权;
  • [ ] 推断不会被展示成用户明确事实;
  • [ ] 更新通过版本或 supersedes 保留历史;
  • [ ] 检索强制租户、主体、用途、时间和删除状态;
  • [ ] 召回内容被视为不可信上下文;
  • [ ] 用户能够查看和纠正持久个性化;
  • [ ] 删除覆盖索引、摘要、缓存、副本和保留流程;
  • [ ] 评测覆盖写入、召回、时态、冲突、拒答、隐私和成本。

总结

有用的 Agent 记忆应该是选择性且可逆的。质量来自严格写入、权威来源、时态语义、安全检索、明确冲突处理和可测量的用户收益。

先实现线程持久化与权威工具。只有真实跨会话任务需要时,才增加一种边界清晰的长期记忆。一个用户能够理解、纠正和删除的小型记忆库,远好于静默积累猜测的庞大向量索引。

一手资料