package memory import ( "context" "errors" "log" "github.com/LoveLosita/smartflow/backend/model" llmservice "github.com/LoveLosita/smartflow/backend/services/llm" memorycleanup "github.com/LoveLosita/smartflow/backend/services/memory/internal/cleanup" memoryorchestrator "github.com/LoveLosita/smartflow/backend/services/memory/internal/orchestrator" memoryrepo "github.com/LoveLosita/smartflow/backend/services/memory/internal/repo" memoryservice "github.com/LoveLosita/smartflow/backend/services/memory/internal/service" memoryvectorsync "github.com/LoveLosita/smartflow/backend/services/memory/internal/vectorsync" memoryworker "github.com/LoveLosita/smartflow/backend/services/memory/internal/worker" memorymodel "github.com/LoveLosita/smartflow/backend/services/memory/model" memoryobserve "github.com/LoveLosita/smartflow/backend/services/memory/observe" ragservice "github.com/LoveLosita/smartflow/backend/services/rag" "gorm.io/gorm" ) // Module 是 memory 模块对外暴露的统一门面。 // // 职责边界: // 1. 负责把 repo、service、worker、orchestrator 组装成一个稳定入口; // 2. 负责对外暴露“写入 / 读取 / 管理 / 启动 worker”这些高层意图; // 3. 不负责替代应用层 DI,也不负责替代上层事务管理器,事务边界仍由调用方掌控。 type Module struct { db *gorm.DB cfg memorymodel.Config llmClient *llmservice.Client ragRuntime ragservice.Runtime observer memoryobserve.Observer metrics memoryobserve.MetricsRecorder jobRepo *memoryrepo.JobRepo itemRepo *memoryrepo.ItemRepo auditRepo *memoryrepo.AuditRepo settingsRepo *memoryrepo.SettingsRepo enqueueService *memoryservice.EnqueueService readService *memoryservice.ReadService manageService *memoryservice.ManageService vectorSyncer *memoryvectorsync.Syncer dedupRunner *memorycleanup.DedupRunner runner *memoryworker.Runner } // ObserveDeps 描述 memory 模块可选的观测依赖。 type ObserveDeps struct { Observer memoryobserve.Observer Metrics memoryobserve.MetricsRecorder } // LoadConfigFromViper 复用 memory 子包里的配置加载逻辑,对外收口一个统一入口。 func LoadConfigFromViper() memorymodel.Config { return memoryservice.LoadConfigFromViper() } // NewModule 创建 memory 模块门面。 // // 设计说明: // 1. 这里做的是“轻组装”,不引入额外容器概念,方便先接进现有项目; // 2. llmClient 允许为 nil,此时写入链路会自动回退到本地 fallback 抽取; // 3. ragRuntime 允许为 nil,此时读取/向量同步自动回退旧逻辑; // 4. 若后续接入统一 DI 容器,也应优先注册这个 Module,而不是把内部 repo/service 继续向外泄漏。 func NewModule(db *gorm.DB, llmClient *llmservice.Client, ragRuntime ragservice.Runtime, cfg memorymodel.Config) *Module { return NewModuleWithObserve(db, llmClient, ragRuntime, cfg, ObserveDeps{}) } // NewModuleWithObserve 创建带观测依赖的 memory 模块门面。 func NewModuleWithObserve( db *gorm.DB, llmClient *llmservice.Client, ragRuntime ragservice.Runtime, cfg memorymodel.Config, deps ObserveDeps, ) *Module { return wireModule(db, llmClient, ragRuntime, cfg, deps) } // WithTx 返回绑定到指定事务连接的同构门面。 // // 步骤化说明: // 1. 上层事务管理器先创建 tx; // 2. 再通过 WithTx(tx) 把 memory 内部所有 repo/service 一次性切到同一个事务连接; // 3. 这样外部无需重新 new 一堆 repo,也不会破坏既有跨表事务边界。 func (m *Module) WithTx(tx *gorm.DB) *Module { if m == nil { return nil } if tx == nil { return m } return wireModule(tx, m.llmClient, m.ragRuntime, m.cfg, ObserveDeps{ Observer: m.observer, Metrics: m.metrics, }) } // EnqueueExtract 把一次记忆抽取请求入队到 memory_jobs。 func (m *Module) EnqueueExtract( ctx context.Context, payload memorymodel.ExtractJobPayload, sourceEventID string, ) error { if m == nil || m.enqueueService == nil { return errors.New("memory module enqueue service is nil") } return m.enqueueService.EnqueueExtractJob(ctx, payload, sourceEventID) } // Retrieve 读取后续可供 prompt 注入使用的候选记忆。 func (m *Module) Retrieve(ctx context.Context, req memorymodel.RetrieveRequest) ([]memorymodel.ItemDTO, error) { if m == nil || m.readService == nil { return nil, errors.New("memory module read service is nil") } return m.readService.Retrieve(ctx, req) } // ListItems 列出用户当前可管理的记忆条目。 func (m *Module) ListItems(ctx context.Context, req memorymodel.ListItemsRequest) ([]memorymodel.ItemDTO, error) { if m == nil || m.manageService == nil { return nil, errors.New("memory module manage service is nil") } return m.manageService.ListItems(ctx, req) } // GetItem 返回当前用户自己的单条记忆详情。 func (m *Module) GetItem(ctx context.Context, req model.MemoryGetItemRequest) (*memorymodel.ItemDTO, error) { if m == nil || m.manageService == nil { return nil, errors.New("memory module manage service is nil") } return m.manageService.GetItem(ctx, req) } // CreateItem 手动新增一条用户记忆。 func (m *Module) CreateItem(ctx context.Context, req model.MemoryCreateItemRequest) (*memorymodel.ItemDTO, error) { if m == nil || m.manageService == nil { return nil, errors.New("memory module manage service is nil") } return m.manageService.CreateItem(ctx, req) } // UpdateItem 手动修改一条用户记忆。 func (m *Module) UpdateItem(ctx context.Context, req model.MemoryUpdateItemRequest) (*memorymodel.ItemDTO, error) { if m == nil || m.manageService == nil { return nil, errors.New("memory module manage service is nil") } return m.manageService.UpdateItem(ctx, req) } // DeleteItem 软删除一条记忆,并补写审计日志。 func (m *Module) DeleteItem(ctx context.Context, req model.MemoryDeleteItemRequest) (*memorymodel.ItemDTO, error) { if m == nil || m.manageService == nil { return nil, errors.New("memory module manage service is nil") } return m.manageService.DeleteItem(ctx, req) } // RestoreItem 恢复一条 deleted/archived 记忆。 func (m *Module) RestoreItem(ctx context.Context, req model.MemoryRestoreItemRequest) (*memorymodel.ItemDTO, error) { if m == nil || m.manageService == nil { return nil, errors.New("memory module manage service is nil") } return m.manageService.RestoreItem(ctx, req) } // GetUserSetting 读取用户当前生效的记忆开关。 func (m *Module) GetUserSetting(ctx context.Context, userID int) (memorymodel.UserSettingDTO, error) { if m == nil || m.manageService == nil { return memorymodel.UserSettingDTO{}, errors.New("memory module manage service is nil") } return m.manageService.GetUserSetting(ctx, userID) } // UpsertUserSetting 写入用户记忆开关。 func (m *Module) UpsertUserSetting(ctx context.Context, req memorymodel.UpdateUserSettingRequest) (memorymodel.UserSettingDTO, error) { if m == nil || m.manageService == nil { return memorymodel.UserSettingDTO{}, errors.New("memory module manage service is nil") } return m.manageService.UpsertUserSetting(ctx, req) } // RunDedupCleanup 执行一次离线 dedup 治理。 func (m *Module) RunDedupCleanup(ctx context.Context, req model.MemoryDedupCleanupRequest) (model.MemoryDedupCleanupResult, error) { if m == nil || m.dedupRunner == nil { return model.MemoryDedupCleanupResult{}, errors.New("memory module dedup runner is nil") } return m.dedupRunner.Run(ctx, req) } // MemoryObserver 暴露 memory 模块当前使用的 observer,供注入桥接等外围能力复用。 func (m *Module) MemoryObserver() memoryobserve.Observer { if m == nil || m.observer == nil { return memoryobserve.NewNopObserver() } return m.observer } // MemoryMetrics 暴露 memory 模块当前使用的轻量计数器。 func (m *Module) MemoryMetrics() memoryobserve.MetricsRecorder { if m == nil || m.metrics == nil { return memoryobserve.NewNopMetrics() } return m.metrics } // StartWorker 启动 memory 后台 worker。 // // 说明: // 1. 这里只负责按当前配置拉起轮询循环; // 2. 若 memory.enabled=false,则直接记录日志并返回; // 3. 当前不做重复启动保护,生命周期仍假设由应用启动层统一掌控。 func (m *Module) StartWorker(ctx context.Context) { if m == nil || m.runner == nil { log.Println("Memory worker is not initialized") return } if !m.cfg.Enabled { log.Println("Memory worker is disabled") return } go memoryworker.RunPollingLoop(ctx, m.runner, m.cfg.WorkerPollEvery, m.cfg.WorkerClaimBatch) log.Println("Memory worker started") } func wireModule( db *gorm.DB, llmClient *llmservice.Client, ragRuntime ragservice.Runtime, cfg memorymodel.Config, deps ObserveDeps, ) *Module { jobRepo := memoryrepo.NewJobRepo(db) itemRepo := memoryrepo.NewItemRepo(db) auditRepo := memoryrepo.NewAuditRepo(db) settingsRepo := memoryrepo.NewSettingsRepo(db) observer := deps.Observer if observer == nil { observer = memoryobserve.NewLoggerObserver(log.Default()) } metrics := deps.Metrics if metrics == nil { metrics = memoryobserve.NewMetricsRegistry() } vectorSyncer := memoryvectorsync.NewSyncer(ragRuntime, itemRepo, observer, metrics) enqueueService := memoryservice.NewEnqueueService(jobRepo) readService := memoryservice.NewReadService(itemRepo, settingsRepo, ragRuntime, cfg, observer, metrics) manageService := memoryservice.NewManageService(db, itemRepo, auditRepo, settingsRepo, vectorSyncer, observer, metrics) extractor := memoryorchestrator.NewLLMWriteOrchestrator(llmClient, cfg) // 决策编排器:仅在 DecisionEnabled 时才创建有效实例。 // 原因:cfg.DecisionEnabled=false 时,Runner 不走决策路径,编排器不会使用, // 但仍然创建以保持构造签名统一,避免上层调用方感知条件逻辑。 var decisionOrchestrator *memoryorchestrator.LLMDecisionOrchestrator if cfg.DecisionEnabled && llmClient != nil { decisionOrchestrator = memoryorchestrator.NewLLMDecisionOrchestrator(llmClient, cfg) } runner := memoryworker.NewRunner(db, jobRepo, itemRepo, auditRepo, settingsRepo, extractor, ragRuntime, cfg, decisionOrchestrator, vectorSyncer, observer, metrics) dedupRunner := memorycleanup.NewDedupRunner(db, itemRepo, auditRepo, vectorSyncer, observer, metrics) return &Module{ db: db, cfg: cfg, llmClient: llmClient, ragRuntime: ragRuntime, observer: observer, metrics: metrics, jobRepo: jobRepo, itemRepo: itemRepo, auditRepo: auditRepo, settingsRepo: settingsRepo, enqueueService: enqueueService, readService: readService, manageService: manageService, vectorSyncer: vectorSyncer, dedupRunner: dedupRunner, runner: runner, } }