feat(memory): P1 长期记忆升级 —— 异步攒批 Consolidate + 软删 + importance/last_seen
把"逐轮盲写抽取"升级为 Mem0 式对账(方案见 memory_industry_analysis.md 落地节):
mcp-go:
- Profile 加 Importance(1~10, poignancy) + LastSeenAt(为 Generative Agents 读路径
Score=w1·Relevance+w2·Recency+w3·Importance 铺路)。
- Upsert 收 importance + 每次置 last_seen(印证);新增 Delete(软删,BaseModel.DeletedAt
已具备,失效不物删可审计)+ Touch;memory_upsert 透传 importance、新增 memory_delete 工具。
dispatcher:
- extractMemory → consolidateMemory:一次 LLM 调用同时做 抽取+对账,输出
[{op:ADD|UPDATE|DELETE|NOOP,key,value,importance}];ADD/UPDATE→upsert、DELETE→软删;
sanitizeOps 防幻删(DELETE 须命中已有)/夹 importance[1,10]/同key保末个/丢 NOOP。
- 攒批:每 3 轮(per-session 计数)才 consolidate 一次,省成本,对齐 ChatGPT 周期整理。
从根上解决 exact-key 盲写的记忆腐烂。
验证:parseOps/sanitizeOps/parseProfile 纯逻辑单测;store 集成测试(真 PG)覆盖
importance/last_seen 写入 + 软删(live 0 / 物理 1);dispatcher -race 全过。
(注:完整多轮 LLM consolidate 未做实跑,属构造性验证 + 沿用已证 pool.Chat 模式。)
P2 待做:读路径按 Score(Recency+Importance) 排序/衰减/截断 + 桌面端记忆面板。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -226,3 +226,83 @@ memory_upsert 时同时写入 Neo4j:
|
||||
> **最务实的改造优先级**:Phase 1(Consolidate)> Phase 2(语义检索)> Phase 3(图谱)。
|
||||
> Phase 1 改动最小(只改 `memory_extract.go` 的抽取 prompt 和处理逻辑),
|
||||
> 但效果最显著——直接解决记忆腐烂这个最大的 Day-2 问题。
|
||||
|
||||
---
|
||||
|
||||
# 落地方案(修正版 · 实施计划)
|
||||
|
||||
> 在上面分析基础上做了三处修正:**① Consolidate 改异步攒批(不逐轮,省成本);② DELETE 用 soft-delete(失效不物删);③ Phase 2 语义检索暂缓(当前画像小,全量注入够用)**。并把读路径目标定为 Generative Agents 检索打分公式。
|
||||
|
||||
## 设计四原则
|
||||
|
||||
1. **对账而非盲写** —— 引入 Consolidate(ADD / UPDATE / DELETE / NOOP),解决 exact-key 去重不可控导致的记忆腐烂。
|
||||
2. **异步攒批而非逐轮** —— 每 N 轮跑一次"抽取+对账",一次 LLM 调用,省成本,对齐 ChatGPT 周期整理。
|
||||
3. **失效不物删** —— DELETE = GORM 软删(`BaseModel.DeletedAt` 已具备,零成本),白拿审计/可恢复。
|
||||
4. **按当下规模右尺寸** —— 小画像继续全量注入;语义检索/图谱留接口、暂不做。
|
||||
|
||||
## 数据模型(扩 mcp-go 的 `Profile`)
|
||||
|
||||
```go
|
||||
type Profile struct {
|
||||
BaseModel // 已含 id/created/updated/deleted_at(软删)
|
||||
UserID string
|
||||
Key string // (user_id,key) 唯一
|
||||
Value string
|
||||
Importance float64 // 1~10,consolidate 时 LLM 打分(poignancy)→ 读路径权重
|
||||
Confidence float64 // 0~1 信念强度(可选,强弱偏好)
|
||||
Source string // "user"(显式) / "extracted"(推断)
|
||||
LastSeenAt time.Time // 最近被印证时间 → Recency 衰减依据
|
||||
}
|
||||
```
|
||||
|
||||
## 写路径:异步攒批 Consolidate(核心)
|
||||
|
||||
```
|
||||
每轮结束 → 只 append history(已有,便宜)
|
||||
↓ 每 N 轮(用 history 长度判断,无需新基建)
|
||||
Consolidate(1 次 LLM 调用,extract + reconcile 合一,Mem0 式):
|
||||
输入 = 近 K 轮对话 + 当前 active 画像(带 key/importance)
|
||||
输出 = [{op: ADD|UPDATE|DELETE|NOOP, key, value, importance, reason}]
|
||||
↓ 按 op 执行
|
||||
ADD/UPDATE → memory_upsert(带 importance, last_seen=now)
|
||||
DELETE → memory_delete(软删)
|
||||
NOOP → bump last_seen(印证即强化)
|
||||
```
|
||||
|
||||
- 决策(LLM 对账)在 dispatcher;落库在 mcp-go 工具(新增 `memory_delete` 软删 + `memory_upsert` 收 importance)。
|
||||
- 触发:`memorize` 里 append 后看 `history_get` 长度,跨 N 轮才跑。
|
||||
|
||||
## 读路径目标 ★ Generative Agents 检索打分公式
|
||||
|
||||
最终读路径按此公式排序、取 top-K(论文 Park et al. 2023):
|
||||
|
||||
```
|
||||
Score = w1·Relevance + w2·Recency + w3·Importance
|
||||
```
|
||||
|
||||
| 项 | 取值 | 本项目映射 | 何时上 |
|
||||
|---|---|---|---|
|
||||
| **Importance** | 写入时 LLM 打 1~10(存下,不在检索时算)| `Profile.Importance`,consolidate 顺手打分 | **P1 就存** |
|
||||
| **Recency** | `decay^(距 last_seen)` 指数衰减 | `LastSeenAt` + 衰减 | **P2** |
|
||||
| **Relevance** | query×memory 余弦相似 | 需 embedding → Milvus | **P3(画像大才上)** |
|
||||
|
||||
- 三项各 min-max 归一到 [0,1],加权求和;**w1/w2/w3 设为可配**。
|
||||
- 现在画像小、全量注入:先用 **Recency + Importance** 两项排序/截断/遗忘(无向量、便宜);画像变大后补 Relevance 凑齐三项。
|
||||
- `memory_get(user_id, query?)` 签名预留 `query`,将来接 Milvus 不破坏调用方。
|
||||
|
||||
## 分阶段
|
||||
|
||||
| 阶段 | 内容 | 解决 |
|
||||
|---|---|---|
|
||||
| **P1** | 异步攒批 Consolidate(ADD/UPDATE/DELETE/NOOP)+ soft-delete + Profile 加 Importance/LastSeenAt(consolidate 时 LLM 打 importance 分)| 记忆腐烂 + 成本 |
|
||||
| **P2** | 读路径:按 Score(Recency+Importance) 排序 + 衰减遗忘 + 截断 top-N;桌面端记忆面板可看/改/删 | 噪声 + 遗忘 + 用户控制 |
|
||||
| **P3(暂不做)** | 补 Relevance:memory 存时 embedding→Milvus,读时 query top-K,凑齐三项公式;Neo4j 图谱记忆 | 大规模 / 关系推理 |
|
||||
|
||||
**故意不做**:双时态完整模型(soft-delete + last_seen 已够)、Letta 式 Agent 自管理(与现有外部编排架构不合)。
|
||||
|
||||
## P1 改动点(下午动手)
|
||||
|
||||
- `mcp-go/internal/memory/store.go`:`Profile` 加 `Importance/LastSeenAt`;`Upsert` 收 importance + 置 last_seen;新增 `Delete`(软删)。
|
||||
- `mcp-go/internal/mcp/gateway.go`:新增 `memory_delete` 工具;`memory_upsert` 透传 importance。
|
||||
- `dispatcher/internal/eino/memory_extract.go`:`extractMemory` → `consolidateMemory`:合并 extract+reconcile 为一次 LLM 调用,输出 op 列表(含 importance 打分),按 op 调 upsert/delete;触发改为每 N 轮(看 history 长度)。
|
||||
- 纯逻辑(op 解析 / 应用决策)可单测。
|
||||
|
||||
Reference in New Issue
Block a user