Agent 08-记忆与检索

介绍记忆系统(Memory System)和检索增强生成(Retrieval-Augmented Generation, RAG)。采用"框架扩展 + 知识科普"的方式,在构建过程中深入理解Memory和RAG的理论基础,最终实现一个具有完整记忆和知识检索能力的智能体系统。

智能体记忆系统与RAG系统 - 问题详解

目录

  1. 情景记忆 vs 语义记忆评分机制对比
  2. 个人健康管理助手的记忆设计
  3. 工作记忆整合机制设计
  4. 基于语义边界的分块算法
  5. MQE vs HyDE 检索策略对比
  6. 嵌入方案选型对比
  7. 智能遗忘策略实现
  8. 记忆归档机制设计
  9. 隐私数据彻底清除方案
  10. RAG vs Memory 智能路由
  11. 智能学习报告生成器设计
  12. 多用户数据隔离方案
  13. 知识图谱质量评估
  14. 图检索 vs 向量检索对比

1. 情景记忆 vs 语义记忆评分机制对比

评分公式对比

情景记忆评分公式:

1
score = 0.4 × recency + 0.3 × importance + 0.2 × access + 0.1 × embedding

语义记忆评分公式:

1
score = 0.3 × graph_retrieval + 0.25 × embedding + 0.25 × access + 0.2 × importance

设计原因分析

维度 情景记忆 语义记忆 设计原因
时间权重 (recency) 0.4 0.0 情景记忆记录"我在何时何地做了什么",新鲜度至关重要;语义记忆是持久知识,与时间无关
图检索权重 (graph_retrieval) 0.0 0.3 语义记忆基于知识图谱,概念间的关系推理是核心优势
嵌入权重 (embedding) 0.1 0.25 语义记忆更注重语义相似性匹配
重要性权重 (importance) 0.3 0.2 情景记忆中某些事件可能特别重要(如里程碑事件)
访问权重 (access) 0.2 0.25 两者都考虑访问频率,但语义记忆更重视知识的复用

核心设计理念

情景记忆 = “时间线上的故事”

  • 强调时间近因性:最近发生的事情更相关
  • 类比人类的短期事件记忆
  • 适用于:“我昨天看了什么电影?“这类查询

语义记忆 = “知识网络中的概念”

  • 强调关系推理:通过图结构发现关联
  • 类比人类的知识体系
  • 适用于:“什么是深度学习?“这类概念查询

2. 个人健康管理助手的记忆设计

整体架构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
┌─────────────────────────────────────────────────────────────┐
│                   个人健康管理助手                            │
├─────────────────────────────────────────────────────────────┤
│  工作记忆              情景记忆              语义记忆         │
│  ┌─────────┐        ┌─────────┐        ┌─────────┐         │
│  │当前对话│        │饮食记录 │        │营养知识 │         │
│  │输入状态 │        │运动日志 │        │医学概念 │         │
│  │暂存数据 │        │睡眠数据 │        │健康图谱 │         │
│  └─────────┘        └─────────┘        └─────────┘         │
│       ↓                  ↓                  ↓                │
│  TTL=5分钟         重要性衰减          知识推理             │
│                                                ↑            │
│  感知记忆 ────────────────────────────────────┘            │
│  ┌─────────┐                                                 │
│  │设备数据 │  实时同步:心率、步数、血氧、体温                │
│  │环境数据 │  定期采样:天气、空气质量、季节                   │
│  └─────────┘                                                 │
└─────────────────────────────────────────────────────────────┘

具体应用场景

记忆类型 应用场景 数据示例 检索示例
工作记忆 对话状态管理 用户当前输入的饮食内容 “我刚才说今天早餐吃了什么?”
情景记忆 健康行为追踪 “2024-01-15早餐:燕麦牛奶” “上周三的跑步记录是什么?”
语义记忆 健康知识推理 “缺乏维生素D → 需要晒太阳” “素食者容易缺乏什么营养素?”
感知记忆 实时数据流 手环心率:85 bpm “我当前心率是多少?”

数据流向示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
用户说:"我今天跑步了5公里"
┌─────────────────┐
│  工作记忆(暂存) │ ← 存储原始输入
└─────────────────┘
    ↓ (5分钟后未提及)
┌─────────────────┐
│  整合到情景记忆  │ ← "2024-01-20 跑步5公里,耗时30分钟"
└─────────────────┘
    ↓ (与历史数据关联)
┌─────────────────┐
│  更新语义记忆    │ ← "用户每周运动3次,属于活跃用户"
└─────────────────┘

3. 工作记忆整合机制设计

整合决策矩阵

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
┌────────────────────────────────────────────────────────────┐
│                    工作记忆 → 长期记忆 整合触发条件             │
├────────────────────────────────────────────────────────────┤
│                                                             │
│  触发信号            │  整合目标           │  延迟           │
│  ─────────────        │  ─────────         │  ────           │
│  用户明确要求记住      │  → 情景记忆         │  立即            │
│  对话中反复提及(≥3次)  │  → 语义记忆         │  第3次触发       │
│  与现有知识关联        │  → 语义记忆(关联)   │  匹配时          │
│  情感标记(重要/紧急)   │  → 情景记忆(高重要性)│ 检测时          │
│  时间窗口持续(>1小时)  │  → 模式识别         │  到期触发         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

自动整合算法实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class MemoryConsolidation:
    """工作记忆整合机制"""

    def should_consolidate(self, work_item, context):
        """判断工作记忆是否需要整合"""
        score = 0
        reasons = []

        # 1. 用户明确指示 (最高优先级)
        if work_item.get("explicit_remember"):
            return {"action": "immediate", "target": "episodic"}

        # 2. 提及频率检测
        mention_count = work_item.get("mention_count", 0)
        if mention_count >= 3:
            score += 0.4
            reasons.append("频繁提及")
            return {"action": "consolidate", "target": "semantic"}

        # 3. 知识关联检测
        if self._has_related_knowledge(work_item, context):
            score += 0.3
            reasons.append("存在关联知识")

        # 4. 情感重要性检测
        sentiment = work_item.get("sentiment")
        if sentiment in ["urgent", "important", "emotional"]:
            score += 0.5
            reasons.append("情感标记")
            return {"action": "immediate", "target": "episodic"}

        # 5. 时间阈值检测
        age_seconds = time.time() - work_item.get("created_at", time.time())
        if age_seconds > 3600:  # 1小时
            score += 0.2
            reasons.append("时间阈值")

        # 综合判断
        if score >= 0.5:
            return {
                "action": "consolidate",
                "target": "semantic" if score > 0.6 else "episodic",
                "reasons": reasons
            }

        return {"action": "keep", "score": score}

    def _has_related_knowledge(self, item, context):
        """检测是否与现有知识关联"""
        # 检查实体重叠
        item_entities = set(item.get("entities", []))
        context_entities = set(context.get("recent_entities", []))

        overlap = item_entities & context_entities
        return len(overlap) > 0

4. 基于语义边界的分块算法

问题背景

对于没有明确标题结构的文档(如小说、法律条文),传统的基于Markdown标题的分块策略失效,需要基于语义边界进行智能分块。

语义边界检测算法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import numpy as np
from sentence_transformers import SentenceTransformer

class SemanticChunker:
    """基于语义边界的智能分块"""

    def __init__(self, model_name="paraphrase-multilingual-MiniLM"):
        self.encoder = SentenceTransformer(model_name)

    def chunk(self, text, max_chunk_size=1000, min_chunk_size=200):
        """
        主分块方法

        Args:
            text: 输入文本
            max_chunk_size: 最大块大小(字符数)
            min_chunk_size: 最小块大小(字符数)
        """
        # 1. 按句子分割
        sentences = self._split_sentences(text)

        # 2. 计算句子嵌入
        embeddings = self.encoder.encode(sentences)

        # 3. 检测语义边界
        boundaries = self._detect_boundaries(embeddings)

        # 4. 在边界处创建chunks
        chunks = self._create_chunks(
            sentences, boundaries, max_chunk_size, min_chunk_size
        )

        return chunks

    def _detect_boundaries(self, embeddings, window_size=3, threshold=0.5):
        """
        检测语义边界

        核心思想:如果相邻两个窗口内的语义相似度都显著高于跨边界的相似度,
        则认为此处存在语义边界。
        """
        boundaries = [0]  # 第一个句子总是边界

        for i in range(window_size, len(embeddings) - window_size):
            # 计算前窗口内的平均相似度
            before_window = embeddings[i-window_size:i]
            before_similarity = self._avg_similarity(before_window)

            # 计算后窗口内的平均相似度
            after_window = embeddings[i:i+window_size]
            after_similarity = self._avg_similarity(after_window)

            # 计算跨边界的相似度
            cross_similarity = self._cosine_sim(
                before_window[-1], after_window[0]
            )

            # 判断是否为边界:跨边界相似度显著低于窗口内相似度
            avg_window_sim = (before_similarity + after_similarity) / 2

            if cross_similarity < threshold * avg_window_sim:
                boundaries.append(i)

        boundaries.append(len(embeddings))
        return boundaries

    def _avg_similarity(self, embeddings):
        """计算嵌入序列内的平均相似度"""
        if len(embeddings) < 2:
            return 1.0

        similarities = [
            self._cosine_sim(embeddings[i], embeddings[i+1])
            for i in range(len(embeddings) - 1)
        ]
        return np.mean(similarities)

    def _cosine_sim(self, a, b):
        """计算余弦相似度"""
        return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

不同文档类型的分块策略

文档类型 分块策略 实现要点
小说 章节边界 + 场景转换 检测"第X章"模式 + 对话/叙述交替
法律条文 条款边界 + 层级结构 检测"第X条"模式 + 缩进层级
学术论文 章节结构 + 段落完整性 Markdown标题 + 段落边界
技术文档 代码块独立 + API分组 识别代码块 + 按功能分组
新闻文章 段落语义连贯性 语义边界检测

5. MQE vs HyDE 检索策略对比

检索方法对比

检索方法 原理 查询示例 优缺点
基础检索 直接查询embedding “如何配置API密钥?” ✅ 简单快速 ❌ 召回率低
MQE 多查询扩展 → [“如何配置API密钥?”, “如何设置密钥?”, “在哪里配置凭证?”] ✅ 召回率高 ⚠️ 融合复杂
HyDE 假设文档嵌入 → 生成假设文档,用假设文档embedding检索 ✅ 精确度高 ❌ 依赖生成质量

场景选择矩阵

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
┌─────────────────────────────────────────────────────────────┐
│                    检索策略选型指南                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  查询类型             │  最佳策略          │  原因          │
│  ──────────             │  ─────────         │  ────          │
│  精确术语查询            │  基础检索          │  术语明确,无需扩展│
│  模糊/口语化查询         │  MQE               │  多样化表达提高召回│
│  复杂概念查询            │  HyDE              │  假设文档捕获语义  │
│  多步骤问题              │  MQE + HyDE        │  组合拳效果最好    │
│  领域专业查询            │  基础 + 重排序      │  减少幻觉风险      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

实际场景效果对比

场景:技术文档问答

查询:“如何配置API密钥?”

检索方法 召回结果 效果评估
基础检索 找到3个相关片段 遗漏了使用"设置”、“凭证"等同义词的文档
MQE 找到8个相关片段 ✅ 召回率提升约150%
HyDE 找到5个相关片段 精确度更高,匹配到配置步骤详解

6. 嵌入方案选型对比

三种方案全面对比

维度 百炼API 本地Transformer TF-IDF
准确性 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐
速度 ⭐⭐⭐ (网络延迟50-200ms) ⭐⭐⭐⭐ (GPU加速,<50ms) ⭐⭐⭐⭐⭐ (<10ms)
成本 按调用付费 硬件+电费 免费
离线部署
语言支持 中英双语优秀 需选择合适模型 统计方法,无语义理解
维护复杂度 中(模型更新)
可定制性
规模化成本 随调用量线性增长 固定成本 几乎无成本

选型决策树

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
是否需要离线部署?
├── 是 → 是否有GPU资源?
│        │
│        ├── 是 → 本地Transformer (推荐)
│        └── 否 → TF-IDF (备用方案)
└── 否 → 是否有预算?
         ├── 是 → 百炼API (推荐,准确性最高)
         └── 否 → 考虑混合方案:
                  • 热数据用API (保证质量)
                  • 冷数据用本地 (降低成本)

混合方案建议

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class HybridEmbedding:
    """混合嵌入方案:API + 本地兜底"""

    def __init__(self):
        self.api_client = BailianEmbedding()  # 主力
        self.local_model = LocalModel()       # 备用

    def encode(self, texts):
        """智能路由编码请求"""
        try:
            # 正常情况下使用API
            return self.api_client.encode(texts)
        except Exception as e:
            # API失败时降级到本地模型
            logger.warning(f"API failed, using local: {e}")
            return self.local_model.encode(texts)

7. 智能遗忘策略实现

多因素加权遗忘算法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class IntelligentForgetting:
    """智能遗忘策略:综合多因素的遗忘决策"""

    def __init__(self, weights=None):
        self.weights = weights or {
            'importance': 0.30,    # 重要性权重
            'frequency': 0.25,     # 访问频率权重
            'recency': 0.25,       # 时间权重
            'connectivity': 0.20   # 连接性权重
        }

    def calculate_forgetting_score(self, memory):
        """
        计算遗忘分数:分数越高越应该遗忘

        返回: (forgetting_score, detail_scores)
        """
        scores = {
            'importance': self._importance_score(memory),
            'frequency': self._frequency_score(memory),
            'recency': self._recency_score(memory),
            'connectivity': self._connectivity_score(memory)
        }

        # 加权综合
        forgetting_score = sum(
            scores[k] * self.weights[k]
            for k in scores
        )

        return forgetting_score, scores

    def _importance_score(self, memory):
        """
        重要性转换为遗忘倾向(反向)

        重要性越高 → 遗忘倾向越低
        """
        original = memory.get('importance', 0.5)
        return 1 - original

    def _frequency_score(self, memory):
        """
        访问频率转换为遗忘倾向(反向)

        访问越多 + 距离上次访问越短 → 遗忘倾向越低
        """
        access_count = memory.get('access_count', 0)
        days_since_last = memory.get('days_since_last_access', 0)

        # 访问越多 → 遗忘倾向越低
        frequency_factor = 1 / (1 + access_count * 0.1)

        # 距上次访问越久 → 遗忘倾向越高
        recency_penalty = min(days_since_last * 0.02, 0.5)

        return frequency_factor + recency_penalty

    def _recency_score(self, memory):
        """
        时间衰减:越旧越容易遗忘

        使用S曲线:刚开始遗忘慢,中间加速,后期趋缓
        """
        age_days = memory.get('age_days', 0)

        # Sigmoid函数
        return 1 / (1 + np.exp(-0.05 * (age_days - 60)))

    def _connectivity_score(self, memory):
        """
        连接性:与其他记忆关联少 → 更容易遗忘

        在图数据库中查询相关节点数量
        """
        related_count = self._get_related_memory_count(memory['id'])
        return 1 / (1 + related_count * 0.2)

遗忘决策阈值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 遗忘阈值设定
FORGETTING_THRESHOLDS = {
    'immediate': 0.8,      # 立即遗忘
    'gradual': 0.6,        # 逐渐降低优先级
    'archive': 0.4,        # 归档
    'keep': 0.4            # 保留
}

def decide_forget_action(memory):
    """根据遗忘分数决定操作"""
    score, _ = forgetting.calculate_forgetting_score(memory)

    if score >= FORGETTING_THRESHOLDS['immediate']:
        return 'delete'
    elif score >= FORGETTING_THRESHOLDS['gradual']:
        return 'deprioritize'
    elif score >= FORGETTING_THRESHOLDS['archive']:
        return 'archive'
    else:
        return 'keep'

8. 记忆归档机制设计

记忆生命周期管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
┌─────────────────────────────────────────────────────────────┐
│                    记忆生命周期管理                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────┐    迁移条件    ┌─────────┐    迁移条件    ┌───────┐│
│  │ 活跃记忆 │  ──────────>  │  温记忆  │  ──────────>  │ 冷存储 ││
│  │ Hot     │              │  Warm    │              │  Cold  ││
│  └─────────┘              └─────────┘              └───────┘│
│                                                             │
│  特征:                    特征:                    特征:    │
│  • 频繁访问                • 偶尔访问                • 长期未访问│
│  • 高重要性                • 中重要性                • 可能有价值│
│  • 最近创建                • 中等时间                • 历史记录  │
│                                                             │
│  迁移策略:                                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  活跃 → 温:  7天无访问 + 重要性<0.7                 │   │
│  │  温 → 冷:    30天无访问 + 累计访问<3次                │   │
│  │  冷 → 删除:  90天无访问 + 重要性<0.3                 │   │
│  │  冷 → 活跃:  被关联查询触发                          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  存储优化:                                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  活跃: Qdrant/Neo4j (热索引,毫秒级响应)              │   │
│  │  温:   压缩存储 + 定期重建索引                       │   │
│  │  冷:   对象存储(S3) + 元数据索引                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

归档实现示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class MemoryArchiver:
    """记忆归档管理器"""

    def __init__(self):
        self.storage = {
            'hot': HotStorage(),      # Qdrant + Neo4j
            'warm': WarmStorage(),    # 压缩格式
            'cold': ColdStorage()     # S3 + 元数据
        }

    def evaluate_and_archive(self, memories):
        """评估并执行归档"""
        for memory in memories:
            tier = self._evaluate_tier(memory)
            current_tier = memory.get('storage_tier', 'hot')

            if tier != current_tier:
                self._move_memory(memory, current_tier, tier)

    def _evaluate_tier(self, memory):
        """评估记忆应处于的存储层级"""
        # 计算访问热度
        access_score = self._calculate_access_score(memory)

        # 计算重要性
        importance = memory.get('importance', 0.5)

        if access_score > 0.7 and importance > 0.7:
            return 'hot'
        elif access_score > 0.3 or importance > 0.5:
            return 'warm'
        else:
            return 'cold'

    def _calculate_access_score(self, memory):
        """计算访问热度分数"""
        # 最近访问权重高
        now = time.time()
        last_access = memory.get('last_access', 0)
        days_since = (now - last_access) / 86400

        # 指数衰减
        time_factor = np.exp(-days_since / 7)  # 7天半衰期

        # 访问次数
        count_factor = min(memory.get('access_count', 0) / 10, 1)

        return (time_factor * 0.7 + count_factor * 0.3)

9. 隐私数据彻底清除方案

多层验证清除机制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
┌─────────────────────────────────────────────────────────────┐
│              隐私数据清除的多层验证机制                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  第1层:应用层删除                                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  DELETE FROM memories                                │   │
│  │  WHERE id = ? AND user_id = ?                        │   │
│  │  同时标记相关引用为"待删除"                            │   │
│  └─────────────────────────────────────────────────────┘   │
│                         ↓                                   │
│  第2层:向量数据库清除 (Qdrant)                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  qdrant_client.delete(                                │   │
│  │      collection_name=f"user_{user_id}",              │   │
│  │      points_selector=[memory_id]                     │   │
│  │  )                                                    │   │
│  │  验证:retrieve(memory_id) 应返回空                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                         ↓                                   │
│  第3层:图数据库清除 (Neo4j)                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  MATCH (m:Memory {id: $id})                          │   │
│  │  DETACH DELETE m, m-[r]-()                            │   │
│  │  处理孤立节点                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                         ↓                                   │
│  第4层:备份区清除                                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  从归档存储删除                                     │   │
│  │  清除日志中的敏感信息                               │   │
│  │  清除缓存                                           │   │
│  └─────────────────────────────────────────────────────┘   │
│                         ↓                                   │
│  第5层:验证确认                                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  全域搜索验证                                         │   │
│  │  生成清除报告供审计                                   │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

实现代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class PrivacyCleaner:
    """隐私数据彻底清除"""

    def __init__(self, qdrant_client, neo4j_driver, storage):
        self.qdrant = qdrant_client
        self.neo4j = neo4j_driver
        self.storage = storage

    def delete_memory(self, user_id, memory_id):
        """彻底删除记忆"""
        deletion_report = {
            'memory_id': memory_id,
            'steps': [],
            'success': True
        }

        # 第1步:应用层删除
        deletion_report['steps'].append(self._delete_from_app(memory_id))

        # 第2步:向量数据库删除
        deletion_report['steps'].append(
            self._delete_from_qdrant(user_id, memory_id)
        )

        # 第3步:图数据库删除
        deletion_report['steps'].append(
            self._delete_from_neo4j(memory_id)
        )

        # 第4步:存储删除
        deletion_report['steps'].append(
            self._delete_from_storage(memory_id)
        )

        # 第5步:验证
        deletion_report['steps'].append(
            self._verify_deletion(memory_id)
        )

        return deletion_report

    def _delete_from_qdrant(self, user_id, memory_id):
        """从Qdrant删除向量"""
        collection = f"user_{user_id}_vectors"

        # 删除向量点
        self.qdrant.delete(
            collection_name=collection,
            points_selector=[memory_id]
        )

        # 验证删除
        result = self.qdrant.retrieve(
            collection_name=collection,
            ids=[memory_id]
        )

        if len(result) == 0:
            return {'step': 'qdrant', 'status': 'success'}
        else:
            return {'step': 'qdrant', 'status': 'failed', 'remains': len(result)}

10. RAG vs Memory 智能路由

智能路由机制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class RetrievalRouter:
    """智能路由:自动选择最佳检索方式"""

    def route(self, query, context):
        """根据查询特征决定检索策略"""
        # 1. 分析查询类型
        query_type = self._classify_query(query)

        # 2. 评估上下文
        context_score = self._evaluate_context(context)

        # 3. 路由决策
        strategy = self._decide(query_type, context_score)

        return strategy

    def _classify_query(self, query):
        """查询分类"""
        features = {
            'has_entity': self._has_named_entity(query),
            'is_personal': self._is_personal_reference(query),
            'is_factual': self._is_factual_query(query),
            'temporal': self._has_temporal_aspect(query)
        }

        # 决策树
        if features['is_personal']:
            return 'memory_primary'
        elif features['has_entity'] and features['is_factual']:
            return 'rag_primary'
        elif features['temporal']:
            return 'memory_primary'
        else:
            return 'hybrid'

    def _decide(self, query_type, context_score):
        """路由决策表"""
        decision_matrix = {
            ('memory_primary', 'high'): ['memory', 'rag'],
            ('memory_primary', 'low'): ['memory'],
            ('rag_primary', 'any'): ['rag'],
            ('hybrid', 'high'): ['rag', 'memory', 'merge'],
            ('hybrid', 'low'): ['rag', 'memory']
        }

        return decision_matrix.get((query_type, context_score), ['rag'])

路由决策表

查询类型 上下文丰富度 推荐策略 理由
“我之前说过…” Memory → RAG 优先检索个人记忆
“什么是Transformer?” 任意 RAG 事实性查询
“上周讨论了什么?” Memory 时间范围查询
“深度学习原理是什么?” RAG + Memory 结合知识和记忆

11. 智能学习报告生成器设计

报告生成器架构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
┌─────────────────────────────────────────────────────────────┐
│              智能学习报告生成器架构                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  输入:用户学习历史(问答记录、笔记、行为数据)                │
│                         ↓                                   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              分析引擎模块                              │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  1. 学习轨迹分析        (情景记忆 + 时间序列)         │   │
│  │  2. 知识图谱构建        (语义记忆 + 图推理)           │   │
│  │  3. 兴趣偏好识别        (访问频率 + 停留时间)         │   │
│  │  4. 学习风格检测        (问题类型 + 笔记模式)         │   │
│  └─────────────────────────────────────────────────────┘   │
│                         ↓                                   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              知识盲点识别                              │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  • 未覆盖的知识领域      (文档覆盖 vs 学习覆盖)       │   │
│  │  • 浅度学习的内容        (单次访问 vs 深度交互)       │   │
│  │  • 遗忘的知识点          (学习后未复习)               │   │
│  │  • 误解的概念            (矛盾问答记录)               │   │
│  └─────────────────────────────────────────────────────┘   │
│                         ↓                                   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              推荐引擎                                  │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  • 复习优先级排序        (遗忘曲线 + 重要性)         │   │
│  │  • 扩展学习建议          (知识图谱关联)               │   │
│  │  • 个性化学习路径        (学习风格匹配)               │   │
│  └─────────────────────────────────────────────────────┘   │
│                         ↓                                   │
│  输出:结构化学习报告 + 可视化图表 + 行动建议                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

报告结构示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
    "report_id": "report_20240120_user123",
    "generated_at": "2024-01-20T10:30:00Z",
    "summary": {
        "total_study_time": "5.2小时",
        "documents_studied": 3,
        "questions_asked": 15,
        "notes_created": 8
    },
    "learning_analysis": {
        "strengths": ["深度学习基础", "Transformer架构"],
        "weaknesses": ["注意力机制细节", "训练技巧"],
        "knowledge_graph": {
            "nodes": 25,
            "edges": 38,
            "central_concepts": ["神经网络", "反向传播"]
        }
    },
    "knowledge_gaps": [
        {
            "topic": "注意力机制",
            "reason": "从未提问过相关问题",
            "priority": "high",
            "recommendation": "建议学习:Self-Attention、Multi-Head Attention"
        }
    ],
    "review_suggestions": [
        {
            "topic": "Transformer架构",
            "reason": "3天前学习,未复习",
            "urgency": "medium"
        }
    ],
    "next_steps": [
        "深入理解注意力机制",
        "学习BERT和GPT的区别",
        "实践:实现一个简单的Transformer"
    ]
}

12. 多用户数据隔离方案

Qdrant 隔离方案

方案1:独立Collection(推荐)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 每个用户独立的collection
collection_name = f"user_{user_id}_vectors"

# 优点:
# - 完全隔离,安全
# - 可独立删除
# - 性能互不影响

# 缺点:
# - Collection数量多时管理复杂

方案2:共享Collection + Payload过滤

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 共享collection,通过payload过滤
collection_name = "global_vectors"

# 插入时添加用户标识
point = {
    "id": f"{user_id}_{doc_id}",
    "vector": embedding,
    "payload": {
        "user_id": user_id,
        "document": doc_name
    }
}

# 查询时过滤
search_result = qdrant.search(
    collection_name="global_vectors",
    query_vector=embedding,
    query_filter={
        "must": [
            {"key": "user_id", "match": {"value": user_id}}
        ]
    }
)

Neo4j 隔离方案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 方案1:用户根节点(推荐)
MATCH (u:User {id: $user_id})-[:HAS_MEMORY]->(m:Memory)
RETURN m

-- 方案2:节点属性过滤
MATCH (m:Memory {user_id: $user_id})
RETURN m

-- 方案3:标签隔离
MATCH (m:Memory:User_123)
RETURN m

性能优化策略

策略 实现方式 效果
索引优化 CREATE INDEX ON :Memory(user_id) 查询速度提升10x
分片策略 按user_id哈希分片 负载均衡
连接池 限制每用户并发数 防止资源耗尽
缓存层 Redis缓存热点用户session 减少数据库压力

13. 知识图谱质量评估

质量评估维度

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class KnowledgeGraphQuality:
    """知识图谱质量评估器"""

    def evaluate(self, graph):
        """综合质量评分"""
        return {
            'completeness': self._check_completeness(graph),
            'accuracy': self._check_accuracy(graph),
            'consistency': self._check_consistency(graph),
            'coverage': self._check_coverage(graph),
            'overall': self._calculate_overall(graph)
        }

    def _check_completeness(self, graph):
        """完整性检查"""
        total_nodes = graph.count_nodes()
        isolated_nodes = graph.count_isolated_nodes()
        total_relationships = graph.count_relationships()

        # 完整性指标
        isolation_ratio = isolated_nodes / total_nodes if total_nodes > 0 else 0
        relationship_density = total_relationships / max(total_nodes * (total_nodes - 1) / 2, 1)

        return {
            'isolation_ratio': isolation_ratio,        # 越低越好
            'density': relationship_density,           # 适度最佳
            'score': 1 - isolation_ratio * 0.5         # 转换为分数
        }

    def _check_accuracy(self, graph):
        """准确性检查(需要样本验证)"""
        # 实体类型一致性
        entities = graph.get_all_entities()
        type_conflicts = 0
        for entity in entities:
            if self._has_type_conflict(entity):
                type_conflicts += 1

        # 关系合理性
        relationships = graph.get_all_relationships()
        invalid_rels = 0
        for rel in relationships:
            if not self._is_valid_relation(rel):
                invalid_rels += 1

        return {
            'entity_consistency': 1 - type_conflicts / len(entities),
            'relation_validity': 1 - invalid_rels / len(relationships)
        }

常见错误类型

错误类型 检测方法 修正策略
实体边界错误 统计共现频率 重新分词
关系方向错误 传递性检查 反转关系
类型冲突 类型一致性验证 多类型标签
冗余关系 重复检测 关系合并
虚假关联 置信度阈值 过滤低置信度

14. 图检索 vs 向量检索对比

查询类型对比表

查询类型 向量检索 图检索 推荐方案
“机器学习的定义” ✅ 语义相似 ❌ 无优势 向量
“与Transformer相关的所有技术” ⚠️ 召回有限 ✅ 多跳关联
“A如何影响B” ❌ 无法判断 ✅ 路径查找
“与X类似的概念” ✅ 语义相似 ⚠️ 需要同类型 向量
“X在知识体系中的位置” ❌ 无结构信息 ✅ 层级结构
“我学过哪些相关内容” ⚠️ 语义匹配 ✅ 关联遍历

混合检索示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 查询:找出所有与"Transformer"相关的技术,并按关联强度排序
MATCH path = (t:Concept {name: "Transformer"})-[:RELATED_TO*1..3]-(related:Concept)
WHERE related.category = "技术"

// 同时考虑向量相似度(通过预计算)
WITH related,
     apoc.cypher.run(
         'CALL db.index.vector.queryNodes("embedding_index", 10, $embedding)
          YIELD node, score RETURN node.name AS name, score',
         {embedding: $query_embedding}
     ) AS vector_result

// 合并图检索和向量检索分数
MATCH (related)
WHERE related.name IN vector_result.name

RETURN related.name,
       sum(r.weight) as graph_score,
       vector_result.score as vector_score,
       (graph_score * 0.6 + vector_score * 0.4) as combined_score
ORDER BY combined_score DESC
LIMIT 10

图检索优势场景

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌─────────────────────────────────────────────────────────────┐
│                  图检索优势场景示例                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  场景1:关系推理                                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  问题:"A如何影响B?"                                │   │
│  │  图检索:FIND PATH A->...->B                         │   │
│  │  向量检索:无法理解"影响"关系                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  场景2:层级遍历                                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  问题:"列出所有机器学习的子领域"                    │   │
│  │  图检索:MATCH (p:Concept)-[:SUBTYPE*]->(c)         │   │
│  │  向量检索:只能找到语义相似的概念                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  场景3:社区发现                                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  问题:"找出所有相互关联的概念群"                    │   │
│  │  图检索:社区检测算法                                 │   │
│  │  向量检索:需要先聚类再分析                           │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

总结

本解答涵盖了智能体记忆系统和RAG系统的核心设计理念:

  1. 记忆类型设计需要理解每种记忆的本质特征
  2. RAG系统优化需要在召回率、精确度、效率之间权衡
  3. 工程实践需要考虑隐私、性能、可扩展性
  4. 多用户场景需要仔细设计数据隔离方案
  5. 质量保证需要建立评估和验证机制

这些设计原则不仅适用于学习助手,也适用于其他智能体应用场景,如个人助手、知识管理、教育培训等。

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计