feat: 第四轮功能增强 - LLM 思维记忆优化、DevTools 记忆UI、9个新工具、5分钟自我思考
- 优化 LLM 思维方式和记忆方法(类别/重要性/关键词/相似度合并/衰减) - DevTools 记忆查询 UI 重新设计(类别筛选/排序/星标/搜索) - 新增 9 个 LLM 工具:calculator, datetime, file_ops, http_request, json_ops, text, random, crypto, markdown - 管理员主对话 5 分钟自我思考增强(工具调用/记忆提取/记忆维护)
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MemoryPriority 记忆优先级
|
||||
type MemoryPriority int
|
||||
|
||||
const (
|
||||
MemoryTemp MemoryPriority = 0 // 临时记忆 (会话内)
|
||||
MemoryNormal MemoryPriority = 1 // 普通记忆
|
||||
MemoryTemp MemoryPriority = 0 // 临时记忆 (会话内)
|
||||
MemoryNormal MemoryPriority = 1 // 普通记忆
|
||||
MemoryImportant MemoryPriority = 2 // 重要记忆
|
||||
MemoryCore MemoryPriority = 3 // 核心记忆 (永远保留)
|
||||
MemoryCore MemoryPriority = 3 // 核心记忆 (永远保留)
|
||||
)
|
||||
|
||||
// String 返回优先级的中文描述
|
||||
@@ -32,37 +35,167 @@ func (p MemoryPriority) String() string {
|
||||
type MemoryCategory string
|
||||
|
||||
const (
|
||||
CategoryPreference MemoryCategory = "preference" // 喜好/偏好
|
||||
CategoryFact MemoryCategory = "fact" // 事实信息
|
||||
CategoryEvent MemoryCategory = "event" // 事件/经历
|
||||
CategoryRelationship MemoryCategory = "relationship" // 关系/情感
|
||||
CategoryHabit MemoryCategory = "habit" // 习惯
|
||||
CategoryOther MemoryCategory = "other" // 其他
|
||||
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"`
|
||||
SessionID string `json:"session_id" db:"session_id"` // 来源会话
|
||||
Source string `json:"source" db:"source"` // 来源文本片断
|
||||
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"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty" db:"expires_at"` // 临时记忆过期时间
|
||||
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
|
||||
Limit int
|
||||
Offset int
|
||||
UserID string
|
||||
Query string // 查询文本
|
||||
Category MemoryCategory
|
||||
Priority MemoryPriority
|
||||
MinImportance int // 最低重要程度筛选
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user