Agent 记忆系统落地:别把用户上下文当垃圾桶

Agent 做到第二阶段,迟早会碰到记忆系统。用户希望它记住偏好、项目背景、历史任务和常用工具;产品也希望 Agent 越用越懂业务。但记忆不是把聊天记录全塞进向量库。那不叫智能,那叫把用户上下文当垃圾桶,迟早又贵又乱。

我见过一个团队的做法很有代表性:他们把用户过去三个月的聊天记录全部切分入库,然后用 RAG 做"个性化记忆"。上线第一周效果还行,Agent 能引用用户之前说过的话。第二周开始出问题——Agent 把用户去年随口提的"考虑明年换供应商"当成了当前任务计划,给用户生成了一个周报,里面赫然写着"推进供应商切换"。用户直接炸了,在群里说"你们这个 AI 是不是在帮我做决策?"

我做 Agent 记忆系统时,先把记忆分成三类:短期任务上下文、长期用户偏好、可审计业务事实。三类存储、召回和删除策略都不同。只有分清类型,Agent 才不会一边说自己懂用户,一边把过期信息拿出来胡乱发挥。

一、先分清记忆类型

短期上下文服务当前任务,比如"刚才生成的方案"、"上一轮确认的价格范围"。长期偏好服务体验,比如"输出偏简洁"、"技术文档用中文"、"代码风格偏好 gofmt"。业务事实则必须来自可靠系统,比如"客户合同状态"、"订单编号"、"会员等级"。

这三类不能混在一个向量集合里。不是说技术上不行,而是召回时根本分不清。用户问"我的订单怎么样了",Agent 的做法是把所有记忆一起做向量检索,结果可能把"用户喜欢简洁摘要"和"之前订单有延迟"两个完全不同的信息拼在一起生成回答——简洁是偏好,延迟是事实,但 Agent 可能说"您的订单延迟了,但我帮您简化了"。

最危险的是模型从聊天里推断业务事实。用户随口说"这个客户快签了",Agent 就记成"客户已签约",后面再生成周报,事故就来了。

flowchart TD
  A[用户输入] --> B{记忆类型判断}
  B --> C[短期任务上下文]
  B --> D[长期偏好]
  B --> E[业务事实]
  C --> C1[存储: Redis/内存]
  C1 --> C2[过期: 会话结束]
  D --> D1[存储: 结构化DB]
  D1 --> D2[管理: 用户可查看可删除]
  E --> E1[存储: 引用业务系统]
  E1 --> E2[来源: 只从系统同步]

这里有一个重要的工程细节:什么时候把一段对话"转存"进记忆?不是用户每说一句就存。比较好的做法是任务完成后做一次"记忆提取"——给模型这次对话的摘要,让它判断有哪些信息应该更新到用户偏好或业务事实中,生成记忆候选,由系统规则做最终决定(比如"推断类记忆默认不进长期偏好"、"业务事实必须校验来源系统")。

二、记忆对象要有来源和时效

每条记忆都要记录来源、置信度、更新时间和过期时间。没有这些字段,后续就无法判断该不该召回、该信多少、是否该过期。

type MemoryItem struct {
    ID         string
    UserID     string
    Kind       string        // session, preference, business_fact
    Content    string
    Source     string        // explicit: 用户明确告诉 Agent
                            // inferred: 模型从对话推断
                            // system_sync: 业务系统同步
    Confidence float64       // 0.0 ~ 1.0
    ExpiresAt  time.Time
    CreatedAt  time.Time
    UpdatedAt  time.Time
}

// 写入规则:系统层做最终判断,不让模型直接写入
func (m *MemoryItem) Validate() error {
    switch m.Source {
    case "system_sync":
        // 业务事实,置信度默认 1.0,但必须校验来源系统
        if m.Kind != "business_fact" {
            return errors.New("system_sync must be business_fact")
        }
        m.Confidence = 1.0
    case "inferred":
        // 模型推断的,默认低置信度
        if m.Confidence > 0.6 {
            return errors.New("inferred memory confidence too high")
        }
    case "explicit":
        // 用户明确说的,置信度较高
        if m.Confidence < 0.8 {
            m.Confidence = 0.9
        }
    }
    return nil
}

这里我会把 inferred 记忆默认放低权重,且需要用户确认后才能进入长期偏好。业务事实只接受系统同步,不接受模型猜。这个 validate 函数就是系统层判断——不是模型说了算,是规则说了算。

三、召回要按任务最小化

Agent 不是每次都要加载全部记忆。写邮件只需要语气偏好和客户资料,查订单只需要业务事实,做代码生成不需要用户生活偏好。召回范围越大,越容易把模型带偏,也越贵。

memory_policy:
  write_email:
    include: [preference, business_fact]
    max_items: 8
  query_order:
    include: [business_fact]
    max_items: 5
    require_system_sync: true  # 只召回系统同步的事实
  code_generation:
    include: [preference]
    max_items: 3
  weekly_report:
    include: [business_fact, preference]
    max_items: 15
    exclude_kind: [session]    # 不含临时上下文

这类策略最好写成配置,由系统控制。不要把"哪些记忆该用"完全交给模型自己判断。模型容易"联想过度"——你问个代码问题,它也可能把用户说过的"不喜欢加班"放进上下文,虽然无害,但浪费 token 和推理时间。

四、记忆要能删除和复盘

用户应该能看到 Agent 记住了什么,并能删除。后台也要能复盘:某次错误回答用了哪几条记忆,记忆来自哪里,是否过期。没有复盘能力,记忆系统出了错就只能靠猜。

生产里我会记录 memory trace:召回了哪些记忆、分数多少、是否进入 prompt。这样成本、质量和隐私边界都有证据。trace 不需要存太久,但最近 N 天的一定要能查——用户投诉 Agent "胡说八道"时,trace 是唯一的证据链。

记忆冲突也要有处理规则。用户曾经说"输出用英文",后来又说"这次用中文",系统要理解新指令的优先级高于历史偏好。每次任务的显式指令应该覆盖长期记忆,而不是把两者平等合并扔给模型。

还有隐私合规。不同国家对用户记忆的保存要求不同——GDPR 要求用户可删除、可导出、可被遗忘。如果记忆系统不能支持"按用户一键删除全部推断记忆",迟早会有合规问题。工程师在设计阶段就要把删除接口留出来,不要等法务来问再改。

五、总结

Agent 记忆系统不是聊天记录仓库。短期上下文、长期偏好、业务事实要分开存,记忆要有来源和时效,召回要按任务最小化,还要支持删除和复盘。

能记住不是本事,知道什么该忘、什么不能乱记,才是工程化 Agent 的基本功。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐