我把AI的“脑子“从Excel改成了知识图谱,token省了71%
我把AI的"脑子"从Excel改成了知识图谱,token省了71%
大家好,我是LameGz。这周我做了一件很酷的事——给AI Agent造了一个"外接大脑"。不是向量数据库,不是RAG,是一个用Markdown文件拼出来的图记忆系统。
这篇博客不讲代码,讲我为什么要做、踩了什么坑、怎么想的。如果你也在折腾LLM的上下文管理,应该会共鸣。
一、先吐槽:AI的记性为什么这么差?
你们有没有发现,现在的AI(GPT-4、Claude、DeepSeek)有个诡异的矛盾:
- 理论上:能读几十万字的上下文
- 实际上:你让它看10页文档,问到第8页的细节,它就开始胡编了
这不是我瞎说,有论文的(Liu et al., 2023):LLM的注意力在远距离token上会显著退化。换句话说,你塞给它的信息越多,它越记不住后面的东西。
更要命的是信息噪音。假设你在做一个电商项目:
你问AI:"帮我改一下登录按钮的颜色"
AI实际读到的:
├─ 前端组件库文档(5页)
├─ 数据库表结构(8页) ← 关我屁事?
├─ 支付接口规范(12页) ← 改按钮需要看这个?
├─ 用户鉴权逻辑(6页) ← ???
├─ 部署配置(3页) ← ???
└─ ...
结果:2万token没了,AI说"我没找到登录按钮"
这就是扁平记忆的诅咒——你把所有东西塞进一个文件(或者一股脑塞给AI),AI读的时候不分青红皂白,全塞脑子里。更长的窗口≠更好的利用。
我受够了。我要让AI只读它需要的那部分。
二、我的解决方案:把知识切成块,用线连起来
2.1 核心思想
想象你的项目知识是一本百科全书:
- 扁平记忆:全书只有一章,所有内容从头到尾写在一起。你想查"登录",得从头翻到尾。
- 图记忆:书有目录、有章节、有交叉引用。你想查"登录",先看目录找到"用户系统"那一章,如果那一章提到"登录依赖鉴权模块",你再翻到"鉴权"那一章。
这就是Synapse的核心设计——图拓扑分区记忆。
┌─────────────────────────────────────────┐
│ MEMORY_MAP.md │ ← 目录(200字)
│ "项目有5个模块:用户、支付、商品、订单、后台" │
└──────────────────┬──────────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ 用户模块 │ │ 支付模块 │ │ 商品模块 │ ← 章节(各500字)
│ mod_user│ │mod_pay │ │mod_item│
└────┬───┘ └───┬────┘ └────────┘
│ │
▼ ▼
┌────────┐ ┌────────┐
│登录功能 │ │退款功能 │ ← 子章节(各300字)
│feat_login│ │feat_refund│
└────────┘ └────────┘
每个模块是一个Markdown文件(我叫它"节点"),文件开头用YAML写元数据:
---
id: mod_auth-api
type: module
summary: "用户鉴权模块,JWT+RS256"
depends_on:
- meta/mod_project.md
tags: [auth, security, api]
aliases: [login, signin, session]
---
depends_on就是那条线——“我依赖谁”。系统会自动反向计算:“谁依赖我”,生成blocks字段。
2.2 AI怎么读?
我设计了一个三层渐进式披露协议:
| 层级 | 读什么 | 多少token | 目的 |
|---|---|---|---|
| Layer 1 | MEMORY_MAP.md 摘要 | ~200 | “哪个模块可能相关?” |
| Layer 2 | 目标节点的summary + Current State | ~400-800 | “这个模块现在什么情况?” |
| Layer 3 | BFS遍历依赖节点(depth≤2, width≤5) | ~200-600/节点 | “改这个会影响到谁?” |
关键规则:AI不能跳过Layer 1直接读Layer 2。就像你不能翻开书的第300页之前,得先知道第300页属于哪一章。
实测结果:相比把全部文档塞给AI(~2,600 tokens),Synapse平均只加载750 tokens,省了71%。
三、v0.1:能跑,但全靠AI自觉
3.1 第一个版本长什么样
最开始,Synapse特别简单:
- 手动创建
meta/*.md文件 - 手动写
depends_on - 手动运行脚本生成
MEMORY_MAP.md - 然后祈祷AI遵守"先读目录再读章节"的协议
对,祈祷。因为没有任何强制手段,AI想怎么读就怎么读。我觉得:“反正我在SKILL.md里写了协议,AI应该会遵守吧?”
3.2 现实给我上了一课
我让一个friend试用,结果他跟我说:“我让它改个API,它把全部8个模块文档都读了一遍,token烧了几千,最后还没改对地方。”
我检查日志,发现:
- AI确实读了MEMORY_MAP.md
- 然后它"为了保险起见",把全部模块都读了
- 协议形同虚设
这就好比你给小学生一本课本,说"先读目录再找章节",但他直接把整本书背下来了——不是他不懂规则,是没有约束的情况下,人(和AI)会选择最省脑力的路径。
v0.1的教训:协议写在纸上没用,必须有人盯着。
四、v0.2:给系统装上"摄像头"
4.1 第一个钩子:post-tool-use
我想到一个办法:在AI写完文档后,自动检查它写的东西对不对。
AI:我修改了meta/mod_auth-api.md
系统(自动执行):
├─ frontmatter有id吗?
├─ depends_on指向的文件存在吗?
├─ updated日期更新了吗?
└─ 没问题?好的,通过。
这就是post-tool-use.sh——AI每改一次文档,系统自动做一次格式校验。
4.2 第二个钩子:session-end
但还有个问题:AI可能读了一堆文件,改了其中几个, session结束时啥也不说就走了。索引没更新,其他模块的blocks关系还是旧的。
于是加了session-end.sh:
会话结束(自动执行):
├─ 重新生成MEMORY_MAP.md
├─ 检查有没有"死链接"(depends_on指向不存在的文件)
├─ 检查有没有"孤儿节点"(没被任何模块依赖的孤立文件)
└─ 输出本次改了什么、建议下一步做什么
4.3 但还是有漏洞
这两个钩子都是事后检查:
- post-tool-use:改完了才检查
- session-end:会话结束了才处理
事前没有任何拦截。 AI在"读"的阶段可以随便读,在"改"的阶段可以随便改,系统只是事后说一句"你刚才那样不太好"。
就像路上的摄像头——它能拍到你闯红灯,但它拦不住你闯红灯。
v0.2的教训:事后审计不如事前拦截。
五、v0.3:从"摄像头"升级到"门卫"
这是本次更新的重头戏。我新增了4个钩子中的前两个——在AI行动之前就拦住它。
5.1 pre-read-check:AI想读文档?先问我
AI:我要读meta/mod_auth-api.md
系统(拦截):
├─ 你读过MEMORY_MAP.md了吗?
│ └─ 没有?→ 警告:"请先读目录索引!"
│
└─ 你今天已经读了多少个节点了?
└─ 超过5个?→ 警告:"BFS预算超限!"
这个钩子的注册方式特别巧妙。Claude Code有一个PreToolUse机制:在AI执行任何工具(如Read、Write)之前,先运行一个外部脚本。我把pre-read-check.sh注册成PreToolUse钩子,每次AI想读文件前,系统先检查规则。
实现细节(感兴趣可以看):
# 用.marker文件记录"是否已读目录"
# AI读MEMORY_MAP.md时,touch .claude/.synapse_cache/.map_read
# AI读其他文档时,检查这个文件是否存在
5.2 pre-modify-check:AI想改代码?先告诉它影响面
这个更酷。假设AI要改src/auth/login.ts,系统在AI动笔之前,自动查:
哪些记忆节点引用了src/auth/login.ts?
├─ feat_checkout.md(结账功能依赖登录接口)
├─ feat_user-profile.md(用户资料页依赖登录状态)
└─ feat_admin-panel.md(后台管理依赖登录鉴权)
→ 警告AI:"你改这个文件,会影响到3个下游功能!"
这不是阻断,是提醒。 AI仍然可以改,但它现在知道"改这个有风险",会主动去检查下游模块的接口契约。
这个功能依赖MEMORY_MAP.json里的blocks字段(自动计算的反向依赖)。但这里我踩了一个大坑——我最初从前置元数据读blocks,但blocks根本不在那里,导致这个功能刚开始一直是空的。后来改成从JSON文件实时查询,才解决。(见CHANGELOG C3)
5.3 为什么这很重要?对比学术方案
我读了一堆论文(10篇,从MemGPT到Kumiho),发现一个现象:
| 系统 | 怎么做协议执行 | 效果 |
|---|---|---|
| MemGPT | 靠AI自己调用function | AI可能忘 |
| Zep | 靠向量检索引导 | 不可预测 |
| GAM | 靠多因素加权 | 复杂难调 |
| A-MEM | 靠AI自律 | 可能违规 |
| Synapse | Hook强制拦截 | 确定性强 |
学术界大多把协议写在prompt里,指望AI"自觉遵守"。但我发现工程上不能依赖自律——尤其是当你不在旁边盯着的时候。
Synapse的做法是基础设施层强制:不是"请你遵守",而是"你必须通过检查才能继续"。这和操作系统的系统调用拦截是一个思想。
六、v0.3的其他改进:让系统更好用
6.1 一键安装:从30分钟到2分钟
以前新用户要:
- 手动创建目录
- 手动拷贝脚本
- 手动创建第一个节点
- 手动注册hooks
- 手动生成索引
现在:
bash .claude/skills/synapse-graph-memory/scripts/init.sh
一键搞定。它能自动识别你的技术栈(看到package.json就知道是Node,看到go.mod就知道是Go),自动推断模块边界(看到src/api就创建api模块文档),自动安装4个钩子。
6.2 标签别名:AI说"登录"也能找到"auth"
以前如果节点的标签是auth,但用户问"登录功能怎么样",AI搜索"登录"搜不到——因为标签里没有"登录"这个词。
现在每个节点可以加aliases:
aliases: [login, signin, session, 登录, 登入]
搜索"登录"→匹配到auth节点。灵感来自A-MEM的Zettelkasten方法,但实现上简单得多——纯字符串匹配,不需要embedding。
6.3 Change Log时间索引:回答"上周改了什么"
以前节点的Change Log是扁平的:
- 2026-05-01 加了登录
- 2026-05-03 改了支付
现在系统会自动按月分桶建立索引:
## Change Log Index
### 2026-05
- 2026-05-05 feat_checkout: Added checkout flow
- 2026-05-03 mod_payment: Currency param refactor
- 2026-05-01 mod_auth-api: Switched to RS256
这样AI回答"5月份改了什么"时,不需要读全部节点,只看索引就行。灵感来自Zep的时序知识图谱,但简化为文件索引。
6.4 Filtered BFS:复合查询不再傻搜
用户问:“上周支付模块的鉴权改了什么”
这不是单维度查询,包含:
- 时间:上周
- 领域:支付
- 子领域:鉴权
- 动作:改了什么
v0.3会把查询拆成四个维度,同时命中才加载。不再像v0.2那样把整个"支付"子图全读一遍。
七、和学术界大佬的对比反思
读了10篇论文后,我发现Synapse的定位很清晰:不做最强,做最适合中小项目的。
我们的优势(别人没有)
1. 零外部依赖
所有系统(Mem0、Zep、Kumiho、GAM)都需要:
- 向量数据库(Pinecone/Milvus)
- 图数据库(Neo4j)
- 或者至少一个Python服务
Synapse只需要:
- Markdown文件
- bash脚本
部署成本趋近于零。适合不想引入复杂infra的小团队。
2. 成本可见性
每个节点标注了token估算:
mod_auth-api (~280 tok)
feat_checkout (~340 tok)
AI在每一步都知道"如果我读这个,要消耗多少token"。这就像购物时看到价格标签——现有系统大多没有这个价格标签。
3. 确定性遍历
现有系统最终都依赖向量相似度做入口检索(“找和查询最像的文档”)。这在开放域对话好用,但在项目开发场景有问题:
"POST /api/v1/auth/login"和"GET /api/v1/auth/session"语义上很像,但API契约完全不同。向量相似度会混淆它们,但标签索引不会。
Synapse完全不用向量,用标签→别名→关键词→停止的确定性链条。在结构化项目知识场景,这是优势。
我们的劣势(需要补课)
1. 没有自动图构建
A-MEM能自动发现"这两个记忆相关"并建立链接。Synapse只能靠人手工写depends_on,或者运行suggest_edges.sh做半自动建议。
2. 没有时序推理
Zep能回答"上周二下午3点发生了什么"。Synapse只能回答"5月份改了什么"——粒度粗很多。
3. 没有形式化保证
Kumiho基于AGM信念修正公理,有数学保证。Synapse基于工程直觉,“感觉这样是对的”。
但这些是设计取舍,不是技术缺陷。如果场景是"中小项目的模块知识管理",简单就是优势。
八、踩坑总结:3天修16个bug我学到了什么
这次更新修了16个问题(4个Critical+6个Important+6个Minor)。几个印象深刻的教训:
教训1:最危险的bug是"静默失效"
settings.template.json格式错了→Claude Code静默忽略→用户以为hooks装好了→实际上全部失效。
以后每个功能都要验证"它真的在工作",而不是"没报错就算过了"。
教训2:自动生成的字段,绝不能手动维护
blocks是自动计算的,但我一开始从前置元数据读它,结果永远是空的。后来改成从JSON实时查询才解决。
single source of truth,说起来简单,做的时候要时刻警惕。
教训3:简单≠简陋
学术界用各种高级技术(向量、GNN、RL),我们用Markdown+bash。但在正确场景下,简单是优势。
关键是:简单的同时,核心功能必须可靠。
教训4:文档即产品
v0.2加了6个新功能,但README里一个字没提。用户看完文档,以为自己用的是旧版本。
用户不知道的功能等于没有。
九、写在最后
从v0.1到v0.3,Synapse的核心变化不是"加了什么功能",而是"从靠AI自觉,变成靠系统强制"。
- v0.1:写在纸上的协议, praying it works
- v0.2:事后审计,“你刚才闯红灯了”
- v0.3:事前拦截,“红灯亮了,请停车”
这让我想起操作系统的演进:
- 早期程序直接操作硬件
- 后来有了操作系统做资源管理
- 现在Synapse在做LLM Agent的"上下文操作系统"
Context is currency. 我们不仅省了token,更重要的是让AI知道自己在花什么钱。
代码在 GitHub,欢迎试用、提issue、或者告诉我哪里还能改进。
我们下篇博客见 👋
PS:如果你也在折腾LLM的上下文管理,遇到过"AI读了太多无关信息"的问题,欢迎在评论区交流。说不定你的场景正是Synapse下一个要支持的 😄
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)