logo

Agent Memory:短期记忆、长期记忆与用户画像

Published on

Agent Memory:短期记忆、长期记忆与用户画像

很多 Agent Demo 都会说自己“记住了你”。用户上一轮说过喜欢简洁回答,下一轮它真的少写了几段,看起来很神奇。接着问题就来了:它到底记住了什么?记了多久?谁可以看到?用户改口以后,旧偏好会不会一直影响结果?

我见过一个系统,把所有聊天记录都塞进向量数据库,再在每次请求时检索几段相关内容。刚开始效果不错,时间一长却变得越来越奇怪:模型把用户几个月前的临时计划当成长期事实,把一次玩笑当成个人偏好,把已经撤回的内容继续带进答案。系统确实“记住”了很多东西,但它没有真正理解记忆。

Agent Memory 的难点不是存储,而是判断什么值得存、什么时候使用、什么时候更新,以及什么时候必须忘记。记忆越多不一定越聪明,未经管理的记忆往往只是另一种上下文噪声。

一、先区分四种记忆

1. 短期上下文

当前对话中的消息、工具结果和最近几步任务状态。它服务当前请求,通常有明确的生命周期,超过上下文预算就需要摘要或淘汰。

2. 长期事实

相对稳定、经过确认的信息,例如用户所在时区、项目名称或已经确认的技术栈。事实必须有来源和更新时间,不能因为模型说过一次就永久保存。

3. 用户偏好

回答风格、语言、通知方式和常用格式等。偏好可以改变,但应该允许用户查看和修改,不能把模型推测的习惯当成用户明确同意的设置。

4. 任务记忆

长任务中的目标、检查点、已完成步骤、待处理事项和外部操作结果。任务记忆服务的是一个具体工作,不应自动变成用户画像。

type MemoryKind = 'context' | 'fact' | 'preference' | 'task'

type Memory = {
  id: string
  userId: string
  tenantId: string
  kind: MemoryKind
  content: string
  source: 'user' | 'tool' | 'inference'
  confidence: number
  createdAt: string
  updatedAt: string
  expiresAt?: string
  consentRequired: boolean
}

类型分开以后,存储时间、读取范围和删除方式才有机会分别设计。一个临时任务的中间结果,不应该和长期偏好使用同一套策略。

二、短期记忆首先是上下文预算问题

短期记忆最常见的做法是把最近若干轮消息拼到 Prompt 里,但上下文窗口和成本都是有限的。可以按重要性选择:

当前问题和最近消息
当前任务目标与未完成步骤
最近工具结果
历史对话摘要
低价值闲聊逐步淘汰
function buildContext(messages: Message[], budget: number) {
  const recent = takeRecent(messages, budget * 0.5)
  const taskState = loadTaskState()
  const summary = loadConversationSummary()
  return fitTokens([summary, taskState, ...recent], budget)
}

摘要不是简单截断。它应该保留目标、约束、已确认事实、未完成事项和关键决策,去掉重复寒暄和已经失效的尝试。摘要本身也要标记生成时间和来源,不能把推测写成事实。

三、长期事实必须有来源和置信度

“用户住在上海”可能来自用户明确说明,也可能只是模型从一次物流问题里猜出来的。两者不能同等对待。

type MemoryEvidence = {
  memoryId: string
  sourceMessageId?: string
  sourceToolCallId?: string
  confirmedByUser: boolean
  confidence: number
  observedAt: string
}

只有用户明确说过、工具可靠返回或经过业务确认的信息,才适合作为长期事实。模型推测可以作为临时线索,但默认不应持久化,尤其是健康、财务、政治观点和家庭关系等敏感信息。

事实会变化,所以要有更新策略。用户说“我下个月搬到杭州”,系统不能立刻把“居住地”永久替换;它可能只是一个计划。记忆里应该区分当前事实、计划、历史事实和不确定推断。

四、用户画像不是越详细越好

用户画像很容易从“帮助用户”滑向“收集用户”。如果为了让回答更个性化,把所有对话自动总结成兴趣、性格、收入和关系,系统可能已经越过了用户能接受的边界。

我建议每条画像信息都回答三个问题:为什么需要它,用户是否知道,什么时候失效。无法回答用途的信息,不应该因为“以后也许有用”就保存。

type ProfileField = {
  name: string
  value: string
  purpose: string
  visibleToUser: boolean
  editableByUser: boolean
  retentionDays: number
  sensitivity: 'normal' | 'sensitive'
}

产品上可以提供“记住了什么”的页面,让用户查看、修改和删除。用户说“不要记住这件事”时,应该真正阻止进入长期记忆,同时处理已经写入的摘要、向量和缓存。

五、记忆读取也需要权限

不是所有记忆都能被所有任务读取。个人偏好可以用于个人对话,但不应该自动进入共享项目;一个项目的任务状态不能被另一个租户的 Agent 检索;管理员也不应因为拥有后台权限就默认看到所有用户私密记忆。

function canReadMemory(memory: Memory, scope: RequestScope) {
  if (memory.tenantId !== scope.tenantId) return false
  if (memory.kind === 'task' && !scope.taskIds.includes(memory.id)) return false
  if (memory.consentRequired && !scope.consentedMemoryKinds.includes(memory.kind)) {
    return false
  }
  return true
}

权限过滤必须在检索前和返回后都执行。向量相似度只能回答“内容像不像相关”,不能回答“当前用户能不能看”。

六、记忆写入不要完全交给模型

一个常见设计是让模型自己判断“这条信息值得记住吗”,然后直接调用 saveMemory。这很灵活,也很危险。模型可能把临时情绪、敏感信息或未经确认的推测写进去。

可以先让模型提出候选,再由规则和策略决定:

type MemoryCandidate = {
  content: string
  kind: MemoryKind
  reason: string
  sensitive: boolean
}

function acceptCandidate(candidate: MemoryCandidate) {
  if (candidate.sensitive) return 'needs_consent'
  if (candidate.kind === 'fact' && !candidate.reason) return 'reject'
  return 'review'
}

高敏感信息需要用户明确同意;低风险偏好可以提示用户并允许关闭;任务状态由工作流系统写入,不应该让模型自由总结后覆盖真实状态。

七、更新要避免旧记忆和新记忆打架

用户先说“我喜欢详细解释”,后来又说“以后简洁一点”。两条记忆都存在时,检索到哪一条取决于向量相似度,结果可能不稳定。

偏好和事实应该使用有版本的记录:

type MemoryRevision = {
  key: string
  value: string
  version: number
  validFrom: string
  validTo?: string
  supersedes?: string
  confirmedBy: 'user' | 'system'
}

更新时明确覆盖同一语义键,保留历史但默认读取当前有效版本。对于互相冲突的事实,不要让模型自行投票;应该标记冲突并请求用户确认,或交给业务规则解决。

八、过期和删除是记忆的一部分

记忆不是写进去就永远存在。任务完成后,临时记忆应自动过期;用户删除对话时,相关摘要、向量、缓存和备份要进入删除流程;租户离开后,不能只删除主表而留下搜索索引。

删除请求
按 memoryId 查找所有派生记录
删除数据库、向量和缓存
禁止再次写入训练或评测集
生成删除审计结果

定期清理也要有失败重试和核验。系统应该能回答“这条记忆现在还会在哪些地方被读取”,而不是只说“主记录已经删除”。

九、记忆检索要评测“帮忙还是添乱”

记忆系统有两个方向的错误:该记住的没找着,不该用的被找出来。评测时至少看:

  • 相关记忆召回率;
  • 过期记忆误用率;
  • 跨用户或跨租户泄漏率;
  • 冲突记忆处理准确率;
  • 用户要求忘记后的残留率;
  • 加入记忆后任务成功率是否真的提升。

如果加入长期记忆后,回答变得更个性化,但事实错误和隐私投诉增加,那就不是成功。记忆应该减少用户重复说明的负担,而不是迫使用户不断纠正系统的错误印象。

总结:真正的记忆包含忘记的能力

Agent Memory 最容易被理解成“给模型一个更大的聊天记录”,但真正设计以后会发现,它更像一个有权限、有生命周期、有证据来源的个人数据系统。

短期上下文解决当前对话,长期事实解决重复说明,偏好让交互更顺手,任务记忆让长流程可以继续。但每类记忆都要明确来源、置信度、读取范围、更新方式、过期时间和删除路径。

我现在不会因为 Agent 记住了更多东西就认为它更聪明。一个可靠的 Agent 应该知道什么值得记,什么只是临时话题,什么需要用户同意,什么已经过期,什么必须忘掉。记忆的价值不是积累数据,而是让用户少解释一次,同时仍然清楚地拥有自己的信息。

当系统能够让用户查看、修改和删除记忆,能够在冲突时承认不确定,能够在权限不足时拒绝读取,Agent 才真正拥有了值得信任的记忆。会记住是能力,会忘记是边界,而知道什么时候不该记住,才是成熟。

🤪 您也可以编辑此页: