乐于分享
好东西不私藏

AI Agent 记忆写入控制器设计:别把临时指令写成长期偏好

AI Agent 记忆写入控制器设计:别把临时指令写成长期偏好

AI Agent 记忆写入控制器设计:别把临时指令写成长期偏好

1. 问题:长期记忆不能从对话摘要直接生成

很多 AI Agent 在实现长期记忆时,会采用一种看似自然的做法:每轮对话结束后,让大模型总结“值得记住的信息”,然后写入数据库或向量库。

这种方式实现简单,但很容易造成记忆污染。

例如,用户只是说了一句:

这次周报写短一点,只保留本周进展、当前风险和下周计划。

如果系统没有写入控制,就可能把它记成:

用户喜欢简短回答,只保留核心内容。

这就是典型错误。

用户要求的是“这次周报”更短,而不是以后所有回答都要短。如果系统把它写成长期偏好,后续用户让 Agent 写技术方案、需求文档或论文分析时,系统可能仍然强行压缩内容,导致回答深度不足。

所以,长期记忆的核心问题不是“怎么存”,而是:

什么信息有资格被写入长期记忆?

存储层解决“能不能记”,写入控制器解决“该不该记”。


2. 写入控制器不是摘要器

记忆写入控制器位于用户交互和长期记忆库之间。它不负责回答生成,也不负责记忆检索,而是在一轮交互结束后判断:哪些信息可以写入长期记忆,哪些信息只能用于当前任务,哪些信息应该直接跳过。

它的输入包括:

user_input:用户本轮输入agent_response:Agent 本轮回复session_state:当前任务状态existing_memories:已有长期记忆

它的输出不是一段自然语言摘要,而是一组结构化写入决策:

{ ”action”: ”WRITE”, ”memory_type”: ”constraint”, ”scope”: ”current_task”, ”content”: ”当前项目周报需要压缩篇幅,只保留本周进展、当前风险和下周计划。”, ”evidence”: ”用户明确要求“这次周报写短一点”。”, ”confidence”: 0.96, ”importance”: 5, ”expires_at”: ”task_finished”}

一个可用的写入控制器至少要判断四件事:

1. 这条信息来自用户明确表达,还是系统推断?2. 它是临时指令、任务约束,还是长期偏好?3. 它应该影响当前任务、同类任务,还是所有后续对话?4. 它在什么时候失效?

其中最关键的不是生成 content,而是确定 scopeevidenceconfidence 和 expires_at

content 说明系统记住什么;scope 说明这条记忆影响多大范围;evidence 说明系统凭什么这样记;confidence 说明系统有多确定;expires_at 说明这条记忆什么时候失效。


3. 一句话如何拆成两条候选记忆

仍然看这个输入:

这次周报写短一点,只保留本周进展、当前风险和下周计划。

这句话不能直接被总结成“用户喜欢简短回答”。更合理的做法是先拆成候选记忆。

[ { ”content”: ”当前项目周报需要压缩篇幅,只保留本周进展、当前风险和下周计划。”, ”memory_type”: ”constraint”, ”scope”: ”current_task”, ”evidence”: ”用户明确说“这次周报写短一点”。”, ”confidence”: 0.96 }, { ”content”: ”用户在工作汇报类写作中可能偏好先突出进展、风险和下一步计划。”, ”memory_type”: ”preference”, ”scope”: ”writing_tasks”, ”evidence”: ”由一次周报任务要求推断得到,不是用户直接声明。”, ”confidence”: 0.70 }]

这两条候选记忆的性质不同。

第一条是当前任务约束。它来自用户明确表达,置信度高,但只应该影响当前周报。

第二条是弱偏好推断。它可能对以后写工作汇报有参考价值,但并不是用户明确说出的长期偏好,所以不能写成高置信度全局记忆。

如果系统把这两条混在一起,就会出现过度泛化。

错误写法是:

{ ”content”: ”用户喜欢简短回答。”, ”memory_type”: ”preference”, ”scope”: ”global”, ”confidence”: 0.92}

正确写法是:

{ ”content”: ”当前项目周报需要压缩篇幅,只保留本周进展、当前风险和下周计划。”, ”memory_type”: ”constraint”, ”scope”: ”current_task”, ”evidence”: ”用户明确限定了这次周报的篇幅和结构。”, ”confidence”: 0.96, ”expires_at”: ”task_finished”}

也可以保留一条弱偏好,但必须限制范围并降低置信度:

{ ”content”: ”用户在工作汇报类写作中可能偏好先突出进展、风险和下一步计划。”, ”memory_type”: ”preference”, ”scope”: ”writing_tasks”, ”evidence”: ”由一次周报任务要求推断得到。”, ”confidence”: 0.70}

4. 单次写入决策链

为了让写入过程可解释,可以把一次用户输入拆成完整决策链。

阶段
判断结果
原始输入
用户要求这次周报写短,只保留进展、风险和计划
候选记忆 1
当前周报格式约束
候选记忆 2
工作汇报类写作的弱偏好
错误写入
global
 偏好:“用户喜欢简短回答”
正确作用域
当前任务约束写入 current_task
证据绑定
用户只说“这次周报”,不是长期声明
置信度设置
明确任务约束高置信;推断偏好低置信
生命周期设置
current_task
 记忆在任务结束后过期
最终动作
写入当前任务约束;弱偏好可低置信写入或请求确认

这条链路的重点是:写入控制器不是把一句话摘要成一条记忆,而是把用户输入拆成不同层级的信息,并分别决定是否写入、写到哪里、以多大权重写入、什么时候失效。


5. 四个关键字段:scope、evidence、confidence、expires_at

记忆写入质量主要由四个字段控制。

5.1 scope:控制作用范围

scope 是最重要的字段。很多记忆污染不是内容完全错误,而是作用范围写得太大。

可以先设计四类作用域:

current_task:只影响当前任务project:影响当前项目writing_tasks:影响写作类任务global:影响所有后续对话

例如:

这次周报写短一点。

应该优先写成:

{ ”scope”: ”current_task”}

而不是:

{ ”scope”: ”global}

只有用户明确说:

以后所有回答都尽量简洁。

才可能写入 global

作用域判定可以遵循一个保守原则:

没有长期信号,默认 current_task;没有同类任务信号,不提升到 writing_tasks;没有明确全局信号,不提升到 global

5.2 evidence:保存记忆来源

evidence 用来说明这条记忆从哪里来。

记忆内容回答“系统记住了什么”,证据字段回答“系统凭什么这样记”。

没有 evidence 的记忆,在后续冲突处理时很难判断可信度。例如:

{ ”content”: ”用户喜欢简短回答。”}

这条记忆本身缺少上下文,系统无法知道它是用户明确声明,还是模型从一次任务中推断出来的。

更合理的是:

{ ”content”: ”用户在当前周报任务中要求压缩篇幅。”, ”evidence”: ”用户说“这次周报写短一点”。”}

这样后续如果出现冲突,系统可以根据证据判断这条记忆是否应该继续生效。


5.3 confidence:区分用户事实和系统推断

confidence 决定一条记忆在后续系统中的话语权。

可以采用简单分级:

来源
示例
建议置信度
用户明确声明
“以后写周报时按进展、风险、计划组织”
0.85–0.98
多次行为一致
多次要求工作汇报压缩到关键事项
0.75–0.90
单次任务推断
从一次周报要求中推断偏好
0.55–0.75

因此,下面这种写法风险很高:

{ ”content”: ”用户喜欢简短回答。”, ”scope”: ”global”, ”confidence”: 0.92}

它把一次任务要求变成了高置信度全局偏好。

更合理的是:

{ ”content”: ”用户在工作汇报类写作中可能偏好突出进展、风险和下一步计划。”, ”scope”: ”writing_tasks”, ”confidence”: 0.70}

低置信度记忆可以保留,但在后续检索和上下文注入时应降低权重,不能覆盖用户明确表达的高置信度记忆。


5.4 expires_at:让任务约束及时失效

不是所有记忆都应该永久有效。

例如:

当前项目周报需要压缩篇幅。

这条记忆只对当前周报任务有效。任务完成后,它应该过期。

{ ”scope”: ”current_task”, ”expires_at”: ”task_finished”}

如果不设置过期条件,当前任务中的临时约束会继续影响后续任务。

因此,只要 scope 是 current_task,就应该考虑 expires_at。这能防止阶段性上下文长期污染 Agent 的行为。


6. 写入控制器的最小实现

最小可用版本不需要复杂模型,但必须保留可解释的判断流程。

用户交互 ↓候选记忆抽取 ↓证据绑定 ↓准入判断 ↓作用域判定 ↓置信度设置 ↓相似记忆检查 ↓执行 WRITE / SKIP / MERGE / UPDATE

关键伪代码如下:

def memory_write_controller(user_input, agent_response, session_state, user_id): candidates = extract_candidates(user_input, agent_response) decisions = [] for candidate in candidates: candidate.evidence = attach_evidence(candidate, user_input) if is_temporary_instruction(candidate): decisions.append(skip(candidate, reason=”current_turn_instruction”)) continue candidate.scope = assign_scope(candidate, user_input) candidate.confidence = assign_confidence(candidate) if candidate.scope == ”current_task”: candidate.expires_at = ”task_finished” similar = search_similar_memories(user_id, candidate) decision = resolve_memory_relation(candidate, similar) decisions.append(decision) apply_decisions(decisions) return decisions

其中 assign_scope 是核心判断函数:

def assign_scope(candidate, text): current_task_words = [”这次”, ”当前”, ”本次”, ”先”, ”暂时”] task_family_words = [”以后写”, ”写周报时”, ”写汇报时”, ”做这类任务时”] global_words = [”以后所有回答”, ”每次回答”, ”一直保持”] if contains_any(text, current_task_words): return ”current_task” if contains_any(text, task_family_words): return ”writing_tasks” if contains_any(text, global_words): return ”global” return ”current_task”

assign_confidence 可以先按证据来源实现:

def assign_confidence(candidate): if candidate.source == ”explicit_user_statement”: return 0.92 if candidate.source == ”repeated_behavior”: return 0.82 if candidate.source == ”single_task_inference”: return 0.65 return 0.50

这不是完整算法,但足以体现一个关键原则:默认收窄作用域,默认降低推断型记忆的置信度。


7. 数据结构:字段是风险控制手段

记忆表不需要一开始设计得很复杂,但必须支持作用域、证据、置信度和过期控制。

CREATE TABLE agent_memory ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id VARCHAR(64NOT NULL, agent_id VARCHAR(64NOT NULL, memory_type VARCHAR(32NOT NULL, scope VARCHAR(32NOT NULL, content TEXT NOT NULL, evidence TEXT, confidence DECIMAL(4,3DEFAULT 0.800, importance INT DEFAULT 3, source_session_id VARCHAR(64), source_message_id VARCHAR(64), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, expires_at DATETIME NULL, status VARCHAR(16DEFAULT 'active', version INT DEFAULT 1);

这些字段不是为了让表结构更复杂,而是为了控制具体风险。

风险
字段设计
临时指令被长期化
scope
expires_at
当前任务约束被全局化
scope
系统推断被事实化
evidence
confidence
旧偏好被错误覆盖
version
statusconfidence
低价值记忆污染检索
importance
confidence

字段设计必须服务风险治理。否则,记忆表只是保存文本的容器,无法支撑长期协作。


8. 如何测试写入控制器

记忆写入控制器不能只测试“数据是否成功写入数据库”,更要测试判断质量。

可以设计四类指标:

指标
检测的问题
误写率
是否把临时指令写进长期记忆
漏写率
是否错过明确长期偏好
作用域错误率
是否把 current_task 写成 global
冲突处理准确率
是否错误覆盖已有记忆

典型测试用例:

8.1 临时指令

输入:

这次回答短一点。

期望:

{ ”action”: ”SKIP”, ”reason”: ”current_turn_instruction”}

8.2 当前任务约束

输入:

这次周报写短一点,只保留本周进展、当前风险和下周计划。

期望:

{ ”action”: ”WRITE”, ”memory_type”: ”constraint”, ”scope”: ”current_task”, ”expires_at”: ”task_finished”}

8.3 明确长期偏好

输入:

以后写项目周报时,尽量先写进展、风险和下一步计划。

期望:

{ ”action”: ”WRITE”, ”memory_type”: ”preference”, ”scope”: ”writing_tasks”}

8.4 偏好覆盖风险

已有记忆:

用户偏好详细、系统化的技术解释。

新输入:

这次简单说。

期望:

{ ”action”: ”SKIP”, ”reason”: ”temporary_instruction_cannot_override_existing_preference”}

这些测试验证的是写入控制器的判断能力,而不是数据库写入能力。只有能稳定通过这类测试,长期记忆才不会在真实使用中逐渐失控。


9. 总结

AI Agent 的长期记忆不是聊天记录摘要,而是经过准入判断后的结构化上下文。

记忆写入控制器的核心价值,是在信息进入长期记忆前判断:是否该写入、写成什么类型、作用范围多大、证据是否充分、置信度是多少、什么时候过期。

在最小可用实现中,重点不在于复杂算法,而在于四个关键控制点:

scope:防止当前任务约束变成全局规则evidence:防止记忆来源不可追踪confidence:防止系统推断被当成用户事实expires_at:防止阶段性任务状态长期污染后续对话

真正可靠的长期记忆,不是记得越多越好,而是写入越克制、证据越清楚、作用域越准确、生命周期越可控越好。