2026-04-11 · 架构
32
架构 · 2026-04-11

GBrain“永久记忆”实现原理白皮书

摘要

gbrain 这套系统最值得分析的地方,不是“它能存记忆”,而是它把“记忆”拆成了三层彼此配合的工程结构:可读的 Markdown 脑仓、可检索的 Postgres 索引层、可持续写入的 Agent 工作流。这三层缺一不可。

很多项目把“长期记忆”理解成向量库:把历史内容切块、嵌入、召回,然后让模型在回答时带上几段相关上下文。gbrain 不是这一路。它真正试图做的是:把人的世界抽成一套长期维护的知识底座,并让 Agent 在每次遇到新信号时都去更新它。也就是说,它追求的不是“把过去存起来”,而是“把过去编译成当前最可信的知识状态”。

这正是它口中的“永久记忆”真正成立的地方。永久,不是因为数据库永远不删;记忆,也不是因为模型记住了什么。它成立于四个条件:

  1. 源数据可回放:Markdown、原始 JSON、Timeline、版本快照都在。
  2. 知识可重写:新证据到来时,compiled_truth 会更新,而不是只追加。
  3. 检索可持续:全文、向量、模糊匹配、RRF 融合是一条稳定链路。
  4. 写入可自动发生:会议、邮件、社交、联系人这些信号会驱动 enrich 流程,而不是等人手工维护。

如果只看代码实现,gbrain 的“永久记忆”并没有什么神秘算法。它的价值主要来自两件事:

这份白皮书只讨论它怎么实现,不讨论它是什么产品。

一、先说结论:GBrain 的“永久记忆”并不是一个功能,而是一条闭环

gbrain 的实现核心可以压缩成一句话:

Markdown 作为人类可编辑的知识源,Postgres 作为机器可检索的索引与一致性层,Agent/Skill/Cron 作为持续写入和修正这套知识的执行系统。

从仓库代码和文档看,这个闭环长这样:

  1. 外部世界产生信号:会议、邮件、推文、联系人、文档、人工对话。
  2. Agent 通过 Skill 判断哪些实体值得入脑。
  3. 内容被写成标准 Markdown 页面,分成 compiled_truthtimeline 两层。
  4. gbrain importgbrain sync 读取这些文件,解析 frontmatter、正文、时间线。
  5. 系统为页面生成 content hash,判断是否真正变更。
  6. 变更内容被切块,必要时生成 embedding,写入 pagescontent_chunkstagspage_versionsraw_data 等表。
  7. 查询时,系统同时跑全文搜索与向量搜索,再用 RRF 做结果融合,并通过多层去重压缩噪音。
  8. Agent 拿到检索结果后回答问题;如果对话里又产生了新信息,它会再次写回 Markdown 或通过 operation 写回数据库。
  9. sync / cron 再把新增修改纳入索引。

这不是“记住一次”模型,而是“持续再编译”模型。

也正因为这样,gbrain 的永久记忆并不依赖某个超强模型的上下文窗口。模型只是两个环节里的可替换部件:

真正的记忆载体,是文件系统加数据库。

二、它先把“记忆”定义成一种页面结构,而不是一堆向量

这套系统最重要的设计,不在 embedding.ts,而在知识模型本身。

README 和 docs/GBRAIN_RECOMMENDED_SCHEMA.md 把页面统一成一个非常明确的二层结构:

src/core/markdown.ts 可以看到,这个结构不是文档约定而已,而是实际解析协议。parseMarkdown() 会:

  1. 先用 gray-matter 解析 YAML frontmatter;
  2. 再在正文里找第一个独立的 ---
  3. 将前半部分视为 compiled_truth
  4. 将后半部分视为 timeline

这意味着,gbrain 不是把整页文本粗暴视为同一种知识,而是先在文件协议层把两类信息分开:

这一步很关键。

传统聊天式记忆系统常见的问题是:所有历史消息都只是“过去发生过什么”的序列。模型每次都要从原始堆积里重新理解“现在到底是什么状态”。gbrain 不这样做。它要求你把“当前结论”单独维护出来,把“历史证据”单独保留下来。

换句话说,它不是让模型每次 query 时重新推导真相,而是把真相预先编译成页面上半部分。

这就是它能撑起“永久记忆”的第一根柱子:记忆先被结构化成了可长期维护的知识页面。

三、永久性的第一层:Markdown 作为长期源头,而不是缓存副本

gbrain 反复强调 repo 是 system of record。这个判断非常工程化。

原因很简单:

  1. Markdown 对人类可读;
  2. 可以直接用 Git 管;
  3. 脱离 gbrain 本身也还能活;
  4. 可以批量编辑、回滚、迁移;
  5. 对 Agent 来说,文件就是最低耦合的写入目标。

这和很多“记忆产品”的做法不同。很多系统的事实源只存在于某个私有向量库或专有存储格式里。那种存储可以检索,但很难维护,也难做人工纠偏。

gbrain 反过来:数据库只是索引层,Markdown 才是内容层。README 的架构图也写得很直白:

从这个角度看,“永久记忆”的“永久”首先来自不绑定单一 runtime

即使数据库损坏,只要 Markdown 还在,系统就能重新 import、重新 chunk、重新 embed、重新建索引。只要 repo 还在,记忆就能再生。

这是比“数据库里还留着记录”更强的永久性。

四、永久性的第二层:导入链路不是 append-only,而是带幂等和版本化的再编译

记忆系统真正容易烂掉的地方,不在第一次写入,而在后续迭代。

gbrainsrc/core/import-file.ts 里做了几件很重要的事。

4.1 内容哈希保证导入幂等

importFromContent() 会把这些字段一起算 SHA-256:

如果现有页面的 content_hash 和新 hash 一样,就直接 skipped

这意味着系统不是看到文件就重建一遍 embedding,而是先判断知识状态是否真的变了。这解决了两个问题:

  1. 避免反复重复索引;
  2. sync 能安全地周期运行。

“永久记忆”如果没有幂等,最后就会变成“永久重复写入”。这个项目至少在内容层避开了这个坑。

4.2 写入前先快照旧版

如果页面已存在,事务里第一步是 createVersion(slug)

也就是说,页面不是被直接覆盖,而是会把旧的 compiled_truthfrontmatter 先存进 page_versions。这张表在 schema 里明确存在。

这件事的价值不只是回滚,更重要的是给“长期记忆修正”留下历史切面。因为长期知识一定会变:

如果没有版本层,所谓永久记忆最后只会变成“当前状态的脆弱覆盖”。有了版本层,它才更接近“可追溯的长期认知演化”。

4.3 事务内同时更新页面、标签、chunks

importFromContent() 的事务边界也很清楚:

这些一起提交。这样页面正文、标签和 chunk 索引不会出现一半更新、一半没更新的状态。

长期记忆系统最怕的就是索引和内容不一致。你改了文章,但召回的还是旧 chunk;你改了标签,但页面没更新;你删了内容,向量还在。gbrain 至少在导入事务这一层有明确防御。

五、数据库层真正存的是什么:它不是“聊天历史”,而是知识图谱的基础砖块

src/core/schema-embedded.ts 可以很清楚看到,这套系统的核心表不是消息表,而是知识页与检索表。

最关键的有这些:

5.1 pages

这是核心内容表,字段包括:

这张表的意义很清楚:它把“当前知识页面”变成了数据库的一等对象。

5.2 content_chunks

这里保存页面切块后的内容:

注意这里把 compiled_truthtimeline 的 chunk source 区分开了。这个细节很重要,因为它允许检索结果里保留“这是当前判断还是历史证据”的语义来源。

5.3 timeline_entries

虽然页面里已经有一个 timeline 文本字段,它还额外做了一张结构化时间线表:

这让 timeline 不只是原文的一部分,也能被结构化查询和索引。

5.4 page_versions

用来保存知识快照。

5.5 raw_data

这个很关键。它专门保存外部 API 的原始响应,字段有:

这张表让系统能同时保存“蒸馏后的知识”和“原始证据”。

5.6 linkstagsfiles

这几张表分别承担:

如果只看实现,gbrain 其实更接近一个“面向 Agent 的知识数据库”,而不是一个单纯的笔记索引器。

六、它为什么能检索得像“记得住”:因为检索不是单一向量召回

“永久记忆”在用户感知层最容易被误解成“它总能记起我说过的话”。

实际上,用户感受到的“记得住”,大多数时候来自检索质量,而不是底层存储本身。

gbrain 在检索层做得相对完整。关键实现集中在:

6.1 全文与向量双通道并行

全文搜索走 PostgreSQL 的 tsvector + ts_rank

向量搜索走 pgvector,并且 content_chunks.embedding 上建了 HNSW 索引:

CREATE INDEX IF NOT EXISTS idx_chunks_embedding ON content_chunks USING hnsw (embedding vector_cosine_ops);

这意味着它不是“全文 fallback 到向量”或者“向量 fallback 到全文”,而是两路都跑,再做融合。

6.2 RRF 融合,而不是粗暴加权

hybrid.ts 的 RRF 逻辑很清楚:

score = 1 / (60 + rank)

每个结果在多个结果列表中出现时,累加这个分值。这样做的好处是:

这其实是工程上很稳的一种融合方式。

6.3 Query expansion 提升召回面

expansion.ts 里会在 query 字数足够时调用 Claude Haiku 生成两条替代表达,再把原 query 和扩展 query 一起做向量检索。

这一步不是必须,但它解决了一个真实问题:用户的问法未必和页面里的措辞一致。概念性的记忆,如果只靠原 query,很容易漏召回。

6.4 四层去重控制噪音

dedup.ts 做了四层压缩:

  1. 每页先保留 top 3 chunks;
  2. 用文本 Jaccard 相似度做近重复去重;
  3. 限制单一 type 占比不超过 60%;
  4. 每页最多保留 2 个 chunk。

这个设计非常实用。因为长期记忆系统常见问题不是“找不到”,而是“同一页、同一类内容刷屏”,最后模型上下文被噪音占满。

换句话说,gbrain 看上去像在做检索,实际上也在做上下文预算管理。

七、它如何把“长期记忆”从内容存储推进到持续生长:关键不在数据库,在 enrich 工作流

如果只看 src/core/*,你会以为 gbrain 就是一个 Markdown + Postgres 检索系统。

但它要成立为“永久记忆”,真正的决定性部分在文档和 skill 里,尤其是 docs/GBRAIN_RECOMMENDED_SCHEMA.mdskills/enrich/SKILL.md 所描述的流程。

核心原则只有一句:

每当系统遇到一个人、公司、会议、邮件、社交信号,就要触发 enrich。

这件事非常重要,因为它把“记忆形成”从一次性导入,改成了持续增量更新。

也就是说,gbrain 不只是把历史知识导进来,然后让模型去查;它要求 Agent 在每个新信号到来时做几件事:

  1. 判断这个实体有没有已有 page;
  2. 没有就创建;
  3. 有但薄,就做更深一层 enrich;
  4. 把新证据写入 timeline;
  5. 把更成熟的判断重写进 compiled truth;
  6. 把原始 API 数据写进 raw_data;
  7. 更新 cross-links。

真正让记忆“永久”的,不是永远保留旧对话,而是让知识结构随现实持续更新,而不是冻结在第一次写入时。

这是 gbrain 最值钱的地方。

很多所谓 Memory 项目,停在“存下来了”。gbrain 的方向是“持续整理、持续重写、持续回填上下文”。

八、它为什么坚持 compiled truth + timeline:因为长期记忆系统最大的敌人是混淆“状态”和“证据”

这是这个仓库里我最认同的设计之一。

长期记忆系统经常会碰到一个问题:

如果你把所有内容都当作一串聊天消息或者一堆段落,模型每次都得自己判断哪条是当前状态,哪条只是历史背景。

gbrain 直接把这件事编码到页面模型中:

这会带来三个工程收益:

8.1 当前状态不需要每次从历史中重新推导

这让查询更稳定,也减少模型误判旧信息为现状。

8.2 历史证据仍然保留,不会因为总结而丢

这保证知识可审计。

8.3 编译层与证据层可以分别更新

系统可以只追加 timeline,也可以在证据累积足够后重写 compiled truth。

这比 append-only 聊天日志成熟很多。因为人在现实世界里形成认知,本来就不是把历史原样背下来,而是不断用新证据改写当前判断。

gbrain 的“永久记忆”本质上是在模拟这种认知机制,只不过把它工程化了。

九、PGLite 与 Supabase 双引擎设计,解决的是记忆的部署连续性

engine.tspostgres-engine.tspglite-engine.tsdocs/ENGINES.md 看,gbrain 把引擎层抽象得很干净。

BrainEngine 接口基本定义了所有核心能力:

背后有两个实现:

  1. PGLiteEngine
  2. PostgresEngine

9.1 PGLite:让记忆系统先活起来

PGLite 的价值不是性能,而是零门槛。

gbrain init 默认就是本地 PGLite,这意味着:

这对“永久记忆”很关键。因为很多长期系统死在第一步:太难启动,于是根本没有持续输入。

9.2 Postgres / Supabase:让它撑住规模化运行

当页面数、设备数、远程访问需求上来后,再切到 Supabase。仓库里甚至直接提供 migrate --to supabase 的迁移路径。

这让系统不是在“玩具原型”和“生产系统”之间断裂,而是在同一个抽象接口下平滑切换。

长期记忆系统如果没有部署连续性,用户早晚会被迁移成本劝退。gbrain 至少在架构上提前考虑了这一点。

十、sync 机制决定了它是不是“活记忆”,而不是“导入后静止的记忆”

src/commands/sync.ts 很值得看。它说明 gbrain 并不是只会做全量 import,而是把 Git repo 当作不断变动的知识源。

核心逻辑:

  1. 记录 sync.last_commit
  2. git diff --name-status -M last..head 生成变更清单;
  3. 按 added / modified / deleted / renamed 分类;
  4. 过滤非 syncable 文件;
  5. 对删除、重命名、修改分别处理;
  6. 更新 sync.last_commit
  7. 写 ingest log。

这里有几个长期记忆层面的关键点:

10.1 它把 Git 历史纳入了知识同步协议

这意味着手工改 Markdown、Agent 改 Markdown、外部脚本改 Markdown,最后都能被同一条 sync 链纳入数据库。

10.2 它支持 rename,不只是重导

rename 这件事很重要,因为知识页的 slug 会随着认知修正而变化。比如公司正式更名、人名去歧义、目录重组。updateSlug 保持 page_id 不变,说明作者在尽量保住知识连续性,而不是删掉旧页新建一页。

10.3 它用 ingest log 把同步本身变成可追踪事件

记忆系统如果不能解释“这页是什么时候、因为什么被更新的”,长期使用会越来越黑盒。

ingest_log 至少给了运维和审计一个基本抓手。

十一、真正的“永久”,不在于不丢,而在于可校正、可迁移、可重建

如果把这套系统拆开看,我认为它对“永久记忆”的实现有三层含义。

11.1 存储层永久:内容不依赖模型上下文

只要文件和数据库在,内容在。

11.2 认知层永久:结论可以随着证据不断重编译

不是把旧内容永远堆上去,而是允许知识页持续演化。

11.3 工程层永久:换引擎、换索引、换模型,知识主体不丢

embedding 模型、搜索融合策略、数据库实现都可以变,但 Markdown 页面与知识组织方式还能延续。

这比“大模型能记住多少轮对话”强得多。

十二、这套实现的真正边界:它并没有在代码里解决“真知识”,而是把正确性压力转移给工作流

说到这里,必须泼一盆冷水。

gbrain 很强的地方,在于它把记忆工程结构搭得很清楚;它的弱点也恰恰在这里:很多关键正确性不在核心代码里,而在 Skill 和 Agent 行为里。

具体来说:

12.1 compiled truth 的质量,不由数据库保证

数据库只负责存。至于 compiled_truth 写得准不准、有没有过度总结、有没有把一条低质量信号误写成稳定判断,这不是代码层能兜住的。

12.2 enrich 是否触发,依赖外围工作流是否真接上

文档里写得很漂亮:会议、邮件、社交都要 call enrich。但如果实际部署里某条 ingest pipeline 没有接这个步骤,记忆增长就会断。

12.3 raw_data 存在,不等于 compiled truth 有证据链约束

系统提供了保存原始数据的能力,但当前 schema 并没有把 compiled truth 的每条断言严格绑定到 source span。它更像是“留了证据”,而不是“强制证据驱动的知识编译器”。

12.4 timeline 文本与 timeline_entries 双轨并存,会有一致性治理问题

现在 schema 里同时有:

这对兼容性有好处,但长期来看会带来双写一致性问题。README 里谈的是理想结构,代码里目前还是一种折中实现。

所以如果你问我:gbrain 真的已经把“永久记忆”解决了吗?

我的判断是:

它解决了 70% 的基础设施问题,剩下 30% 是知识治理问题。

而后者恰好更难。

十三、如果把它抽象成一套方法论,它实际上做了五件正确的事

13.1 把记忆对象从“消息”升级成“页面”

消息适合记录交互,页面适合维护长期知识。

13.2 把页面拆成“当前判断”和“历史证据”

这让知识更新不再混乱。

13.3 把记忆存储从“黑箱数据库”变成“Markdown + DB 双层”

这让系统可读、可改、可迁移。

13.4 把检索从“单一向量召回”升级成“全文 + 向量 + 扩展 + 融合 + 去重”

这直接提升“像记得住”的感知。

13.5 把更新从“手工维护”改成“工作流驱动的持续 enrich”

这一步最容易被忽略,也最决定系统寿命。

十四、站在 Agent 系统角度看,gbrain 的真正贡献不是 Memory,而是把 Agent 从一次性问答机改造成长期知识操盘手

如果把这个仓库放回 Agent 架构里看,你会发现它真正重塑的不是存储,而是 Agent 的行为顺序。

传统 Agent:

  1. 收到问题;
  2. 查外部资料;
  3. 组织回答;
  4. 结束。

带 gbrain 的 Agent:

  1. 收到问题;
  2. 先查脑;
  3. 再补外部;
  4. 回答;
  5. 把新知识写回脑;
  6. 让未来问题更便宜。

这里最大的变化是,每次回答不再只是消费上下文,也在生产未来上下文。

这就是为什么作者强调“compound”。

从实现角度看,复利不是什么高级模型能力,而是下面这组工程动作持续发生:

“永久记忆”在这里不再是一个静态存档概念,而是一个不断降低未来认知成本的系统。

十五、最终判断:gbrain 的“永久记忆”本质上是一套知识编译系统

如果必须用一句最准确的话给这个项目下定义,我不会说它是“有长期记忆的 Agent 插件”,我会说:

它是一套把人类世界持续编译成 Agent 可检索知识状态的系统。

它的实现原理可以收束为下面这五层:

  1. 知识协议层:Markdown + frontmatter + compiled truth / timeline 双层结构。
  2. 索引层:pages、content_chunks、timeline_entries、raw_data、page_versions、links、tags。
  3. 检索层:全文搜索、向量搜索、query expansion、RRF 融合、四层 dedup。
  4. 同步层:content hash 幂等、git diff 增量同步、rename continuity、ingest log。
  5. 增长层:Skill 驱动的 enrich、cron 周期输入、Agent 的 read-before-answer / write-after-learn 纪律。

如果这五层都跑起来,它就能接近作者说的“永久记忆”。

如果只跑前两层,它只是一个不错的 Markdown RAG。

如果只有第五层没有前面的结构,它又会退化成脏乱的自动笔记系统。

所以它的真正实现原理不是某个算法,而是一个完整的分层闭环。

这也是我看完代码后的最终结论:

gbrain 最有价值的地方,不在“把记忆存进去”,而在“把长期知识更新这件事工程化”。这一步一旦做对,Agent 才有可能从会聊天的工具,变成真正有连续性的长期助手。

参考实现位置

一句话收尾

它没有发明记忆。

它做对的是:把“记忆应该怎样被写入、修正、检索、迁移和持续增长”这件事,做成了一套能落地的工程系统。

目录 最新
← 左侧翻上一屏 · 右侧翻下一屏 · 中间唤出菜单