15 KiB
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() 方法
// 在 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() 方法
// 在 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.go 或 pkg/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%
- 集成测试通过
- 性能指标达标