跳转到内容

Gatekeeper 分层记忆提取在 LobeHub 中的实现

学习目标

理解 LobeHub 如何在 UserMemory 服务中实现 Gatekeeper 模式的 5 层分层记忆提取,包括 Gatekeeper 决策、多层独立模型配置和提取器并行调度。

前置知识

本章涉及记忆系统和分层记忆的通用原理,建议先阅读:

下文假设你已理解上述概念,直接聚焦 LobeHub 的具体实现。

项目实践

Gatekeeper → 5 层提取流水线

LobeHub 的 UserMemory 服务使用 Gatekeeper 作为门控,5 个提取器分别处理不同维度的记忆:

Gatekeeper 决策

Gatekeeper 使用 LLM 判断是否需要对当前交互进行记忆提取,以及哪些层需要提取:

// 伪代码:Gatekeeper 决策
interface GatekeeperDecision {
activity: { shouldExtract: boolean; reasoning: string };
context: { shouldExtract: boolean; reasoning: string };
experience: { shouldExtract: boolean; reasoning: string };
identity: { shouldExtract: boolean; reasoning: string };
preference: { shouldExtract: boolean; reasoning: string };
}

Gatekeeper 的 prompt 包含:

  • 用户当前交互的摘要
  • 已有的相关记忆(通过 context provider 检索)
  • 现有身份信息
  • 提取语言(用户偏好语言)

多层独立模型配置

LobeHub 允许每个记忆层使用不同的模型:

// 伪代码:多层模型配置
interface MemoryExtractionLLMConfig {
gateModel: string; // Gatekeeper 使用的模型
layerModels: {
activity?: string; // 活动提取模型
context?: string; // 上下文提取模型
experience?: string; // 经验提取模型
identity?: string; // 身份提取模型
preference?: string; // 偏好提取模型
};
embeddingsModel?: string; // 嵌入向量模型
provider?: string;
}

设计决策:Gatekeeper 可以使用较便宜的模型(只需要分类判断),而 identity 和 preference 层可以使用更强的模型(需要复杂推理)。这样在保证质量的同时降低成本。

提取器架构

每个提取器实现统一的接口:

// 伪代码:提取器依赖
interface BaseExtractorDependencies {
agent: MemoryExtractionAgent; // "gatekeeper" | "layer-activity" | ...
model: string;
modelRuntime: ModelRuntime;
}

提取器使用 structuredCall 进行 LLM 调用,返回结构化输出:

// 伪代码:提取结果
interface MemoryExtractionLayerOutputs {
activity?: { data?: ExtractedActivity; error?: unknown };
context?: { data?: ExtractedContext; error?: unknown };
experience?: { data?: ExtractedExperience; error?: unknown };
identity?: { data?: ExtractedIdentity; error?: unknown };
preference?: { data?: ExtractedPreference; error?: unknown };
}

提取作业管理

LobeHub 使用 MemoryExtractionJob 管理提取任务:

// 伪代码:提取作业
interface MemoryExtractionJob {
userId: string;
source: MemorySourceType; // 消息来源
sourceId: string;
layers: LayersEnum[]; // 需要提取的层
force?: boolean; // 强制提取(跳过 Gatekeeper)
}

提取结果记录包含父级追踪键,支持离线分析时从单个 generateObject 调用回溯到作业级追踪:

// 伪代码:结果记录
interface MemoryResultRecorder {
recordComplete(job, result): Promise<void>;
recordFail?(job, error): Promise<void>;
}
// parentMemoryTraceKey 传递到 llm_generation_tracing 表
// 作为 metadata.parent_memory_trace_key

问题与规避

问题场景规避策略
Gatekeeper 误判Gatekeeper 判断某层不需要提取,但实际有重要信息force: true 强制提取模式,绕过 Gatekeeper
某层提取失败单模型调用异常每层独立错误处理,不影响其他层,processedErrorsCount 记录
Gatekeeper 自身 LLM 调用失败Gatekeeper 调用异常回退到提取所有层(安全默认值)
提取延迟5 层串行执行延迟高可并行执行互不依赖的层

设计取舍

为什么用 Gatekeeper 而不是每层都提取?

如果每次交互都运行 5 个 LLM 调用,成本过高且产生大量低价值记忆。Gatekeeper 通过一次 LLM 调用判断哪些层值得提取,平均减少 60%+ 的不必要调用。

代价:Gatekeeper 本身需要一次 LLM 调用。Gatekeeper 误判会导致信息遗漏。

对策

  • 对 Gatekeeper 使用较强模型(如 Sonnet)
  • 定期用历史数据评估 Gatekeeper 准确率
  • 支持 force: true 手动触发全层提取

为什么各层可以配置独立模型?

不同层对 LLM 能力要求不同:

  • Activity:简单分类和摘要 → 便宜模型即可
  • Identity:复杂推理和自我理解 → 需要强模型
  • Preference:模式识别 → 中等模型

统一使用最强模型浪费成本,统一使用最弱模型影响质量。

参考来源

  • LobeHub UserMemory 类型定义 — packages/memory-user-memory/src/types.ts
  • LobeHub UserMemory 提取器 — packages/memory-user-memory/src/extractors/
  • 记忆系统概述 — 通用记忆系统原理