
摘要
gbrain 这套系统最值得分析的地方,不是“它能存记忆”,而是它把“记忆”拆成了三层彼此配合的工程结构:可读的 Markdown 脑仓、可检索的 Postgres 索引层、可持续写入的 Agent 工作流。这三层缺一不可。
很多项目把“长期记忆”理解成向量库:把历史内容切块、嵌入、召回,然后让模型在回答时带上几段相关上下文。gbrain 不是这一路。它真正试图做的是:把人的世界抽成一套长期维护的知识底座,并让 Agent 在每次遇到新信号时都去更新它。也就是说,它追求的不是“把过去存起来”,而是“把过去编译成当前最可信的知识状态”。
这正是它口中的“永久记忆”真正成立的地方。永久,不是因为数据库永远不删;记忆,也不是因为模型记住了什么。它成立于四个条件:
- 源数据可回放:Markdown、原始 JSON、Timeline、版本快照都在。
- 知识可重写:新证据到来时,
compiled_truth会更新,而不是只追加。 - 检索可持续:全文、向量、模糊匹配、RRF 融合是一条稳定链路。
- 写入可自动发生:会议、邮件、社交、联系人这些信号会驱动 enrich 流程,而不是等人手工维护。
如果只看代码实现,gbrain 的“永久记忆”并没有什么神秘算法。它的价值主要来自两件事:
- 第一,它把记忆从“对话上下文缓存”提升成“可审计、可迁移、可重建的知识系统”;
- 第二,它把 Agent 的能力边界重新定义为:先查脑,再回答;学到新东西,再写回脑。
这份白皮书只讨论它怎么实现,不讨论它是什么产品。
一、先说结论:GBrain 的“永久记忆”并不是一个功能,而是一条闭环
gbrain 的实现核心可以压缩成一句话:
Markdown 作为人类可编辑的知识源,Postgres 作为机器可检索的索引与一致性层,Agent/Skill/Cron 作为持续写入和修正这套知识的执行系统。
从仓库代码和文档看,这个闭环长这样:
- 外部世界产生信号:会议、邮件、推文、联系人、文档、人工对话。
- Agent 通过 Skill 判断哪些实体值得入脑。
- 内容被写成标准 Markdown 页面,分成
compiled_truth和timeline两层。 gbrain import或gbrain sync读取这些文件,解析 frontmatter、正文、时间线。- 系统为页面生成 content hash,判断是否真正变更。
- 变更内容被切块,必要时生成 embedding,写入
pages、content_chunks、tags、page_versions、raw_data等表。 - 查询时,系统同时跑全文搜索与向量搜索,再用 RRF 做结果融合,并通过多层去重压缩噪音。
- Agent 拿到检索结果后回答问题;如果对话里又产生了新信息,它会再次写回 Markdown 或通过 operation 写回数据库。
sync/ cron 再把新增修改纳入索引。
这不是“记住一次”模型,而是“持续再编译”模型。
也正因为这样,gbrain 的永久记忆并不依赖某个超强模型的上下文窗口。模型只是两个环节里的可替换部件:
- 做 embedding;
- 在部分高级链路里做 query expansion 或 LLM-guided chunking。
真正的记忆载体,是文件系统加数据库。
二、它先把“记忆”定义成一种页面结构,而不是一堆向量
这套系统最重要的设计,不在 embedding.ts,而在知识模型本身。
README 和 docs/GBRAIN_RECOMMENDED_SCHEMA.md 把页面统一成一个非常明确的二层结构:
- Above the line:compiled truth
- Below the line:timeline
从 src/core/markdown.ts 可以看到,这个结构不是文档约定而已,而是实际解析协议。parseMarkdown() 会:
- 先用
gray-matter解析 YAML frontmatter; - 再在正文里找第一个独立的
---; - 将前半部分视为
compiled_truth; - 将后半部分视为
timeline。
这意味着,gbrain 不是把整页文本粗暴视为同一种知识,而是先在文件协议层把两类信息分开:
compiled_truth:当前综合判断,允许重写;timeline:证据流与事件流,原则上追加不覆写。
这一步很关键。
传统聊天式记忆系统常见的问题是:所有历史消息都只是“过去发生过什么”的序列。模型每次都要从原始堆积里重新理解“现在到底是什么状态”。gbrain 不这样做。它要求你把“当前结论”单独维护出来,把“历史证据”单独保留下来。
换句话说,它不是让模型每次 query 时重新推导真相,而是把真相预先编译成页面上半部分。
这就是它能撑起“永久记忆”的第一根柱子:记忆先被结构化成了可长期维护的知识页面。
三、永久性的第一层:Markdown 作为长期源头,而不是缓存副本
gbrain 反复强调 repo 是 system of record。这个判断非常工程化。
原因很简单:
- Markdown 对人类可读;
- 可以直接用 Git 管;
- 脱离
gbrain本身也还能活; - 可以批量编辑、回滚、迁移;
- 对 Agent 来说,文件就是最低耦合的写入目标。
这和很多“记忆产品”的做法不同。很多系统的事实源只存在于某个私有向量库或专有存储格式里。那种存储可以检索,但很难维护,也难做人工纠偏。
gbrain 反过来:数据库只是索引层,Markdown 才是内容层。README 的架构图也写得很直白:
- Brain Repo(git)= source of truth
- GBrain = retrieval layer
- AI Agent = read/write actor
从这个角度看,“永久记忆”的“永久”首先来自不绑定单一 runtime。
即使数据库损坏,只要 Markdown 还在,系统就能重新 import、重新 chunk、重新 embed、重新建索引。只要 repo 还在,记忆就能再生。
这是比“数据库里还留着记录”更强的永久性。
四、永久性的第二层:导入链路不是 append-only,而是带幂等和版本化的再编译
记忆系统真正容易烂掉的地方,不在第一次写入,而在后续迭代。
gbrain 在 src/core/import-file.ts 里做了几件很重要的事。
4.1 内容哈希保证导入幂等
importFromContent() 会把这些字段一起算 SHA-256:
- title
- type
- compiled_truth
- timeline
- frontmatter
- tags
如果现有页面的 content_hash 和新 hash 一样,就直接 skipped。
这意味着系统不是看到文件就重建一遍 embedding,而是先判断知识状态是否真的变了。这解决了两个问题:
- 避免反复重复索引;
- 让
sync能安全地周期运行。
“永久记忆”如果没有幂等,最后就会变成“永久重复写入”。这个项目至少在内容层避开了这个坑。
4.2 写入前先快照旧版
如果页面已存在,事务里第一步是 createVersion(slug)。
也就是说,页面不是被直接覆盖,而是会把旧的 compiled_truth 和 frontmatter 先存进 page_versions。这张表在 schema 里明确存在。
这件事的价值不只是回滚,更重要的是给“长期记忆修正”留下历史切面。因为长期知识一定会变:
- 人会换职位;
- 公司会转方向;
- 旧判断会失效;
- 同一事实会被更可靠来源推翻。
如果没有版本层,所谓永久记忆最后只会变成“当前状态的脆弱覆盖”。有了版本层,它才更接近“可追溯的长期认知演化”。
4.3 事务内同时更新页面、标签、chunks
importFromContent() 的事务边界也很清楚:
putPage- tag reconciliation
upsertChunks
这些一起提交。这样页面正文、标签和 chunk 索引不会出现一半更新、一半没更新的状态。
长期记忆系统最怕的就是索引和内容不一致。你改了文章,但召回的还是旧 chunk;你改了标签,但页面没更新;你删了内容,向量还在。gbrain 至少在导入事务这一层有明确防御。
五、数据库层真正存的是什么:它不是“聊天历史”,而是知识图谱的基础砖块
从 src/core/schema-embedded.ts 可以很清楚看到,这套系统的核心表不是消息表,而是知识页与检索表。
最关键的有这些:
5.1 pages
这是核心内容表,字段包括:
slugtypetitlecompiled_truthtimelinefrontmatter(JSONB)content_hashcreated_at/updated_atsearch_vector
这张表的意义很清楚:它把“当前知识页面”变成了数据库的一等对象。
5.2 content_chunks
这里保存页面切块后的内容:
page_idchunk_indexchunk_textchunk_source(compiled_truth 或 timeline)embedding(1536 维)modeltoken_countembedded_at
注意这里把 compiled_truth 和 timeline 的 chunk source 区分开了。这个细节很重要,因为它允许检索结果里保留“这是当前判断还是历史证据”的语义来源。
5.3 timeline_entries
虽然页面里已经有一个 timeline 文本字段,它还额外做了一张结构化时间线表:
datesourcesummarydetail
这让 timeline 不只是原文的一部分,也能被结构化查询和索引。
5.4 page_versions
用来保存知识快照。
5.5 raw_data
这个很关键。它专门保存外部 API 的原始响应,字段有:
page_idsourcedata(JSONB)fetched_at
这张表让系统能同时保存“蒸馏后的知识”和“原始证据”。
5.6 links、tags、files
这几张表分别承担:
- typed edge 关系
- 分类和过滤
- 二进制附件与存储映射
如果只看实现,gbrain 其实更接近一个“面向 Agent 的知识数据库”,而不是一个单纯的笔记索引器。
六、它为什么能检索得像“记得住”:因为检索不是单一向量召回
“永久记忆”在用户感知层最容易被误解成“它总能记起我说过的话”。
实际上,用户感受到的“记得住”,大多数时候来自检索质量,而不是底层存储本身。
gbrain 在检索层做得相对完整。关键实现集中在:
src/core/search/hybrid.tssrc/core/search/dedup.tssrc/core/search/expansion.ts- 两个 engine 内部的
searchKeyword/searchVector
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)
每个结果在多个结果列表中出现时,累加这个分值。这样做的好处是:
- 不需要把
ts_rank和 cosine score 强行归一化到一个尺度; - 只关心排序位置,不关心不同检索器分数定义;
- 一条内容如果在多种检索渠道里都靠前,会自然冒出来。
这其实是工程上很稳的一种融合方式。
6.3 Query expansion 提升召回面
expansion.ts 里会在 query 字数足够时调用 Claude Haiku 生成两条替代表达,再把原 query 和扩展 query 一起做向量检索。
这一步不是必须,但它解决了一个真实问题:用户的问法未必和页面里的措辞一致。概念性的记忆,如果只靠原 query,很容易漏召回。
6.4 四层去重控制噪音
dedup.ts 做了四层压缩:
- 每页先保留 top 3 chunks;
- 用文本 Jaccard 相似度做近重复去重;
- 限制单一 type 占比不超过 60%;
- 每页最多保留 2 个 chunk。
这个设计非常实用。因为长期记忆系统常见问题不是“找不到”,而是“同一页、同一类内容刷屏”,最后模型上下文被噪音占满。
换句话说,gbrain 看上去像在做检索,实际上也在做上下文预算管理。
七、它如何把“长期记忆”从内容存储推进到持续生长:关键不在数据库,在 enrich 工作流
如果只看 src/core/*,你会以为 gbrain 就是一个 Markdown + Postgres 检索系统。
但它要成立为“永久记忆”,真正的决定性部分在文档和 skill 里,尤其是 docs/GBRAIN_RECOMMENDED_SCHEMA.md 和 skills/enrich/SKILL.md 所描述的流程。
核心原则只有一句:
每当系统遇到一个人、公司、会议、邮件、社交信号,就要触发 enrich。
这件事非常重要,因为它把“记忆形成”从一次性导入,改成了持续增量更新。
也就是说,gbrain 不只是把历史知识导进来,然后让模型去查;它要求 Agent 在每个新信号到来时做几件事:
- 判断这个实体有没有已有 page;
- 没有就创建;
- 有但薄,就做更深一层 enrich;
- 把新证据写入 timeline;
- 把更成熟的判断重写进 compiled truth;
- 把原始 API 数据写进 raw_data;
- 更新 cross-links。
真正让记忆“永久”的,不是永远保留旧对话,而是让知识结构随现实持续更新,而不是冻结在第一次写入时。
这是 gbrain 最值钱的地方。
很多所谓 Memory 项目,停在“存下来了”。gbrain 的方向是“持续整理、持续重写、持续回填上下文”。
八、它为什么坚持 compiled truth + timeline:因为长期记忆系统最大的敌人是混淆“状态”和“证据”
这是这个仓库里我最认同的设计之一。
长期记忆系统经常会碰到一个问题:
- 你知道某人去年是 CTO;
- 今年 TA 变成了顾问;
- 期间有两次公开发言表达过不同态度;
- 用户问“他现在是谁、在做什么、之前发生过什么”。
如果你把所有内容都当作一串聊天消息或者一堆段落,模型每次都得自己判断哪条是当前状态,哪条只是历史背景。
gbrain 直接把这件事编码到页面模型中:
compiled_truth负责“现在怎么看”;timeline负责“事情怎么一路走过来”。
这会带来三个工程收益:
8.1 当前状态不需要每次从历史中重新推导
这让查询更稳定,也减少模型误判旧信息为现状。
8.2 历史证据仍然保留,不会因为总结而丢
这保证知识可审计。
8.3 编译层与证据层可以分别更新
系统可以只追加 timeline,也可以在证据累积足够后重写 compiled truth。
这比 append-only 聊天日志成熟很多。因为人在现实世界里形成认知,本来就不是把历史原样背下来,而是不断用新证据改写当前判断。
gbrain 的“永久记忆”本质上是在模拟这种认知机制,只不过把它工程化了。
九、PGLite 与 Supabase 双引擎设计,解决的是记忆的部署连续性
从 engine.ts、postgres-engine.ts、pglite-engine.ts 和 docs/ENGINES.md 看,gbrain 把引擎层抽象得很干净。
BrainEngine 接口基本定义了所有核心能力:
- CRUD
- keyword / vector search
- chunk 管理
- links / tags / timeline / raw_data / versions
- stats / health
- config
- migration support
背后有两个实现:
PGLiteEnginePostgresEngine
9.1 PGLite:让记忆系统先活起来
PGLite 的价值不是性能,而是零门槛。
gbrain init 默认就是本地 PGLite,这意味着:
- 不需要先买 Supabase;
- 不需要先配 Docker;
- 不需要先搞远程 Postgres;
- 几乎立刻就能起一个本地脑。
这对“永久记忆”很关键。因为很多长期系统死在第一步:太难启动,于是根本没有持续输入。
9.2 Postgres / Supabase:让它撑住规模化运行
当页面数、设备数、远程访问需求上来后,再切到 Supabase。仓库里甚至直接提供 migrate --to supabase 的迁移路径。
这让系统不是在“玩具原型”和“生产系统”之间断裂,而是在同一个抽象接口下平滑切换。
长期记忆系统如果没有部署连续性,用户早晚会被迁移成本劝退。gbrain 至少在架构上提前考虑了这一点。
十、sync 机制决定了它是不是“活记忆”,而不是“导入后静止的记忆”
src/commands/sync.ts 很值得看。它说明 gbrain 并不是只会做全量 import,而是把 Git repo 当作不断变动的知识源。
核心逻辑:
- 记录
sync.last_commit; - 用
git diff --name-status -M last..head生成变更清单; - 按 added / modified / deleted / renamed 分类;
- 过滤非 syncable 文件;
- 对删除、重命名、修改分别处理;
- 更新
sync.last_commit; - 写 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 里同时有:
pages.timeline文本字段timeline_entries结构化表
这对兼容性有好处,但长期来看会带来双写一致性问题。README 里谈的是理想结构,代码里目前还是一种折中实现。
所以如果你问我:gbrain 真的已经把“永久记忆”解决了吗?
我的判断是:
它解决了 70% 的基础设施问题,剩下 30% 是知识治理问题。
而后者恰好更难。
十三、如果把它抽象成一套方法论,它实际上做了五件正确的事
13.1 把记忆对象从“消息”升级成“页面”
消息适合记录交互,页面适合维护长期知识。
13.2 把页面拆成“当前判断”和“历史证据”
这让知识更新不再混乱。
13.3 把记忆存储从“黑箱数据库”变成“Markdown + DB 双层”
这让系统可读、可改、可迁移。
13.4 把检索从“单一向量召回”升级成“全文 + 向量 + 扩展 + 融合 + 去重”
这直接提升“像记得住”的感知。
13.5 把更新从“手工维护”改成“工作流驱动的持续 enrich”
这一步最容易被忽略,也最决定系统寿命。
十四、站在 Agent 系统角度看,gbrain 的真正贡献不是 Memory,而是把 Agent 从一次性问答机改造成长期知识操盘手
如果把这个仓库放回 Agent 架构里看,你会发现它真正重塑的不是存储,而是 Agent 的行为顺序。
传统 Agent:
- 收到问题;
- 查外部资料;
- 组织回答;
- 结束。
带 gbrain 的 Agent:
- 收到问题;
- 先查脑;
- 再补外部;
- 回答;
- 把新知识写回脑;
- 让未来问题更便宜。
这里最大的变化是,每次回答不再只是消费上下文,也在生产未来上下文。
这就是为什么作者强调“compound”。
从实现角度看,复利不是什么高级模型能力,而是下面这组工程动作持续发生:
- 新信号进入;
- 实体被识别;
- 页被创建或更新;
- 索引刷新;
- 下次 query 命中更准。
“永久记忆”在这里不再是一个静态存档概念,而是一个不断降低未来认知成本的系统。
十五、最终判断:gbrain 的“永久记忆”本质上是一套知识编译系统
如果必须用一句最准确的话给这个项目下定义,我不会说它是“有长期记忆的 Agent 插件”,我会说:
它是一套把人类世界持续编译成 Agent 可检索知识状态的系统。
它的实现原理可以收束为下面这五层:
- 知识协议层:Markdown + frontmatter + compiled truth / timeline 双层结构。
- 索引层:pages、content_chunks、timeline_entries、raw_data、page_versions、links、tags。
- 检索层:全文搜索、向量搜索、query expansion、RRF 融合、四层 dedup。
- 同步层:content hash 幂等、git diff 增量同步、rename continuity、ingest log。
- 增长层:Skill 驱动的 enrich、cron 周期输入、Agent 的 read-before-answer / write-after-learn 纪律。
如果这五层都跑起来,它就能接近作者说的“永久记忆”。
如果只跑前两层,它只是一个不错的 Markdown RAG。
如果只有第五层没有前面的结构,它又会退化成脏乱的自动笔记系统。
所以它的真正实现原理不是某个算法,而是一个完整的分层闭环。
这也是我看完代码后的最终结论:
gbrain 最有价值的地方,不在“把记忆存进去”,而在“把长期知识更新这件事工程化”。这一步一旦做对,Agent 才有可能从会聊天的工具,变成真正有连续性的长期助手。
参考实现位置
- 核心入口:
README.md - 知识结构:
docs/GBRAIN_RECOMMENDED_SCHEMA.md - 基础设施说明:
docs/architecture/infra-layer.md - 引擎抽象:
src/core/engine.ts - Markdown 解析:
src/core/markdown.ts - 导入链路:
src/core/import-file.ts - 增量同步:
src/commands/sync.ts - 混合检索:
src/core/search/hybrid.ts - 去重逻辑:
src/core/search/dedup.ts - Query expansion:
src/core/search/expansion.ts - Embedding:
src/core/embedding.ts - Postgres 实现:
src/core/postgres-engine.ts - PGLite 实现:
src/core/pglite-engine.ts - Schema:
src/core/schema-embedded.ts
一句话收尾
它没有发明记忆。
它做对的是:把“记忆应该怎样被写入、修正、检索、迁移和持续增长”这件事,做成了一套能落地的工程系统。