Files
X-Agents/server/internal/service/memory_service.go
DESKTOP-72TV0V4\caoxiaozhu 7b5d4b20a5 feat: 增强Chat记忆模块功能
- 新增记忆搜索API
- 集成向量检索能力
- 引入智能摘要和预压缩机制

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:34:38 +08:00

238 lines
6.1 KiB
Go
Raw Permalink 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.
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]
}