首页
/ mem0 import 技能实战:从导出文件、第三方 AI 工具与 MEMORY.md 迁移记忆到当前项目

mem0 import 技能实战:从导出文件、第三方 AI 工具与 MEMORY.md 迁移记忆到当前项目

2026-09-04 22:44:53作者:曹令琨Iris

mem0-plugin 的 import 技能负责把已有知识灌入当前项目的 Mem0 记忆空间,覆盖三种来源:mem0 自己导出的 Markdown 备份文件、其他 AI 编码工具(Cursor / Copilot / Cline / Continue)的项目配置,以及 Claude Code 原生的 MEMORY.md。读完本文,你能完整掌握 /mem0:import 的五步执行流程、--tools 迁移子命令、导出文件的块级格式与解析器实现细节,并能通过测试用例验证导入结果的正确性。

三种导入来源与适用场景

import 技能的定义位于 SKILL.md,其 frontmatter 中的 description 明确了四个使用场景:从另一个项目迁移、从备份恢复、导入 Claude Code 原生 MEMORY.md 内容、为已有知识初始化新项目。具体到导入来源,分三条路径:

来源 触发方式 底层脚本
mem0 导出文件(mem0-export-*.md /mem0:import <filename> 或无参数自动发现 parse_export_file.py
第三方 AI 工具配置 /mem0:import --tools import_competing_tools.py
Claude Code 原生 MEMORY.md 传入路径,或会话启动钩子检测到后用户选择导入 直接调用 add_memory(无独立脚本)

导出文件的"另一半"由姊妹技能 export 生成,格式定义在 export/SKILL.md 中,命名约定为 mem0-export-<project_id>-<YYYY-MM-DD>.md(日期取 UTC)。两者构成一对可逆的"备份—恢复"通道,这也是本文解析器部分的重点。

标准导入流程:五步解析与写入

Step 1:确定要导入的导出文件

若用户通过 /mem0:import <filename> 显式指定了文件名,直接使用该文件。否则在当前目录列出文件名包含 mem0-export.md 文件:

ls -1 *.md 2>/dev/null | grep mem0-export || echo "No export files found"
  • 找到多个文件时,询问用户选择哪一个导入;
  • 一个都没找到时,输出提示并引导用户:
No mem0-export files found in the current directory.
Run /mem0:export first, or provide the filename: /mem0:import <path-to-file>

Step 2:解析导出文件为 JSON

先确定插件根目录,按当前平台使用对应变量:

  • Claude Code:${CLAUDE_PLUGIN_ROOT}
  • Codex:${CODEX_PLUGIN_ROOT}
  • Cursor:${CURSOR_PLUGIN_ROOT}

然后运行解析器脚本,将 Markdown 导出文件提取为 JSON 记录数组:

python3 "<PLUGIN_ROOT>/scripts/parse_export_file.py" "<path-to-export-file>"

输出的 JSON 数组中,每个元素包含以下字段:

  • id — 原始记忆 ID(仅用于参考;导入时会分配新 ID)
  • type — 元数据类型
  • confidence — 元数据置信度
  • branch — 元数据分支
  • files — 关联文件列表
  • categories — 分类列表
  • content — 记忆正文文本

脚本失败或输出 [] 时,打印 Failed to parse <filename> or file contains no valid memory blocks. 并停止。

源码层解析实现parse_export_file.py 的核心是 parse_blocksL33-L94)。导出文件由恰好为 --- 的行分隔成块,结构上奇数索引是 frontmatter、偶数索引是正文。解析逻辑有三个值得注意的细节:

  1. 成对扫描:从索引 1 开始按 frontmatter + content 成对取值(i += 2),frontmatter 用 _parse_frontmatterL97-L113)逐行匹配 key: value,且只用第一个冒号作分隔符,因此时间戳等含冒号的值不会被截断——测试 test_parse_blocks_value_with_colon 专门验证了这一点(test_parse_export_file.py)。
  2. 列表字段拆分filescategories 在文件中是逗号分隔的单行值,_parse_list_fieldL116-L123)将其拆成去除空白的列表,空值返回 []
  3. 空内容过滤content 为空的块被直接跳过(对应 SKILL.md 中"跳过 content 为空的记录"的防御性要求),标量字段缺失时默认为空字符串、列表字段缺失时默认为空列表。

main 入口(L126-L146)保证"总是以 0 退出"的约定:参数缺失、文件不存在或读取失败时,都向 stderr 输出错误信息、向 stdout 输出 [] 并退出码为 0,方便上游以统一方式判断"无有效记录"。

Step 3:解析身份(user_id 与 project_id)

确定活动身份:

  • user_idMEM0_USER_ID 环境变量 → $USER"default" 三级回退;
  • project_id(作为 app_id 使用):MEM0_PROJECT_ID 环境变量,或由项目解析器推导。

源码级回退链 _identity.pyresolve_user_idL71-L75)实现了上述 user_id 回退;而 project_id 的"项目解析器"在 _project.pyresolve_project_idL21-L73)中,实际优先级更长:

  1. MEM0_PROJECT_ID 显式覆盖;
  2. ~/.mem0/project_map.json 按当前工作目录查表,未命中时按 git remote URL 的 SHA-256 前 16 位(remote:<hash>)做自愈查表——文件夹被移动/重命名后仍能命中,并回写新 CWD 键;
  3. git remote get-url origin 推导 slug(如 git@github.com:mem0ai/mem0.gitmem0ai-mem0),支持 HTTPS / SSH / ssh:// / git:// 等写法;
  4. 兜底为当前目录名。

另外,API key 的解析(resolve_api_keyL55-L68)还有一条 Desktop 场景专属路径:桌面应用不继承 shell profile 的环境变量,因此会直接从 ~/.zshrc~/.bashrc 等文件中用正则提取 MEM0_API_KEY=... 的值。

Step 4:逐条调用 add_memory 导入

对解析出的每条记录调用 add_memory

  • text="<record.content>"
  • user_id=<active_user_id>
  • app_id=<active_project_id>
  • metadata={
    • "type": "<record.type>"(非空时)
    • "confidence": "<record.confidence>"(非空时)
    • "branch": "<record.branch>"(非空时)
    • "files": <record.files>(非空列表时)
    • "source": "import"
    • }
  • infer=False

三条关键约定:

  • 不传原始 id——平台会为导入的记忆分配新 ID;
  • 跳过 content 为空的记录(解析器已过滤,但调用侧仍需防御);
  • 单条失败不中断,继续导入并统计成功数。

infer=False 意味着写入的记忆不会被 LLM 重新提取/改写,而是原样落库——这对备份恢复语义至关重要,保证导入内容与导出内容一致。从 import_competing_tools.pypost_memoryL68-L99)可以看到 add_memory 在平台 API 上的真实形态:POST https://api.mem0.ai/v3/memories/add/,请求体为 messages(user 角色包裹 content)、user_idapp_idmetadatainfer 五个字段,鉴权使用 Authorization: Token <MEM0_API_KEY>,成功判定为 HTTP 200/201。

Step 5:打印结果

Imported <N> memories into project <project_id>

若存在失败,输出 Imported <N>/<total> memories into project <project_id> (<failed> failed)

导出文件块格式:import 与 export 的可逆性

理解 import 的前提是理解它解析的对象。export 技能(export/SKILL.md)规定每条记忆输出为一个精确格式的 YAML-frontmatter 块:

---
id: <memory.id>
created_at: <memory.created_at>
type: <memory.metadata.type or "">
confidence: <memory.metadata.confidence or "">
branch: <memory.metadata.branch or "">
files: <memory.metadata.files joined with ", " or "">
categories: <memory.categories joined with ", " or "">
---
<memory.memory or memory content string>

格式约束:--- 分隔符独占一行且无多余空白;filescategories 写成逗号分隔的单行;缺失字段写空字符串而非 "null";正文后留空行。export 侧通过 get_memories 分页拉取(filters={"AND": [{"user_id": ...}, {"app_id": ...}]}page_size=200),写入 mem0-export-<project_id>-<YYYY-MM-DD>.md

测试文件 test_parse_export_file.py 中的 test_parse_blocks_round_trip 正是按 export 技能的格式构造块并断言解析结果完全还原(含 filescategories 列表、created_at 附加字段),从工程上锁定了"export → import" 的往返一致性。该文件还覆盖了多块解析、缺失可选字段默认值、空内容过滤、多行正文、无分隔符内容返回空列表、CLI 无参数/缺文件输出 [] 且退出码为 0 等边界场景。

从第三方 AI 工具迁移:--tools 子命令

--tools 调用时(如 /mem0:import --tools),技能会检测并导入其他 AI 编码工具的项目配置文件。

支持的工具

Tool 文件/目录
Cursor .cursorrules
GitHub Copilot .github/copilot-instructions.md
Cline memory-bank/.md 文件目录)
Continue .continue/rules.md

T1:检测

test -f .cursorrules && echo "cursor: .cursorrules"
test -f .github/copilot-instructions.md && echo "copilot: .github/copilot-instructions.md"
test -d memory-bank/ && echo "cline: memory-bank/"
test -f .continue/rules.md && echo "continue: .continue/rules.md"

T2:询问用户

列出发现的文件,请用户以编号(逗号分隔)或 "all" 选择要导入的项。若什么都没找到,输出:

No competing tool configuration files found.
Checked: .cursorrules, .github/copilot-instructions.md, memory-bank/, .continue/rules.md

T3:执行导入

对每个选中的工具运行:

python3 "<PLUGIN_ROOT>/scripts/import_competing_tools.py" <tool> --path <file>

<tool> 取值为 cursorrulescopilotclinecontinue,与脚本中 COMMANDS 分发表(L274-L279)一一对应。

T4:报告

Imported <N> memories into <project_id> (cursor: <N>, copilot: <N>)

源码剖析:分块策略与幂等去重

import_competing_tools.py 的每个子命令都遵循"读文件 → 分块 → 过滤截断 → 逐块写入"的流水线,且各工具的切分策略不同:

  • cursorrules / copilot:按 ## 二级标题切分(split_by_headers),标题行保留在每个块开头;若文件没有二级标题,整文件作为一个块(见测试 test_split_by_headers_cursorrulestest_split_by_headers_no_headerstest_import_competing_tools.py);
  • clinememory-bank/ 目录下每个 .md 文件整体作为一个块(按文件名排序逐个处理);
  • continue.continue/rules.md 可能用 --- 水平线或 ## 标题分隔,故用 split_by_hr_or_headers(两者都作为分隔点,但 --- 后不带标题行,## 保留标题行)。

所有分块随后经过 _chunking.pyfilter_and_truncateL66-L75):短于 MIN_CHUNK_CHARS = 50 的块被丢弃,长于 MAX_CHUNK_CHARS = 10_000 的块被硬截断——这正对应 SKILL.md 中"sections <50 chars skipped,chunks >10k chars truncated" 的说明。

写入侧有两个工程细节:

  1. metadata 打标:每块以 metadata.type="project_profile"metadata.source=<tool>-import(如 cursor-importcopilot-import)、以及当前 git 分支写入,infer=False 原样落库;
  2. 幂等去重import_chunksL102-L126)将同一次导入的所有块拼接后做 SHA-256,与 ~/.mem0/import_hashes.json 中以 project_id:source:path 为键的历史哈希比对,内容未变化则整体跳过并打印 Already imported (unchanged) -- skipping: ...。这就是"safe to re-run" 的依据——重复运行 --tools 不会产生重复记忆。

导入 Claude Code 原生 MEMORY.md

当传入 Claude Code 原生 MEMORY.md 的路径(通常为 ~/.claude/projects/<proj-key>/memory/MEMORY.md),或者 on_session_start.sh 检测到原生 auto-memory 且用户选择导入时,执行以下流程:

  1. 读取文件。其内容为按行分隔的记忆条目(一行一个事实,可能带 - 前缀);
  2. 按非空行切分,每一行成为一条记忆;
  3. 跳过短于 20 字符的行,以及纯标题行(# 开头);
  4. 对每行调用 add_memory
    • text="<line>"
    • user_id=<active_user_id>
    • app_id=<active_project_id>
    • metadata={"type": "task_learning", "source": "memory-md-import", "confidence": 0.8}
    • infer=False
  5. 报告:Imported <N> memories from MEMORY.md into project <project_id>
  6. 建议关闭原生 auto-memory,避免双记忆系统:
To avoid duplicate memory systems, add to ~/.claude/settings.json:
  "autoMemoryEnabled": false

这条路径解决的是冷启动空窗问题:用户一直在用 Claude Code 内置记忆、切换到 mem0 时,既有知识不会丢失,而是以 task_learning 类型、0.8 置信度一次性迁移过来。

错误处理与故障排查

  • 解析脚本缺失:若 <PLUGIN_ROOT>/scripts/parse_export_file.py 不存在,打印错误并停止(通常意味着插件未完整安装);
  • add_memory 持续失败:例如认证错误(MEM0_API_KEY 未设置或失效)导致连续失败时,应报告问题并提前终止,而不是把剩余记忆全部打一遍失败请求。API key 的来源优先级与环境变量设置方式,可参考插件 README 的 Step 1(README.md);
  • 解析器总是退出码 0:如前所述,parse_export_file.py 的失败语义靠 stdout 的 [] 表达,上游(技能)需要以"输出是否为空数组"作为停止条件,而不是依赖退出码。

相关能力对照

import 只是 mem0-plugin 记忆生命周期的一环,可结合以下脚本理解完整链路:

  • export/SKILL.md:生成 import 所消费的备份文件,两者构成往返可逆的备份对;
  • auto_import.py:会话启动时后台自动导入 CLAUDE.mdAGENTS.md.cursorrules 等项目声明文件,同样采用 SHA-256 哈希跳过未变化文件(哈希存储于 ~/.mem0/file_hashes.json),并用文件锁防止并发重复执行——与 import_competing_tools.py 的幂等设计同源;
  • parse_mem0_config.py_identity.py:身份与配置解析的公共依赖。

小结

/mem0:import 是 mem0-plugin 中把"外部知识资产"转成平台记忆的统一入口:标准路径通过 parse_export_file.py--- 分隔的 frontmatter 块还原为带 type/confidence/branch/files/categories 元数据的记录,以 infer=False 原样写回,保证备份恢复不失真;--tools 路径按各工具的文件组织方式分块(50 字符下限、10k 字符截断),并以内容哈希实现幂等重跑;MEMORY.md 路径则以固定元数据(task_learning / 0.8 置信度)弥合从 Claude Code 原生记忆迁移过来的冷启动空窗。三条路径共享同一套身份解析(_identity.py + _project.py 的 project_id 四级回退链),测试文件 test_parse_export_file.pytest_import_competing_tools.py 则为上述解析、分块与往返行为提供了可复现的验证依据。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384