orca.ai/thoughts/shared/plans/2025-05-11-orca-memory-plan.md
2026-05-12 00:09:01 +08:00

15 KiB
Raw Permalink Blame History

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.goinitSchema() 方法

// 在 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.goinitSchema() 方法

// 在 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需要小心处理

// 在 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

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

// 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

// 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

// 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

// 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

package actor

const DefaultMemoryExtractorPrompt = `...`

依赖: 无 测试: 验证 prompt 加载


4.2 实现 MemoryExtractorAgent

文件: pkg/actor/memory_extractor_agent.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)

类型定义:

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

// 在 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() 方法:

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

在消息保存后添加记忆维护:

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.gopkg/session/memory_manager.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

// DetectMemoryReference 检测 Agent 回复是否引用了某条记忆
func (mm *MemoryManager) DetectMemoryReference(response string, memories []LongTermMemory) []int64 {
    // 简单实现:检查记忆中关键词是否出现在回复中
    // 高级实现:使用 Embedding 相似度
}

依赖: Phase 3 测试: 验证检测准确率


6.2 自动归档任务

文件: pkg/session/memory_manager.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

// orca memory list
// orca memory search "query"
// orca memory delete <id>
// 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 集成测试

// 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%
  • 集成测试通过
  • 性能指标达标