☰
mem9 如何从一段对话中提炼永久记忆?Facts 事实抽取管线深度解析
2026/10/14 14:57:34 网站建设 项目流程
  • 人工智能
  • AI 应用
  • Agent 记忆
  • AI Agent
  • RAG
  • 后端
  • 前端
  • dsh-plugin

【免费下载链接】mem9

Unlimited memory for OpenClaw

项目地址:https://gitcode.com/gh_mirrors/me/mem9
点击查看免费下载

mem9 是面向 AI Agent 的持久化记忆服务,它的核心能力之一,就是从一段普通对话中自动提炼出可长期保存的"原子事实"(Facts),让 Agent 跨会话、跨设备都能记得你。本文带你完整看懂 mem9 的 Facts 事实抽取管线:从输入清洗、LLM 抽取契约、长期事实过滤,到时间归一化与来源追踪,每一步是如何把"闲聊"变成"永久记忆"的。

一、为什么需要"事实抽取"?

Agent 的原始对话是短暂的,但用户希望它记住稳定的信息:偏好、项目、关系、决策。mem9 把记忆分成三类,各司其职:

记忆类型含义主要写入路径
session原始对话轮次,保留上下文SessionService.BulkCreate
insight从对话中提炼的长期事实Facts 抽取 + Reconcile(本文主角)
pinned用户显式要求固定的内容MemoryService.CreatePinned

换句话说:session 是"录音笔",insight 才是"笔记"。Facts 抽取管线负责的就是后者——它不做整段总结,而是生成一条条可独立检索、适合长期保存的原子陈述。

二、Facts 抽取管线全景:五步走

整条管线由 IngestService 编排,Handler 层的入口在 ingestMessages。完整流程可以概括为五步:

  1. 输入预处理:清洗插件注入内容、去空消息、按 rune 上限截断
  2. LLM 抽取:按"抽取契约"输出 JSON 格式的原子事实
  3. 长期事实过滤:两层门禁丢弃短期、临时性内容
  4. 时间归一化:把"昨天""下周三"解析为结构化时间元数据
  5. 来源追踪:为每条事实标注它出自哪几条原始消息

官方中文文档对这条链路的完整描述见 facts-extraction.zh-CN.md,四条算法链路的导读见 README.zh-CN.md。

三、输入预处理:先给对话"消毒"

在把对话交给 LLM 之前,stripInjectedContext 会先移除插件注入的<relevant-memories>内容——这一步很关键,否则上一次召回的记忆会被当作新事实再写回数据库,形成"记忆自我复制"的反馈环。

随后 prepareExtractionInputWithPolicy 完成三件事:

  • 跳过空消息,规范化 role 与 content,并记录清洗后消息对应的原始索引(供后续来源追踪用)
  • 长度控制:整段对话最多保留 1,000,000 个 rune;单条content入口最多 32,000 个 rune
  • 角色准入:默认只允许user消息贡献事实,system和tool永远不参与;开启MNEMO_FACT_EXTRACTION_INCLUDE_ASSISTANT后,assistant也只允许贡献关于用户世界、项目、决策的具体长期断言,建议、猜测、自我描述均被排除

四、LLM 抽取契约:什么样的句子才配成为"事实"?

mem9 对 LLM 有一套严格的"输出契约",这是保证记忆质量的第一道防线:

  • 原子化:每个事实必须是单一、自包含、可独立检索的陈述。因果、条件、先后这类强语义关系允许保留在一条事实里,避免拆散后丢失关系
  • 保真:保留原始语言和具体名称,尽量把"他""那个项目"替换为实体名,但不猜测不清楚的指代
  • 附标签:每条事实可带 1–3 个短小写 tags,用于后续检索与分类
  • 必须返回 JSON:首次解析失败时,extractFactsAndTagsWithRouting 会发起一次"JSON 修复重试";仍失败则依次尝试兼容旧版{"facts":["..."]}字符串数组格式、以及字段被错误展开到顶层的 flattened-fact 格式。三种手段都救不回来,Phase 1 才返回错误

这种"一次重试 + 两级降级"的设计,让管线在 LLM 输出不稳定时依然鲁棒。

五、长期事实过滤:两层门禁拦住"临时话"

不是所有值得说的话都值得记一辈子。mem9 用两层门禁保证只有 durable(可持续)事实能进入长期记忆:

  • 第一层(Prompt 层):要求 LLM 只产出 durable facts,并为可疑内容打上fact_type
  • 第二层(服务端确定性兜底):filterLongTermFacts 用fact_type判断 + 中英文正则 guard 双重检查

以下五类内容会被直接丢弃,不进入后续 Reconcile:

类型含义示例
query_intent用户只是在提问、搜索"帮我查下部署日志"
transient_status当前短暂状态"我现在正在健身"
ephemeral_intent一次性意图或请求"帮我临时监控一下这个接口"
activity_log单次饮食/体重/训练/睡眠记录"今天吃了 60g 蛋白质"
operational_log调试、导入、运行状态日志"冒烟测试通过"

细节上很讲究:服务端正则(见 ingest.go 中的chineseEphemeralIntentRE、chineseActivityLogRE等)特意避免误伤——普通人物、地点、项目、事件或承诺,不会因为包含"今天/明天/计划"就被丢弃;带"和某某一起"这类社交叙事线索的事件,也不会被简单当成 activity log。

六、时间归一化:让"昨天"也能被精确检索

"昨天开会讨论了迁移方案"——这句话的价值很大,但"昨天"是相对时间。

normalizeTemporalFacts 会把事实中的时间解析为结构化的TemporalMetadata(含kind、anchor_source、granularity、resolved_start、resolved_end、display等字段),并区分四种锚点:显式绝对时间、本句锚定的相对时间、消息头锚定的相对时间、以当前时间为锚的指示性时间。

注意一个设计取舍:正文保持自然语言时间,不把[time: ...]硬塞进文本;只有 Recall 或 Reconcile 需要精确匹配时,TemporalRecallProjection才临时投影出只读的[time: ...]后缀。正文干净,检索精准,两全其美。

七、来源追踪:每条记忆都能"指认"原始对话

记忆最怕"无根"——你不知道它从哪来,就无法信任它。

annotateFactsWithSourceSeqs 会为每条事实标注source_seqs(最多 6 个来源消息序号)和source_turns摘要。如果 LLM 没直接返回来源,服务会用"事实文本与候选源消息的 token 重合度"推断归属。

这一设计带来两个好处:

  1. 任何一条 insight 都可以追溯回原始对话轮次,在 Dashboard 中可审计
  2. 后续 Reconcile 改写事实文本后,仍可通过相似 token 重新关联到最可能的来源,记忆链条不断裂

八、空结果与降级语义:失败≠错误

两个新手容易误解的点,值得专门说明:

  • "没抽到事实"是正常结果,不是故障。用户聊的全是临时话题时,facts 为空,但原始 session turns 仍被独立保存——长期 insight 不创建,对话本身没丢
  • 无 LLM 时自动降级:MNEMO_LLM_API_KEY未配置或 LLM 不可用时,Ingest 会改走 raw 路径,把整段原文直接写入,保证记忆写入不中断

排障时建议按"源角色不允许 → LLM 输出为空 → 被长期门禁过滤 → JSON 无法恢复"的顺序定位,详见官方文档的排障检查点。

写在最后:在 Dashboard 里"看见"记忆生长

抽取出来的记忆不止躺在数据库里。mem9 的 dashboard/ 提供可视化面板,把记忆数据渲染成可交互的像素农场场景,让"Agent 记住了什么"变得直观可感:

至此,一条完整链路就串起来了:对话 → Facts 抽取 → 过滤与归一化 → Reconcile 判定 ADD/UPDATE/DELETE/NOOP → 写入持久化存储 → 未来 Recall。想继续深入,推荐阅读官方文档集:Reconcile 流程、记忆存入流程、Recall 算法。

  • 人工智能
  • AI 应用
  • Agent 记忆
  • AI Agent
  • RAG
  • 后端
  • 前端
  • dsh-plugin

【免费下载链接】mem9

Unlimited memory for OpenClaw

项目地址:https://gitcode.com/gh_mirrors/me/mem9
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询