docs: update README/INTERNALS for import feature, harden .gitignore
This commit is contained in:
481
INTERNALS.md
Normal file
481
INTERNALS.md
Normal file
@@ -0,0 +1,481 @@
|
||||
# Ombre Brain — 内部开发文档 / INTERNALS
|
||||
|
||||
> 本文档面向开发者和维护者。记录功能总览、环境变量、模块依赖、硬编码值和核心设计决策。
|
||||
> 最后更新:2026-04-19
|
||||
|
||||
---
|
||||
|
||||
## 0. 功能总览——这个系统到底做了什么
|
||||
|
||||
### 记忆能力
|
||||
|
||||
**存储与组织**
|
||||
- 每条记忆 = 一个 Markdown 文件(YAML frontmatter 存元数据),直接兼容 Obsidian 浏览/编辑
|
||||
- 四种桶类型:`dynamic`(普通,会衰减)、`permanent`(固化,不衰减)、`feel`(模型感受,不浮现)、`archived`(已遗忘)
|
||||
- 按主题域分子目录:`dynamic/日常/`、`dynamic/情感/`、`dynamic/编程/` 等
|
||||
- 钉选桶(pinned):importance 锁 10,永不衰减/合并,始终浮现为「核心准则」
|
||||
|
||||
**每条记忆追踪的元数据**
|
||||
- `id`(12位短UUID)、`name`(可读名≤80字)、`tags`(10~15个关键词)
|
||||
- `domain`(1~2个主题域,从 8 大类 30+ 细分域选)
|
||||
- `valence`(事件效价 0~1)、`arousal`(唤醒度 0~1)、`model_valence`(模型独立感受)
|
||||
- `importance`(1~10)、`activation_count`(被想起次数)
|
||||
- `resolved`(已解决/沉底)、`digested`(已消化/写过 feel)、`pinned`(钉选)
|
||||
- `created`、`last_active` 时间戳
|
||||
|
||||
**四种检索模式**
|
||||
1. **自动浮现**(`breath()` 无参数):按衰减分排序推送,钉选桶始终展示,Top-1 固定 + Top-20 随机打乱(引入多样性),有 token 预算(默认 10000)
|
||||
2. **关键词+向量双通道搜索**(`breath(query=...)`):rapidfuzz 模糊匹配 + Gemini embedding 余弦相似度,合并去重
|
||||
3. **Feel 独立检索**(`breath(domain="feel")`):按创建时间倒序返回所有 feel
|
||||
4. **随机浮现**:搜索结果 <3 条时 40% 概率漂浮 1~3 条低权重旧桶(模拟人类随机联想)
|
||||
|
||||
**四维搜索评分**(归一化到 0~100)
|
||||
- topic_relevance(权重 4.0):name×3 + domain×2.5 + tags×2 + body
|
||||
- emotion_resonance(权重 2.0):Russell 环形模型欧氏距离
|
||||
- time_proximity(权重 2.5):`e^(-0.1×days)`
|
||||
- importance(权重 1.0):importance/10
|
||||
- resolved 桶全局降权 ×0.3
|
||||
|
||||
**记忆随时间变化**
|
||||
- **衰减引擎**:改进版艾宾浩斯遗忘曲线
|
||||
- 公式:`Score = Importance × activation_count^0.3 × e^(-λ×days) × combined_weight`
|
||||
- 短期(≤3天):时间权重 70% + 情感权重 30%
|
||||
- 长期(>3天):情感权重 70% + 时间权重 30%
|
||||
- 新鲜度加成:`1.0 + e^(-t/36h)`,刚存入 ×2.0,~36h 半衰,72h 后 ≈×1.0
|
||||
- 高唤醒度(arousal>0.7)且未解决 → ×1.5 紧迫度加成
|
||||
- resolved → ×0.05 沉底;resolved+digested → ×0.02 加速淡化
|
||||
- **自动归档**:score 低于阈值(0.3) → 移入 archive
|
||||
- **自动结案**:importance≤4 且 >30天 → 自动 resolved
|
||||
- **永不衰减**:permanent / pinned / protected / feel
|
||||
|
||||
**记忆间交互**
|
||||
- **智能合并**:新记忆与相似桶(score>75)自动 LLM 合并,valence/arousal 取均值,tags/domain 并集
|
||||
- **时间涟漪**:touch 一个桶时,±48h 内创建的桶 activation_count +0.3(上限 5 桶/次)
|
||||
- **向量相似网络**:embedding 余弦相似度 >0.5 建边
|
||||
- **Feel 结晶化**:≥3 条相似 feel(相似度>0.7)→ 提示升级为钉选准则
|
||||
|
||||
**情感记忆重构**
|
||||
- 搜索时若指定 valence,展示层对匹配桶 valence 微调 ±0.1,模拟「当前心情影响回忆色彩」
|
||||
|
||||
**模型感受/反思系统**
|
||||
- **Feel 写入**(`hold(feel=True)`):存模型第一人称感受,标记源记忆为 digested
|
||||
- **Dream 做梦**(`dream()`):返回最近 10 条 + 自省引导 + 连接提示 + 结晶化提示
|
||||
- **对话启动流程**:breath() → dream() → breath(domain="feel") → 开始对话
|
||||
|
||||
**自动化处理**
|
||||
- 存入时 LLM 自动分析 domain/valence/arousal/tags/name
|
||||
- 大段日记 LLM 拆分为 2~6 条独立记忆
|
||||
- 浮现时自动脱水压缩(LLM 压缩保语义,API 不可用降级到本地关键词提取)
|
||||
- Wikilink `[[]]` 由 LLM 在内容中标记
|
||||
|
||||
---
|
||||
|
||||
### 技术能力
|
||||
|
||||
**6 个 MCP 工具**
|
||||
|
||||
| 工具 | 关键参数 | 功能 |
|
||||
|---|---|---|
|
||||
| `breath` | query, max_tokens, domain, valence, arousal, max_results | 检索/浮现记忆 |
|
||||
| `hold` | content, tags, importance, pinned, feel, source_bucket, valence, arousal | 存储记忆 |
|
||||
| `grow` | content | 日记拆分归档 |
|
||||
| `trace` | bucket_id, name, domain, valence, arousal, importance, tags, resolved, pinned, digested, content, delete | 修改元数据/内容/删除 |
|
||||
| `pulse` | include_archive | 系统状态 |
|
||||
| `dream` | (无) | 做梦自省 |
|
||||
|
||||
**工具详细行为**
|
||||
|
||||
**`breath`** — 两种模式:
|
||||
- **浮现模式**(无 query):无参调用,按衰减引擎活跃度排序返回 top 记忆,permanent/pinned 始终浮现
|
||||
- **检索模式**(有 query):关键词 + 向量双通道搜索,四维评分(topic×4 + emotion×2 + time×2.5 + importance×1),阈值过滤
|
||||
- **Feel 检索**(`domain="feel"`):特殊通道,按创建时间倒序返回所有 feel 类型桶,不走评分逻辑
|
||||
- 若指定 valence,对匹配桶的 valence 微调 ±0.1(情感记忆重构)
|
||||
|
||||
**`hold`** — 两种模式:
|
||||
- **普通模式**(`feel=False`,默认):自动 LLM 分析 domain/valence/arousal/tags/name → 向量相似度查重 → 相似度>0.85 则合并到已有桶 → 否则新建 dynamic 桶 → 生成 embedding
|
||||
- **Feel 模式**(`feel=True`):跳过 LLM 分析,直接存为 `feel` 类型桶(存入 `feel/` 目录),不参与普通浮现/衰减/合并。若提供 `source_bucket`,标记源记忆为 `digested=True` 并写入 `model_valence`。返回格式:`🫧feel→{bucket_id}`
|
||||
|
||||
**`dream`** — 做梦/自省触发器:
|
||||
- 返回最近 10 条 dynamic 桶摘要 + 自省引导词
|
||||
- 检测 feel 结晶化:≥3 条相似 feel(embedding 相似度>0.7)→ 提示升级为钉选准则
|
||||
- 检测未消化记忆:列出 `digested=False` 的桶供模型反思
|
||||
|
||||
**`trace`** — 记忆编辑:
|
||||
- 修改任意元数据字段(name/domain/valence/arousal/importance/tags/resolved/pinned)
|
||||
- `digested=0/1`:隐藏/取消隐藏记忆(控制是否在 dream 中出现)
|
||||
- `content="..."`:替换正文内容并重新生成 embedding
|
||||
- `delete=True`:删除桶文件
|
||||
|
||||
**`grow`** — 日记拆分:
|
||||
- 大段日记文本 → LLM 拆为 2~6 条独立记忆 → 每条走 hold 普通模式流程
|
||||
|
||||
**`pulse`** — 系统状态:
|
||||
- 返回各类型桶数量、衰减引擎状态、未解决/钉选/feel 统计
|
||||
|
||||
**REST API(17 个端点)**
|
||||
|
||||
| 端点 | 方法 | 功能 |
|
||||
|---|---|---|
|
||||
| `/health` | GET | 健康检查 |
|
||||
| `/breath-hook` | GET | SessionStart 钩子 |
|
||||
| `/dream-hook` | GET | Dream 钩子 |
|
||||
| `/dashboard` | GET | Dashboard 页面 |
|
||||
| `/api/buckets` | GET | 桶列表 |
|
||||
| `/api/bucket/{id}` | GET | 桶详情 |
|
||||
| `/api/search?q=` | GET | 搜索 |
|
||||
| `/api/network` | GET | 向量相似网络 |
|
||||
| `/api/breath-debug` | GET | 评分调试 |
|
||||
| `/api/config` | GET | 配置查看(key 脱敏) |
|
||||
| `/api/config` | POST | 热更新配置 |
|
||||
| `/api/import/upload` | POST | 上传并启动历史对话导入 |
|
||||
| `/api/import/status` | GET | 导入进度查询 |
|
||||
| `/api/import/pause` | POST | 暂停/继续导入 |
|
||||
| `/api/import/patterns` | GET | 导入完成后词频规律检测 |
|
||||
| `/api/import/results` | GET | 已导入记忆桶列表 |
|
||||
| `/api/import/review` | POST | 批量审阅/批准导入结果 |
|
||||
|
||||
**Dashboard(5 个 Tab)**
|
||||
1. 记忆桶列表:6 种过滤器 + 主题域过滤 + 搜索 + 详情面板
|
||||
2. Breath 模拟:输入参数 → 可视化五步流程 → 四维条形图
|
||||
3. 记忆网络:Canvas 力导向图(节点=桶,边=相似度)
|
||||
4. 配置:热更新脱水/embedding/合并参数
|
||||
5. 导入:历史对话拖拽上传 → 分块处理进度条 → 词频规律分析 → 导入结果审阅
|
||||
|
||||
**部署选项**
|
||||
1. 本地 stdio(`python server.py`)
|
||||
2. Docker + Cloudflare Tunnel(`docker-compose.yml`)
|
||||
3. Docker Hub 预构建镜像(`docker-compose.user.yml`,`p0luz/ombre-brain`)
|
||||
4. Render.com 一键部署(`render.yaml`)
|
||||
5. Zeabur 部署(`zbpack.json`)
|
||||
6. GitHub Actions 自动构建推送 Docker Hub(`.github/workflows/docker-publish.yml`)
|
||||
|
||||
**迁移/批处理工具**:`migrate_to_domains.py`、`reclassify_domains.py`、`reclassify_api.py`、`backfill_embeddings.py`、`write_memory.py`、`check_buckets.py`、`import_memory.py`(历史对话导入引擎)
|
||||
|
||||
**降级策略**
|
||||
- 脱水 API 不可用 → 本地关键词提取 + 句子评分
|
||||
- 向量搜索不可用 → 纯 fuzzy match
|
||||
- 逐条错误隔离(grow 中单条失败不影响其他)
|
||||
|
||||
**安全**:路径遍历防护(`safe_path()`)、API Key 脱敏、API Key 不持久化到 yaml、输入范围钳制
|
||||
|
||||
**监控**:结构化日志、Health 端点、Breath Debug 端点、Dashboard 统计栏、衰减周期日志
|
||||
|
||||
---
|
||||
|
||||
## 1. 环境变量清单
|
||||
|
||||
| 变量名 | 用途 | 必填 | 默认值 / 示例 |
|
||||
|---|---|---|---|
|
||||
| `OMBRE_API_KEY` | 脱水/打标/嵌入的 LLM API 密钥,覆盖 `config.yaml` 的 `dehydration.api_key` | 否(无则 API 功能降级到本地) | `""` |
|
||||
| `OMBRE_BASE_URL` | API base URL,覆盖 `config.yaml` 的 `dehydration.base_url` | 否 | `""` |
|
||||
| `OMBRE_TRANSPORT` | 传输模式:`stdio` / `sse` / `streamable-http` | 否 | `""` → 回退到 config 或 `"stdio"` |
|
||||
| `OMBRE_BUCKETS_DIR` | 记忆桶存储目录路径 | 否 | `""` → 回退到 config 或 `./buckets` |
|
||||
| `OMBRE_HOOK_URL` | SessionStart 钩子调用的服务器 URL | 否 | `"http://localhost:8000"` |
|
||||
| `OMBRE_HOOK_SKIP` | 设为 `"1"` 跳过 SessionStart 钩子 | 否 | 未设置(不跳过) |
|
||||
|
||||
环境变量优先级:`环境变量 > config.yaml > 硬编码默认值`。所有环境变量在 `utils.py` 中读取并注入 config dict。
|
||||
|
||||
---
|
||||
|
||||
## 2. 模块结构与依赖关系
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ server.py │ MCP 主入口,6 个工具 + Dashboard + Hook
|
||||
└──────┬───────┘
|
||||
┌───────────────┼───────────────┬────────────────┐
|
||||
▼ ▼ ▼ ▼
|
||||
bucket_manager.py dehydrator.py decay_engine.py embedding_engine.py
|
||||
记忆桶 CRUD+搜索 脱水压缩+打标 遗忘曲线+归档 向量化+语义检索
|
||||
│ │ │
|
||||
└───────┬───────┘ │
|
||||
▼ ▼
|
||||
utils.py ◄────────────────────────────────────┘
|
||||
配置/日志/ID/路径安全/token估算
|
||||
```
|
||||
|
||||
| 文件 | 职责 | 依赖(项目内) | 被谁调用 |
|
||||
|---|---|---|---|
|
||||
| `server.py` | MCP 服务器主入口,注册工具 + Dashboard API + 钩子端点 | `bucket_manager`, `dehydrator`, `decay_engine`, `embedding_engine`, `utils` | `test_tools.py` |
|
||||
| `bucket_manager.py` | 记忆桶 CRUD、多维索引搜索、wikilink 注入、激活更新 | `utils` | `server.py`, `check_buckets.py`, `backfill_embeddings.py` |
|
||||
| `decay_engine.py` | 衰减引擎:遗忘曲线计算、自动归档、自动结案 | 无(接收 `bucket_mgr` 实例) | `server.py` |
|
||||
| `dehydrator.py` | 数据脱水压缩 + 合并 + 自动打标(LLM API + 本地降级) | `utils` | `server.py` |
|
||||
| `embedding_engine.py` | 向量化引擎:Gemini embedding API + SQLite + 余弦搜索 | `utils` | `server.py`, `backfill_embeddings.py` |
|
||||
| `utils.py` | 配置加载、日志、路径安全、ID 生成、token 估算 | 无 | 所有模块 |
|
||||
| `write_memory.py` | 手动写入记忆 CLI(绕过 MCP) | 无(独立脚本) | 无 |
|
||||
| `backfill_embeddings.py` | 为存量桶批量生成 embedding | `utils`, `bucket_manager`, `embedding_engine` | 无 |
|
||||
| `check_buckets.py` | 桶数据完整性检查 | `bucket_manager`, `utils` | 无 |
|
||||
| `import_memory.py` | 历史对话导入引擎(支持 Claude JSON/ChatGPT/DeepSeek/Markdown/纯文本),分块处理+断点续传+词频分析 | `utils` | `server.py` |
|
||||
| `reclassify_api.py` | 用 LLM API 重打标未分类桶 | 无(直接用 `openai`) | 无 |
|
||||
| `reclassify_domains.py` | 基于关键词本地重分类 | 无 | 无 |
|
||||
| `migrate_to_domains.py` | 平铺桶 → 域子目录迁移 | 无 | 无 |
|
||||
| `test_smoke.py` | 冒烟测试 | `utils`, `bucket_manager`, `dehydrator`, `decay_engine` | 无 |
|
||||
| `test_tools.py` | MCP 工具端到端测试 | `utils`, `server`, `bucket_manager` | 无 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 硬编码值清单
|
||||
|
||||
### 3.1 固定分数 / 特殊返回值
|
||||
|
||||
| 值 | 位置 | 用途 |
|
||||
|---|---|---|
|
||||
| `999.0` | `decay_engine.py` calculate_score | pinned / protected / permanent 桶永不衰减 |
|
||||
| `50.0` | `decay_engine.py` calculate_score | feel 桶固定活跃度分数 |
|
||||
| `0.02` | `decay_engine.py` resolved_factor | resolved + digested 时的权重乘数(加速淡化) |
|
||||
| `0.05` | `decay_engine.py` resolved_factor | 仅 resolved 时的权重乘数(沉底) |
|
||||
| `1.5` | `decay_engine.py` urgency_boost | arousal > 0.7 且未解决时的紧迫度加成 |
|
||||
|
||||
### 3.2 衰减公式参数
|
||||
|
||||
| 值 | 位置 | 用途 |
|
||||
|---|---|---|
|
||||
| `36.0` | `decay_engine.py` _calc_time_weight | 新鲜度半衰期(小时),`1.0 + e^(-t/36)` |
|
||||
| `0.3` (指数) | `decay_engine.py` calculate_score | `activation_count ** 0.3`(记忆巩固指数) |
|
||||
| `3.0` (天) | `decay_engine.py` calculate_score | 短期/长期切换阈值 |
|
||||
| `0.7 / 0.3` | `decay_engine.py` combined_weight | 短期权重分配:time×0.7 + emotion×0.3 |
|
||||
| `0.7` | `decay_engine.py` urgency_boost | arousal 紧迫度触发阈值 |
|
||||
| `4` / `30` (天) | `decay_engine.py` execute_cycle | 自动结案:importance≤4 且 >30天 |
|
||||
|
||||
### 3.3 搜索/评分参数
|
||||
|
||||
| 值 | 位置 | 用途 |
|
||||
|---|---|---|
|
||||
| `×3` / `×2.5` / `×2` | `bucket_manager.py` _calc_topic_score | 桶名 / 域名 / 标签的 topic 评分权重 |
|
||||
| `1000` (字符) | `bucket_manager.py` _calc_topic_score | 正文截取长度 |
|
||||
| `0.1` | `bucket_manager.py` _calc_time_score | 时间亲近度衰减系数 `e^(-0.1 × days)` |
|
||||
| `0.3` | `bucket_manager.py` search_multi | resolved 桶的归一化分数乘数 |
|
||||
| `0.5` | `server.py` breath/search | 向量搜索相似度下限 |
|
||||
| `0.7` | `server.py` dream | feel 结晶相似度阈值 |
|
||||
|
||||
### 3.4 Token 限制 / 截断
|
||||
|
||||
| 值 | 位置 | 用途 |
|
||||
|---|---|---|
|
||||
| `10000` | `server.py` breath 默认 max_tokens | 浮现/搜索 token 预算 |
|
||||
| `20000` | `server.py` breath 上限 | max_tokens 硬上限 |
|
||||
| `50` / `20` | `server.py` breath | max_results 上限 / 默认值 |
|
||||
| `3000` | `dehydrator.py` dehydrate | API 脱水内容截断 |
|
||||
| `2000` | `dehydrator.py` merge | API 合并内容各截断 |
|
||||
| `5000` | `dehydrator.py` digest | API 日记整理内容截断 |
|
||||
| `2000` | `embedding_engine.py` | embedding 文本截断 |
|
||||
| `100` | `dehydrator.py` | 内容 < 100 token 跳过脱水 |
|
||||
|
||||
### 3.5 时间/间隔/重试
|
||||
|
||||
| 值 | 位置 | 用途 |
|
||||
|---|---|---|
|
||||
| `60.0s` | `dehydrator.py` | OpenAI 客户端 timeout |
|
||||
| `30.0s` | `embedding_engine.py` | Embedding API timeout |
|
||||
| `60s` | `server.py` keepalive | 保活 ping 间隔 |
|
||||
| `48.0h` | `bucket_manager.py` touch | 时间涟漪窗口 ±48h |
|
||||
| `2s` | `backfill_embeddings.py` | 批次间等待 |
|
||||
|
||||
### 3.6 随机浮现
|
||||
|
||||
| 值 | 位置 | 用途 |
|
||||
|---|---|---|
|
||||
| `3` | `server.py` breath search | 结果不足 3 条时触发 |
|
||||
| `0.4` | `server.py` breath search | 40% 概率触发随机浮现 |
|
||||
| `2.0` | `server.py` breath search | 随机池:score < 2.0 的低权重桶 |
|
||||
| `1~3` | `server.py` breath search | 随机浮现数量 |
|
||||
|
||||
### 3.7 情感/重构
|
||||
|
||||
| 值 | 位置 | 用途 |
|
||||
|---|---|---|
|
||||
| `0.2` | `server.py` breath search | 情绪重构偏移系数 `(q_valence - 0.5) × 0.2`(最大 ±0.1) |
|
||||
|
||||
### 3.8 其他
|
||||
|
||||
| 值 | 位置 | 用途 |
|
||||
|---|---|---|
|
||||
| `12` | `utils.py` gen_id | bucket ID 长度(UUID hex[:12]) |
|
||||
| `80` | `utils.py` sanitize_name | 桶名最大长度 |
|
||||
| `1.5` / `1.3` | `utils.py` count_tokens_approx | 中文/英文 token 估算系数 |
|
||||
| `8000` | `server.py` | MCP 服务器端口 |
|
||||
| `30` 字符 | `server.py` grow | 短内容快速路径阈值 |
|
||||
| `10` | `server.py` dream | 取最近 N 个桶 |
|
||||
|
||||
---
|
||||
|
||||
## 4. Config.yaml 完整键表
|
||||
|
||||
| 键路径 | 默认值 | 用途 |
|
||||
|---|---|---|
|
||||
| `transport` | `"stdio"` | 传输模式 |
|
||||
| `log_level` | `"INFO"` | 日志级别 |
|
||||
| `buckets_dir` | `"./buckets"` | 记忆桶目录 |
|
||||
| `merge_threshold` | `75` | 合并相似度阈值 (0-100) |
|
||||
| `dehydration.model` | `"deepseek-chat"` | 脱水用 LLM 模型 |
|
||||
| `dehydration.base_url` | `"https://api.deepseek.com/v1"` | API 地址 |
|
||||
| `dehydration.api_key` | `""` | API 密钥 |
|
||||
| `dehydration.max_tokens` | `1024` | 脱水返回 token 上限 |
|
||||
| `dehydration.temperature` | `0.1` | 脱水温度 |
|
||||
| `embedding.enabled` | `true` | 启用向量检索 |
|
||||
| `embedding.model` | `"gemini-embedding-001"` | Embedding 模型 |
|
||||
| `decay.lambda` | `0.05` | 衰减速率 λ |
|
||||
| `decay.threshold` | `0.3` | 归档分数阈值 |
|
||||
| `decay.check_interval_hours` | `24` | 衰减扫描间隔(小时) |
|
||||
| `decay.emotion_weights.base` | `1.0` | 情感权重基值 |
|
||||
| `decay.emotion_weights.arousal_boost` | `0.8` | 唤醒度加成系数 |
|
||||
| `matching.fuzzy_threshold` | `50` | 模糊匹配下限 |
|
||||
| `matching.max_results` | `5` | 匹配返回上限 |
|
||||
| `scoring_weights.topic_relevance` | `4.0` | 主题评分权重 |
|
||||
| `scoring_weights.emotion_resonance` | `2.0` | 情感评分权重 |
|
||||
| `scoring_weights.time_proximity` | `2.5` | 时间评分权重 |
|
||||
| `scoring_weights.importance` | `1.0` | 重要性评分权重 |
|
||||
| `scoring_weights.content_weight` | `3.0` | 正文评分权重 |
|
||||
| `wikilink.enabled` | `true` | 启用 wikilink 注入 |
|
||||
| `wikilink.use_tags` | `false` | wikilink 包含标签 |
|
||||
| `wikilink.use_domain` | `true` | wikilink 包含域名 |
|
||||
| `wikilink.use_auto_keywords` | `true` | wikilink 自动关键词 |
|
||||
| `wikilink.auto_top_k` | `8` | wikilink 取 Top-K 关键词 |
|
||||
| `wikilink.min_keyword_len` | `2` | wikilink 最短关键词长度 |
|
||||
| `wikilink.exclude_keywords` | `[]` | wikilink 排除关键词表 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 核心设计决策记录
|
||||
|
||||
### 5.1 为什么用 Markdown + YAML frontmatter 而不是数据库?
|
||||
|
||||
**决策**:每个记忆桶 = 一个 `.md` 文件,元数据在 YAML frontmatter 里。
|
||||
|
||||
**理由**:
|
||||
- 与 Obsidian 原生兼容——用户可以直接在 Obsidian 里浏览、编辑、搜索记忆
|
||||
- 文件系统即数据库,天然支持 git 版本管理
|
||||
- 无外部数据库依赖,部署简单
|
||||
- wikilink 注入让记忆之间自动形成知识图谱
|
||||
|
||||
**放弃方案**:SQLite/PostgreSQL 全量存储。过于笨重,失去 Obsidian 可视化优势。
|
||||
|
||||
### 5.2 为什么 embedding 单独存 SQLite 而不放 frontmatter?
|
||||
|
||||
**决策**:向量存 `embeddings.db`(SQLite),与 Markdown 文件分离。
|
||||
|
||||
**理由**:
|
||||
- 3072 维浮点向量无法合理存入 YAML frontmatter
|
||||
- SQLite 支持批量查询和余弦相似度计算
|
||||
- embedding 是派生数据,丢失可重新生成(`backfill_embeddings.py`)
|
||||
- 不污染 Obsidian 可读性
|
||||
|
||||
### 5.3 为什么搜索用双通道(关键词 + 向量)而不是纯向量?
|
||||
|
||||
**决策**:关键词模糊匹配(rapidfuzz)+ 向量语义检索并联,结果去重合并。
|
||||
|
||||
**理由**:
|
||||
- 纯向量在精确名词匹配上表现差("2024年3月"这类精确信息)
|
||||
- 纯关键词无法处理语义近似("很累" → "身体不适")
|
||||
- 双通道互补,关键词保精确性,向量补语义召回
|
||||
- 向量不可用时自动降级到纯关键词模式
|
||||
|
||||
### 5.4 为什么有 dehydration(脱水)这一层?
|
||||
|
||||
**决策**:存入前先用 LLM 压缩内容(保留信息密度,去除冗余表达),API 不可用时降级到本地关键词提取。
|
||||
|
||||
**理由**:
|
||||
- MCP 上下文有 token 限制,原始对话冗长,需要压缩
|
||||
- LLM 压缩能保留语义和情感色彩,纯截断会丢信息
|
||||
- 降级到本地确保离线可用——关键词提取 + 句子排序 + 截断
|
||||
|
||||
**放弃方案**:只做截断。信息损失太大。
|
||||
|
||||
### 5.5 为什么 feel 和普通记忆分开?
|
||||
|
||||
**决策**:`feel=True` 的记忆存入独立 `feel/` 目录,不参与普通浮现、不衰减、不合并。
|
||||
|
||||
**理由**:
|
||||
- feel 是模型的自省产物,不是事件记录——两者逻辑完全不同
|
||||
- 事件记忆应该衰减遗忘,但"我从中学到了什么"不应该被遗忘
|
||||
- feel 的 valence 是模型自身感受(不等于事件情绪),混在一起会污染情感检索
|
||||
- feel 可以通过 `breath(domain="feel")` 单独读取
|
||||
|
||||
### 5.6 为什么 resolved 不删除记忆?
|
||||
|
||||
**决策**:`resolved=True` 让记忆"沉底"(权重 ×0.05),但保留在文件系统中,关键词搜索仍可触发。
|
||||
|
||||
**理由**:
|
||||
- 模拟人类记忆:resolved 的事不会主动想起,但别人提到时能回忆
|
||||
- 删除是不可逆的,沉底可随时 `resolved=False` 重新激活
|
||||
- `resolved + digested` 进一步降权到 ×0.02(已消化 = 更释然)
|
||||
|
||||
**放弃方案**:直接删除。不可逆,且与人类记忆模型不符。
|
||||
|
||||
### 5.7 为什么用分段式短期/长期权重?
|
||||
|
||||
**决策**:≤3 天时间权重占 70%,>3 天情感权重占 70%。
|
||||
|
||||
**理由**:
|
||||
- 刚发生的事主要靠"新鲜"驱动浮现(今天的事 > 昨天的事)
|
||||
- 时间久了,决定记忆存活的是情感强度(强烈的记忆更难忘)
|
||||
- 这比单一衰减曲线更符合人类记忆的双重存储理论
|
||||
|
||||
### 5.8 为什么 dream 设计成对话开头自动执行?
|
||||
|
||||
**决策**:每次新对话启动时,Claude 执行 `dream()` 消化最近记忆,有沉淀写 feel,能放下的 resolve。
|
||||
|
||||
**理由**:
|
||||
- 模拟睡眠中的记忆整理——人在睡觉时大脑会重放和整理白天的经历
|
||||
- 让 Claude 对过去的记忆有"第一人称视角"的自省,而不是冷冰冰地搬运数据
|
||||
- 自动触发确保每次对话都"接续"上一次,而非从零开始
|
||||
|
||||
### 5.9 为什么新鲜度用连续指数衰减而不是分段阶梯?
|
||||
|
||||
**决策**:`bonus = 1.0 + e^(-t/36)`,t 为小时,36h 半衰。
|
||||
|
||||
**理由**:
|
||||
- 分段阶梯(0-1天=1.0,第2天=0.9...)有不自然的跳变
|
||||
- 连续指数更符合遗忘曲线的物理模型
|
||||
- 36h 半衰期使新桶在前两天有明显优势,72h 后接近自然回归
|
||||
- 值域 1.0~2.0 保证老记忆不被惩罚(×1.0),只是新记忆有额外加成(×2.0)
|
||||
|
||||
**放弃方案**:分段线性(原实现)。跳变点不自然,参数多且不直观。
|
||||
|
||||
### 5.10 情感记忆重构(±0.1 偏移)的设计动机
|
||||
|
||||
**决策**:搜索时如果指定了 `valence`,会微调结果桶的 valence 展示值 `(q_valence - 0.5) × 0.2`。
|
||||
|
||||
**理由**:
|
||||
- 模拟认知心理学中的"心境一致性效应"——当前心情会影响对过去的回忆
|
||||
- 偏移量很小(最大 ±0.1),不会扭曲事实,只是微妙的"色彩"调整
|
||||
- 原始 valence 不被修改,只影响展示层
|
||||
|
||||
---
|
||||
|
||||
## 6. 目录结构约定
|
||||
|
||||
```
|
||||
buckets/
|
||||
├── permanent/ # pinned/protected 桶,importance=10,永不衰减
|
||||
├── dynamic/
|
||||
│ ├── 日常/ # domain 子目录
|
||||
│ ├── 情感/
|
||||
│ ├── 自省/
|
||||
│ ├── 数字/
|
||||
│ └── ...
|
||||
├── archive/ # 衰减归档桶
|
||||
└── feel/ # 模型自省 feel 桶
|
||||
```
|
||||
|
||||
桶文件格式:
|
||||
```markdown
|
||||
---
|
||||
id: 76237984fa5d
|
||||
name: 桶名
|
||||
domain: [日常, 情感]
|
||||
tags: [关键词1, 关键词2]
|
||||
importance: 5
|
||||
valence: 0.6
|
||||
arousal: 0.4
|
||||
activation_count: 3
|
||||
resolved: false
|
||||
pinned: false
|
||||
digested: false
|
||||
created: 2026-04-17T10:00:00+08:00
|
||||
last_active: 2026-04-17T14:00:00+08:00
|
||||
type: dynamic
|
||||
---
|
||||
|
||||
桶正文内容...
|
||||
```
|
||||
Reference in New Issue
Block a user