feat: 记忆系统 Phase 0 技术原型 (sqlite-vec + FTS5 + DashScope)#73
Conversation
|
Claude finished @lishuceo's task in 5m 3s —— View job Code Review: Memory System Phase 0
Verdict: ✅ ApprovedThis is a well-structured Phase 0 prototype with solid defensive programming. After reading all 6 source files in full, tracing cross-module interactions, and verifying design decisions against the plan document, no critical or high-confidence bugs were found. What I verified
Minor observations (informational, no action required)
Test coverageThe 79 tests provide comprehensive coverage including: FTS5 trigger sync (insert→match, update→old miss/new match, delete→miss), BM25-only degradation, confidence caps, TTL expiry filtering, agent/workspace isolation, empty/special-char query sanitization, and two-phase write resilience when embedding fails. |
b70746d to
58fdff1
Compare
|
Claude finished @lishuceo's task in 3m 25s —— View job Code Review: Memory System Phase 0 + Phase 1
Verdict:
|
| # | Severity | File | Confidence | Description |
|---|---|---|---|---|
| 1 | 🔴 Critical | init.ts:75-83 |
92 | Maintenance queries silently broken: datetime('now') returns "YYYY-MM-DD HH:MM:SS" (space separator) but stored timestamps use new Date().toISOString() → "YYYY-MM-DDTHH:MM:SS.sssZ" (T separator). Since T > lexicographically, ttl < datetime('now') and created_at < datetime('now', '-90 days') are never true. Both maintenance ops are no-ops. |
| 2 | 🔴 Critical | init.ts:79-83 |
85 | Orphaned vec0 entries: Raw DELETE FROM memories bypasses MemoryStore.delete() which handles vec0 cleanup. Even after fixing #1, deleted memories leave orphaned memories_vec rows. |
| 3 | 🟡 Warning | database.ts:172 |
80 | Default dimension mismatch: MemoryDatabase.create() defaults to 1024, but config defaults to 1536. Safe today (init.ts passes config value), but misleading for direct callers. |
| 4 | 🟡 Warning | injector.ts:87-105 |
78 | Empty section headers on budget exhaustion: Inner loop break on token budget doesn't break outer loop, potentially rendering ### 项目事实 headers with no items underneath. |
What I verified (no issues found)
| Area | Assessment |
|---|---|
| FTS5 injection defense | sanitizeFtsQuery strips * " ( ) { } ^ : and wraps tokens in double-quoted literals. ✅ |
| SQL injection | All queries parameterized. float[${dimension}] interpolation validated by dimension bounds check. ✅ |
| Two-phase write | Phase 1 sync INSERT + FTS5 trigger, Phase 2 async embed with .catch() and flush(). ✅ |
| Confidence clamping | L0 ≤ 0.7 enforced in store.create() via clampConfidence. Extractor correctly sets confidenceLevel: 'L0'. ✅ |
| vec0 cosine distance | 1 - distance conversion and < 0.15 threshold for findConflicting are correct. ✅ |
| Provider race condition | clientReady: Promise pattern in both DashScopeEmbeddingProvider and getExtractionClient(). ✅ |
| Agent/workspace isolation | Search filters correctly enforce agentId (with * wildcard), userId, workspaceDir. ✅ |
| Event-handler integration | Both executeClaudeTask and executeDirectTask correctly inject (before) and extract (after, fire-and-forget). ✅ |
| Executor memoryContext | Injected between persona/workspace prompt and history summaries. Hash excludes memory (avoids cache invalidation). ✅ |
| Startup/shutdown lifecycle | initializeMemory() idempotent, closeMemory() in shutdown with 3s delay, maintenance in 30min interval. ✅ |
| ESM & conventions | .js extensions, Pino logger, singleton pattern, barrel exports. ✅ |
Implement sqlite-vec + FTS5 + DashScope embedding prototype for the agent memory system (Plan 5). Includes review findings documentation, hybrid search with BM25-only fallback, confidence level caps (L0/L1/L2), two-phase write, and workspace isolation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, 5 info) Critical: sanitize FTS5 query input to prevent injection, add distance_metric=cosine to vec0 table, fix DashScope provider async init race condition via clientReady promise. Warning: add input validation (content length, tags count, metadata size), log errors in catch blocks, use safe Buffer.from with byteOffset/byteLength, check memory existence before vector insert to prevent orphans, clamp search limit to 100, move sqlite-vec to optionalDependencies, fix updateMemory nullish coalescing for nullable fields. Info: replace readonly mutation hack with getter, batch updateLastAccessed in transaction, add flush() for pending embeddings, fix false-positive embed test, add 18 new test cases (61 → 79 total). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 文档状态更新为 Phase 0 ✅ 已完成 - 补充完整验证结果表(语义搜索、跨语言、延迟指标等) - 补充生产环境配置方式和尚未集成的环节说明 - Phase 1 章节重整为"抽取 + 注入集成" - 新增 integration.test.ts: 7 个场景 11 个测试用例 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 新增 init.ts: singleton 初始化器,启动时创建 DB/store/search - 新增 extractor.ts: 对话结束后调 Qwen (qwen3.5-plus) 提取记忆, fire-and-forget,L0 置信度,查重 + supersede - 新增 injector.ts: 对话开始前搜索记忆,格式化为 system prompt 片段 - executor.ts: 新增 memoryContext 字段,插入 persona 和 history 之间 - event-handler.ts: executeClaudeTask + executeDirectTask 两条路径 均接入注入 (执行前) 和抽取 (执行后) - index.ts: 启动初始化 + 30min 维护 + 优雅关闭 - config: extractionModel 默认 qwen3.5-plus, maxInjectTokens 改为 4000 - 新增 27 个测试 (init/injector/extractor), 总计 117 tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- extractor: extractMemories() 跳过条件、不抛异常、L0 confidence、 supersede/updateEvidence、null 容错 - injector: injectMemories() 主函数测试(disabled/未初始化/无匹配/ 有匹配/agent 隔离)、token 截断边界、空分组跳过 - init: 低置信度 >90 天清理、近期低置信度不清理 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 场景7: 调真实 qwen3.5-plus 从对话中抽取结构化记忆,验证写入 store - 场景8: 注入格式化验证(类型分组、日期、token 预估) - 场景9: 抽取→注入全链路闭环(写入记忆后搜索命中并注入 prompt) - 集成测试 11 → 14 个,总计 141 tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
text-embedding-v4 支持 512/1024/1536,1536 精度最高, 当前记忆量级下存储和速度开销可忽略。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
基准测试显示 plus 延迟极不稳定(8s~623s),flash 在真实抽取场景下 稳定 7-8s,质量相同,价格便宜 5 倍。同时新增质量评估测试。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- init.ts: 维护查询改用 ISO 8601 格式匹配存储时间戳,DELETE 改为 通过 MemoryStore.delete() 清理 vec0 孤儿记录 - database.ts: 默认维度与 config 一致 (1536) - injector.ts: 修复 token 预算耗尽时可能输出空 section header 的问题 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
a8cfd95 to
c526fbf
Compare
|
Claude finished @lishuceo's task in 3m 22s —— View job Code Review: Memory System Phase 0 + Phase 1
Verdict: ✅ ApprovedAll 4 previous issues fixed (2 critical, 2 warning). After re-reading all 13 source files in full and tracing cross-module interactions, no new issues found above the confidence threshold. Previous issues resolved
What I verified (no issues found)
Overall assessmentSolid implementation of a memory system with good defensive programming throughout. The fixes for all 4 previous review findings are clean and correct. The architecture (two-phase write, hybrid BM25+vector search, fire-and-forget extraction, budget-aware injection) is well-designed for production use with proper graceful degradation when sqlite-vec is unavailable. |
Summary
Phase 0: 技术原型 ✅
src/memory/模块:类型定义、SQLite 独立数据库(FTS5 + sqlite-vec)、两阶段写入 Store、混合检索(BM25 + vector score fusion)、DashScope embedding providerPhase 1: 抽取 + 注入集成 ✅
init.ts: singleton 初始化器,启动时创建 DB/store/search,定期维护,优雅关闭extractor.ts: 对话结束后调 DashScope Qwen (qwen3.5-flash) 从 prompt+output 中提取记忆,fire-and-forget,L0 置信度上限 0.7,自动查重 + supersedeinjector.ts: 对话开始前搜索相关记忆,按类型分组格式化为 system prompt 片段,受 maxInjectTokens 约束executor.ts: 新增 memoryContext 字段,插入 knowledge 和 history summaries 之间event-handler.ts: executeClaudeTask + executeDirectTask 两条路径均接入注入(执行前)和抽取(执行后)index.ts: 启动初始化 + 30min 定期维护 + 优雅关闭质量评估结果
检索质量: 命中率 90%, Top-1 准确率 78%, Top-3 准确率 89%
抽取质量: 类型准确率 100%, 关键词命中率 100%, 噪声过滤通过
生产环境配置
Test plan
npm run typecheck通过npx vitest run src/memory— 141 tests 全部通过 (8 test files)🤖 Generated with Claude Code