OpenChronicle 记忆数据流全解:从 S1 解析到 FTS5 索引的 5 级压缩漏斗
【免费下载链接】OpenChronicle项目地址: https://gitcode.com/gh_mirrors/op/OpenChronicle
OpenChronicle 是一个开源、本地优先的 AI Agent 记忆系统:它在 macOS 上持续捕获你的屏幕上下文(AX 可访问性树),通过一条5 级压缩漏斗把原始事件逐级压缩成可读的 Markdown 记忆,并最终建入SQLite FTS5 全文索引,让任意支持工具调用的 AI Agent 都能快速检索"你正在做什么、做过什么决定"。
🗺️ 一眼看懂:5 级压缩漏斗总览
整条数据流只有一条入口,没有多模式分支。每一级都只做一件事,输出有界、prompt 可控:
| 级别 | 阶段 | 输入 → 输出 | 核心动作 |
|---|---|---|---|
| 1️⃣ | S0 分发 + S1 解析 | AX 事件流 → 结构化 JSON 捕获 | 去抖、去重,提取focused_element/visible_text/url |
| 2️⃣ | Timeline 归一化 | 原始捕获 → 1 分钟时间块 | 保字面归一化,剔除 UI 噪音 |
| 3️⃣ | 会话切割 + S2 归约 | 时间块 → 会话级日记条目 | 按空闲/切换/超时切会话,5 分钟增量刷写 |
| 4️⃣ | Classifier 分类 | 日记条目 → 结构化记忆文件 | 提取持久事实,写入 user-/project-/person- 等文件 |
| 5️⃣ | FTS5 索引 + MCP 检索 | Markdown + 捕获 → 索引库 | BM25 全文检索,Agent 随时可查 |
官方对整体管线的完整描述见 docs/architecture.md,端到端流程图与运行时序列都在里面。
1️⃣ 第一级:S1 解析——把"屏幕事件"变成结构化上下文
macOS 的屏幕信号来自一个 Swift 编写的mac-ax-watcher二进制,它订阅所有应用的可访问性事件(窗口聚焦、输入值变化、标题变化),每个事件吐出一行 JSON。
原始事件像消防水带一样嘈杂,先要过S0 分发器(event_dispatcher.py)的四道时间闸:
| 旋钮 | 默认值 | 作用 |
|---|---|---|
debounce_seconds | 3.0s | 连续击键只触发一次捕获 |
dedup_interval_seconds | 1.0s | 同类型事件直接丢弃 |
min_capture_gap_seconds | 2.0s | 两次捕获的硬性最小间隔 |
same_window_dedup_seconds | 5.0s | 同窗口非聚焦事件折叠 |
之后才是真正的S1 解析(s1_parser.py)。它从庞大的 AX 树里只抽取下游 LLM 真正需要的三个字段:
focused_element—— 光标所在元素(你正在输入什么、选中了哪一行)visible_text—— 屏幕上可见内容的 Markdown 渲染(上限约 10 KB)url—— 从可见文本中正则提取的网址
这一步是"压缩"的关键起手:下游所有 LLM 阶段读的都是这三个字段,而不是动辄 200–400 KB 的原始 AX 树。捕获结果以{时间戳}.json写入~/.openchronicle/capture-buffer/,细节见 docs/capture.md。
2️⃣ 第二级:Timeline 时间块——1 分钟一格的"保真存档"
如果直接把原始捕获丢给归约器,prompt 预算瞬间爆掉。于是 Timeline 阶段(timeline/aggregator.py)每 60 秒扫描一次已关闭的整分钟窗口(对齐真实时钟,如[10:00, 10:01)),调用 LLM 把窗口内最多 30 条捕获归一化成一组活动记录:
[Notes] 购物清单: 用户起草清单,最新版本 "milk, eggs, flour, butter" [Chrome] ACME Q3 roadmap (https://docs.example/roadmap): 阅读文档,记录 Owner Alice它的规则非常克制,核心是verbatim-preserving(保字面):
- ✅ 用户亲手输入的文字、URL、窗口标题、专名——原样保留,绝不改写
- ✅ 剔除 UI 框架噪音、折叠重复快照
- ✅ 防幻觉:不同对话里的人/话题不得互相串供
时间块存入 SQLite 的timeline_blocks表(timeline/store.py),(start_time, end_time)唯一键保证幂等。设计动机完整解释见 docs/timeline.md。
3️⃣ 第三级:会话切割 + S2 归约——"一段专注工作"成为日记条目
人的记忆单位不是"事件",而是一段专注的工作(session)。session/manager.py 用三条规则切分会话:
| 规则 | 条件(默认) | 直觉 |
|---|---|---|
| 🔪 硬切 | 空闲超过 5 分钟 | 你去吃饭了,会话在"停下来那一刻"结束 |
| 🪄 软切 | 单个无关应用持续聚焦 3 分钟 | 你转去看视频了(快速多应用切换时自动豁免) |
| ⏰ 超时 | 会话超过 2 小时 | 兜底,防止会话失控 |
会话结束后,S2 归约器(writer/session_reducer.py)把时间范围内的所有时间块交给 LLM,压缩成一条带精确时间范围的日记条目,追加到当天的event-YYYY-MM-DD.md。
亮点在于增量刷写:长会话期间每 5 分钟就 flush 一次([flush]标记的条目),长工作不会被"先写后丢"地漏报——这正是 v1 逐捕获写入踩过的坑。会话状态机、重试队列(5/15/30/60/120 分钟退避)与书签机制(flush_end/classified_end)见 docs/session.md 和 docs/writer.md。
4️⃣ 第四级:Classifier 分类——从"活动日志"提炼"持久记忆"
活动日志回答"今天做了什么",但"用户换了新公司"、"Alice 是设计负责人"这类持久事实需要单独沉淀。
Classifier(writer/classifier.py)每 30 分钟(活跃会话中)加会话结束时各跑一轮,通过一个有上限(12 次迭代)的工具调用循环操作记忆库:
| 工具 | 用途 |
|---|---|
read_memory/search_memory | 追加前先去重 |
append/create | 写入新事实 |
supersede | 事实变更时"划线取代",永不删除 |
commit | 结束本轮(无持久信号时直接空提交) |
它默认偏向"什么都不做":纯粹的刷了 2 小时 Cursor 不算可分类事实。最终产出的是~/.openchronicle/memory/下一系列人类可读的 Markdown 文件——user-*.md、project-*.md、tool-*.md、topic-*.md、person-*.md、org-*.md。文件结构、supersede 语义、压缩(compact)保护机制见 docs/memory-format.md,完整记忆规范(也是 MCPget_schema返回的内容)在 prompts/schema.md。
5️⃣ 第五级:FTS5 索引——让记忆"秒级可查"
最后一级是检索。store/fts.py 里建了两套 FTS5 虚拟表,全部使用unicode61 remove_diacritics 2分词器(大小写不敏感、Unicode 友好):
| 索引表 | 索引对象 | 支撑的 MCP 工具 |
|---|---|---|
entries_fts | 压缩后的记忆条目(Markdown 层) | search/list_memories/read_memory |
captures_fts | 原始捕获的 S1 字段(visible_text、url…) | search_captures/current_context |
两层设计的妙处:压缩层答"我知道什么",原始捕获层答"屏幕上当时到底写了什么"。MCP 服务端明确教会 Agent 这个下钻路径——先查压缩层,查不到再搜原始层。
几个工程细节值得留意:
- SQLite WAL 模式:MCP 读与写入方并存不互锁(paths.py 管理
~/.openchronicle/index.db路径) - 触发器同步:
captures表的插入/删除/更新由触发器自动同步进captures_fts,索引不漂移 - 随时可重建:
openchronicle rebuild-index从 Markdown 全量重建entries_fts,rebuild-captures-index同理——索引永远只是派生副本 - Agent 入口:守护进程内置只读 MCP 服务(
http://127.0.0.1:8742/mcp),Claude Code、Claude Desktop、Codex 等一次配置即可接入,详见 docs/mcp.md
💡 磁盘上你能看到什么
跑起来之后,一切状态都摊在~/.openchronicle/下,随手可查:
~/.openchronicle/ ├── capture-buffer/ # 第一级:S1 增强 JSON 捕获 ├── index.db # SQLite:timeline_blocks / sessions / FTS5 ├── memory/ # 第四、五级:Markdown 记忆 + 索引 │ ├── index.md # 自动生成的总览 │ ├── event-2026-04-22.md │ └── user-/project-/person-… .md └── logs/ # 每个阶段一份日志,方便排查常用巡检命令:openchronicle status(守护进程状态)、openchronicle timeline list(看时间块)、openchronicle writer run(补跑未完成的会话)、openchronicle rebuild-index(重建 FTS5 索引)。
📌 小结:为什么是"漏斗"而不是"一把梭"
OpenChronicle 的数据流把"压缩"和"分类"刻意拆开:
- 每级 prompt 有界——S1 只给结构化字段,Timeline 只给 1 分钟窗口,S2 只给时间块,Classifier 看到的已是会话级摘要;
- 保真与压缩分层——用户亲手输入的文字在每一级都被"verbatim"保护,压缩的是 UI 噪音而非事实;
- 永不删除——记忆变更走 supersede 划线取代,FTS5 索引可随时从 Markdown 重建,整条链可审计、可手工修复。
想动手体验?安装只需 macOS 13+ 与 Xcode Command Line Tools,克隆仓库后执行bash install.sh再运行openchronicle start即可。更多配置与模型选择见 docs/config.md,遇到问题可查 docs/troubleshooting.md。
【免费下载链接】OpenChronicle项目地址: https://gitcode.com/gh_mirrors/op/OpenChronicle
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考