memoryweave/go/internal/selfoptimize/vprop.go

207 lines
5.0 KiB
Go

// 织忆 MemoryWeave — V 值反向传播引擎
package selfoptimize
import (
"encoding/json"
"math"
"strconv"
"sync"
"time"
"github.com/xiaoxue/memoryweave/internal/storage"
)
// ─── V 值反向传播 ──────────────────────────────────────
// VDecision V 值决策记录
type VDecision struct {
ID string `json:"id"`
MemoryIDs []string `json:"memory_ids"` // 引用的记忆 ID
Action string `json:"action"` // 牧尘的决策行动
Outcome string `json:"outcome"` // success / failure / partial
VValue float64 `json:"v_value"` // 决策 V 值 (0-1)
ParentID string `json:"parent_id"` // 上级决策(级联链路)
Timestamp time.Time `json:"timestamp"`
}
// VPropagator V 值传播器
type VPropagator struct {
mu sync.RWMutex
decisions map[string]*VDecision
memVValues map[string]float64 // memory_id → 累积 V 值
decayRate float64 // V 值衰减率 (default 0.01/day)
// Redis 持久化
redisClient *storage.RedisClient
}
const vpropRedisKey = "zhiyi:vprop_decisions"
const vpropValsRedisKey = "zhiyi:vprop_values"
func (vp *VPropagator) EnableVPropRedisPersistence() {
rc := storage.GetRedisClient()
if rc == nil {
return
}
vp.redisClient = rc
vp.loadVPropFromRedis()
}
func (vp *VPropagator) loadVPropFromRedis() {
if vp.redisClient == nil {
return
}
// 加载决策
data, err := vp.redisClient.HGetAll(vpropRedisKey)
if err == nil && len(data) > 0 {
for id, jsonStr := range data {
var d VDecision
if json.Unmarshal([]byte(jsonStr), &d) == nil {
vp.decisions[id] = &d
}
}
}
// 加载 V 值
valData, err := vp.redisClient.HGetAll(vpropValsRedisKey)
if err == nil && len(valData) > 0 {
for memID, valStr := range valData {
if v, err := strconv.ParseFloat(valStr, 64); err == nil {
vp.memVValues[memID] = v
}
}
}
}
func (vp *VPropagator) persistVProp() {
if vp.redisClient == nil {
return
}
for id, d := range vp.decisions {
data, _ := json.Marshal(d)
vp.redisClient.HSet(vpropRedisKey, id, string(data))
}
vp.redisClient.Expire(vpropRedisKey, 90*24*time.Hour)
for memID, v := range vp.memVValues {
vp.redisClient.HSet(vpropValsRedisKey, memID, strconv.FormatFloat(v, 'f', 4, 64))
}
vp.redisClient.Expire(vpropValsRedisKey, 90*24*time.Hour)
}
var VProp = &VPropagator{
decisions: make(map[string]*VDecision),
memVValues: make(map[string]float64),
decayRate: 0.01,
}
// RecordDecision 记录一条决策及其引用的记忆
func (vp *VPropagator) RecordDecision(id string, memoryIDs []string, action, outcome string, parentID string) *VDecision {
vp.mu.Lock()
defer vp.mu.Unlock()
vValue := 0.0
switch outcome {
case "success": vValue = 1.0
case "partial": vValue = 0.5
case "failure": vValue = 0.0
}
d := &VDecision{
ID: id,
MemoryIDs: memoryIDs,
Action: action,
Outcome: outcome,
VValue: vValue,
ParentID: parentID,
Timestamp: time.Now(),
}
vp.decisions[id] = d
// 反向传播:引用的记忆获得 V 值
boostPerMem := vValue / float64(maxInt(1, len(memoryIDs)))
for _, memID := range memoryIDs {
vp.memVValues[memID] += boostPerMem
}
// 级联传播:如果父决策存在,修改父决策的 outcome
if parentID != "" {
if parent, ok := vp.decisions[parentID]; ok {
if vValue > 0.8 {
parent.Outcome = "success"
parent.VValue = math.Min(1.0, parent.VValue+0.1)
}
}
}
vp.persistVProp()
return d
}
// GetMemVValue 获取某条记忆的累积 V 值
func (vp *VPropagator) GetMemVValue(memoryID string) float64 {
vp.mu.RLock()
defer vp.mu.RUnlock()
v := vp.memVValues[memoryID]
// 应用时间衰减
if v > 0 {
// 检查最后引用时间
lastRef := time.Time{}
for _, d := range vp.decisions {
for _, mid := range d.MemoryIDs {
if mid == memoryID && d.Timestamp.After(lastRef) {
lastRef = d.Timestamp
}
}
}
if !lastRef.IsZero() {
days := time.Since(lastRef).Hours() / 24
v *= math.Exp(-vp.decayRate * days)
}
}
return math.Round(v*100) / 100
}
// GetDecisionChain 获取某条决策的因果链路
func (vp *VPropagator) GetDecisionChain(decisionID string) []*VDecision {
vp.mu.RLock()
defer vp.mu.RUnlock()
var chain []*VDecision
current := vp.decisions[decisionID]
for current != nil {
chain = append(chain, current)
if current.ParentID == "" {
break
}
current = vp.decisions[current.ParentID]
}
return chain
}
// ListRecentDecisions 列出最近 N 条决策
func (vp *VPropagator) ListRecentDecisions(n int) []*VDecision {
vp.mu.RLock()
defer vp.mu.RUnlock()
var all []*VDecision
for _, d := range vp.decisions {
all = append(all, d)
}
// 按时间降序简单排列
sortByTime(all)
if len(all) > n {
all = all[:n]
}
return all
}
func sortByTime(decisions []*VDecision) {
for i := 0; i < len(decisions); i++ {
for j := i + 1; j < len(decisions); j++ {
if decisions[j].Timestamp.After(decisions[i].Timestamp) {
decisions[i], decisions[j] = decisions[j], decisions[i]
}
}
}
}