fix(memory): 打通场景/索引/召回残余矛盾点,清理死代码与冗余索引

- DecaySceneRefs 真半衰期:新增 scene_refs.decayed_at 作计时起点,
  每个引用至多每 halfLife 衰减一次。此前只按 created_at 判龄 + 每次
  心跳对半砍,30 天阈值配 60 分钟心跳会在几小时内清空老关联(不是半
  衰期是骤死);时间基准改走 SQLite datetime('now'),不再与 Go 本地
  时间混用。
- Indexer.syncIfStale 基线口径与 Sync 对齐(min(实体数, 全量召回上限)):
  实体数超过上限时原实现永远不相等,每 retrainInterval 全量重训一次。
- EnsureScene 去掉从不使用的 Situation 参数;修正 EnterSceneWithHint /
  resolveTurnScenes / 测试里「声明场景会学整轮指纹」的过时注释(实际
  刻意不学,否则会吃死被动路)。
- SituationFeature.Weight 补 peer_group → wFeatPeer:此前落到 default
  话题级 0.4,群聊身份被降级成软信号。
- RecallBySituation 注释改为与实现一致(相似度只决定命中哪些场景,
  不参与每条关系排序)。
- 移除只被测试使用的 EmergentScenes(SceneStats 已含 origin/strength/
  features,完全覆盖)。
- 清理与 UNIQUE 隐含索引重复的 idx_entity_name / idx_sentences_text。
- 新增 TestSyncIfStaleBaseline / TestPeerGroupWeight,衰减测试补「同一
  半衰期内不重复衰减」用例。

go build/vet 干净,internal/... 全绿。
This commit is contained in:
JianFeeeee
2026-09-15 10:20:40 +08:00
parent 246306a989
commit b5a20113cb
6 changed files with 248 additions and 67 deletions

View File

@ -3,6 +3,7 @@ package memory
import (
"path/filepath"
"testing"
"time"
)
func TestNewIndexer(t *testing.T) {
@ -48,6 +49,52 @@ func TestIndexerSync(t *testing.T) {
}
}
// TestSyncIfStaleBaseline 钉住增量同步的基线口径:
// - 实体数未变 → 不重训
// - 实体数变了但不足 retrainInterval → 先不重训(避免密集写入时写放大)
// - 实体数变了且间隔已过 → 重训,并把基线追到新值
func TestSyncIfStaleBaseline(t *testing.T) {
db, err := NewGraphDB(filepath.Join(t.TempDir(), "test.db"))
if err != nil {
t.Fatal(err)
}
defer db.Close()
if _, _, err := db.Commit([]Triple{
{Subject: "张三", Relation: "喜欢", Object: "篮球"},
}, "test", 0); err != nil {
t.Fatal(err)
}
idx := NewIndexer(db)
if !idx.syncIfStale() {
t.Fatal("首次应建立索引")
}
if idx.syncIfStale() {
t.Error("实体数未变不该重训")
}
if _, _, err := db.Commit([]Triple{
{Subject: "李四", Relation: "喜欢", Object: "足球"},
}, "test", 0); err != nil {
t.Fatal(err)
}
if idx.syncIfStale() {
t.Error("retrainInterval 内不该重训(避免写放大)")
}
// 把上次同步时刻推老,计数变化才该触发重训
idx.mu.Lock()
idx.lastSyncAt = time.Now().Add(-2 * retrainInterval)
idx.mu.Unlock()
if !idx.syncIfStale() {
t.Error("计数变化且间隔已过应重训")
}
if idx.syncIfStale() {
t.Error("重训后基线应追上,不该再重训")
}
}
func TestIndexerBuildContext(t *testing.T) {
db, err := NewGraphDB(filepath.Join(t.TempDir(), "test.db"))
if err != nil {