Files
sundynix-agentix/memory_industry_analysis.md
T
2026-06-19 22:18:54 +08:00

229 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 生产级 AI Agent 长期记忆 — 行业主流方案
## 行业共识:四层记忆架构
2025 年以来,行业在长期记忆上已经形成了**四层分类共识**(模仿认知科学):
| 层 | 类型 | 内容 | 生命周期 |
|---|---|---|---|
| L1 | **工作记忆** | 当前对话的上下文窗口 | 会话内 |
| L2 | **短期/情景记忆** | 近期对话摘要、事件日志 | 数小时 ~ 数天 |
| L3 | **语义记忆** | 提炼出的事实、偏好、知识 | 持久(可衰减) |
| L4 | **程序记忆** | 学到的行为规则、约束、SOP | 持久 |
> **sundynix-agentix 当前只覆盖了 L1context window+ L2history+ L3 的最简形态(KV profile)。**
---
## 四大主流方案详解
### 1️⃣ OpenAI ChatGPT — "Dreaming" 机制
**思路**:不靠向量检索,靠**后台异步合成摘要**。
```
用户对话 → 结束后异步触发 "Dreaming"
分析历史 → 合成/更新 Memory Summary(结构化事实列表)
下次对话 → 整个 Memory Summary 注入 context window
```
**架构特点**
- **存储**:结构化事实列表(非向量),存为 user-scoped 文档
- **写入**:后台异步 "Dreaming" 进程,周期性回顾历史、合成偏好
- **读取****全量注入** context window(不做检索,依赖结构化摘要足够紧凑)
- **治理**:自动失效旧记忆("traveling to Singapore" → "went to Singapore"),用户可查看/编辑/删除
- **不用 RAG**:刻意避免向量检索的延迟和复杂度,用轻量摘要替代
> [!NOTE]
> sundynix-agentix 的方案与 ChatGPT 早期版本(2024.4 Saved Memories)非常接近——都是**逐轮 LLM 抽取 + 全量注入**。
> ChatGPT 后来演进到 Dreaming 是因为发现逐轮抽取会导致"记忆腐烂"noisy/stale),需要**周期性整理合并**。
---
### 2️⃣ Mem0 — 行业最主流的记忆中间件
**思路**:三阶段流水线 + 混合存储。被认为是当前**事实上的行业标准**。
```
对话消息 → Extract → Consolidate → Store
┌───────────┴───────────┐
Vector Store Graph Store (Mem0g)
(语义检索) (关系推理)
查询时 → Retrieve (语义相似 + 图谱关联) → 注入 prompt
```
**三阶段流水线**
| 阶段 | 做什么 | 关键区别 |
|---|---|---|
| **Extract** | LLM 从对话中抽取结构化事实和关系 | 类似 sundynix 的 `extractMemory` |
| **Consolidate** | LLM 将新事实与已有记忆**对账**,输出 ADD / UPDATE / DELETE / NOOP | ⚠️ sundynix 缺失这一层 |
| **Store** | 分别写入 Vector Store + Graph Store + KV Store | sundynix 只有 KV |
**Consolidate 是核心差异**
```
已有记忆: "用户喜欢 Vue"
新抽取: "用户说最近在学 React,打算迁移"
Mem0 Consolidate → UPDATE: "用户从 Vue 迁移到 React"
sundynix → 如果 key 不同 → 两条并存("前端框架:Vue" + "技术学习:React"
如果 key 恰好相同 → 覆盖(但依赖 LLM 用同一个 key,不可控)
```
**检索方式**
- **不是全量注入**,而是用当前 query embedding 做 top-K 语义检索
- 同时走 Graph Store 做关系关联检索("用户在 X 公司" → 关联出 "X 公司用 Go 技术栈"
- 大幅减少 token 浪费
---
### 3️⃣ Letta(原 MemGPT)— 操作系统式自管理记忆
**思路**:Agent 像操作系统一样**自己管理**自己的内存层级。
```
┌─────────────────────────────────────────┐
│ Context Window ("RAM") │
│ ┌─────────┐ ┌──────────┐ ┌─────┐ │
│ │ System │ │ Recall │ │User │ │
│ │ Prompt │ │ Memory │ │Msg │ │
│ │(可编辑) │ │(检索结果) │ │ │ │
│ └─────────┘ └──────────┘ └─────┘ │
└─────────────┬───────────────────────────┘
│ Agent 主动调用内存管理工具
┌─────────────────────────────────────────┐
│ Archival Memory ("Disk") — 向量存储 │
│ Agent 可以 search / insert / delete │
└─────────────────────────────────────────┘
```
**关键设计**
- Agent 自己决定什么时候存记忆(不是外部异步抽取)
- Agent 自己决定什么时候查记忆(通过 tool call)
- System prompt 是**可编辑的**Agent 可以修改自己的"人格"和"核心记忆"
- Archival Memory 是无限的向量存储,Agent 按需检索
**与 sundynix 的对比**sundynix 是外部驱动(orchestrator 异步抽取),Letta 是 Agent 自驱动。
---
### 4️⃣ Zep / Graphiti — 双时态知识图谱
**思路**:面向**时间敏感**场景,用图谱追踪事实的时间演变。
```
对话/文档 → Graphiti 引擎 → 三层子图:
├─ Episode 子图:原始事件(无损保留,带时间戳)
├─ Semantic Entity 子图:提取的实体和关系(语义检索)
└─ Community 子图:实体聚类(高层概览)
```
**双时态模型(Bi-temporal**
每条事实记录两个时间维度:
| 维度 | 含义 | 示例 |
|---|---|---|
| **Valid Time** | 事实在现实中为真的时间 | "2024-01 ~ 2025-03 用户在 A 公司" |
| **Ingestion Time** | 系统何时发现这个事实 | "2025-04 从对话中得知" |
```
2024-01: 用户说 "我在 A 公司做后端"
2025-03: 用户说 "我上个月从 A 离职了"
Zep 存储:
(用户) -[就职于]-> (A公司) valid: 2024-01 ~ 2025-02, invalidated_at: 2025-03
(用户) -[离职于]-> (A公司) valid: 2025-03 ~
普通 KV 存储:
公司: A公司 → 被覆盖为空,或两条矛盾并存
```
- 可以回答"用户去年这个时候在做什么?"
- 旧事实**失效但不删除**,保留完整时间线
- 企业级场景(合规、审计、保单变更)的首选
---
## 对比总览
| 维度 | sundynix 现状 | ChatGPT | Mem0 | Letta | Zep |
|---|---|---|---|---|---|
| **存储** | PG 扁平 KV | 结构化文档 | Vector + Graph + KV | Vector (Archival) | 双时态知识图谱 |
| **抽取** | 逐轮 LLM 抽取 | 后台周期 Dreaming | Extract 阶段 | Agent 自主存储 | Graphiti 引擎 |
| **去重/合并** | exact key match | Dreaming 合成 | **Consolidate (ADD/UPDATE/DELETE)** | Agent 自管理 | 双时态失效 |
| **检索** | **全量注入** | 全量注入(紧凑摘要) | **语义 top-K + 图谱关联** | Agent 主动搜索 | 语义 + 时间 + 图谱 |
| **时间维度** | ❌ | 有(自动过时) | ❌ | ❌ | ✅ 双时态 |
| **遗忘** | ❌ | ✅ 自动 | ✅ DELETE 操作 | ✅ Agent 决定 | ✅ 失效不删 |
| **用户控制** | ❌ | ✅ 查看/编辑/删除 | 有 API | 有 API | 有 API |
| **复杂度** | ★☆☆☆☆ | ★★☆☆☆ | ★★★☆☆ | ★★★★☆ | ★★★★★ |
---
## sundynix-agentix 的差距与改造路线
### 与行业标准(Mem0 模式)的 3 个关键差距
```mermaid
flowchart LR
A["1. 缺 Consolidate 层<br/>无法 ADD/UPDATE/DELETE<br/>只有盲目 upsert"] --> D["记忆腐烂<br/>冗余/过时/矛盾"]
B["2. 全量注入<br/>不做相关性检索"] --> E["token 浪费<br/>噪声干扰"]
C["3. 无时间/置信度<br/>维度"] --> F["无法遗忘<br/>无法区分强弱偏好"]
```
### 建议改造路线(利用已有基础设施)
项目已有 **Milvus + Neo4j + Postgres**,完全可以不引入新依赖,渐进实现 Mem0 级别的能力:
#### Phase 1 — 加 Consolidate 层(改 `memory_extract.go`,约 50 行)
把现在的 "抽取 → 盲目 upsert" 改为 "抽取 → LLM 对账 → 决策执行":
```go
// 现在的流程:
fresh := filterNewPrefs(parsePrefs(txt), existing) // exact match 去重
for _, p := range fresh {
o.upsertMemory(ctx, uid, p.Key, p.Value) // 盲 upsert
}
// 改为 Mem0 式 Consolidate
// 1. 把新抽取 + 已有画像一起交给 LLM
// 2. LLM 输出操作列表:[{op:"ADD/UPDATE/DELETE", key, value, reason}]
// 3. 按操作执行
```
#### Phase 2 — 按需检索代替全量注入(复用 Milvus,改 `compile.go`
```go
// 现在 (compile.go:27-31):全量注入
if rc.Profile != "" {
sys.WriteString("\n\n关于当前用户的已知信息:\n")
sys.WriteString(rc.Profile) // 全部偏好
}
// 改为:memory_get 传入 queryMilvus 语义检索 top-5 相关偏好
// memory store 存偏好时同时 embedding → Milvus memory collection
// 读取时用 query embedding → top-K → 只注入相关的
```
#### Phase 3 — 图谱记忆(复用 Neo4j)
```
memory_upsert 时同时写入 Neo4j
(User:uid) -[偏好 {key, since, confidence}]-> (Value:value)
检索时可以做关系推理:
"用户的公司" → "公司的技术栈" → 关联出相关偏好
```
> [!TIP]
> **最务实的改造优先级**Phase 1Consolidate> Phase 2(语义检索)> Phase 3(图谱)。
> Phase 1 改动最小(只改 `memory_extract.go` 的抽取 prompt 和处理逻辑),
> 但效果最显著——直接解决记忆腐烂这个最大的 Day-2 问题。