- 只翻注释/文档字符串:820 增 / 820 删 严格 1:1;剥掉整行注释与行尾注释尾巴后, 逐文件与 HEAD 比对「代码部分差异 = 0」;裁决/design 编号计数逐项与 HEAD 一致 - 保留可执行 SQL 样例、SQLite 报错原文、pgvector/HNSW/ANN/BLOB 等技术词原文 - 复核:go build 绿 + memory/projectindex/vector 三包测试绿;gofmt 仅剩 2 个既有未格式化文件(改动前即如此,未顺手修)
80 lines
2.6 KiB
Go
80 lines
2.6 KiB
Go
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},
|
||
// 默认半衰期为 30 天:60 天前 = 2 个半衰期 = 2^-2。
|
||
{name: "two half-lives halves twice", updatedAt: now.Add(-60 * 24 * time.Hour), decayWeight: 1.0, want: 0.25},
|
||
// 较低的基础 decay_weight 会整体压低曲线。
|
||
{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},
|
||
// 未来时间戳不得把得分抬到基础权重以上。
|
||
{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()
|
||
// 一条新鲜但 decay_weight 近乎零的条目(design:用户可削弱某记忆)。
|
||
addItem(t, s, MemoryItem{
|
||
PersonaID: "persona.a", Content: "低权重但新鲜", UpdatedAt: now,
|
||
DecayWeight: 0.1, Scope: ScopePersona, Kind: KindWorldEvent,
|
||
})
|
||
// 稍有历史、但 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)
|
||
// query 为空时无任何命中,因此排序完全由衰减因子
|
||
// (decay_weight × 时间衰减)驱动:低权重的新条目必须
|
||
// 排在满权重、稍旧的条目之后。
|
||
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)
|
||
}
|
||
}
|