Mem0 插件 mem0-tour 技能实战:在 OpenCode 中分门别类地巡检 Agent 记忆库
在编码 Agent 的长期记忆体系中,"存进去容易,看全了难"。Mem0 插件(integrations/mem0-plugin)提供的 mem0-tour 技能解决的就是"看全"这件事:它以 /mem0-tour 命令为入口,把当前项目下已存储的全部记忆按类别分组、按数量排序并紧凑呈现,让你在一次会话启动前就能掌握这个项目的架构决策、踩坑教训、编码约定与工具链配置。读完本文,你将掌握该技能的三种运行模式(完整巡检、跨项目巡检、Peek 紧凑搜索)的完整执行协议,理解其背后的 user_id/app_id 身份解析机制、Mem0 平台的类别自动分类体系,以及"全量拉取 + 三路语义检索 + 按 ID 去重归组"这套记忆盘点算法的设计考量。
技能定位:mem0-tour 是什么
mem0-tour 是 mem0 插件为 OpenCode 环境注册的一个 skill,其定义文件位于 mem0-tour/SKILL.md。技能 frontmatter 中的描述明确了它的适用场景:
Browses all stored memories grouped by category with full content display. Use when reviewing all project memories, exploring stored knowledge, onboarding to a project, or getting an overview of captured decisions, conventions, and learnings.
也就是:审阅当前项目的全部记忆、探索已存储的知识、项目 onboarding,或对已捕获的决策/约定/经验获得全景概览。它与插件通用技能目录中的 tour/SKILL.md 是"同源异体"的关系——两者逻辑骨架一致(跨项目模式、Peek 模式、完整巡检三步流程相同),差异主要在展示层:通用版要求展示记忆全文、每组最多 10 条,而 OpenCode 版(本文主角)针对 OpenCode TUI 的渲染特性改为截断到 100 字符、每组最多 5 条,并增加了一张类别汇总表。这正对应 plugin.json 中对该插件的定位:"Persistent semantic memory... 16 slash commands, lifecycle hooks for auto-capture and metadata enforcement"——/mem0-tour 是其中负责"读侧总览"的命令。
技能运行所依赖的记忆身份(谁、哪个项目、哪个分支)不是技能自己现场计算的,而是由插件入口在每次工具调用时解析注入。从 opencode-mem0.ts 的源码结构看:
getUserId():优先取环境变量MEM0_USER_ID,否则取系统用户名(userInfo().username或$USER),兜底为unknown。这与 Python 侧钩子脚本 scripts/_identity.py 中resolve_user_id()的解析优先级(MEM0_USER_ID→$USER→"default")保持一致。getProjectId()(即技能中的app_id):优先取MEM0_APP_ID环境变量;否则执行git remote get-url origin并解析出owner-repo形式的 slug,跨克隆、worktree 与子目录都保持稳定;没有 remote 时退化为 git 仓库根目录名,再退化为当前目录名。Python 侧的 scripts/_project.py 还额外提供了~/.mem0/project_map.json手工映射与"remote hash 自愈"(目录改名/移动后仍能命中同一project_id)两级增强。getBranch():执行git branch --show-current,失败时回退为main。
理解了这三个值,才能理解下文所有 filters={"AND": [...]} 中占位符 <active_user_id>、<active_project_id>、<active_branch> 的真实来源。
模式一:跨项目巡检(--all-projects)
以 /mem0-tour --all-projects 或 /mem0-tour --all-projects auth middleware 这类带 --all-projects 标志的方式调用时,技能切换到跨项目模式——同一个用户可能同时维护多个仓库,此模式把所有项目下的记忆一次拉出对比:
- 调用
get_memories,过滤条件为filters={"AND": [{"user_id": "<active_user_id>"}]}、page_size=200——注意不带app_id过滤,以此覆盖该用户名下的所有项目; - 如果同时提供了搜索 query(如
auth middleware),再执行search_memories:query=<query>、filters={"AND": [{"user_id": "<active_user_id>"}]}、top_k=20,同样不带app_id; - 结果先按
app_id分组(项目级),每个项目内再按类别分组; - 按如下格式展示:
## <app_id_1> (<N> memories) ← current
**Architecture Decisions** — <memory content>
...
## <app_id_2> (<N> memories)
...
<N> memories across <M> projects
- 当前项目所在的分节标题用
← (current)标注,方便快速定位。
若未提供 --all-projects,则不进入此模式,走下文的标准单项目流程。
模式二:Peek 紧凑搜索(带 query 但不跨项目)
当 /mem0-tour 收到一个搜索参数(例如 /mem0-tour auth middleware)且没有 --all-projects 时,进入 Peek 模式,输出为紧凑的单行结果:
- 并行发起两个
search_memories调用,一个宽口径、一个定向:- 宽口径(Broad):
query=<query>,filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}]},top_k=10,rerank=true; - 定向(Targeted):在宽口径基础上追加
{"metadata": {"type": "decision"}}条件,top_k=5,rerank=true——单独把"决策类"记忆捞出来,因为架构决策往往比一般性陈述更有价值。
- 宽口径(Broad):
- 按记忆 ID 去重后,输出紧凑格式:
## mem0 search: "<query>" (<N> results)
1. [decision] Auth module uses JWT with RS256 keys (2025-05-15) [mem0:a3f8b2c1]
2. [anti_pattern] Don't use symmetric HS256 — leaked in env (2025-05-10) [mem0:7e2d9f4a]
3. [convention] All middleware in src/middleware/ (2025-05-08) [mem0:c4d5e6f7]
每行的固定格式为 <number>. [<type>] <content, 80 chars> (<date>) [mem0:<short_id>]:先给类型标签,正文截断到 80 字符,附日期与短 ID(mem0:<short_id> 方便后续用 /mem0-forget 等命令精确定位这条记忆)。
- 若无命中结果,输出:
No memories matching "<query>" for project <project_id>.
从插件 Python 侧的检索辅助层 scripts/_search.py 可以看到,这套 {"AND": [...]} 过滤格式与 app_id + metadata 子条件的拼接方式正是钩子脚本调用 POST /v3/memories/search/ 时使用的原生格式——技能文档与底层实现使用的是同一套平台 API 语义。另值得注意的是 should_rerank() 的注释说明:平台 REST 检索端点在不显式传 rerank 时不做重排,结果仅按原始向量相似度排序,最相关的一条记忆可能跌出 top_k 窗口;因此钩子注入路径默认开启 rerank(额外约 150–200ms 延迟在钩子的 curl 预算之内),可用环境变量 MEM0_RERANK 关闭。这也解释了为什么 tour 的各路检索调用都显式带上 rerank=true。
若既无 query 参数也无 --all-projects 标志,则进入下面的完整巡检流程。
模式三:完整巡检(Full Tour)六步执行协议
完整巡检是技能的主流程,共六步。它的设计核心是"全量拉取保证不漏、语义检索保证排序、按 ID 合并去重保证一致"。
Step 1: 拉取本项目全部记忆
调用 get_memories:
filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}, page_size=100
这一步不依赖语义相似度,确保项目下的每条记忆(无论内容形态如何)都进入工作集。
Step 2: 并行三路补充语义检索
并行执行三个 search_memories 调用,为关键主题拿到相关性排序的结果:
| query | top_k | rerank |
|---|---|---|
"architecture decisions design choices" |
10 | true |
"bugs errors failures anti-patterns" |
10 | true |
"project setup tooling conventions preferences" |
10 | true |
三者的过滤条件均为 filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}]}。
技能文档中有一条关键约束:在这三路调用中不要按 metadata.type 过滤。原因是平台会自动为每条记忆分配 categories 字段(自动分类),部分记忆虽有类别却没有显式的 metadata.type,一旦加该过滤就会系统性漏掉这批"自动分类记忆"。
Step 3: 合并去重并按三级优先规则归组
把 Step 1 的全量结果与 Step 2 的三路检索结果按记忆 ID 合并去重。对每条记忆,按以下优先级确定它所属的分组:
- 平台
categories字段(每条记忆上的数组,由 Mem0 平台自动分配)——取第一个类别值; metadata.type字段(若存在,通常由钩子/Agent 显式写入)——无categories时作为兜底;- 两者皆无的记忆归入 "other" 桶。
平台类别名/metadata.type 值到展示名的映射表(原文档完整继承):
| Platform category / metadata.type | Display name |
|---|---|
architecture decisions, architecture_decisions, decision |
Architecture Decisions |
anti patterns, anti_patterns, anti_pattern |
Anti-Patterns |
task learnings, task_learnings, task_learning |
Task Learnings |
coding conventions, coding_conventions, convention |
Coding Conventions |
user preferences, user_preferences, user_preference |
User Preferences |
project profile, project_profile |
Project Profile |
tooling setup, tooling_setup, environmental |
Tooling & Setup |
technology, professional_details |
Tooling & Setup |
session_state |
Session State |
compact_summary |
Compact Summaries |
| 其他一切值 | Other |
这张映射表并非凭空设计——这些类别名正是插件为编码场景定制的"编程类别体系"。从 scripts/setup_coding_categories.py 的 CODING_CATEGORIES 可见,插件在初始化时会通过 mem0ai SDK 的 client.project.update 把 Mem0 默认的"消费者取向"类别(food、hobbies、music 等)替换为面向代码工作的 12 类体系,包括 architecture_decisions、anti_patterns、task_learnings、tooling_setup、bug_fixes、coding_conventions、user_preferences、dependency_decisions、performance_findings、security_constraints、testing_patterns、data_model、api_contracts、deployment_runbook 等。tour 展示样例中出现的 bug_fixes (78)、tooling_setup (119) 等分组,正是这一体系落地的结果;而映射表中"其他一切值归 Other"的兜底规则,也兼容了平台上未被该脚本覆盖的类别写法(空格变体、单复数变体)。
Step 4: 按数量降序展示
分组按记忆数降序排列。先输出类别汇总表:
mem0 tour
Session (ses_abc123) branch: main
Project: my-project - 349 memories
Category Count
-----------------------------------------
tooling_setup 119
bug_fixes 78
architecture_decisions 32
task_learnings 14
...
然后逐类别输出编号单行记忆,每条截断到最多 100 字符:
tooling_setup (119)
1. User requires that no git commit or push be performed without explicit permission...
2. OpenCode plugins are loaded from ~/.config/opencode/plugins/ for global installation...
3. Assistant determined that the symlink method for loading the Mem0 plugin was failing...
... and 116 more
bug_fixes (78)
1. Fixed getAll filter format from flat object to AND-wrapped array for mem0ai TS SDK v3...
2. Root cause of user_id mismatch: plugin derived kartik.labhshetwar from git email...
... and 76 more
规则:每个类别按时间新近度展示前 5 条;超过 5 条的类别以 ... and <N> more 提示余量;空类别整段跳过,不输出无内容分组。
Step 5: 输出总计
<N> memories across <M> categories
project: <project_id> branch: <active_branch>
Identity - user: <user_id> project: <project_id> branch: <branch>
最后一行把本次巡检的完整身份三元组(用户、项目、分支)回显出来,便于在多项目、多分支并存时确认"我看到的确实是这个项目的记忆"。
Step 6: 空态处理
若该项目下记忆数为零,输出:
No memories stored yet for project <project_id>.
Start working - mem0 captures learnings automatically, or use /mem0-remember to save something now.
即提示两条出路:正常开发(插件的生命周期钩子会自动捕获经验,auto_capture.py、on_stop.sh 等钩子负责),或立即用 /mem0-remember 手动存一条。
输出格式约束:为什么全文禁用 Markdown
技能文档末尾有一条强约束:输出中禁止使用任何 Markdown。原因是 OpenCode TUI 按字面量渲染文本——**bold**、## 标题、| 表格 | 语法都会以原始字符呈现出来,形成噪声。因此技能要求:用缩进表达结构、用短横线表达列表、用空格对齐列宽代替 Markdown 表格。
值得注意的是一个表面矛盾:跨项目模式与 Peek 模式的展示模板里写着 ## <app_id_1>、**Architecture Decisions**,而格式约束又禁用 Markdown。从文档语义理解,这些模板片段是面向"内容组织"的示意(表明层级与加粗意图),真正落到 OpenCode TUI 时必须按末条约束改写为纯文本(如用大写标题行 + 缩进替代 ## 与 **)。这也是 OpenCode 版技能相对通用版 tour/SKILL.md 的刻意分叉:通用宿主支持 Markdown 渲染时可保留原样,OpenCode 宿主则必须纯文本化。
小结
mem0-tour 是 mem0 插件"写侧自动捕获 + 读侧主动巡检"闭环中的总览入口。它的价值不在单条记忆的检索(那是 mem0-search/Peek 模式的活),而在于用一套确定性协议——全量拉取、三路语义检索、ID 去重、三级类别归组、数量降序展示——把分散在多轮会话中的记忆资产整理成一张可读的项目记忆地图。结合 scripts/setup_coding_categories.py 注入的编码类别体系、scripts/_project.py 保证 app_id 跨目录稳定的解析策略,以及平台 categories 自动分类能力,这一技能在 onboarding 新项目、跨项目对比知识沉淀、以及巡检记忆质量时都能直接复用。若你的 Agent 宿主同样是 OpenCode,部署该插件后直接输入 /mem0-tour、/mem0-tour <关键词> 或 /mem0-tour --all-projects 即可体验三种模式。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00