feat(memory): M4 记忆完善(三类作用域访问规则+项目索引分层+LLM异步提取+衰减溯源+向量接入,兼容既有57用例)
This commit is contained in:
1 parent
5b37f5a551
commit
d947ec41a9
10 files changed
+1349
-53
No files matched your search
@@ -0,0 +1,79 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRetriever_DecayFactor_久未使用降权(t *testing.T) {
|
||||
t.Parallel()
|
||||
r := NewRetriever(newTestStore(t))
|
||||
now := time.Now().UTC()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
updatedAt time.Time
|
||||
decayWeight float64
|
||||
want float64
|
||||
}{
|
||||
{name: "fresh keeps full weight", updatedAt: now, decayWeight: 1.0, want: 1.0},
|
||||
// Default half-life is 30 days: 60 days ago = 2 half-lives = 2^-2.
|
||||
{name: "two half-lives halves twice", updatedAt: now.Add(-60 * 24 * time.Hour), decayWeight: 1.0, want: 0.25},
|
||||
// Lower base decay_weight scales the whole curve down.
|
||||
{name: "low decay_weight scales down", updatedAt: now, decayWeight: 0.5, want: 0.5},
|
||||
{name: "low weight plus age compounds", updatedAt: now.Add(-60 * 24 * time.Hour), decayWeight: 0.5, want: 0.125},
|
||||
// A future timestamp must not boost above the base weight.
|
||||
{name: "future timestamp clamps to zero", updatedAt: now.Add(time.Hour), decayWeight: 1.0, want: 1.0},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := r.decayFactor(MemoryItem{
|
||||
DecayWeight: tt.decayWeight,
|
||||
UpdatedAt: tt.updatedAt,
|
||||
}, now)
|
||||
if math.Abs(got-tt.want) > 1e-9 {
|
||||
t.Fatalf("decayFactor = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetriever_Retrieve_DecayWeight_降权(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
s := newTestStore(t)
|
||||
|
||||
now := time.Now().UTC()
|
||||
// A fresh item but with a near-zero decay_weight (design: 用户可削弱某记忆).
|
||||
addItem(t, s, MemoryItem{
|
||||
PersonaID: "persona.a", Content: "低权重但新鲜", UpdatedAt: now,
|
||||
DecayWeight: 0.1, Scope: ScopePersona, Kind: KindWorldEvent,
|
||||
})
|
||||
// A slightly older item with a full decay_weight.
|
||||
addItem(t, s, MemoryItem{
|
||||
PersonaID: "persona.a", Content: "权重高稍旧", UpdatedAt: now.Add(-time.Hour),
|
||||
DecayWeight: 1.0, Scope: ScopePersona, Kind: KindWorldEvent,
|
||||
})
|
||||
|
||||
r := NewRetriever(s)
|
||||
// A blank query matches nothing, so ordering is driven by the decay factor
|
||||
// (decay_weight × time decay) alone: the low-weight fresh item must fall
|
||||
// below the full-weight slightly-older item.
|
||||
results, err := r.Retrieve(ctx, "persona.a", "", 10)
|
||||
if err != nil {
|
||||
t.Fatalf("Retrieve: %v", err)
|
||||
}
|
||||
if len(results) != 2 {
|
||||
t.Fatalf("got %d results, want 2", len(results))
|
||||
}
|
||||
if got := results[0].Item.Content; got != "权重高稍旧" {
|
||||
t.Fatalf("top result = %q, want the full-decay-weight item", got)
|
||||
}
|
||||
if got := results[1].Item.Content; got != "低权重但新鲜" {
|
||||
t.Fatalf("second result = %q, want the low-decay-weight item", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user