memoryweave/go/internal/governance/governance.go

244 lines
6.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 织忆 MemoryWeave — 治理引擎:冲突检测 + 遗忘 + 知识图谱
package governance
import (
"math"
"strings"
"sync"
"time"
)
// ─── 冲突检测 ────────────────────────────────────────────
type ConflictType string
const (
ConflictEntityRelation ConflictType = "entity_relation"
ConflictFact ConflictType = "fact_conflict"
ConflictDecision ConflictType = "decision_conflict"
)
type Conflict struct {
ID string `json:"id"`
Type ConflictType `json:"type"`
Entity string `json:"entity"`
Description string `json:"description"`
Strategy string `json:"strategy"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
type ConflictDetector struct {
mu sync.RWMutex
active map[string]*Conflict
}
func NewConflictDetector() *ConflictDetector {
return &ConflictDetector{active: make(map[string]*Conflict)}
}
// Scan 扫描新记忆与已有记忆的冲突
func (cd *ConflictDetector) Scan(newContent string, newEntities []string, existing []map[string]interface{}) []*Conflict {
var conflicts []*Conflict
for _, existing := range existing {
existingContent := existing["content"].(string)
existingEntities := toStringSlice(existing["entities"])
// 检查相同实体但不同关系
for _, e1 := range newEntities {
for _, e2 := range existingEntities {
if e1 == e2 {
// 检测事实冲突:内容语义矛盾
if isContradiction(newContent, existingContent) {
conflicts = append(conflicts, &Conflict{
Type: ConflictFact,
Entity: e1,
Description: "事实冲突:新内容与已有记录矛盾",
Strategy: "ask_user",
Status: "pending",
CreatedAt: time.Now(),
})
}
}
}
}
}
return conflicts
}
// AutoResolve 自动裁决冲突latest_wins / primary_wins
func (cd *ConflictDetector) AutoResolve(conflict *Conflict) string {
if conflict.Strategy == "latest_wins" {
return "latest"
}
if conflict.Strategy == "primary_wins" {
return "primary"
}
return "pending"
}
func toStringSlice(v interface{}) []string {
if arr, ok := v.([]string); ok {
return arr
}
if arr, ok := v.([]interface{}); ok {
var result []string
for _, item := range arr {
if s, ok := item.(string); ok {
result = append(result, s)
}
}
return result
}
return nil
}
func isContradiction(a, b string) bool {
// 简单启发式:重叠词 > 50% 但存在否定词差异
wordsA := strings.Fields(strings.ToLower(a))
wordsB := strings.Fields(strings.ToLower(b))
setA := make(map[string]bool)
for _, w := range wordsA {
setA[w] = true
}
overlap := 0
negInA := containsNeg(wordsA)
negInB := containsNeg(wordsB)
for _, w := range wordsB {
if setA[w] {
overlap++
}
}
totalOverlap := float64(overlap) / math.Max(float64(len(wordsA)), float64(len(wordsB)))
return totalOverlap > 0.5 && negInA != negInB
}
func containsNeg(words []string) bool {
negs := []string{"not", "no", "don't", "doesn't", "false", "错误", "不是", "没有", "禁止", "不允许"}
for _, w := range words {
for _, n := range negs {
if strings.Contains(w, n) {
return true
}
}
}
return false
}
// ─── 遗忘策略 ────────────────────────────────────────────
// AgentTypeDecay 不同 Agent 类型的衰减率
// 短期 Agent如一次性的任务 agent衰减快长期 Agent如主 agent衰减慢
var AgentTypeDecay = map[string]float64{
"default": 0.015, // 通用
"longterm": 0.005, // 长期记忆型
"shortterm": 0.050, // 短期任务型
"ephemeral": 0.200, // 会话级别
"hermes": 0.008, // Hermes 主 agent
"researcher": 0.030, // 研究型 agent
"executor": 0.040, // 执行型 agent
"watcher": 0.025, // 监控型 agent
}
type Forgetter struct {
agentType string
decayRate float64
}
func NewForgetter() *Forgetter {
return &Forgetter{agentType: "default", decayRate: 0.015}
}
// NewForgetterWithType 按 Agent 类型创建遗忘器
func NewForgetterWithType(agentType string) *Forgetter {
rate, ok := AgentTypeDecay[agentType]
if !ok {
rate = 0.015
}
return &Forgetter{agentType: agentType, decayRate: rate}
}
// SetAgentType 动态调整遗忘器类型
func (f *Forgetter) SetAgentType(agentType string) {
rate, ok := AgentTypeDecay[agentType]
if !ok {
rate = 0.015
}
f.agentType = agentType
f.decayRate = rate
}
// AgentType 返回当前类型
func (f *Forgetter) AgentType() string {
return f.agentType
}
// ShouldForget 判断记忆是否该被遗忘
func (f *Forgetter) ShouldForget(lastAccessed time.Time, recallCount int, tier string) bool {
if tier == "core" {
return false // 核心记忆永不遗忘
}
days := time.Since(lastAccessed).Hours() / 24
score := 1.0 - days*f.decayRate
if score < 0.1 {
score = 0.1
}
// recallCount > 0 减缓衰减
score += float64(recallCount) * 0.05
return score < 0.2
}
// DecayScore 计算衰减分数
func (f *Forgetter) DecayScore(lastAccessed time.Time, recallCount int) float64 {
days := time.Since(lastAccessed).Hours() / 24
score := 1.0 - days*f.decayRate
if score < 0.1 {
score = 0.1
}
score += float64(recallCount) * 0.05
if score > 1.0 {
score = 1.0
}
return math.Round(score*100) / 100
}
// ScanAndForget 检查单条记忆是否需要衰减(供触发器 decay 使用)
// 实际遗忘操作:降低 importance 到 0.1,标记为 stale
func (f *Forgetter) ScanAndForget(memoryID string) bool {
// 生产版从存储读取并真正执行软删除/降权
// 当前版本返回 true 确认建议遗忘
_ = memoryID
return true
}
// ListActive 返回所有活跃冲突
func (cd *ConflictDetector) ListActive() []*Conflict {
cd.mu.RLock()
defer cd.mu.RUnlock()
var list []*Conflict
for _, c := range cd.active {
if c.Status == "pending" {
list = append(list, c)
}
}
return list
}
// Resolve 解决冲突
func (cd *ConflictDetector) Resolve(id, resolution, winner string) error {
cd.mu.Lock()
defer cd.mu.Unlock()
if c, ok := cd.active[id]; ok {
c.Status = "resolved"
c.Strategy = resolution
}
return nil
}