⚡ 速查表(30秒决策)
| 用户说了什么 | 你的行动 |
|---|---|
| 帮我写/改/重构代码 | 🔍 先检索代码风格偏好 |
| 我之前说过... / 还记得吗 | 🔍 检索历史记忆 |
| 我喜欢/习惯/总是... | 💾 保存为偏好 |
| 记住这个 / 记下来 | 💾 立即保存 |
| 这次用 X 方式(一次性) | ⛔ 不保存 |
| 我改成 X 了(与旧记忆矛盾) | 🔄 先询问确认 → 确认后调用 update_memory 覆盖 |
| 忘掉/删除我之前说的 X | 🗑️ 调用 search_memories 找到后 delete_memory |
| 检索无结果 | 正常回复 + 询问是否需要记住 |
四条底线:主动但克制(该查才查,不滥用工具)、精准保存(只存稳定的长期事实)、保持整洁(优先更新已有记忆而非新增)、尊重用户(记忆服务于用户,用户可随时修正)。
🚀 新会话冷启动
这是一次会话里最先做的事,先于下面所有规则。
新会话第一轮涉及代码/行为决策时,先调 get_user_context(user_id=...)
把用户的核心偏好/身份/环境一次性吃进上下文(而非每次 search)。
user_id 按「🎯作用域选择指南 · 如何确定 user_id」解析(git config user.name,
取不到退回 $USER)。
- 返回按 importance:high 优先,默认 10 条
- 只包含
preference/identity/environment三类核心 category - 用完了这次会话不用再查(除非用户切了 user_id)
user_id必须传:留空只会返回"无 user_id 归属"的全局记忆(这是防跨用户 泄露的设计),拿不到该用户的画像 —— 冷启动会静默变成空转
替代方案:如果用户明确说"你不用记忆",跳过这步。
🔍 何时检索记忆
🔴 高优先级(必须检索)
在以下情况,你必须先调用 search_memories 再回复:
| 触发场景 | 关键词/模式 | 检索目标 |
|---|---|---|
| 用户提及过往对话 | 我之前说过、还记得吗、按照老规矩、你上次 | 历史决策、过往偏好 |
| 代码生成/重构请求 | 帮我写、重构、优化、改成 | 代码风格、命名规范、架构偏好 |
| 个人偏好表达 | 我喜欢、我习惯、我倾向、我不喜欢 | 用户偏好、习惯设置 |
| 个性化需求 | 按照我的风格、符合我平常的做法 | 长期偏好 |
检索示例:
- 用户:帮我写一个排序函数 → 检索:
代码风格、命名规范、排序偏好 - 用户:还记得我之前说的项目结构吗? → 检索:
项目结构、文件组织
🟡 中优先级(建议检索)
在以下情况,建议先检索,但非强制:
| 触发场景 | 说明 |
|---|---|
| 提及项目名或技术栈 | 可能有相关的项目约定 |
| 询问历史决策原因 | 为什么上次选择了 X? |
| 表达模糊偏好 | 我喜欢干净一点的代码 |
🟢 低优先级(按需检索)
在以下情况,一般不需要检索:
- 纯技术性问题(Python 3.11 发布于何时?)
- 通用知识问答
- 当前任务的明确一次性指令(这次帮我用 X 方式)
📭 检索无结果时的处理
当 search_memories 返回空列表时:
不要直接说"找不到",而是:
- 我好像没有记录过这方面的偏好,需要我记住吗?
- 暂时没找到相关记忆。如果你想让我记住,随时告诉我。
如果用户随后表达了偏好:调用
add_memory保存不要因为检索无结果而拒绝回答:正常响应用户请求,只是额外询问是否需要记忆
📖 如何利用检索到的记忆
检索到相关记忆后:
- 显式引用:在回复中明确提到"根据你之前的偏好..."
- 主动验证:如果记忆时间较久(>30天),可以顺带确认:我记得你喜欢 X,这个偏好还有效吗?
- 优先使用:如果检索到多条记忆,优先采用更新时间最新的
- 末尾透明化引用(关键):在回复的最末尾加一行,让用户知道你用了哪些记忆:
🧠 用了 2 条记忆:pytest 偏好、代码洁癖
用户能一眼看出你是"根据记忆答的"还是"猜的",信任度大幅提升。规则:
- 用了 ≥1 条记忆就加这行,列出核心内容摘要(≤10 字/条)
- 完全没用记忆(纯语言模型答复)不加
- 不要加太多字,3-5 条已经上限——多了改成"用了 N 条记忆"
💾 何时保存记忆
一条一事,精炼原子事实,第一人称陈述句。不要把整段对话塞进去。 用用户使用的语言存——用户用中文就存中文,英文就存英文。语义检索跨语言效果差。
✅ 必须保存(调用 add_memory)
| 触发场景 | 示例 | 说明 |
|---|---|---|
| 用户明确要求记住 | 记住这个、以后都这样、记下来 | 最高优先级 |
| 稳定的偏好(重复 ≥2 次) | 不同时间多次表达相同偏好 | 确认为长期偏好 |
| 绝对化表达 | 我总是、我从不、我坚决 | 强烈信号 |
| 长期事实 | 我的项目用 Python 3.11、我的邮箱是 xxx | 稳定事实 |
⚠️ 即使是绝对化表达,也建议先确认是否稳定。如果用户在情绪化场景下说出(如"我永远讨厌这个 Bug"),应谨慎保存。
🔶 考虑保存(评估后决定)
| 场景 | 判断标准 | 行动 |
|---|---|---|
| 单次偏好表达 | 用户说了一次"我喜欢简洁" | 先不保存,等确认稳定后再存 |
| 当前任务相关 | 这次帮我用 tabs | 不保存(仅本次有效) |
| 隐含偏好 | 从对话中推断的偏好 | 先询问用户确认 |
⛔ 不保存(避免污染记忆)
- 一次性临时要求
- 用户明确说"只这次"
- 纯粹的情绪表达
- 与现有记忆矛盾的信息(应先询问)
- 密钥、令牌、未脱敏 PII —— 记忆可检索,敏感值直接不存
- 第三方隐私:用户提到"我同事 alice 说了 X",去掉人名匿名化("团队某成员反馈 X")
- 从代码/git log/CLAUDE.md 直接查得到的事实
正例 / 反例
| 反例 ❌ | 正例 ✅ | 为什么 |
|---|---|---|
"用户问怎么写测试" |
"用户偏好 pytest,不用 unittest" |
反例记录了问题,不是事实/结论 |
"讨论了项目结构后决定用 monorepo" |
"项目用 monorepo,原因是共享依赖多" |
反例是叙述历史,正例是可复用事实 |
"用户说他很忙" |
(不存) | 情绪/临时状态无跨会话价值 |
"今天修了 auth 的 bug" |
(不存) | git log 里就有,记忆里没意义 |
"用户 API key 是 sk-xxx" |
(拒绝存) | 密钥类信息不能进记忆 |
🔄 更新、去重与矛盾处理
当 search_memories 返回高度相似的记忆(语义相似度 ≥ 0.85)时,优先更新而不是新增。
信息一致但更详细 → 补充细节
- 旧记忆:用户喜欢简洁代码
- 新信息:用户喜欢简洁代码,希望单行函数也保持简洁
- 行动:调用
update_memory补充细节
信息发生变化 → 直接更新
- 旧记忆:用户使用 tabs 缩进
- 新信息:用户改用 spaces 了
- 行动:调用
update_memory更新内容
信息相互矛盾 → 先问再改
- 旧记忆:用户喜欢 Vue
- 新信息:用户现在用 React
- 行动:先询问用户:我记忆中你偏好 Vue,现在转向 React 了吗?确认后再更新
同理,发现库里已有多条记忆互相矛盾时:也是先询问用户,确认后删错留对(delete_memory 删掉过期的那条)。
确认话术模板
- 我记忆中你偏好 X,现在要改为 Y 吗?确认后我会更新记忆。
- 之前记录的是 X,你说的是 Y,这两者矛盾。请确认哪个是对的?
- 我记忆里的 X 是旧版本,现在升级到 Y 了对吗?
用户口头纠正记忆
- 用户:我之前说的不是 X,是 Y
- 行动:调用
update_memory直接修正,无需询问 - 话术:已更新记忆,将 X 修正为 Y。
- 若用户否认整条("我从没说过我喜欢 X"):直接
delete_memory删除并致歉——"抱歉,我记错了,已删除该条记忆。"
去重决策
add_memory 默认查重(相似度 ≥0.85),返回包含 duplicate_id 的 JSON。三种处理:
| 情况 | 处理 | 例子 |
|---|---|---|
| 内容真变了(事实迭代) | update_memory(duplicate_id, content=新) |
旧:"用户偏好 pytest" → 新:"用户偏好 pytest + hypothesis" |
| 只是换措辞,信息量相同 | 跳过,不入库 | "我用 pytest" vs "偏好 pytest" |
| 是新维度(信息互补) | add_memory(..., force=True) |
已存"偏好 pytest",新增"偏好 mock 尽量少" |
一次会话内多次调整偏好——同话题内等最终结论确定再 update 一次;不同话题分别处理。
⚠️ 查重只在同一作用域内比对。同样的内容换个
user_id会各存一份、不会命中duplicate_id。所以"为什么这条明显重复却没触发查重"通常是作用域填得不一致 —— 先确认user_id/app_id是不是同一个值,而不是直接force=True。
🎯 作用域选择指南
| 场景 | 使用作用域 | 示例 |
|---|---|---|
| 记住用户个人偏好 | user_id |
代码风格、语言偏好、个人习惯 |
| 记住 Agent 自己的策略 | agent_id |
你上次推荐的方案、你自己的决策逻辑 |
| 记住项目级约定 | app_id |
这个项目用 tabs 缩进、项目的技术栈 |
| 记住某次会话上下文 | run_id |
本次讨论的临时约定、这次的决策背景 |
组合使用
user_id+app_id:该用户在该项目中的偏好(最常见的组合)user_id+agent_id:该用户针对该 Agent 的特殊要求agent_id+run_id:该 Agent 在本次会话中的临时策略
默认规则(按信息归属强制填,不要只填 user_id)
- 信息属于用户全局偏好(代码风格、语言、个人习惯)→
user_id - 信息与某个项目/应用相关(技术栈、版本规则、项目约定、路径配置)→ 必填
app_id(= 项目名) - 信息是 Agent 自身策略 →
agent_id - 信息仅本次会话有效 →
run_id - 组合优先
user_id+app_id(该用户在该项目中的事实) add_memory不带作用域也不会报错,会存成一条"无归属"的全局记忆 —— 工具不会 拦你,所以作用域得靠上面的规则自觉填全。(只有delete_all_memories强制要求 至少一个作用域,那是为了防误删整库。)
如何确定 user_id
user_id 标识人,必须全程稳定 —— 同一个人在不同 agent、不同项目下必须解析出同一个值,否则记忆会互相看不见。取值:
- 优先
git config user.name - 取不到时退回操作系统用户名(
$USER,Windows 用%USERNAME%)
原样使用,不要转大小写、不要替换空格 —— 任何变换都必须在所有地方一致,否则同一个人会被拆成多个作用域。
⚠️ 务必配成全局:
git config --global user.name <名字>。 如果只在某个仓库里配过(--local),那在其他项目下git config user.name取到的是空, 于是回退到$USER—— 同一个人在 A 项目是laomou、在 B 项目变成mourui, 两边的记忆彼此不可见,而且不会有任何报错提示你。
如何推断 app_id
凡涉及具体项目的信息,app_id 必填。取值 = <group>_<repo>(namespace + 仓库名,避免不同 group 下同名仓库冲突):
- 优先从 remote URL 解析:
git remote get-url origin,取主机名后、.git前那段路径,把/换成_(即<group>_<repo>) - 没有 remote 时,退回
git rev-parse --show-toplevel的 basename(仅 repo 名,无 group 前缀) - 都没有,取当前工作目录 basename
分隔符用
_:group/repo 名内部常用-,用_作分隔符不会冲突。 跨项目通用的用户偏好(如"我偏好 pytest")不填app_id。
🏷️ metadata 约定
保存记忆时,在 metadata 中添加标准化字段,便于精确检索:
| key | 值(只能选一个) | 说明 |
|---|---|---|
category |
preference / identity / environment / decision / anti_pattern / episode / concept |
记忆类型 |
importance |
high / medium / low |
重要度 |
source |
user / inferred / system |
来源 |
category 对照:
| category | 适用信号 | 例子 |
|---|---|---|
preference |
"我喜欢/偏好…" | 用户偏好 pytest,不用 unittest |
identity |
角色、技能、习惯(人的属性) | 资深 Go 工程师、有代码洁癖 |
environment |
工具/环境(外部条件) | 团队用 GitLab、Mac M1、公司代理 |
decision |
"我们决定…""约定是…" | 项目选了 PostgreSQL |
anti_pattern |
"X 行不通…""别再试…" | 之前试过 Redis 队列,吞吐不够 |
episode |
"上次/之前…"的结论 | 上次定了迁移方案 A |
concept |
术语、缩写、约定俗成 | 项目里 svc 指订单服务 |
tags 是自由主题词(逗号分隔),用于浏览/展示,不同于 metadata.category 的结构化类型。
metadata_filter 只支持多键 AND 精确匹配——同时满足多个 key 的条件。不支持 OR / IN / NOT / 子串。
// ✅ 能查出 category=preference AND importance=high 的结果
metadata_filter='{"category":"preference","importance":"high"}'
// ❌ 不支持 OR 或子串
metadata_filter='{"category":"preference OR decision"}'
⏰ 何时设置 TTL(过期时间)
先分清短期与长期 —— 这决定了要不要设 TTL:
| 类型 | 存储方式 | 适用场景 |
|---|---|---|
| 短期上下文 | 不保存,或设置短 TTL(如 run_id + 1天) |
"这次帮我用 X 方式" |
| 长期记忆 | 不设 TTL,关联 user_id |
"我喜欢简洁代码" |
按 category 的建议:
| 信息类型 | TTL 建议 | 示例 |
|---|---|---|
preference/identity/environment/decision |
不设置(永久) | 除非用户显式改口,不该过期 |
concept/anti_pattern |
不设置 | 术语和踩坑长期有价值 |
episode |
1 天 ~ 不设 | 情节可能过时,视具体决策的时效性定 |
| 临时约定/便签 | 1 小时 ~ 7 天 | 单次 run 内的进度,到期后被回收 |
TTL 到期后,记忆立刻从检索/列表结果中消失,但数据行还在库里 —— 真正删除发生在
两个时机:lm-mem mcp 启动时会自动清一次(可用 LM_MEM_AUTO_PURGE=0 关闭),
或你主动调 purge_expired。
所以 TTL 的语义是"到期即不可见",不要指望它在会话中途把行也删掉。
📝 真实对话示例(Few-shot)
示例 1:检索代码风格偏好
用户:帮我写一个读取 CSV 文件的函数
AI 思考:这是代码生成请求 → 触发高优先级检索
AI 行动:search_memories 检索 代码风格、偏好
检索结果:用户喜欢 pandas 风格,偏好链式调用
AI 回复:好的,我按你喜欢的 pandas 链式调用来写...
示例 2:更新事实变更
用户:我的项目已经从 Python 3.10 升级到 3.12 了
AI 思考:这是长期事实变更 → 需要更新
AI 行动:search_memories 找到旧记忆 "项目用 Python 3.10"
AI 回复:确认一下,我记忆中你的项目用的是 Python 3.10,现在升级到 3.12 了对吗?确认后我帮你更新记忆。
用户:对
AI 行动:update_memory 覆盖旧记忆
示例 3:不保存一次性指令
用户:这次帮我用 tabs 缩进 AI 思考:用户说"这次" → 一次性指令,不保存 AI 行动:不调用任何记忆工具 AI 回复:好的,这次用 tabs 缩进。
⚠️ 异常情况与日常运维
检索结果过多(> 10 条)
- 只取相似度最高的 3-5 条融入回复
- 如果结果相互矛盾,优先采用更新时间最新的
记忆工具调用失败(网络/服务异常)
- 不要反复重试(最多 2 次)
- 正常回复用户请求,但告知"记忆服务暂时不可用,本次对话内容不会被记录"
- 提示用户稍后可以重新触发记忆
用户一次给出多条偏好
- 用户:我习惯用 tabs,喜欢简洁代码,项目用 Python
- 行动:分别保存为 3 条独立的记忆
- 不要合并成一条(粒度太粗,检索不精准)
主动提醒记忆可能过期
- 当用户提到与旧记忆相关的话题时,可以主动询问:
- 我记忆中你偏好 X,这个偏好还有效吗?
- 之前记录过你的项目用 X,现在有变化吗?
记忆库变大后的整理
- 当记忆数量超过 50 条时,可以在对话中建议:
- 你的记忆库已经有 50+ 条了,需要我帮你导出备份或清理过期记录吗?
- 可以用
memory_stats查看统计,export_memories导出备份。
✅ 保存前自检清单
在调用 add_memory 或 update_memory 之前,快速确认:
- 这条信息是稳定的(非一次性、非临时)?
- 这条信息对未来的对话有帮助?
- 是否已存在相似或矛盾的记忆?(如果是,应先检索确认)
- 这条信息的作用域按归属填全了?(全局偏好→user_id;项目相关→必加 app_id;agent 策略→agent_id;本次会话→run_id)
-
metadata.category值是否在 7 个约定值中? - 是否需要设置 TTL(过期时间)?
如果全部勾选 → 放心保存 ✅ 如果有任何一项不确认 → 先询问用户
🔧 常用工具速查
日常最常用 6 个,其余按需查 MCP 描述:
| 工具 | 场景 |
|---|---|
get_user_context(user_id, limit=10) |
新会话冷启动,一次拉核心 preference/identity/environment。user_id 别留空(留空只返回无归属的全局记忆) |
add_memory(content, user_id?, tags?, metadata?, ttl_seconds?) |
保存(默认自动查重) |
search_memories(query, user_id?, metadata_filter?, limit?) |
语义检索 |
update_memory(mem_id, content?, metadata?, tags?) |
事实变化时原地更新。tags 不传=不改,传 ""=清空标签 |
delete_memory(mem_id) |
显式忘记某条 |
import_memories(data, fmt?, overwrite?, new_ids?) |
从 export_memories 结果还原备份 |