AI干货系列:在 AI 对话系统中实现长期记忆自主管理

在 AI 对话系统中实现自主记忆管理

目录

  1. 引言
  2. 整体架构
  3. 数据模型
  4. 记忆提取
  5. 向量化存储
  6. 记忆召回与对话注入
  7. 生命周期管理
  8. 踩坑记录
  9. 总结

1. 引言

你有没有遇到过这样的场景——用户跟 AI 助手聊了三天,第四天打开对话,AI 完全不记得之前说过什么。用户说”我上次提到的那个项目”,AI 只能回一句”请问您指的是哪个项目?”。

这就是无状态对话的核心痛点:每次对话都是一张白纸

市面上有不少记忆框架(比如 mem0)试图解决这个问题,但在实际落地时我们发现几个绕不开的限制:

  • 黑盒提取逻辑:无法精确控制”记住什么、忘记什么”
  • 存储耦合:通常绑定特定向量数据库,难以与已有基础设施整合
  • 生命周期缺失:记忆只增不减,随着对话积累会变成噪声源

所以我们选择自己实现一套完整的记忆管理系统,核心闭环只有五步:

提取 → 向量化存储 → 召回 → 注入 → 清理

本文将以一个实际的 FastAPI + PostgreSQL 项目为背景,完整拆解这套方案的架构与实现,记忆提取/更新/删除,向量化存储,召回,记忆应用,过期记忆清理全流程都可以在本文中进行参考。

提示一下,本文中虽然是使用 DIFY 工作流提取记忆,使用 RAGFLOW 作为对话服务,实现方案实际上是通用的,对这些工具做了弱化处理。


2. 整体架构

全景流程图

用户发送消息
     │
     ▼
┌──────────────────────────────────────────────────────────┐
│                    对话引擎                               │
│                                                          │
│  ① 向量检索记忆  ──→  ② 格式化记忆文本  ──→  ③ 注入提示词    │
│                                                          │
└──────────────────────────┬───────────────────────────────┘
                           │
                           ▼
                     LLM 生成回复
                           │
                           ▼
                     用户收到回答
                           │
        ┌──────────────────┴──────────────────┐
        │          后台定时任务(每5分钟)        │
        │                                     │
        │  ④ 扫描新对话                        │
        │     │                               │
        │     ▼                               │
        │  ⑤ LLM 提取记忆(add/update/delete) │
        │     │                               │
        │     ▼                               │
        │  ⑥ 写入 PostgreSQL + pgvector       │
        └─────────────────────────────────────┘
                           │
        ┌──────────────────┴──────────────────┐
        │       后台定时任务(每天凌晨)          │
        │                                     │
        │  ⑦ 清理过期的知识类记忆                │
        └─────────────────────────────────────┘

核心组件

组件 职责
PostgreSQL + pgvector 存储记忆文本和向量,支持余弦距离检索
Embedding 模型 将文本转为向量(bge-m3,1024 维)
LLM 工作流 分析对话历史,决定 add/update/delete 操作
定时任务 增量提取记忆、清理过期记忆

两类记忆

我们将用户记忆分为两类,它们在召回策略和生命周期上完全不同:

类别 说明 召回策略 生命周期
persona(用户画像) 用户的身份、偏好、习惯等 全量召回 永久保留
knowledge(专业知识) 对话中涉及的领域知识、事实 向量相似度检索 定时过期清理

为什么这样分?用户画像数量少且始终相关(”用户是化工行业的技术总监”),全量加载不会产生噪声。知识类记忆数量多且时效性强,必须按相关性检索,并定期淘汰过时信息。


3. 数据模型

Memory 表结构

CREATE TABLE memory (
    id          VARCHAR(36)  PRIMARY KEY,   -- UUID
    user_id     INTEGER      NOT NULL,      -- 用户ID
    fact        VARCHAR(2000) NOT NULL,     -- 记忆内容
    embedding   VECTOR(1024),               -- pgvector 向量字段
    category    VARCHAR(20)  NOT NULL,      -- persona | knowledge
    created_at  TIMESTAMP,
    updated_at  TIMESTAMP
);

CREATE INDEX ix_memory_user_id ON memory (user_id);

SQLModel + pgvector 的字段定义技巧

在 SQLModel(SQLAlchemy 2.0+ 的上层封装)中定义 pgvector 向量字段时,不能直接使用 Python 类型注解,需要通过 sa_column 绕过类型推断:

from pgvector.sqlalchemy import Vector
from sqlalchemy import Column
from sqlmodel import Field

class Memory(BaseModel, TimestampMixin, table=True):
    id: str = Field(
        default_factory=lambda: str(uuid.uuid4()),
        max_length=36,
        primary_key=True,
    )
    user_id: int = Field(index=True, nullable=False)
    fact: str = Field(max_length=2000, nullable=False)
    # 关键:用 sa_column 绕过 SQLModel 的类型推断
    embedding: Optional[Any] = Field(
        default=None,
        sa_column=Column(Vector(1024), nullable=True),
    )
    category: str = Field(max_length=20, nullable=False)

这里有两个细节:

  1. 类型声明为 Optional[Any]:SQLModel 会尝试将 Python 类型映射到 SQL 类型,但它不认识 Vector。声明为 Any 可以跳过这个推断,实际的列类型由 sa_column 决定。

  2. sa_column=Column(Vector(1024)):直接使用 SQLAlchemy 的 Column 来定义,pgvector 提供的 Vector(1024) 指定了向量维度。

会话表扩展

在会话表中添加两个字段来支持增量记忆提取:

class Conversation(table=True):
    # ... 其他字段 ...

    # 上次记忆提取时最后一个用户消息的 ID
    memory_extracted_message_id: str | None = Field(
        None, max_length=64, nullable=True,
    )
    # 上次记忆提取的时间戳
    memory_extracted_at: datetime | None = Field(
        None, nullable=True,
    )
  • memory_extracted_message_id:标记上次提取到哪条消息,下次只处理它之后的新消息
  • memory_extracted_at:与 last_conversation_at 对比,判断是否有新对话需要提取

4. 记忆提取

记忆提取是整个系统最复杂的环节。我们通过定时任务异步完成,不影响对话的实时响应。

9. 总结

回顾整个方案,核心设计决策有三个:

  1. 两类记忆、两种召回:persona 全量 + knowledge 向量检索,在全面性和精确性之间找到平衡
  2. LLM 驱动的增量提取:将”记什么、改什么、删什么”的决策交给 LLM,同时通过输入现有记忆实现去重和冲突消解
  3. 写入即向量化 + 定时清理:保证向量与文本的一致性,同时通过生命周期管理控制记忆噪声

整套方案的依赖非常轻量——PostgreSQL(pgvector 扩展)、一个 Embedding 模型、一个 LLM 工作流,再加上已有的 Celery 定时任务基础设施。不需要额外的向量数据库、不需要第三方记忆框架,所有数据都在自己的 PostgreSQL 里,可控、可查、可调。

对于中小规模的 AI 对话系统(用户量在万级以内),这套方案完全够用。如果规模再大,可以考虑在 pgvector 上加 HNSW 索引加速检索,或者将 Embedding 调用改为批量异步以提高吞吐。

最后,记忆管理的本质不是”存更多”,而是”存对的、忘该忘的”。希望本文对你有所启发。

标题:AI干货系列:在 AI 对话系统中实现长期记忆自主管理

原文链接:https://beltxman.com/4642.html

若无特殊说明本站内容为 行星带 原创,未经同意请勿转载。

发表评论

您的电子邮箱地址不会被公开。

Scroll to top
正在处理,请稍候 . . .