memoryweave/go/internal/selfoptimize/selfoptimize.go

317 lines
7.8 KiB
Go

// 织忆 MemoryWeave — 自优化引擎
package selfoptimize
import (
"math"
"sync"
"time"
)
// ─── 自优化仪表盘 ────────────────────────────────────────
type Dashboard struct {
mu sync.RWMutex
UsefulCount int `json:"useful_count"`
NotUsefulCount int `json:"not_useful_count"`
TotalRecalls int `json:"total_recalls"`
HitCount int `json:"hit_count"`
ClosedGaps int `json:"closed_gaps"`
TotalGaps int `json:"total_gaps"`
CascadeFixedTotal int `json:"cascade_fixed_total"`
TotalFixes int `json:"total_fixes"`
DeprecatedToday int `json:"deprecated_today"`
DistillLossSum float64 `json:"distill_loss_sum"`
DistillLossCount int `json:"distill_loss_count"`
AutoResolvedConflicts int `json:"auto_resolved_conflicts"`
TotalConflicts int `json:"total_conflicts"`
}
var Dash = &Dashboard{}
// Metrics 返回 7 项核心指标
func (d *Dashboard) Metrics() map[string]float64 {
d.mu.RLock()
defer d.mu.RUnlock()
usefulRate := 0.0
if d.UsefulCount+d.NotUsefulCount > 0 {
usefulRate = float64(d.UsefulCount) / float64(d.UsefulCount+d.NotUsefulCount)
}
hitRate := 0.0
if d.TotalRecalls > 0 {
hitRate = float64(d.HitCount) / float64(d.TotalRecalls)
}
gapRate := 0.0
if d.TotalGaps > 0 {
gapRate = float64(d.ClosedGaps) / float64(d.TotalGaps)
}
cascadeRate := 0.0
if d.TotalFixes > 0 {
cascadeRate = float64(d.CascadeFixedTotal) / float64(d.TotalFixes)
}
avgLoss := 0.0
if d.DistillLossCount > 0 {
avgLoss = d.DistillLossSum / float64(d.DistillLossCount)
}
autoRate := 0.0
if d.TotalConflicts > 0 {
autoRate = float64(d.AutoResolvedConflicts) / float64(d.TotalConflicts)
}
return map[string]float64{
"recall_usefulness_rate": math.Round(usefulRate*100) / 100,
"recall_hit_rate": math.Round(hitRate*100) / 100,
"gap_closure_rate": math.Round(gapRate*100) / 100,
"cascade_fix_rate": math.Round(cascadeRate*100) / 100,
"deprecated_per_day": float64(d.DeprecatedToday),
"avg_distill_loss": math.Round(avgLoss*100) / 100,
"auto_resolve_rate": math.Round(autoRate*100) / 100,
}
}
func (d *Dashboard) RecordRecall(hit bool) {
d.mu.Lock()
defer d.mu.Unlock()
d.TotalRecalls++
if hit {
d.HitCount++
}
}
func (d *Dashboard) RecordFeedback(useful bool) {
d.mu.Lock()
defer d.mu.Unlock()
if useful {
d.UsefulCount++
} else {
d.NotUsefulCount++
}
}
// ─── 知识缺口检测 ────────────────────────────────────────
type GapType string
const (
GapUnknown GapType = "A" // 真不知道
GapSynonym GapType = "B" // 同义词不匹配
GapRecallFailed GapType = "C" // 召回失败
GapFragmented GapType = "D" // 碎片化
)
type Gap struct {
Topic string `json:"topic"`
Type GapType `json:"type"`
MissCount int `json:"miss_count"`
CreatedAt time.Time `json:"created_at"`
Closed bool `json:"closed"`
}
type GapDetector struct {
mu sync.RWMutex
gaps map[string]*Gap
misses map[string]int
}
func NewGapDetector() *GapDetector {
return &GapDetector{
gaps: make(map[string]*Gap),
misses: make(map[string]int),
}
}
// RecordMiss 记录一次召回失败
func (gd *GapDetector) RecordMiss(topic string) *Gap {
gd.mu.Lock()
defer gd.mu.Unlock()
gd.misses[topic]++
if gd.misses[topic] >= 3 {
if _, exists := gd.gaps[topic]; !exists {
gap := &Gap{
Topic: topic,
Type: gd.classifyGap(topic),
MissCount: gd.misses[topic],
CreatedAt: time.Now(),
}
gd.gaps[topic] = gap
return gap
}
}
return nil
}
func (gd *GapDetector) classifyGap(topic string) GapType {
// 简单启发式:大写缩写 → 同义词;中文 → 可能是真的不知道
for _, r := range topic {
if r >= 'A' && r <= 'Z' {
return GapSynonym
}
}
return GapUnknown
}
func (gd *GapDetector) List() []*Gap {
gd.mu.RLock()
defer gd.mu.RUnlock()
var result []*Gap
for _, g := range gd.gaps {
result = append(result, g)
}
return result
}
func (gd *GapDetector) Close(topic string) {
gd.mu.Lock()
defer gd.mu.Unlock()
if g, ok := gd.gaps[topic]; ok {
g.Closed = true
}
}
// ─── 因果追踪 ────────────────────────────────────────────
type TraceEntry struct {
MemoryID string `json:"memory_id"`
Version int `json:"version"`
Content string `json:"content"`
Source string `json:"source"` // muchen_oral / config_parse / agent_infer / llm_distill
Trigger string `json:"trigger"` // 什么触发了这次修改
UpdatedAt time.Time `json:"updated_at"`
}
type CausalTracker struct {
mu sync.RWMutex
entries map[string][]*TraceEntry // memory_id → version history
deps map[string][]string // memory_id → depends_on[]
}
func NewCausalTracker() *CausalTracker {
return &CausalTracker{
entries: make(map[string][]*TraceEntry),
deps: make(map[string][]string),
}
}
// RecordVersion 记录版本变更
func (ct *CausalTracker) RecordVersion(memoryID, content, source, trigger string) {
ct.mu.Lock()
defer ct.mu.Unlock()
entry := &TraceEntry{
MemoryID: memoryID,
Version: len(ct.entries[memoryID]) + 1,
Content: content,
Source: source,
Trigger: trigger,
UpdatedAt: time.Now(),
}
ct.entries[memoryID] = append(ct.entries[memoryID], entry)
}
// AddDependency A depends_on B
func (ct *CausalTracker) AddDependency(a, b string) {
ct.mu.Lock()
defer ct.mu.Unlock()
ct.deps[a] = append(ct.deps[a], b)
}
// GetAffected 当 memoryID 被修正时,返回所有依赖它的记忆
func (ct *CausalTracker) GetAffected(memoryID string, visited map[string]bool) []string {
ct.mu.RLock()
defer ct.mu.RUnlock()
if visited == nil {
visited = make(map[string]bool)
}
if visited[memoryID] {
return nil
}
visited[memoryID] = true
var affected []string
for dependent, deps := range ct.deps {
for _, d := range deps {
if d == memoryID && !visited[dependent] {
affected = append(affected, dependent)
affected = append(affected, ct.GetAffected(dependent, visited)...)
}
}
}
return affected
}
// SourceTrust 来源信任度
func SourceTrust(source string) float64 {
switch source {
case "muchen_oral":
return 1.0
case "muchen_feishu":
return 0.95
case "config_parse":
return 0.7
case "agent_infer":
return 0.5
case "llm_distill":
return 0.4
default:
return 0.3
}
}
// IsVolatile 判断一条记忆是否易变(频繁修正)
func (ct *CausalTracker) IsVolatile(memoryID string) bool {
ct.mu.RLock()
defer ct.mu.RUnlock()
return len(ct.entries[memoryID]) >= 3
}
// ─── 记忆预取 ────────────────────────────────────────────
type PrefetchGraph struct {
mu sync.RWMutex
coOccurs map[string]map[string]int // A → {B: count}
}
func NewPrefetchGraph() *PrefetchGraph {
return &PrefetchGraph{coOccurs: make(map[string]map[string]int)}
}
func (pg *PrefetchGraph) RecordCoAccess(a, b string) {
pg.mu.Lock()
defer pg.mu.Unlock()
if pg.coOccurs[a] == nil {
pg.coOccurs[a] = make(map[string]int)
}
pg.coOccurs[a][b]++
}
// GetPrefetch 获取某个 query 的预取候选项
func (pg *PrefetchGraph) GetPrefetch(query string) []string {
pg.mu.RLock()
defer pg.mu.RUnlock()
related := pg.coOccurs[query]
if related == nil {
return nil
}
var result []string
for topic, count := range related {
total := 0
for _, c := range pg.coOccurs[query] {
total += c
}
prob := float64(count) / float64(total)
if prob > 0.6 {
result = append(result, topic)
}
}
return result
}