Agent 记忆系统落地:别把用户上下文当垃圾桶
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 的基本功。
更多推荐
所有评论(0)