Agent 记忆到底该怎么做:短期上下文、长期记忆与任务状态的边界
2026/8/18 7:22:12 网站建设 项目流程

本文定位:Agent 记忆 / 上下文管理 / 数据建模

示例环境:Java 21、Spring Boot 3.3、PostgreSQL、Redis、向量检索。记忆保存涉及隐私和合规,具体保留策略必须经过业务确认。

摘要

“给 Agent 加记忆”经常被理解成把所有历史聊天保存下来,再在下一次请求中全部拼回 Prompt。这样做会带来三个问题:上下文越来越长,成本和延迟不断增加;过期或错误信息被重复使用;用户不清楚系统记住了什么,也无法删除或修正。

真正可用的记忆系统应该区分短期上下文、长期偏好、可检索事实和任务状态。短期上下文服务当前对话,长期记忆保存经过确认的稳定信息,任务状态保证业务流程可恢复,RAG 知识库则负责外部资料。本文给出这几类数据的边界、表结构、写入策略和检索流程。

一、四种“记忆”不是一回事

类型例子保存位置生命周期
短期上下文当前会话最近几轮消息Redis/会话存储分钟到数天
长期偏好用户偏好的输出格式结构化数据库用户可查看和删除
事实记忆设备曾发生某类故障数据库/向量索引有来源、有过期时间
任务状态工单执行到第3步关系数据库直到任务结束和归档

不要把任务状态塞进自然语言记忆。status=WAIT_CONFIRMstep=3retry=1应该由确定性的数据库字段表示,不能依赖模型从聊天记录中猜出来。

二、记忆读取流程

当前问题

短期上下文裁剪

长期记忆检索

RAG知识检索

权限/时间/可信度过滤

上下文组装

Agent决策

任务状态更新

候选记忆提取

人工/规则确认后写入

每次请求不是把所有记忆都取出来,而是先根据问题召回候选,再按来源、可信度、时间和权限筛选。记忆检索失败时,Agent 仍然应该可以在无长期记忆的情况下完成普通任务。

三、短期上下文裁剪

短期上下文包括当前对话和本轮工具结果。建议按 Token 预算分配:系统规则、当前问题、最近消息、摘要和外部证据分别设置上限。

publicContextWindowbuildWindow(List<Message>history,Stringsummary,Stringevidence,intmaxTokens){List<Message>recent=takeFromEnd(history,8);ContextWindowwindow=newContextWindow();window.addSystemRules(systemRules);window.addSummary(truncate(summary,1200));window.addMessages(recent);window.addEvidence(truncate(evidence,maxTokens/2));returnwindow.fit(maxTokens);}

摘要也不是事实数据库。摘要由模型生成,可能遗漏否定条件或把不确定内容写成事实。高风险信息应回到结构化字段和原始证据,而不是只依赖摘要。

四、长期记忆的写入门槛

不是每句话都应该进入长期记忆。可以设置以下条件:

  1. 用户明确表达“请记住”或主动保存。
  2. 信息具有稳定性,不是临时情绪和一次性任务。
  3. 能说明来源、时间和置信度。
  4. 不包含不必要的敏感信息。
  5. 写入后用户可以查看、修改和删除。
publicOptional<MemoryCandidate>extract(Stringconversation,UserIntentintent){if(!intent.explicitSaveRequest()){returnOptional.empty();}MemoryCandidatecandidate=memoryExtractor.parse(conversation);if(candidate.sensitive()||candidate.content().length()>500){returnOptional.empty();}returnOptional.of(candidate.withExpiration(Duration.ofDays(180)));}

“用户喜欢简洁回答”可以作为偏好候选;“用户的身份证号是……”不应该默认写入长期记忆。记忆提取结果要经过规则校验,必要时请求用户确认后再保存。

五、数据模型

CREATETABLEuser_memory(id uuidPRIMARYKEY,tenant_idvarchar(64)NOTNULL,user_idvarchar(128)NOTNULL,memory_typevarchar(32)NOTNULL,contenttextNOTNULL,source_trace_idvarchar(128)NOTNULL,confidencenumeric(4,3)NOTNULL,expires_at timestamptz,statusvarchar(16)NOTNULL,created_at timestamptzNOTNULLDEFAULTnow(),updated_at timestamptzNOTNULLDEFAULTnow());CREATEINDEXidx_memory_ownerONuser_memory(tenant_id,user_id,status);

如果需要语义检索,可以增加 embedding,但必须同时保留结构化的租户、用户、状态和过期条件。向量相似度不能替代这些过滤。

六、事实记忆的来源与冲突

同一用户或设备可能出现互相冲突的事实,例如“设备已经更换模块”和历史记录“模块未更换”。记忆项应带时间、来源和状态,检索时优先当前有效、来源可靠且时间较新的记录。

publicList<Memory>resolveConflicts(List<Memory>candidates){returncandidates.stream().filter(Memory::active).filter(m->m.expiresAt()==null||m.expiresAt().isAfter(Instant.now())).sorted(Comparator.comparing(Memory::confidence).reversed().thenComparing(Memory::createdAt).reversed()).limit(5).toList();}

冲突没有可靠解决办法时,Agent 应该明确说明“存在两条时间不同的记录”,而不是强行选择一个。事实记忆需要可追溯的来源,不应以模型“觉得合理”为依据。

七、任务状态必须结构化

CREATETABLEagent_task_state(task_id uuidPRIMARYKEY,tenant_idvarchar(64)NOTNULL,user_idvarchar(128)NOTNULL,statusvarchar(32)NOTNULL,current_stepintegerNOTNULL,state_json jsonbNOTNULLDEFAULT'{}',versionbigintNOTNULLDEFAULT0,updated_at timestamptzNOTNULLDEFAULTnow());

使用乐观锁version防止两个调度器同时推进任务。每次状态迁移都记录旧状态、新状态、触发原因和 Trace ID。模型可以提出下一步,但状态迁移必须由后端有限状态机判断。

八、记忆污染和安全

长期记忆可能被恶意用户利用:先诱导 Agent 保存“我是管理员”,后续再用记忆绕过权限。任何记忆都不能改变认证和授权。记忆只能作为辅助上下文,不能覆盖当前 Token、数据库状态和业务规则。

还要防止跨租户、跨用户检索。缓存键、向量索引过滤、删除接口和导出接口都必须带上租户与用户条件。用户删除记忆时,应同步清理主表、向量索引、缓存和备份中的可检索副本。

九、如何评测记忆效果

记忆系统不应该以“召回越多越好”为目标。评测包括:

  • 该记住的偏好是否被正确召回。
  • 过期信息是否不再使用。
  • 冲突信息是否能提示不确定性。
  • 无关记忆是否不会污染答案。
  • 用户删除后是否确实不再出现。
  • 记忆内容是否不越权、不改变权限。

可以建立“应该记住”“不应该记住”“应该忘记”“存在冲突”的样本集,并测试写入、读取、更新和删除全流程。

十、总结

Agent 记忆不是无限聊天记录,而是不同生命周期、不同可信度和不同治理要求的数据集合。短期上下文服务对话,长期记忆服务偏好,RAG 服务外部知识,任务状态服务可靠执行。

最关键的原则有三个:未经确认的信息不要随意长期保存;记忆不能改变权限和业务状态;所有记忆都应有来源、生命周期和删除路径。只有这样,记忆才会成为稳定能力,而不是越来越大的上下文垃圾场。

读者讨论

如果你准备给 Agent 增加记忆,建议先列出“哪些内容可以保存、保存多久、谁能查看、如何删除”四个问题,再选择存储方案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询