从SQLite里召回的数据有很多,都要保留吗?可能返回的前 5 条结果可能都在讲同一个话题的不同段落——内容高度重复,信息增益有限。或者有的已经是一年前的信息了。
一、MMR算法
MMR(Maximal Marginal Relevance,最大边际相关性)是用来**平衡 “相关性” 和 “多样性”**的经典算法,最常用于 RAG 检索结果的去重 / 多样化。
解决什么问题?
假设你搜”苹果”,召回 10 条结果:
- 前 5 条全是”苹果手机”(主题重复)
- 后 5 条是”苹果水果””苹果公司””苹果音乐”等
直接按相似度排序,前几条全是重复主题,用户想看其他角度还得往下翻。
MMR 解决的就是:在保证相关性的前提下,让结果尽量多样化,避免扎堆。
核心公式
MMR=λ⋅Sim(Q,Di)−(1−λ)⋅maxDj∈sSim(Di,Dj)
拆解成三部分:
| 部分 | 含义 | 作用 |
|---|---|---|
| λ · Sim(Q, Dᵢ) | 文档和查询的相似度 | 保证相关性 |
| (1-λ) · max Sim(Dᵢ, Dⱼ) | 文档和已选结果的最大相似度 | 惩罚重复度 |
| λ | 权重参数(0~1) | 调平衡的旋钮 |
大白话翻译:
一个文档的最终得分 = (它跟问题有多相关 × 相关性权重) 减去 (它跟已选结果有多像 × 多样性权重)
关键概念详解
1. Sim(Q, D) —— 相似度
含义:查询(Query)和文档(Document)的相关程度,值在 0~1 之间,越大越相关。
常见计算方式:
| 方式 | 原理 | 适用场景 |
|---|---|---|
| 余弦相似度 | 两个向量的夹角余弦 | embedding 向量、语义匹配(最常用) |
| Jaccard 相似度 | 集合交集 ÷ 集合并集 | 关键词、标签、集合型数据 |
| 内积(点积) | 向量点积 | 归一化后等价于余弦 |
余弦相似度公式(最常用):Sim (Q, D) = (Q・D) / (‖Q‖ × ‖D‖)
向量归一化后(模长=1),直接点积就是余弦相似度。
2. λ(lambda)—— 权重旋钮
含义:控制相关性和多样性的平衡,取值 0~1。
| λ 值 | 效果 | 适用场景 |
|---|---|---|
| 1.0 | 完全按相关性排序,不去重 | 就要最相关的,不管重复 |
| 0.7 | 相关性为主,兼顾多样性 | 大多数 RAG 场景(常用) |
| 0.5 | 相关性和多样性各半 | 想要结果多样化 |
| 0.3 | 多样性为主 | 更看重覆盖面 |
| 0.0 | 完全追求多样性 | 只要不一样的,不管相关不相关 |
经验值:RAG 场景一般用 0.5 ~ 0.7。
完整计算示例
场景
- 查询 Q:”苹果”
- 召回 5 个文档:A(手机)、B(手机)、C(水果)、D(公司)、E(手机)
- λ = 0.5(各占一半)
- 相似度矩阵:
| A | B | C | D | E | |
|---|---|---|---|---|---|
| 和查询的相似度 | 0.95 | 0.90 | 0.75 | 0.70 | 0.85 |
| 和A的相似度 | - | 0.98 | 0.30 | 0.40 | 0.95 |
| 和C的相似度 | 0.30 | 0.25 | - | 0.50 | 0.20 |
| 和D的相似度 | 0.40 | 0.35 | 0.50 | - | 0.30 |
第 1 轮:选第一个
所有文档都没选过,直接选最相关的:
- 选 A(相似度 0.95 最高)
- 已选集合 S = {A}
第 2 轮:选第二个
计算剩下每个文档的 MMR 分数:
| 文档 | 相关性 (λ×Sim) | 重复惩罚 ((1-λ)×maxSim) | MMR 分数 |
|---|---|---|---|
| B | 0.5 × 0.90 = 0.45 | 0.5 × 0.98 = 0.49 | 0.45 - 0.49 = -0.04 |
| C | 0.5 × 0.75 = 0.375 | 0.5 × 0.30 = 0.15 | 0.375 - 0.15 = 0.225 |
| D | 0.5 × 0.70 = 0.35 | 0.5 × 0.40 = 0.20 | 0.35 - 0.20 = 0.15 |
| E | 0.5 × 0.85 = 0.425 | 0.5 × 0.95 = 0.475 | 0.425 - 0.475 = -0.05 |
选 C(MMR 最高)
- 虽然 C 和查询的相似度(0.75)比 B(0.90)低
- 但它跟已选的 A 差异大,多样性加分后整体排第二
- 已选集合 S = {A, C}
第 3 轮:选第三个
| 文档 | 相关性 | 重复惩罚(和A、C中最大的) | MMR 分数 |
|---|---|---|---|
| B | 0.45 | 0.5 × max(0.98, 0.25) = 0.49 | -0.04 |
| D | 0.35 | 0.5 × max(0.40, 0.50) = 0.25 | 0.10 |
| E | 0.425 | 0.5 × max(0.95, 0.20) = 0.475 | -0.05 |
选 D
- 已选集合 S = {A, C, D}
最终结果
MMR 排序:A → C → D → B → E
对比纯相似度排序(A → B → E → C → D):
| 排序方式 | 前3名 | 效果 |
|---|---|---|
| 纯相似度 | A(手机) → B(手机) → E(手机) | 全是手机,重复度高 |
| MMR | A(手机) → C(水果) → D(公司) | 三个不同主题,多样性好 |

代码实现(Python)
1 | import numpy as np |
应用场景
- RAG 检索去重:最常用,避免召回的 chunk 内容重复
- 推荐系统:推荐结果多样化,避免全是同类
- 搜索结果多样化:搜索引擎的结果多样性
- 摘要生成:选信息量大、不重复的句子
- 聚类选代表:从每个簇里选最有代表性的
和其他去重方式对比
| 方法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| MMR | 贪心选边际相关性最大的 | 效果好,可调参数,经典 | 计算量稍大(两两算相似度) |
| 聚类后选代表 | 先聚类,每类选一个 | 多样性强 | 聚类质量影响大,实现复杂 |
| 阈值去重 | 相似度超阈值就去掉 | 简单快 | 阈值不好调,可能丢重要信息 |
| 最大最小距离 | 选最远的点 | 多样性最强 | 可能完全不相关 |
一句话总结
MMR = 既要相关,又要不一样。
每次选文档时,都选”跟查询相关度高、但跟已经选过的差异大”的那个,用 λ 来调两者的权重。RAG 里用它来避免召回的内容全是一个意思,提升答案的丰富度。
二、时间衰减
并非所有记忆都应该拥有相同的权重。昨天的对话比去年的对话更可能与当前任务相关。时间衰减通过指数衰减模型实现“近期优先”。
OpenClaw 的时间衰减用的是指数衰减模型,灵感来自心理学的艾宾浩斯遗忘曲线。
核心公式

其中:
- λ(lambda) = ln(2) / 半衰期天数
- 半衰期 = 经过这么多天后,分数变成原来的一半(默认 30 天)
具体例子(默认半衰期 30 天)
| 时间 | 衰减系数 | 权重 |
|---|---|---|
| 今天 | 1.0 | 💯 100% |
| 7 天前 | ~0.84 | 🟢 84% |
| 30 天前 | 0.5 | 🟡 50%(半衰期) |
| 60 天前 | 0.25 | 🟠 25% |
| 90 天前 | 0.125 | 🔴 12.5%(只剩 1/8) |
规律:每过 30 天,权重减半。
代码实现(TypeScript)
1 | function temporalDecay(params: { |
为什么用指数衰减?
- 符合遗忘规律:人的记忆就是指数式遗忘的,刚发生的事记得清楚,越久忘得越多
- 平滑过渡:不是突然降到 0,而是慢慢衰减,更自然
- 只有一个参数:调半衰期就行,简单直观
- 数学性质好:半衰期翻倍,衰减速度减半,容易理解和调参
🔧 怎么调参?
| 半衰期 | 效果 | 适用场景 |
|---|---|---|
| 7 天 | 衰减很快,非常看重时效性 | 新闻、热点、实时数据 |
| 30 天 | 适中,默认值 | 一般对话记忆、日常笔记 |
| 90 天 | 衰减慢,比较看重历史 | 知识库、长期记忆 |
| 365 天 | 几乎不衰减 | 常青知识、文档 |
OpenClaw 还有一些特殊机制
1. 常青文件不衰减
像 MEMORY.md 这种重要的总结文件,不参与时间衰减,永远是满分权重。
2. 访问次数抵抗衰减
- 如果你经常引用某个知识点(accessCount 高),它会抵抗衰减
- 即使几周没提到,也不会很快变”冷”
- 防止重要但不常提的知识被遗忘
3. 重新加热(Reheat)
- 一个很久没访问的”冷”知识,一旦被搜到/被引用
- 就会重新加热,下次排名又变高
- 类似”复习了一下,记忆又清晰了”
一句话总结
OpenClaw 的时间衰减 = 指数衰减 + 半衰期 30 天 + 常青文件不衰减 + 高频访问抵抗衰减。
本质就是模拟人的遗忘曲线:刚发生的记得牢,越久越模糊,但重要的东西忘得慢,复习一下又想起来了。
总结
openclaw的召回流程如下:

技术真的发展到一定程度了,很多时候基建都有了,我们就是组装。但里面有很多很多细节,都属于工程范畴,能够把产品推得更前面、更强一些。