#AI#大模型#AI Agent

Agent 记忆写入门禁:从候选事实到可撤销提交

模型从对话中抽出的偏好和事实不能直接成为长期记忆。本文设计候选、确定性校验、去重提交、撤销血缘与污染率门禁。

本文要点

  • 主题聚焦:AI、大模型、AI Agent
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年7月8日·更新于 2026年7月20日

长期记忆最危险的事故不是少记一条,而是把一句玩笑、模型推断或已经否定的说法永久写成事实。用户说“这次先用 npm”,不等于偏好从 pnpm 改成 npm;模型说“你大概在上海”,更不应变成位置画像。Agent 记忆系统 介绍了记忆类型,本篇只解决生产写入:模型只能提出候选,确定性服务才有提交权。

把抽取结果定义为候选而不是事实

抽取器输出受 JSON Schema 约束的 MemoryProposal,携带租户、主体、规范化 scope、有限类型与键、来源偏移、版本、置信度和期限。确定性证据解析器先按来源子句位置与固定 schema 字段顺序分配 stableCandidateOrdinal,模型再填 value;因此 value 和模型输出顺序都不参与候选身份。

ts
type MemoryProposal = {
  proposalId: string;
  tenantId: string;
  subjectId: string;
  scope: { kind: string; key: string };
  kind: "preference" | "profile" | "decision" | "task_fact";
  normalizedKey: string;
  stableCandidateOrdinal: number;
  value: unknown;
  evidence: { messageId: string; start: number; end: number; sha256: string };
  extractorVersion: string;
  confidence: number;
  expiresAt: string | null;
};

const identity = { schema: 1, tenantId, subjectId, scope, kind, normalizedKey,
  messageId, start, end, stableCandidateOrdinal, extractorVersion };
const proposalId = sha256(jcs(identity));
const payloadHash = sha256(jcs({ value, confidence, expiresAt, evidenceHash }));

JCS 是 RFC 8785 的无歧义规范编码,identity 与 payload 分开哈希。于是同一候选重试可去重,值或期限被篡改会表现为同 ID 不同 payload,而不是悄悄得到新批准。

服务端重新读取 messageId,验证租户、文本哈希和偏移确实对应证据;模型不能自报 explicit=true。密码、令牌、支付数据和未列入 schema 的类型直接拒绝。来自外部网页或工具结果的候选继续携带 taint,按 不可信数据污点传播 隔离,不能因经过摘要就升级为用户事实。

用规则矩阵决定自动提交、确认或丢弃

显式句式“请记住 X”且值通过字段校验,可以自动提交短期可撤销记录;从行为推断的偏好进入 NEEDS_CONFIRMATION;敏感画像和跨主体陈述一律拒绝。置信度只用于排序,不能覆盖来源、同意和敏感性规则。

text
explicit + allowed_key + evidence_exact  -> COMMIT
explicit + conflicts_current             -> CONFIRM_REPLACE
inferred + reversible_preference         -> NEEDS_CONFIRMATION
secret | cross_tenant | missing_evidence -> REJECT

确认请求必须展示规范化后的具体事实与期限,用户批准绑定 proposalId + payloadHash。之后修改 value 会使批准失效,不能复用“用户同意记忆”这类宽泛授权。Agent 人工审批契约 的执行绑定同样适用于此处。

提交要幂等,并保留撤销路径

数据库对 (tenant_id, proposal_id) 建唯一约束。同一候选重放返回原结果;同 ID 不同 payload 进入协议冲突,但 proposal 去重不能串行化两个 proposal 对同一事实的竞争。事务对唯一 memory_fact_key(tenant,subject,scope,kind,normalized_key) 执行 INSERT ... ON CONFLICT DO NOTHING 后 SELECT ... FOR UPDATE,让原本不存在的事实也串行化;随后在锁内读取 current version 并重做替代判断。

只有无冲突时,事务才以 expectedCurrentVersion 做 CAS,写不可变 proposal、事实新版本、current 投影和 outbox 后一起提交。CAS 失败或锁内发现当前值已变化,低风险显式替代进入 CONFIRM_REPLACE,无法自动裁决者进入 CONFLICT,失败者绝不能覆盖。向量 worker 只消费已提交的 memoryId:version。后台任务队列 的至少一次投递由相同版本键去重。

每条记忆保存 derivedFrom 和 supersedes。用户撤销来源消息或拒绝候选时,先把状态改为 REVOKED 并发布失效事件,再清理投影、向量与缓存。不要物理删掉唯一审计证据后才尝试寻找副本。

用污染样本而不是成功 Demo 验收

固定测试集至少包含否定句、引用他人观点、反讽、临时选择、改口、同名主体、恶意工具输出、重复 webhook 和同键不同载荷。并发屏障让两个不同 proposal 在 current 尚不存在时写同一事实键,断言键行锁使其串行,至多一个自动提交,失败者进入 CONFIRM_REPLACE/CONFLICT;再从已有版本并发替代,断言没有丢失更新。还要验证“我不住北京”和“同事住北京”绝不能写成用户地址,撤销后召回为零。

离线计算 false_commit_rate = 错误自动提交数 / 自动提交总数、确认接受率、重复效果数和撤销残留数。发布时先只记录 proposal,不影响召回;人工复核一周真实样本后,仅给低风险键开放 5% 自动提交。任何跨租户写入、秘密入库或撤销残留都触发硬回滚。

回滚只关闭 commit 开关并让读取继续使用上一策略版本;新 proposal 留在隔离表供复盘,不能为了恢复可用性跳过 schema 或证据校验。Agent 回放评测 可用同一来源消息重跑不同抽取器。写入门禁的目标不是记得最多,而是让每条长期事实都能回答:谁提出、依据什么、谁批准、怎样撤销。

延伸阅读

Related

相关推荐