package service import ( "encoding/json" "strings" "time" "x-agents/server/internal/repository" "x-agents/server/internal/model" "github.com/google/uuid" ) // MemoryService 记忆服务 type MemoryService struct { agentRepo *repository.AgentRepository } // NewMemoryService 创建记忆服务 func NewMemoryService(agentRepo *repository.AgentRepository) *MemoryService { return &MemoryService{ agentRepo: agentRepo, } } // CreateMemory 创建记忆 func (s *MemoryService) CreateMemory(agentID, userID, content, memoryType, category, tags, keywords string, importance int, isPinned bool) (*model.AgentMemory, error) { memory := &model.AgentMemory{ ID: uuid.New().String(), AgentID: agentID, UserID: userID, Content: content, MemoryType: memoryType, Category: category, Tags: tags, Keywords: keywords, Importance: importance, IsPinned: isPinned, CreatedAt: time.Now(), UpdatedAt: time.Now(), } // 如果没有提供关键词,自动从内容中提取 if keywords == "" && content != "" { memory.Keywords = extractKeywords(content) } err := s.agentRepo.CreateMemory(memory) if err != nil { return nil, err } return memory, nil } // GetMemories 获取记忆列表 func (s *MemoryService) GetMemories(agentID, userID, category, memoryType string, limit, offset int) ([]model.AgentMemory, int64, error) { return s.agentRepo.FindMemories(agentID, userID, category, memoryType, limit, offset) } // SearchMemories 搜索记忆 func (s *MemoryService) SearchMemories(agentID, userID, keyword, tags, category, memoryType string, minScore, limit, offset int) ([]model.AgentMemory, int64, error) { // 如果有关键词搜索,优先使用模糊匹配 if keyword != "" { return s.agentRepo.SearchMemories(agentID, userID, keyword, tags, category, memoryType, minScore, limit, offset) } // 否则使用过滤条件查询 return s.agentRepo.FindMemories(agentID, userID, category, memoryType, limit, offset) } // GetMemoryByID 获取记忆详情 func (s *MemoryService) GetMemoryByID(memoryID string) (*model.AgentMemory, error) { return s.agentRepo.FindMemoryByID(memoryID) } // UpdateMemory 更新记忆 func (s *MemoryService) UpdateMemory(memoryID, content, memoryType, category, tags, keywords string, importance int, isPinned *bool) (*model.AgentMemory, error) { memory, err := s.agentRepo.FindMemoryByID(memoryID) if err != nil { return nil, err } if content != "" { memory.Content = content } if memoryType != "" { memory.MemoryType = memoryType } if category != "" { memory.Category = category } if tags != "" { memory.Tags = tags } if keywords != "" { memory.Keywords = keywords } if importance > 0 { memory.Importance = importance } if isPinned != nil { memory.IsPinned = *isPinned } memory.UpdatedAt = time.Now() err = s.agentRepo.UpdateMemory(memory) if err != nil { return nil, err } return memory, nil } // DeleteMemory 删除记忆 func (s *MemoryService) DeleteMemory(memoryID string) error { return s.agentRepo.DeleteMemory(memoryID) } // ExportMemories 导出记忆 func (s *MemoryService) ExportMemories(agentID, userID, format string) (interface{}, error) { memories, _, err := s.agentRepo.FindMemories(agentID, userID, "", "", 0, 0) if err != nil { return nil, err } if format == "csv" { // 生成CSV格式 var sb strings.Builder sb.WriteString("id,agent_id,content,memory_type,category,tags,keywords,importance,is_pinned,created_at\n") for _, m := range memories { sb.WriteString(m.ID + ",") sb.WriteString(m.AgentID + ",") sb.WriteString("\"" + strings.ReplaceAll(m.Content, "\"", "\"\"") + "\",") sb.WriteString(m.MemoryType + ",") sb.WriteString(m.Category + ",") sb.WriteString(m.Tags + ",") sb.WriteString(m.Keywords + ",") sb.WriteString(string(rune(m.Importance+'0')) + ",") if m.IsPinned { sb.WriteString("true") } else { sb.WriteString("false") } sb.WriteString("," + m.CreatedAt.Format(time.RFC3339) + "\n") } return sb.String(), nil } // 默认JSON格式 return memories, nil } // ImportMemories 导入记忆 func (s *MemoryService) ImportMemories(agentID, userID string, items []model.ImportItem) (int, error) { count := 0 for _, item := range items { memoryType := item.MemoryType if memoryType == "" { memoryType = "conversation" } importance := item.Importance if importance == 0 { importance = 5 } keywords := item.Keywords if keywords == "" && item.Content != "" { keywords = extractKeywords(item.Content) } memory := &model.AgentMemory{ ID: uuid.New().String(), AgentID: agentID, UserID: userID, Content: item.Content, MemoryType: memoryType, Category: item.Category, Tags: item.Tags, Keywords: keywords, Importance: importance, IsPinned: false, CreatedAt: time.Now(), UpdatedAt: time.Now(), } err := s.agentRepo.CreateMemory(memory) if err != nil { continue } count++ } return count, nil } // GetMemoryCategories 获取记忆分类列表 func (s *MemoryService) GetMemoryCategories(agentID, userID string) ([]string, error) { return s.agentRepo.FindMemoryCategories(agentID, userID) } // GetMemoryTags 获取记忆标签列表 func (s *MemoryService) GetMemoryTags(agentID, userID string) ([]string, error) { // 从所有记忆中的 tags 字段提取所有标签 memories, _, err := s.agentRepo.FindMemories(agentID, userID, "", "", 0, 0) if err != nil { return nil, err } tagSet := make(map[string]bool) for _, m := range memories { if m.Tags != "" { var tags []string if err := json.Unmarshal([]byte(m.Tags), &tags); err == nil { for _, tag := range tags { if tag != "" { tagSet[tag] = true } } } } } tags := make([]string, 0, len(tagSet)) for tag := range tagSet { tags = append(tags, tag) } return tags, nil } // extractKeywords 从内容中提取关键词 func extractKeywords(content string) string { // 简单提取:取内容的前50个字符作为关键词演示 // 实际生产中可使用分词库如 gojieba if len(content) <= 50 { return content } return content[:50] }