ESC
核心概念 架构

架构

系统概览

┌─────────────────────────────────────────────────────────────┐
│                        MCP 客户端                           │
│              (Claude Code / 机器人控制器)                    │
└──────────────────────────┬──────────────────────────────────┘
                           │ MCP Protocol (stdio)
┌──────────────────────────▼──────────────────────────────────┐
│                     MCP 服务器层                             │
│                                                             │
│  learn  recall  save_perception  forget  update  session    │
│    │       │         │             │       │       │        │
│    └───────┴─────────┴─────────────┴───────┴───────┘        │
│                           │                                 │
│                    ┌──────▼───────┐                          │
│                    │  验证器      │  Pydantic L1             │
│                    └──────┬───────┘                          │
│                           │                                 │
│    ┌──────────────────────┼──────────────────────┐          │
│    │                      │                      │          │
│    ▼                      ▼                      ▼          │
│ auto_classify          dedup              search.py         │
│ (类别/标签/            (精确 →           (BM25 + Vec        │
│  置信度/范围)           jaccard →          → RRF 融合)       │
│                         cosine)                             │
│    └──────────────────────┼──────────────────────┘          │
│                           │                                 │
│                    ┌──────▼───────┐                          │
│                    │  操作层      │                          │
│                    │  memories.py │  插入/更新/touch          │
│                    │  sessions.py │  创建/结束/汇总           │
│                    │  search.py   │  fts_search/vec_search   │
│                    │  tags.py     │  添加/移除/规范化         │
│                    └──────┬───────┘                          │
│                           │                                 │
│         ┌─────────────────┼─────────────────┐               │
│         ▼                 ▼                 ▼               │
│    ┌─────────┐     ┌───────────┐     ┌──────────┐          │
│    │ FTS5    │     │ memories  │     │ vec0     │          │
│    │ (BM25)  │     │ (SQLite)  │     │ (向量)   │          │
│    └─────────┘     └───────────┘     └──────────┘          │
│                           │                                 │
│                    ┌──────▼───────┐                          │
│                    │  memory.db   │  ~/.robotmem/            │
│                    └──────────────┘                          │
└─────────────────────────────────────────────────────────────┘
                           │
              ┌────────────┼────────────┐
              ▼            ▼            ▼
         FastEmbed      Ollama     OpenAI 兼容
         (ONNX)        (HTTP)      (HTTP)

搜索管道

recall 搜索管道通过 Reciprocal Rank Fusion 组合两个排序信号:

查询: "how to grasp a cup"
         │
         ├──→ BM25 (FTS5)                    ──→ 排序列表 A
         │    分词 → jieba (CJK) → FTS5 MATCH
         │    ORDER BY bm25() 分数
         │
         └──→ 向量 (vec0)                    ──→ 排序列表 B
              embed_one(query) → float[384]
              WHERE embedding MATCH blob AND k=N
              ORDER BY 余弦距离

         ├──→ RRF 融合 (k=60)
         │    score(d) = Σ 1/(k + rank_i + 1) 对每个列表
         │
         ├──→ 来源加权
         │    真实世界数据 × 1.5 加成
         │
         ├──→ 置信度过滤
         │    confidence >= min_confidence(默认 0.3)
         │
         ├──→ context_filter(结构化)
         │    点路径匹配已解析的 context JSON
         │    运算符: $lt, $lte, $gt, $gte, $ne, 等值
         │
         ├──→ spatial_sort(最近邻)
         │    坐标数组的欧几里得距离
         │    可选 max_distance 截断
         │
         ├──→ Top-K 截断
         │
         └──→ MaxScore 归一化(最佳 = 1.0)

RRF 公式

score(document) = Σ 1 / (k + rank + 1)

其中 k=60(可通过 rrf_k 配置)。较高的 k 值会给同时出现在多个列表中的文档更高的权重,而非看重单个列表中的排名。

数据库 Schema

memories(核心表)

列名 类型 说明
id INTEGER PK 自增 ID
session_id TEXT 关联的会话(external_id)
collection TEXT 逻辑命名空间
type TEXT "fact""perception"
content TEXT 记忆文本(最多 300 字符)
human_summary TEXT 简短摘要(最多 200 字符)
context TEXT JSON 上下文(params/spatial/robot/task)
perception_type TEXT visual/tactile/auditory/proprioceptive/procedural
perception_data BLOB 原始传感器数据
perception_metadata TEXT 格式/单位元数据
category TEXT 自动分类的类别
confidence REAL 0.0-1.0(默认 0.9)
decay_rate REAL 每日衰减率(默认 0.01)
status TEXT active / superseded / invalidated
superseded_by INTEGER 替代记忆的 ID
content_hash TEXT 用于去重的 SHA-256 前缀
embedding BLOB 浮点向量(384d 或 768d)
access_count INTEGER 召回命中计数器
return_count INTEGER 返回给用户的次数
last_accessed TEXT 最后一次召回命中的 ISO 时间戳
created_at TEXT ISO 时间戳
updated_at TEXT ISO 时间戳

sessions

列名 类型 说明
id INTEGER PK 自增
external_id TEXT UNIQUE UUID 会话标识符
collection TEXT 关联的集合
context TEXT 会话上下文 JSON(最大 64KB)
session_count INTEGER 复用计数器
status TEXT active / ended
client_type TEXT "mcp_direct"

session_outcomes

列名 类型 说明
id INTEGER PK 自增
session_id TEXT 会话 external_id
score REAL Episode 成功评分(0.0-1.0)

memory_tags

列名 类型 说明
memory_id INTEGER 外键,指向 memories.id
tag TEXT 来自受控词汇表的标签
source TEXT "auto""user"

主键:(memory_id, tag)

tag_meta

列名 类型 说明
tag TEXT PK 标签标识符
parent TEXT 父标签(NULL = 根维度)
display_name TEXT 人类可读名称

虚拟表

表名 引擎 用途
memories_fts FTS5 全文搜索(content、human_summary、scope_files、scope_entities)
memories_vec vec0 向量相似度搜索(float[384])

索引

idx_mem_collection    ON memories(collection)
idx_mem_status        ON memories(status)
idx_mem_session       ON memories(session_id)
idx_mem_type          ON memories(type)
idx_mem_hash          ON memories(content_hash) WHERE content_hash IS NOT NULL
idx_mem_no_embed      ON memories(collection) WHERE embedding IS NULL AND status='active'
idx_memory_tags_tag   ON memory_tags(tag)

标签分类体系

标签系统使用 9 维树结构,包含 50+ 个标签:

metacognition          ← reasoning, cognitive_bias, decision_framework, ...
capability             ← build, debug, design, review, architecture, ...
domain                 ← cs_fundamentals, ai_ml, finance, ...
technique              ← patterns, anti_patterns, recipes, ...
timing                 ← when_to_start, when_to_stop, when_to_switch
boundary               ← tradeoff, constraint, not_applicable, ...
experience             ← war_story, postmortem, gotcha, root_cause, ...
self_defect            ← hallucination, sycophancy, overengineering, ...
reflection             ← accuracy_calibration, behavior_rule, preference, ...

标签由正则规则(auto_classify.py)自动推断,并以 source="auto" 存储在 memory_tags 中。

自动分类管道

learn(insight="grip_force=12.5N works best because sensor was calibrated")
         │
         ├── classify_category()   → "root_cause"(匹配到 "because")
         ├── estimate_confidence() → 0.90(文件路径 +0.05,因果关系 +0.05)
         ├── extract_scope()       → {scope_files: [], scope_entities: ["grip_force"]}
         ├── classify_tags()       → ["root_cause", "observation"]
         └── build_context_json()  → 合并用户上下文 + 来源标记

所有分类器都是纯正则——不依赖 LLM,亚毫秒级执行。

合并算法

end_session 的合并操作会合并冗余记忆:

1. 查询可合并的记忆:
   - 同一会话 + 集合
   - status = active
   - category 不在 (constraint, postmortem, gotcha) 中  ← 受保护
   - confidence < 0.95  ← 高置信度保留
   - perception_type IS NULL  ← 感知记忆不合并

2. 不足 3 条则跳过

3. 按 category 分组

4. 每组内:两两 Jaccard 相似度
   - > 0.50 阈值 → 贪心聚类
   - 聚类约束:簇内所有对必须超过阈值

5. 每个簇选择代表:
   - 优先级:confidence 降序 → access_count 降序 → created_at 降序

6. 非代表记忆 → status = 'superseded',superseded_by = 代表.id

容错模式

三层防御

每个 MCP 工具都遵循一致的防御模式:

层级 阶段 机制
L1 执行前 Pydantic 验证、类型检查、范围约束
L2 执行中 每个操作 try-except、优雅降级、safe_db_transaction
L3 执行后 结构化响应、日志记录、访问计数器更新

安全数据库原语

原语 用途 失败时行为
safe_db_write 单条 SQL 写入 返回 None(锁超时/磁盘满/数据损坏)
safe_db_transaction 多条 SQL 原子批量操作 返回 (False, None) 并回滚
mcp_error_boundary MCP 工具装饰器 捕获所有异常,返回 {"error": "..."}

服务冷却

当 Ollama/外部嵌入服务失败时: - 指数退避:60s → 120s → 240s → 300s(最大值) - 冷却期间:embedder.available = False,搜索降级为仅 BM25 - 成功后重置冷却计数器

嵌入管道

                    ┌─────────────────────┐
config.embed_backend│                     │
         ┌──────────┤  create_embedder()  │
         │          │                     │
         │          └─────────────────────┘
         │
    ┌────▼──────┐         ┌───────────────┐
    │  "onnx"   │         │   "ollama"    │
    │           │         │               │
    │ FastEmbed │         │ OllamaEmbed   │
    │ ONNX CPU  │         │ HTTP API      │
    │ ~5ms/查询 │         │ ~20-50ms/查询 │
    │ 384d      │         │ 768d          │
    └───────────┘         └───────────────┘
         │                       │
         │    Embedder Protocol  │
         │    ├── embed_one()    │
         │    ├── embed_batch()  │
         │    ├── available      │
         │    └── close()        │
         └───────────┬───────────┘
                     │
              ┌──────▼───────┐
              │  float[dim]  │
              │  → vec0 BLOB │
              └──────────────┘

Web UI 架构

python -m robotmem web
         │
    ┌────▼────────────────────────┐
    │  Flask App Factory          │
    │  create_app()               │
    │                             │
    │  ┌────────────────────────┐ │
    │  │  api_bp (Blueprint)    │ │
    │  │  /api/doctor           │ │
    │  │  /api/stats            │ │
    │  │  /api/memories         │ │
    │  │  /api/search           │ │
    │  │  /api/memory/<id>      │ │
    │  │  /api/sessions         │ │
    │  │  /api/collections      │ │
    │  │  /api/categories       │ │
    │  │  /api/recent-failures  │ │
    │  │  /api/sessions/<id>/   │ │
    │  │       memories         │ │
    │  └────────────────────────┘ │
    │                             │
    │  GET / → index.html         │
    │  CogDatabase(共享连接)     │
    └─────────────────────────────┘

REST API 端点

方法 端点 说明
GET /api/doctor 健康检查:FTS5/vec0 同步、零命中率、数据库大小
GET /api/stats 总计数、类型/类别分布、集合列表
GET /api/memories 分页列表,支持过滤(collection、type、category、confidence、days)
GET /api/search?q= FTS5 全文搜索,跨集合
GET /api/memory/<id> 单条记忆详情
DELETE /api/memory/<id> 软删除(需提供原因)
PUT /api/memory/<id> 更新记忆字段
GET /api/sessions 分页会话列表,包含记忆计数
GET /api/collections 集合列表,包含计数
GET /api/categories 类别列表,包含计数
GET /api/recent-failures 最近的 postmortem/gotcha 记忆
GET /api/sessions/<id>/memories 会话内的记忆(时间线)