feat(memory): 记忆 P2(项目索引物化 / decay 回写 / 作用域写规则 / 静默吞点 + loop 级只读红线测试)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

This commit is contained in:
AskaEth committed 2026-10-05 11:47:39 +08:00
1 parent 9b903a89bc
commit 509dc412eb
12 files changed
+660 -55

No files matched your search

+9 -35
View File
@@ -42,6 +42,7 @@ import (
"time"
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/memory/projectindex"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/memory/vector"
)
@@ -153,6 +154,13 @@ type Store struct {
// nil 表示没有配置持久索引,检索据此回退到内存余弦。
vecMu sync.RWMutex
vec vector.Index
// projMu 保护 proj,使项目索引存储能在装配后并发接入,而写入路径
// 每写一条项目作用域记忆就确保它链接的项目索引条目存在(见
// ensureProjectIndex)。proj 为 nil 表示未接入项目索引,此时写入
// 不触碰项目索引(design §3 项目记忆链接项目索引)。
projMu sync.RWMutex
proj *projectindex.Store
}
// rowScanner 同时被 *sql.Row 与 *sql.Rows 满足,因此单个 scanItem
@@ -230,6 +238,7 @@ func (s *Store) Add(ctx context.Context, item MemoryItem) (MemoryItem, error) {
return MemoryItem{}, fmt.Errorf("memory: insert item: %w", err)
}
s.indexItem(ctx, item)
s.ensureProjectIndex(ctx, item)
return item, nil
}
@@ -459,39 +468,4 @@ func discardLogger(log *slog.Logger) *slog.Logger {
return slog.New(slog.NewTextHandler(io.Discard, nil))
}
// schema DDL。internal/db 中的迁移框架是领域表的权威
// 机制,但这里刻意不用它:db 包的迁移测试断言精确的
// 迁移数量,而记忆存储必须自包含、
// 可在任意 *sql.DB 上使用。表与索引因此
// 由 NewStore 幂等创建。
const createMemoryItemsTable = `
CREATE TABLE IF NOT EXISTS memory_items (
id TEXT PRIMARY KEY,
persona_id TEXT NOT NULL,
role_id TEXT NOT NULL DEFAULT '',
project_id TEXT NOT NULL DEFAULT '',
scope TEXT NOT NULL,
kind TEXT NOT NULL,
content TEXT NOT NULL,
tags TEXT NOT NULL DEFAULT '[]',
confidence REAL NOT NULL DEFAULT 1.0,
source_conversation TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
decay_weight REAL NOT NULL DEFAULT 1.0,
status TEXT NOT NULL DEFAULT 'active',
merged_into TEXT NOT NULL DEFAULT '',
merged_from TEXT NOT NULL DEFAULT '[]',
merge_count INTEGER NOT NULL DEFAULT 1,
occurred_at TEXT NOT NULL DEFAULT '',
valid_to TEXT NOT NULL DEFAULT '',
expires_at TEXT NOT NULL DEFAULT ''
)`
const createMemoryItemsPersonaIndex = `
CREATE INDEX IF NOT EXISTS idx_memory_items_persona_id
ON memory_items (persona_id)`
const createMemoryItemsPersonaCreatedIndex = `
CREATE INDEX IF NOT EXISTS idx_memory_items_persona_id_created_at
ON memory_items (persona_id, created_at)`