Memory向量记忆系统3-数据选取

从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)

ini 复制代码
import numpy as np

def mmr(query_embedding, doc_embeddings, doc_ids, lambda_param=0.5, top_k=5):
    """
    MMR 算法
    
    参数:
        query_embedding: 查询向量 (d,)
        doc_embeddings: 文档向量矩阵 (n, d)
        doc_ids: 文档ID列表
        lambda_param: λ 参数,0~1
        top_k: 返回数量
    
    返回:
        排序后的文档ID列表
    """
    # 归一化,方便算余弦相似度
    query_norm = query_embedding / np.linalg.norm(query_embedding)
    doc_norms = doc_embeddings / np.linalg.norm(doc_embeddings, axis=1, keepdims=True)
    
    # 所有文档和查询的相似度
    query_sims = doc_norms @ query_norm  # shape: (n,)
    
    selected = []
    unselected = list(range(len(doc_ids)))
    
    for _ in range(min(top_k, len(doc_ids))):
        best_mmr = -float('inf')
        best_idx = -1
        
        for idx in unselected:
            # 第一部分:和查询的相关性
            relevance = lambda_param * query_sims[idx]
            
            # 第二部分:和已选文档的最大相似度(惩罚重复)
            if len(selected) == 0:
                diversity_penalty = 0
            else:
                selected_sims = doc_norms[idx] @ doc_norms[selected].T
                diversity_penalty = (1 - lambda_param) * np.max(selected_sims)
            
            # MMR 分数
            mmr_score = relevance - diversity_penalty
            
            if mmr_score > best_mmr:
                best_mmr = mmr_score
                best_idx = idx
        
        selected.append(best_idx)
        unselected.remove(best_idx)
    
    return [doc_ids[i] for i in selected]


# 使用示例
query = np.random.rand(1536).astype(np.float32)
docs = np.random.rand(100, 1536).astype(np.float32)
ids = list(range(100))

result = mmr(query, docs, ids, lambda_param=0.6, top_k=10)
print(result)

应用场景

  1. RAG 检索去重:最常用,避免召回的 chunk 内容重复
  2. 推荐系统:推荐结果多样化,避免全是同类
  3. 搜索结果多样化:搜索引擎的结果多样性
  4. 摘要生成:选信息量大、不重复的句子
  5. 聚类选代表:从每个簇里选最有代表性的

和其他去重方式对比

方法 原理 优点 缺点
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)

typescript 复制代码
function temporalDecay(params: {
  ageInDays: number;      // 距离今天多少天
  halfLifeDays: number;   // 半衰期天数,默认30
}): number {
  const lambda = Math.LN2 / params.halfLifeDays;  // λ = ln2 / 半衰期
  return Math.exp(-lambda * params.ageInDays);    // e^(-λ × 天数)
}

// 使用
const score = 0.9;                    // 原始相似度得分
const days = 30;                      // 30天前的内容
const decay = temporalDecay({ ageInDays: days, halfLifeDays: 30 });
const finalScore = score * decay;     // 0.9 × 0.5 = 0.45

为什么用指数衰减?

  1. 符合遗忘规律:人的记忆就是指数式遗忘的,刚发生的事记得清楚,越久忘得越多
  2. 平滑过渡:不是突然降到 0,而是慢慢衰减,更自然
  3. 只有一个参数:调半衰期就行,简单直观
  4. 数学性质好:半衰期翻倍,衰减速度减半,容易理解和调参

🔧 怎么调参?

半衰期 效果 适用场景
7 天 衰减很快,非常看重时效性 新闻、热点、实时数据
30 天 适中,默认值 一般对话记忆、日常笔记
90 天 衰减慢,比较看重历史 知识库、长期记忆
365 天 几乎不衰减 常青知识、文档

OpenClaw 还有一些特殊机制

1. 常青文件不衰减

像 MEMORY.md 这种重要的总结文件,不参与时间衰减,永远是满分权重。

2. 访问次数抵抗衰减

  • 如果你经常引用某个知识点(accessCount 高),它会抵抗衰减
  • 即使几周没提到,也不会很快变"冷"
  • 防止重要但不常提的知识被遗忘

3. 重新加热(Reheat)

  • 一个很久没访问的"冷"知识,一旦被搜到/被引用
  • 就会重新加热,下次排名又变高
  • 类似"复习了一下,记忆又清晰了"

一句话总结

OpenClaw 的时间衰减 = 指数衰减 + 半衰期 30 天 + 常青文件不衰减 + 高频访问抵抗衰减。

本质就是模拟人的遗忘曲线:刚发生的记得牢,越久越模糊,但重要的东西忘得慢,复习一下又想起来了。

总结

openclaw的召回流程如下:

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

相关推荐
脚本自动化开发2 小时前
折叠双屏MU156LA:写稿修图游戏体验
后端
脚本自动化开发2 小时前
AI短剧800集讲规矩,观众忘了是AI
后端
小虎AI生活3 小时前
WorkBuddy 积分不够烧?我找到了每天白送 5000 的平台
aigc·ai编程
Csvn4 小时前
模型挂了全站 502?降级链让它先保住“有响应”(R04)
人工智能·aigc·agent
Csvn4 小时前
安装、升级与目录结构
后端·nginx
皮皮林5514 小时前
万能通用!权限系统就该这么设计
后端
GoAI5 小时前
DeepSeek Harness Memory (一)主流记忆插件总结
后端·算法·面试
狼爷6 小时前
AI 时代学习真相:答案唾手可得,思考才是稀缺品
aigc
橙子家6 小时前
RESTful 接口:它并非标准或协议,而是一种【架构风格】
后端
用户15741568165347 小时前
让「大肥鱼」在群里开口说话:NapCat + AstrBot + GPT-SoVITS 语音机器人搭建实录
机器人·aigc