# Orca 记忆系统实施计划 **基于设计文档**: `thoughts/shared/designs/2025-05-11-orca-memory-final-design.md` **实施方式**: 增量升级(在已有 v1 基础上扩展) **预估工期**: 5-7 天 --- ## Phase 1: 数据库 Schema 扩展(第 1 天) ### 1.1 新增 `memory_usage_log` 表 **文件**: `pkg/session/sqlite_store.go` — `initSchema()` 方法 ```go // 在 initSchema() 中添加 schema += ` CREATE TABLE IF NOT EXISTS memory_usage_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, memory_id INTEGER NOT NULL, session_id TEXT NOT NULL, query TEXT NOT NULL, was_referenced INTEGER NOT NULL DEFAULT 0, used_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (memory_id) REFERENCES long_term_memories(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_usage_memory ON memory_usage_log(memory_id); CREATE INDEX IF NOT EXISTS idx_usage_session ON memory_usage_log(session_id); ` ``` **依赖**: 无 **测试**: 验证表创建成功,外键约束生效 --- ### 1.2 新增 `dialogue_buffer` 表 **文件**: `pkg/session/sqlite_store.go` — `initSchema()` 方法 ```go // 在 initSchema() 中添加 schema += ` CREATE TABLE IF NOT EXISTS dialogue_buffer ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, user_query TEXT NOT NULL, assistant_response TEXT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_buffer_session ON dialogue_buffer(session_id, created_at); ` ``` **依赖**: 无 **测试**: 验证表创建成功 --- ### 1.3 扩展 `long_term_memories` 表 **文件**: `pkg/session/sqlite_store.go` 当前表缺少 `weight`, `tags`, `archived` 字段。由于是 ALTER TABLE,需要小心处理: ```go // 在 initSchema() 中修改 long_term_memories 定义 // 或添加迁移逻辑 migrations := []string{ `ALTER TABLE long_term_memories ADD COLUMN weight REAL NOT NULL DEFAULT 1.0`, `ALTER TABLE long_term_memories ADD COLUMN tags TEXT`, `ALTER TABLE long_term_memories ADD COLUMN archived INTEGER NOT NULL DEFAULT 0`, } ``` **注意**: SQLite 支持 ADD COLUMN,但有限制(不能加 UNIQUE / PRIMARY KEY) **依赖**: 无 **测试**: 验证迁移后数据完整性 --- ## Phase 2: 配置扩展(第 1-2 天) ### 2.1 扩展 Config 结构体 **文件**: `internal/config/config.go` ```go type Config struct { // ... 现有字段 ... Memory MemoryConfig // 新增 MemoryAgent MemoryAgentConfig // 新增 } type MemoryConfig struct { Enabled bool `toml:"enabled"` MaxHistory int `toml:"max_history"` ShortTerm ShortTermConfig `toml:"short_term"` LongTerm LongTermConfig `toml:"long_term"` Inject InjectConfig `toml:"inject"` } type ShortTermConfig struct { MaxItems int `toml:"max_items"` CompressionThreshold int `toml:"compression_threshold"` } type LongTermConfig struct { Enabled bool `toml:"enabled"` VectorIndex bool `toml:"vector_index"` MaxReturn int `toml:"max_return"` ArchiveThreshold float64 `toml:"archive_threshold"` } type InjectConfig struct { FirstRoundEmpty bool `toml:"first_round_empty"` ShortTermCount int `toml:"short_term_count"` LongTermTrigger string `toml:"long_term_trigger"` MinQueryLength int `toml:"min_query_length"` } type MemoryAgentConfig struct { Enabled bool `toml:"enabled"` Provider string `toml:"provider"` Model string `toml:"model"` APIKey string `toml:"api_key"` BaseURL string `toml:"base_url"` Timeout string `toml:"timeout"` Extract ExtractConfig `toml:"extract"` Summarize SummarizeConfig `toml:"summarize"` } type ExtractConfig struct { BatchSize int `toml:"batch_size"` MaxFacts int `toml:"max_facts"` MinConfidence float64 `toml:"min_confidence"` AutoTag bool `toml:"auto_tag"` } type SummarizeConfig struct { Enabled bool `toml:"enabled"` TriggerTokens int `toml:"trigger_tokens"` } ``` **依赖**: Phase 1 完成 **测试**: 验证 TOML 解析正确 --- ### 2.2 更新 config.toml.example **文件**: `config.toml.example` 添加完整的 `[memory]` 和 `[memory_agent]` 示例配置。 **依赖**: 2.1 完成 --- ## Phase 3: MemoryManager 扩展(第 2-3 天) ### 3.1 添加权重管理方法 **文件**: `pkg/session/memory_manager.go` ```go // RecordMemoryUsage 记录记忆使用并更新权重 func (mm *MemoryManager) RecordMemoryUsage(memoryID int64, sessionID, query string, referenced bool) error // UpdateMemoryWeight 更新单条记忆权重 func (mm *MemoryManager) UpdateMemoryWeight(memoryID int64, delta float64) error // ArchiveLowWeightMemories 归档低权重记忆 func (mm *MemoryManager) ArchiveLowWeightMemories(threshold float64) (int, error) // GetCoreMemories 获取核心记忆(高权重) func (mm *MemoryManager) GetCoreMemories(minWeight float64) ([]LongTermMemory, error) // GetArchivedMemories 获取已归档记忆 func (mm *MemoryManager) GetArchivedMemories() ([]LongTermMemory, error) ``` **依赖**: Phase 1, Phase 2 **测试**: 验证权重计算正确,归档逻辑生效 --- ### 3.2 添加对话缓冲管理 **文件**: `pkg/session/memory_manager.go` ```go // BufferDialogue 缓冲对话 func (mm *MemoryManager) BufferDialogue(sessionID, userQuery, assistantResponse string) error // GetBufferedDialogues 获取缓冲的对话 func (mm *MemoryManager) GetBufferedDialogues(sessionID string, limit int) ([]Dialogue, error) // ClearBuffer 清空缓冲 func (mm *MemoryManager) ClearBuffer(sessionID string) error // ShouldExtract 检查是否触发提取 func (mm *MemoryManager) ShouldExtract(sessionID string) (bool, error) ``` **依赖**: Phase 1 **测试**: 验证缓冲写入、读取、清空 --- ### 3.3 增强记忆注入逻辑 **文件**: `pkg/session/memory_manager.go` ```go // ShouldInjectMemory 判断是否注入记忆 func (mm *MemoryManager) ShouldInjectMemory(query string, msgCount int) bool // BuildMemoryContextWithStats 构建记忆上下文(增强版) // 现有方法需要增强:支持 first_round_empty, min_query_length ``` 修改 `BuildMemoryContextWithStats` 支持配置参数: - `first_round_empty`: msgCount == 0 时返回空 - `min_query_length`: len(query) < threshold 时返回空 - `long_term_trigger`: 检测技术关键词 **依赖**: Phase 2 **测试**: 验证各种注入条件 --- ### 3.4 添加长期记忆写入 **文件**: `pkg/session/memory_manager.go` ```go // AddLongTermMemoryWithEmbedding 添加长期记忆并生成向量 func (mm *MemoryManager) AddLongTermMemoryWithEmbedding(content, memoryType string, tags []string, confidence float64) error // UpdateLongTermMemory 更新长期记忆 func (mm *MemoryManager) UpdateLongTermMemory(id int64, updates map[string]interface{}) error ``` **依赖**: Phase 1 **测试**: 验证写入 + 向量生成 --- ## Phase 4: MemoryExtractorAgent(第 3-4 天) ### 4.1 创建 Agent Prompt 文件 **文件**: `~/.orca/agents/memory_extractor.md` 内容来自设计文档,需要安装到用户目录。 **文件**: `pkg/actor/memory_extractor_prompt.go`(内嵌默认 prompt,作为 fallback) ```go package actor const DefaultMemoryExtractorPrompt = `...` ``` **依赖**: 无 **测试**: 验证 prompt 加载 --- ### 4.2 实现 MemoryExtractorAgent **文件**: `pkg/actor/memory_extractor_agent.go` ```go package actor type MemoryExtractorAgent struct { *SubAgent config MemoryAgentConfig } // NewMemoryExtractorAgent 创建提取 Agent func NewMemoryExtractorAgent(id string, llmBackend llm.LLM, cfg MemoryAgentConfig) (*MemoryExtractorAgent, error) // ExtractFacts 从对话中提取事实 func (mea *MemoryExtractorAgent) ExtractFacts(dialogues []Dialogue) ([]Fact, error) // parseFactJSON 解析提取结果 func parseFactJSON(content string) ([]Fact, error) ``` **类型定义**: ```go type Dialogue struct { UserQuery string AssistantResponse string } type Fact struct { Content string Type string // fact/preference/project Tags []string Confidence float64 Replace *string // 被替换的旧事实 } ``` **依赖**: Phase 2, Phase 3 **测试**: 验证提取逻辑,JSON 解析 --- ### 4.3 集成到 Kernel **文件**: `pkg/kernel/kernel.go` 在 Kernel 初始化时创建 MemoryExtractorAgent: ```go // 在 NewWithConfig() 中 if cfg.MemoryAgent.Enabled { kernel.memoryExtractor, err = actor.NewMemoryExtractorAgent( "memory_extractor", kernel.llmBackend, cfg.MemoryAgent, ) } ``` **依赖**: 4.2 **测试**: 验证 Kernel 启动 --- ## Phase 5: LLMAgent 集成(第 4-5 天) ### 5.1 修改 buildLLMMessages **文件**: `pkg/actor/llm_agent.go` 增强 `buildLLMMessages()` 方法: ```go func (a *LLMAgent) buildLLMMessages(query string) []llm.Message { messages := make([]llm.Message, 0) // 1. System prompt(原有) if a.systemPrompt != "" { messages = append(messages, llm.Message{Role: "system", Content: a.systemPrompt}) } // 2. Tool prompt(原有) if toolPrompt := a.buildToolPrompt(); toolPrompt != "" { messages = append(messages, llm.Message{Role: "system", Content: toolPrompt}) } // 3. 记忆注入(增强) if a.memoryManager != nil { msgCount := a.sessionMgr.GetMessageCount(a.sessionID) if a.memoryManager.ShouldInjectMemory(query, msgCount) { ctx, stats := a.memoryManager.BuildMemoryContextWithStats(a.sessionID, query) if ctx != "" { messages = append(messages, llm.Message{Role: "system", Content: ctx}) log.Printf("[memory] Injected: short=%d, long=%d, tokens=%d", stats.ShortTermCount, stats.LongTermCount, stats.TotalTokens) } } } // 4. 对话历史(原有) // ... return messages } ``` **依赖**: Phase 3 **测试**: 验证注入条件,Token 预算 --- ### 5.2 修改消息处理流程 **文件**: `pkg/actor/llm_agent.go` 在消息保存后添加记忆维护: ```go func (a *LLMAgent) handleUserMessage(ctx context.Context, msg Message) error { // ... 原有逻辑 ... // 保存消息(原有) a.sessionMgr.SaveMessage(...) // 记忆维护(新增) if a.memoryManager != nil { a.memoryManager.MaintainSessionMemory(a.sessionID, query, response) } return nil } ``` **依赖**: Phase 3 **测试**: 验证 STM 生成,缓冲写入 --- ### 5.3 添加异步提取触发 **文件**: `pkg/actor/llm_agent.go` 或 `pkg/session/memory_manager.go` ```go // triggerExtraction 异步触发事实提取 func (mm *MemoryManager) triggerExtraction(sessionID string) { dialogues, _ := mm.GetBufferedDialogues(sessionID, mm.config.Extract.BatchSize) go func() { // 调用 MemoryExtractorAgent facts, err := mm.extractor.ExtractFacts(dialogues) if err != nil { log.Printf("[memory] Extraction failed: %v", err) return } for _, fact := range facts { if fact.Confidence >= mm.config.Extract.MinConfidence { mm.AddLongTermMemoryWithEmbedding(...) } } mm.ClearBuffer(sessionID) }() } ``` **依赖**: Phase 4 **测试**: 验证异步执行,错误处理 --- ## Phase 6: 权重反馈系统(第 5 天) ### 6.1 记忆引用检测 **文件**: `pkg/session/memory_manager.go` ```go // DetectMemoryReference 检测 Agent 回复是否引用了某条记忆 func (mm *MemoryManager) DetectMemoryReference(response string, memories []LongTermMemory) []int64 { // 简单实现:检查记忆中关键词是否出现在回复中 // 高级实现:使用 Embedding 相似度 } ``` **依赖**: Phase 3 **测试**: 验证检测准确率 --- ### 6.2 自动归档任务 **文件**: `pkg/session/memory_manager.go` ```go // RunMaintenance 运行记忆维护任务 func (mm *MemoryManager) RunMaintenance() error { // 1. 归档低权重记忆 mm.ArchiveLowWeightMemories(mm.config.LongTerm.ArchiveThreshold) // 2. 清理过期 STM mm.Cleanup() // 3. 其他维护... } ``` **依赖**: Phase 3 **测试**: 验证归档逻辑 --- ## Phase 7: CLI 命令(第 5-6 天) ### 7.1 添加 CLI 命令 **文件**: `cmd/orca/main.go` 或新建 `cmd/orca/memory.go` ```go // orca memory list // orca memory search "query" // orca memory delete // orca memory clean // orca memory stats // orca memory export > backup.json // orca memory import < backup.json ``` **依赖**: Phase 3 **测试**: 验证命令执行 --- ## Phase 8: 测试与优化(第 6-7 天) ### 8.1 单元测试 | 模块 | 测试项 | 文件 | |------|--------|------| | Schema | 表创建、迁移 | `pkg/session/sqlite_store_test.go` | | Config | TOML 解析 | `internal/config/config_test.go` | | Weight | 权重计算、归档 | `pkg/session/memory_manager_test.go` | | Buffer | 缓冲写入、清空 | `pkg/session/memory_manager_test.go` | | Extractor | 事实提取、JSON 解析 | `pkg/actor/memory_extractor_test.go` | | Injection | 注入条件、Token 预算 | `pkg/actor/llm_agent_test.go` | ### 8.2 集成测试 ```go // test_memory_system_v2.go func TestMemorySystemV2(t *testing.T) { // 1. 创建会话 // 2. 发送消息(验证 STM 生成) // 3. 发送 5 条消息(验证 LTM 提取触发) // 4. 查询长期记忆(验证向量检索) // 5. 验证权重变化 // 6. 验证归档逻辑 } ``` ### 8.3 性能测试 | 指标 | 目标 | 测试方法 | |------|------|---------| | 向量检索 | < 100ms | BenchmarkVectorSearch | | Embedding 缓存 | > 95% 命中 | BenchmarkEmbeddingCache | | 权重更新 | < 10ms | BenchmarkWeightUpdate | | 整体延迟 | < 200ms | BenchmarkMemoryInjection | --- ## 依赖图 ``` Phase 1 (Schema) │ ├──→ Phase 2 (Config) │ │ │ ├──→ Phase 3 (MemoryManager) │ │ │ │ │ ├──→ Phase 4 (ExtractorAgent) │ │ │ │ │ │ │ └──→ Phase 5 (LLMAgent) │ │ │ │ │ └──→ Phase 6 (Weight System) │ │ │ └──→ Phase 7 (CLI) │ └──→ Phase 8 (Test) ``` --- ## 风险与缓解 | 风险 | 可能性 | 影响 | 缓解 | |------|--------|------|------| | Schema 迁移失败 | 中 | 高 | 备份数据库,测试迁移脚本 | | MemoryExtractor 幻觉 | 高 | 中 | 高置信度阈值,人工抽查 | | 权重系统不准确 | 中 | 低 | 提供手动调整 CLI | | 性能退化 | 低 | 中 | 监控,及时优化索引 | | 向后兼容 | 中 | 高 | 保留旧配置解析逻辑 | --- ## 验收标准 - [ ] 所有新增表创建成功 - [ ] 配置解析正确,默认值合理 - [ ] STM 自动生成(每轮对话) - [ ] LTM 批量提取(每 5 轮) - [ ] 向量检索正常(语义相似度) - [ ] 权重自动更新(引用检测) - [ ] 低权重记忆自动归档 - [ ] CLI 命令可用 - [ ] 单元测试覆盖率 > 80% - [ ] 集成测试通过 - [ ] 性能指标达标