594 lines
15 KiB
Markdown
594 lines
15 KiB
Markdown
# 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 <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 集成测试
|
||
|
||
```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%
|
||
- [ ] 集成测试通过
|
||
- [ ] 性能指标达标
|