如果你用过 Claude Code、Cursor、Windsurf 这类 AI 编程助手,一定遇到过这个场景:
你花了两个小时和 AI 讨论了一个复杂的架构决策 —— 为什么选 Supabase 而不是 Firebase,怎么处理 JWT 的刷新逻辑,哪些边界情况需要特殊处理。
第二天你打开项目,AI 对昨天的一切毫无记忆。你不得不从头解释一遍。第三天,同样的事又发生一次。
这不是 bug,这是设计。当前的 AI 编程助手采用「会话隔离」架构 —— 每次对话都是独立的时间胶囊,会话结束,记忆归零。Anthropic 的 Claude Code 虽然有 LCM(Lossless Context Management)做会话内压缩,但那是为了节省 token,不是为了跨会话记忆。
问题的本质:AI 拥有强大的实时推理能力,但缺乏长期知识积累能力。它能理解你今天说的每一句话,但明天它就忘了。
这就像一个天才程序员,每次上班都要重新认识代码库。
2026 年 4 月,一个名为 claude-memory-compiler 的开源项目给出了一个令人惊艳的解法:
把 AI 对话当作「源代码」,把知识提取当作「编译」,把知识库当作「可执行程序」。
这个想法来自 Andrej Karpathy 2024 年提出的 LLM Knowledge Base 架构。Karpathy 的原始想法是:用 LLM 把网络文章「编译」成结构化知识库。claude-memory-compiler 做了一个关键的转向:
不编译网络文章,编译你自己的 AI 对话。
这是一个范式转变。传统的知识管理是你手动整理笔记、打标签、建立关联。这个系统说:你只管和 AI 聊天,编译器自动把对话变成知识。
daily/ = 源代码 (你的对话 —— 原始材料) LLM = 编译器 (提取和组织知识) knowledge/ = 可执行程序 (结构化、可查询的知识库) lint = 测试套件 (一致性健康检查) queries = 运行时 (使用知识)
你不需要手动组织知识。你只需要对话,LLM 负责综合、交叉引用和维护。
这是「源代码」层。每次 Claude Code 会话结束(或中途压缩),系统自动捕获对话 transcript,提取关键信息,追加到当天的日志文件。
daily/ ├── 2026-04-01.md ├── 2026-04-02.md ├── …
日志格式:
# Daily Log: 2026-04-01
Sessions
Session (14:30) - Supabase Auth Setup
Context: 用户在配置 Supabase 认证系统
Key Exchanges:
- 用户询问 JWT 刷新策略,助手解释了 refresh token rotation
- 决定使用 Row Level Security 而不是应用层权限检查
- 发现 Supabase 的 RLS 策略在 JOIN 查询上有性能陷阱
Decisions Made:
- 选择 Supabase 而不是 Firebase(因为需要 PostgreSQL 的 RLS)
- 认证流程:使用 PKCE 而不是 implicit flow
Lessons Learned:
- Supabase RLS 策略必须避免在 WHERE 子句中嵌套 SELECT
- JWT 的 aud 字段必须匹配 Supabase project ref
Action Items:
- [ ] 测试 RLS 策略在复杂 JOIN 下的性能
- [ ] 设置 refresh token 的过期策略
关键设计:
daily/是 append-only,永不修改。这是「不可变源代码」原则 —— 你不会修改编译器的输入,只会追加新输入。这是「可执行程序」层。LLM 完全拥有这个目录,人类只读不写(除非修复错误)。
knowledge/ ├── index.md # 主目录 —— 每篇文章一行摘要 ├── log.md # 追加式构建日志 ├── concepts/ # 原子知识文章 ├── connections/ # 连接 2+ 概念的交叉洞察 └── qa/ # 已归档的查询答案(复利知识)index.md—— 核心检索机制这是整个系统的「心脏」。一个 Markdown 表格,列出每篇知识文章:
# Knowledge Base Index
| Article | Summary | Compiled From | Updated |
|---|---|---|---|
| [[concepts/supabase-auth]] | Row-level security patterns and JWT gotchas | daily/2026-04-02.md | 2026-04-02 |
| [[connections/auth-and-webhooks]] | Token verification patterns shared across Supabase auth and Stripe webhooks | daily/2026-04-02.md, daily/2026-04-04.md | 2026-04-04 |
为什么不用向量数据库?
这是 Karpathy 的核心洞察:
在个人知识库规模(50-500 篇文章),LLM 读取结构化 index.md 的效果 优于 向量相似度搜索。
向量搜索找的是「相似的词」,LLM 理解的是「你真正在问什么」。当知识库超过 2000 篇文章、index.md 超过上下文窗口时,才需要引入 RAG。
三种文章类型
1. Concept Articles(concepts/) —— 原子知识
— title: "Supabase Row-Level Security" aliases: [RLS, supabase-rls] tags: [auth, database, security] sources:
- "daily/2026-04-01.md"
- "daily/2026-04-03.md" created: 2026-04-01 updated: 2026-04-03 —
Supabase Row-Level Security
Row-Level Security (RLS) 在 PostgreSQL 层强制访问控制,无需应用层权限检查。
Key Points
- RLS 策略是 SQL 表达式,对每行返回 boolean
- 必须在 JWT 中嵌入用户 ID,策略通过
auth.uid()获取 - 避免在策略 WHERE 子句中嵌套 SELECT —— 会导致 N+1 查询
Details
[深入解释…]
Related Concepts
- [[concepts/supabase-jwt]] - JWT 结构和验证
- [[concepts/postgres-policies]] - PostgreSQL 策略语法
Sources
- [[daily/2026-04-01.md]] - 初始配置时发现
- [[daily/2026-04-03.md]] - 性能调优时深入
2. Connection Articles(
connections/) —— 交叉洞察当对话揭示了两个概念之间的非显而易见关系时,编译器创建连接文章:
— title: "Connection: Supabase Auth and Stripe Webhooks" connects:- "concepts/supabase-auth"
- "concepts/stripe-webhooks" sources:
- "daily/2026-04-04.md" —
Connection: Supabase Auth and Stripe Webhooks
The Connection
Supabase JWT 验证和 Stripe webhook 签名验证使用相同的「secret + timestamp + payload」模式。
Key Insight
两者都需要防御 timing attack。Supabase 的 auth.jwt() 验证和 Stripe 的 verify_signature() 都应该使用恒定时间比较。
Evidence
[具体代码示例…]
3. Q&A Articles(qa/) —— 复利知识
每次查询可以选择 –file-back,把答案永久存档:
— title: "Q: How do I handle auth redirects?" question: "How do I handle auth redirects in Next.js with Supabase?" consulted:
- "concepts/supabase-auth"
- "concepts/nextjs-middleware" filed: 2026-04-05 —
Q: How do I handle auth redirects?
Answer
使用 Next.js middleware + Supabase 的 getSession()…
Sources Consulted
- [[concepts/supabase-auth]] - 提供了 session 刷新逻辑
- [[concepts/nextjs-middleware]] - middleware 执行顺序
Follow-Up Questions
- 如何处理 OAuth 回调的 race condition?
- middleware 中如何缓存 session?
复利效应:每次查询都让知识库变聪明。第一次问需要综合多篇概念文章,第二次问直接读 Q&A 文章。
这个文件定义了整个系统的「语法」和「语义」—— 告诉 LLM 如何编译、如何维护知识库。这是「编译器源码」。
编译流程:
1. 读取 daily log 文件
- 读取 knowledge/index.md 了解当前知识状态
- 读取可能需要更新的现有文章
- 对于日志中的每个知识片段:
- 如果现有概念文章已覆盖该主题:UPDATE,追加 daily log 为来源
- 如果是新主题:CREATE 新的 concepts/ 文章
- 如果日志揭示了概念间的非显而易见连接:CREATE connections/ 文章
- UPDATE knowledge/index.md
- APPEND knowledge/log.md
增量编译:通过
state.json跟踪每个 daily log 的 SHA-256 哈希,只编译新增或修改的文件。成本:每个 daily log 约 $0.45-0.65(随知识库增长而增加)。
CLI:
uv run python scripts/compile.py # 编译新增/修改 uv run python scripts/compile.py –all # 强制全量重编译 uv run python scripts/compile.py –file daily/2026-04-01.md uv run python scripts/compile.py –dry-run查询流程:
1. 读取 knowledge/index.md(主目录) - 基于问题识别 3-10 篇相关文章
- 读取这些文章的完整内容
- 综合、生成答案,附带 [[wikilink]] 引用
- 如果指定 –file-back:创建 qa/ 文章,更新 index 和 log
为什么不用 RAG?
在个人知识库规模,LLM 读取结构化索引的效果优于向量搜索:
- 向量相似度找「相似的词」
- LLM 理解「你真正在问什么」
Karpathy 的实验表明,在 50-500 篇文章规模,index-guided retrieval 的准确率和相关性都优于 embedding-based RAG。
CLI:
uv run python scripts/query.py "What auth patterns do I use?" uv run python scripts/query.py "What’s my error handling strategy?" –file-back7 项检查:
检查项类型捕获问题 Broken links结构性[[wikilinks]] 指向不存在的文章 Orphan pages结构性零入链文章(无人引用) Orphan sources结构性尚未编译的 daily logs Stale articles结构性源日志在编译后被修改 Missing backlinks结构性A 链接 B 但 B 不链接 A Sparse articles结构性少于 200 字的文章 ContradictionsLLM文章间的冲突声明CLI:
uv run python scripts/lint.py # 全部检查 uv run python scripts/lint.py –structural-only # 仅结构性检查(免费)
这是整个系统的「魔法」部分 —— 你不需要记得运行任何命令,hooks 会在 Claude Code 的生命周期事件中自动触发。
{ "hooks": { "SessionStart": [{ "matcher": "", "hooks": [{ "type": "command", "command": "uv run python hooks/session-start.py", "timeout": 15 }] }], "PreCompact": [{ "matcher": "", "hooks": [{ "type": "command", "command": "uv run python hooks/pre-compact.py", "timeout": 10 }] }], "SessionEnd": [{ "matcher": "", "hooks": [{ "type": "command", "command": "uv run python hooks/session-end.py", "timeout": 10 }] }] } }1. SessionStart —— 注入知识
- 纯本地 I/O,无 API 调用,< 1 秒
- 读取
knowledge/index.md和最近的 daily log - 输出 JSON 到 stdout,Claude 在每次会话开始时看到知识库索引
- 最大 20,000 字符上下文
2. SessionEnd —— 捕获对话
- 从 stdin 读取 hook 输入(包含
session_id、transcript_path、cwd) - 复制原始 JSONL transcript 到临时文件
- 以完全分离的后台进程 启动
flush.py - 递归保护:如果
CLAUDE_INVOKED_BY环境变量已设置,立即退出
3. PreCompact —— 压缩前捕获
- 与 SessionEnd 相同的架构
- 在 Claude Code 自动压缩上下文窗口之前触发
- 防护空
transcript_path(Claude Code 已知 bug #13668)
为什么需要 PreCompact 和 SessionEnd 两个?
长时间会话可能触发多次自动压缩,然后你才关闭会话。如果没有 PreCompact,中间的上下文会在压缩时丢失,SessionEnd 永远看不到那些内容。
由两种 hook 以完全分离的后台进程启动:
- Windows:
CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS标志 - Mac/Linux:
start_new_session=True
这确保
flush.py在 Claude Code 的 hook 进程退出后仍然存活。flush.py 做什么:
1. 设置 CLAUDE_INVOKED_BY=memory_flush 环境变量(防止递归) - 从临时 .md 文件读取预提取的对话上下文
- 如果上下文为空或同一会话在 60 秒内已 flush:跳过(去重)
- 调用 Claude Agent SDK(query() with allowed_tools=[], max_turns=2)
- Claude 决定什么值得保存 —— 返回结构化 bullet points 或 FLUSH_OK
- 追加结果到 daily/YYYY-MM-DD.md
- 清理临时上下文文件
- 【关键】如果当前时间 > 18:00(本地时间)且今天的 daily log 自上次编译后已改变: 以另一个分离后台进程启动 compile.py
自动日编译:不需要 cron job。只要你在下午 6 点后用过 Claude Code,编译就会自动发生。
# 克隆到你的项目 git clone https://github.com/coleam00/claude-memory-compiler.git cd claude-memory-compiler
安装依赖(使用 uv)
uv sync
复制 .claude/settings.json 到你的项目:
cp .claude/settings.json /path/to/your/project/.claude/
或者合并到现有配置:
import json
读取现有配置
with open(‘.claude/settings.json’) as f:
config = json.load(f)
合并 hooks
memory_hooks = {
"SessionStart": [{"matcher": "", "hooks": [{"type": "command", "command": "uv run python hooks/session-start.py", "timeout": 15}]}], "PreCompact": [{"matcher": "", "hooks": [{"type": "command", "command": "uv run python hooks/pre-compact.py", "timeout": 10}]}], "SessionEnd": [{"matcher": "", "hooks": [{"type": "command", "command": "uv run python hooks/session-end.py", "timeout": 10}]}]
}
config.setdefault(‘hooks’, {}).update(memory_hooks)
with open(‘.claude/settings.json’, ‘w’) as f:
json.dump(config, f, indent=2)
# 检查 hooks 配置 cat .claude/settings.json
测试 session-start hook
uv run python hooks/session-start.py
检查依赖
uv run python -c "import claude_agent_sdk; print(‘Claude Agent SDK OK’)"
1. 正常使用 Claude Code
cd /path/to/your/project claude
Hooks 自动激活。你正常和 Claude 对话,不需要做任何特殊操作。
2. 会话结束后检查 daily log
cat daily/\((date +%Y-%m-%d).md
3. 手动触发编译(可选)
uv run python scripts/compile.py
4. 查询知识库
uv run python scripts/query.py "What did I decide about authentication?"
5. 健康检查
uv run python scripts/lint.py
月度成本估算:
- 每天用 5 次 Claude Code 会话
- 每月 22 个工作日
- 每天自动编译 1 次
- 每周查询 10 次
Flush: 5 sessions × 22 days × \)0.03 = \(3.30 Compile: 22 days × \)0.55 = \(12.10 Query: 40 queries × \)0.20 = \(8.00 Lint: 4 weeks × \)0.20 = $0.80
Total: ~$24/月
当前架构的极限:
- index.md 超过 2M tokens(约 2000+ 篇文章)
- 单次查询需要读取的文章超过上下文窗口
引入 RAG 的时机:
# 简单判断:index.md 的 token 数 import tiktoken
def should_use_rag(index_path: str) -> bool:
enc = tiktoken.encoding_for_model("claude-3-5-sonnet-") with open(index_path) as f: tokens = len(enc.encode(f.read())) return tokens > 1_500_000 # 留 500k 给查询和响应
混合检索架构:
def hybrid_retrieve(question: str, index_path: str, kb_dir: str) -> list[str]:
# 1. 关键词搜索(BM25) keyword_results = bm25_search(question, kb_dir, top_k=20) # 2. 语义搜索(Embedding) semantic_results = embedding_search(question, kb_dir, top_k=20) # 3. 合并去重 candidates = list(set(keyword_results + semantic_results)) # 4. LLM 重排序(可选) if len(candidates) > 10: candidates = llm_rerank(question, candidates, top_k=10) return candidates
# scripts/compile.py 的核心逻辑
import hashlib import json
def get_file_hash(filepath: str) -> str:
with open(filepath, 'rb') as f: return hashlib.sha256(f.read()).hexdigest()
def load_state() -> dict:
try: with open('scripts/state.json') as f: return json.load(f) except FileNotFoundError: return {'ingested': {}}
def save_state(state: dict):
with open('scripts/state.json', 'w') as f: json.dump(state, f, indent=2)
def compile_incremental():
state = load_state() for daily_file in Path('daily').glob('*.md'): file_hash = get_file_hash(daily_file) stored = state['ingested'].get(daily_file.name) # 跳过未改变 if stored and stored['hash'] == file_hash: continue # 编译 compile_daily_log(daily_file) # 更新状态 state['ingested'][daily_file.name] = { 'hash': file_hash, 'compiled_at': datetime.now().isoformat(), 'cost': last_compile_cost } save_state(state)
互补关系:LCM 解决「token 预算」,memory-compiler 解决「知识积累」。两者可以共存。
兼容性:memory-compiler 的知识库是纯 Markdown + Wikilinks,可以直接用 Obsidian 打开查看图谱、反向链接。
claude-memory-compiler 解决了一个根本问题:
AI 编程助手拥有强大的实时推理能力,但缺乏长期知识积累能力。
它用「编译器思维」重新定义了知识管理:
- 源代码 = 你的 AI 对话(
daily/) - 编译器 = LLM(提取和组织知识)
- 可执行程序 = 结构化知识库(
knowledge/) - 测试套件 = 健康检查(
lint) - 运行时 = 查询系统(
query)
- 零摩擦:你只管对话,系统自动编译
- 复利效应:每次查询都让知识库变聪明
- 100% 本地:数据主权在你,纯 Markdown 格式
- 成本可控:使用现有 Claude 订阅,无需额外 API credits
- 长期项目的架构决策记录
- 复杂系统的调试经验积累
- 个人编程偏好和模式沉淀
- 团队知识共享(通过 git 同步
knowledge/)
- 多项目知识库:当前是项目级,可以扩展到全局级
- 团队协作:通过 git 合并不同开发者的知识库
- 主动推荐:基于当前上下文主动推送相关知识
- 代码生成增强:用知识库 fine-tune 或作为 RAG 上下文
项目地址:https://github.com/coleam00/claude-memory-compiler
灵感来源:Andrej Karpathy’s LLM Knowledge Base
一句话总结:当 AI 编程助手第一次拥有「编译器思维」的记忆系统,你的对话不再是过眼云烟,而是持续增值的知识资产。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/255922.html