fix(forgetting): 遗忘机制方案A — 碎片快速道+episodes/长内容保护+stats修正
- governance.go: recall加成 0.05→0.005(防 recall_count 500+ 虚高保命); decayRate 试 0.03 误删长内容后回滚 0.015 - server.go decay: 碎片快速道(<30字&>20天→auto_forget_fragment); episodes 不参与遗忘(曾误删67条对话已恢复); >200字长记忆保护 - admin.go Forget: 同步三保护 - rust lancedb_ops.rs: tombstone_count 数 is_deleted=true(移植 f6c4383+类型修) 验证: admin forget 触发 tombstone 0→210(碎片清除); episodes/长内容 0 误删; stats 真实计数
This commit is contained in:
parent
1bf23c42e8
commit
a5b5e217f3
|
|
@ -72,6 +72,22 @@ func (aa *AdminAPI) Forget(w http.ResponseWriter, r *http.Request) {
|
||||||
tier := strVal(mem["tier"])
|
tier := strVal(mem["tier"])
|
||||||
content := strVal(mem["content"])
|
content := strVal(mem["content"])
|
||||||
|
|
||||||
|
// 🔒 2026-09-07 修复(误删事故): episodes(原始对话)不参与遗忘
|
||||||
|
if cat := strVal(mem["category"]); cat == "episodes" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 🔒 长内容保护: >200 字记忆不参与 auto 遗忘(有实质信息)
|
||||||
|
if len([]rune(content)) > 200 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 2026-09-07 方案A 碎片快速道(与 server.go decay trigger 一致):
|
||||||
|
// <30 字且 >20 天未访问 → 直接遗忘(碎片 recall_count 可能虚高, 绕过保命)
|
||||||
|
if len([]rune(content)) < 30 && time.Since(lastAccess).Hours()/24 > 20 {
|
||||||
|
aa.LanceDB.SoftDelete(strVal(mem["id"]), "auto_forget_fragment")
|
||||||
|
forgotten++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// E4.3 修复: 从 content 提取实体(而非读 namespace),取图谱最大度
|
// E4.3 修复: 从 content 提取实体(而非读 namespace),取图谱最大度
|
||||||
degree := extractTopEntityDegree(content, aa.GraphStore)
|
degree := extractTopEntityDegree(content, aa.GraphStore)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1262,6 +1262,19 @@ func NewServer() http.Handler {
|
||||||
}
|
}
|
||||||
for _, m := range memories {
|
for _, m := range memories {
|
||||||
decayScanned++
|
decayScanned++
|
||||||
|
// 🔒 2026-09-07 修复(误删事故): episodes(原始对话)不参与遗忘——
|
||||||
|
// 它们是蒸馏原料/审计历史, 不是可遗忘的记忆。曾因未过滤 category
|
||||||
|
// 导致 67 条 8月对话被 auto_forget 清掉(已恢复)。仅 distilled/general 等记忆可遗忘。
|
||||||
|
if cat, ok := m["category"].(string); ok && cat == "episodes" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 🔒 长内容保护: distilled >200 字(有实质信息)不参与 auto 遗忘
|
||||||
|
// (曾误删 405 字 CNB 修复经验; 长记忆应由蒸馏/整合管理, 非时间遗忘)
|
||||||
|
if cat, _ := m["category"].(string); cat != "episodes" {
|
||||||
|
if cs, ok := m["content"].(string); ok && len([]rune(cs)) > 200 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
// 解析 last_recalled_at
|
// 解析 last_recalled_at
|
||||||
lastAccessed := time.Now().Add(-30 * 24 * time.Hour) // 默认30天前
|
lastAccessed := time.Now().Add(-30 * 24 * time.Hour) // 默认30天前
|
||||||
if t, ok := m["last_recalled_at"].(string); ok && t != "" {
|
if t, ok := m["last_recalled_at"].(string); ok && t != "" {
|
||||||
|
|
@ -1269,6 +1282,20 @@ func NewServer() http.Handler {
|
||||||
lastAccessed = parsed
|
lastAccessed = parsed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 2026-09-07 方案A 碎片快速道: <30 字且 >20 天未访问 → 直接遗忘
|
||||||
|
// (绕过 ShouldForget 的 recallCount 保命——碎片 recall_count 可能虚高)。
|
||||||
|
// 碎片无记忆价值(审计: 30.9% <20字 + 43% <50字 = 蒸馏无门槛产物)。
|
||||||
|
contentStr := ""
|
||||||
|
if cs, ok := m["content"].(string); ok {
|
||||||
|
contentStr = cs
|
||||||
|
}
|
||||||
|
if len([]rune(contentStr)) < 30 && time.Since(lastAccessed).Hours()/24 > 20 {
|
||||||
|
if id, ok := m["id"].(string); ok {
|
||||||
|
_ = ldb.SoftDelete(id, "auto_forget_fragment")
|
||||||
|
decayForgotten++
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
recallCount := 0
|
recallCount := 0
|
||||||
if rc, ok := m["recall_count"].(int); ok {
|
if rc, ok := m["recall_count"].(int); ok {
|
||||||
recallCount = rc
|
recallCount = rc
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,8 @@ type Forgetter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewForgetter() *Forgetter {
|
func NewForgetter() *Forgetter {
|
||||||
|
// 2026-09-07 方案A: 0.015→0.03 曾致误删(36天库龄下 0-recall 长记忆全被清, 405字CNB经验被删),
|
||||||
|
// 回滚 0.015。碎片清除由碎片快速道(server.go <30字&>20天)负责, 普通记忆 53 天老化合理。
|
||||||
return &Forgetter{agentType: "default", decayRate: 0.015}
|
return &Forgetter{agentType: "default", decayRate: 0.015}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +261,10 @@ func (f *Forgetter) ShouldForget(lastAccessed time.Time, recallCount int, tier s
|
||||||
score = 0.1
|
score = 0.1
|
||||||
}
|
}
|
||||||
// recallCount > 0 减缓衰减
|
// recallCount > 0 减缓衰减
|
||||||
score += float64(recallCount) * 0.05
|
// 2026-09-07 方案A: 0.05→0.005。原 0.05/次 + 无 cap → recall_count 457-540 时 +25 分,
|
||||||
|
// 永不遗忘 (日志大量 recall_count 500+ = 每次搜索命中都 ++, 虚高保命)。
|
||||||
|
// 现 0.005/次, cap 30 次后贡献 ≤0.15 分 ≈ 5 天保护, 合理。
|
||||||
|
score += float64(recallCount) * 0.005
|
||||||
// 图谱节点度 > 5 时,每超过 1 度 + 0.03 保留分(E4.3: 图谱推理参与遗忘决策)
|
// 图谱节点度 > 5 时,每超过 1 度 + 0.03 保留分(E4.3: 图谱推理参与遗忘决策)
|
||||||
if len(graphDegree) > 0 && graphDegree[0] > 5 {
|
if len(graphDegree) > 0 && graphDegree[0] > 5 {
|
||||||
score += float64(graphDegree[0]-5) * 0.03
|
score += float64(graphDegree[0]-5) * 0.03
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue