package model import ( "encoding/json" "time" ) // MemoryPriority 记忆优先级 type MemoryPriority int const ( MemoryTemp MemoryPriority = 0 // 临时记忆 (会话内) MemoryNormal MemoryPriority = 1 // 普通记忆 MemoryImportant MemoryPriority = 2 // 重要记忆 MemoryCore MemoryPriority = 3 // 核心记忆 (永远保留) ) // String 返回优先级的中文描述 func (p MemoryPriority) String() string { switch p { case MemoryCore: return "核心" case MemoryImportant: return "重要" case MemoryNormal: return "普通" case MemoryTemp: return "临时" default: return "未知" } } // MemoryCategory 记忆分类 type MemoryCategory string const ( CategoryUserPreference MemoryCategory = "user_preference" // 用户偏好 (食物、颜色、习惯) CategoryPersonalInfo MemoryCategory = "personal_info" // 个人信息 (姓名、年龄、职业) CategoryConversation MemoryCategory = "conversation" // 对话摘要 CategoryKnowledge MemoryCategory = "knowledge" // 知识性信息 CategoryEvent MemoryCategory = "event" // 事件记录 CategoryTask MemoryCategory = "task" // 任务/计划 CategoryRelationship MemoryCategory = "relationship" // 关系信息 // 向后兼容的旧分类别名 CategoryPreference = CategoryUserPreference CategoryFact = CategoryPersonalInfo CategoryHabit = CategoryUserPreference CategoryOther = CategoryKnowledge ) // CategoryDisplayName 返回分类的中文显示名 func (c MemoryCategory) DisplayName() string { switch c { case CategoryUserPreference: return "用户偏好" case CategoryPersonalInfo: return "个人信息" case CategoryConversation: return "对话摘要" case CategoryKnowledge: return "知识信息" case CategoryEvent: return "事件记录" case CategoryTask: return "任务计划" case CategoryRelationship: return "关系情感" default: return "其他" } } // MemoryEntry 记忆条目 type MemoryEntry struct { ID string `json:"id" db:"id"` UserID string `json:"user_id" db:"user_id"` Content string `json:"content" db:"content"` Summary string `json:"summary" db:"summary"` // 简短摘要 Category MemoryCategory `json:"category" db:"category"` Priority MemoryPriority `json:"priority" db:"priority"` Importance int `json:"importance" db:"importance"` // 重要程度 1-10 Keywords []string `json:"keywords" db:"keywords"` // 关键词标签 SessionID string `json:"session_id" db:"session_id"` // 来源会话 Source string `json:"source" db:"source"` // 来源 (conversation/thinking) Embedding []float32 `json:"-" db:"embedding"` // 向量 (pgvector) AccessCount int `json:"access_count" db:"access_count"` LastAccess time.Time `json:"last_access" db:"last_access"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` // 最后更新时间 ExpiresAt *time.Time `json:"expires_at,omitempty" db:"expires_at"` // 临时记忆过期时间 } // KeywordsJSON 将关键词序列化为 JSON 字符串(用于数据库存储) func (e *MemoryEntry) KeywordsJSON() string { if len(e.Keywords) == 0 { return "[]" } data, _ := json.Marshal(e.Keywords) return string(data) } // ParseKeywords 从 JSON 字符串解析关键词 func ParseKeywords(raw string) []string { if raw == "" || raw == "[]" { return nil } var keywords []string if err := json.Unmarshal([]byte(raw), &keywords); err != nil { return nil } return keywords } // SimilarityScore 计算两个记忆条目的简单文本相似度(基于词汇重叠) // 返回值 0.0 - 1.0 func (e *MemoryEntry) SimilarityScore(other *MemoryEntry) float64 { if e.Content == other.Content { return 1.0 } // 基于关键词的重叠度 if len(e.Keywords) > 0 && len(other.Keywords) > 0 { keywordSet := make(map[string]bool, len(e.Keywords)) for _, k := range e.Keywords { keywordSet[k] = true } overlap := 0 for _, k := range other.Keywords { if keywordSet[k] { overlap++ } } keywordScore := float64(overlap) / float64(max(len(e.Keywords), len(other.Keywords))) if keywordScore > 0.6 { return keywordScore } } // 基于内容的字符级 Jaccard 相似度 return jaccardSimilarity(e.Content, other.Content) } // jaccardSimilarity 计算两个字符串的 Jaccard 相似度 func jaccardSimilarity(a, b string) float64 { if a == b { return 1.0 } if len(a) == 0 || len(b) == 0 { return 0.0 } // 使用 bigram 分词 bigramsA := make(map[string]int) runesA := []rune(a) for i := 0; i < len(runesA)-1; i++ { bigramsA[string(runesA[i:i+2])]++ } bigramsB := make(map[string]int) runesB := []rune(b) for i := 0; i < len(runesB)-1; i++ { bigramsB[string(runesB[i:i+2])]++ } intersection := 0 for bg, countA := range bigramsA { if countB, ok := bigramsB[bg]; ok { intersection += min(countA, countB) } } union := 0 allBigrams := make(map[string]bool) for bg := range bigramsA { allBigrams[bg] = true } for bg := range bigramsB { allBigrams[bg] = true } for bg := range allBigrams { union += max(bigramsA[bg], bigramsB[bg]) } if union == 0 { return 0.0 } return float64(intersection) / float64(union) } // MemoryQuery 记忆查询参数 type MemoryQuery struct { UserID string Query string // 查询文本 Category MemoryCategory Priority MemoryPriority MinImportance int // 最低重要程度筛选 Limit int Offset int }