给 SRE agent 加"长记忆":我自己写一套,对比 Headroom 的 SharedContext 和 Learn
上一期:[不抄代码只抄脑子:照着 SmartCrusher 给我那个 SRE agent 写了个压缩中间件]
这一期:把 agent 的"长记忆"那一块给补上,顺便对比 Headroom 自带的 SharedContext / Headroom Learn 是不是同一回事。
一句话剧透:不是同一回事。我做的事 SharedContext 干不了,Headroom Learn 也干不了——但反过来它们能干的事我也不全做。

一、痛点:我那个 SRE agent 是个"健忘症患者"
上一期写完 crusher-lite,token 账单是省下来了,但我盯着它跑两周后又发现一个老问题:
它每次都从零开始想问题。
同一个微服务的 OOM 告警,上周三排过一次,定位到是某个定时任务把缓存撑爆。这周二一模一样的告警来了,agent 又老老实实捞日志、查指标、查 trace,又花了一整套 token 才得出同样的结论。
我不是要它"猜"——SRE 排查不能靠猜。但起码它该知道:"这个症状我们三天前见过,根因可能是 X,先去验证一下。"
这就是"长记忆"该解决的事。
二、我的方案:incident-kb,一个本地"故障经验库"
砍到最朴素的形态:
- 存什么:每次故障排查结束,把"症状指纹 + 根因 + 处置动作 + 链接到当时的工单"五件套写进本地 SQLite。
- 怎么找:新告警进来,先用症状指纹做语义检索,命中就把命中的 1-3 条历史 case 注入到 agent 上下文。
- 谁来写:故障收尾时,agent 自己提交一份"事后小卡片"——人工 review 通过才入库。不让 agent 自动写,免得垃圾进垃圾出。

技术栈三件套就够:
- SQLite:存 case 元数据
- sqlite-vec 扩展:嵌入向量直接存同一份 db
- 本地 sentence-transformers 模型(或者公司内部的 embedding 服务)算嵌入
代码量我估了一下,比 crusher-lite 还少,200 行就够。一个表、一个写入函数、一个检索函数、一个 hook 把它挂到 agent 的告警入口。
三、症状指纹:长记忆能不能用,全看这一步
光说"语义检索"是骗外行的。SRE 场景下,两个告警长得多像才算"同一类"?
我决定用一个组合指纹:
def fingerprint(alert: dict) -> str:
parts = [
alert["service"], # 服务名
alert["alert_type"], # OOM / latency / 5xx / ...
normalize_topology(alert), # 调用上下游归一化
bucketize_metric(alert), # 指标值分桶(不是精确值)
]
return " | ".join(parts)
注意几个我反复改过的地方:
- 指标分桶而不是用精确值——上次 OOM 时内存 7.2G,这次 7.4G,不能因为数字不同就说不是同一类。
- 拓扑归一化——把"实例 ID"这种每次都不一样的字段抹掉,只留"服务 A → 服务 B"这种结构。
- 不直接喂自然语言给嵌入模型。指纹先组装成一个半结构化字符串,召回率比直接 embed 整段告警高一截——这个我用历史 trace 拉了一份对照样本估算过,提升大约从 60% 召回到 80% 上下。
四、和 Headroom 的 SharedContext / Learn 真不是一回事
这部分是这一期重点。SharedContext 和 Headroom Learn 看名字都跟"记忆"沾边,但解决的是完全不同的问题。
SharedContext:跨 agent 共享的"小抄"
我去翻了 Headroom 源码(headroom/memory/),SharedContext 的真身是:
- SQLite + sqlite-vec/HNSW 存底,本地 sentence-transformers 算嵌入(跟我打算用的几乎一样)
- 通过 proxy 的
x-headroom-user-id/x-headroom-project-idheader 做用户和项目隔离 - 注入策略叫
AUTO_TAIL——只往最后一条 user message 尾巴上贴,不动 system prompt,目的是保住 prompt cache - 默认预算极保守:最多注入 1024 token、最多 10 条、相似度阈值 0.3
它的典型用法是这样的:你在 Claude Code 里告诉 agent "这个项目的数据库 URL 是 xxx",切换到 Cursor 干活时,那个 URL 自动浮出来。它存的是"工作中产生的杂事"——账号、路径、约定、偏好。
Headroom Learn:从失败会话里自动总结规则
这块定位完全不同——它是个离线分析器:
headroom learn --agent claude --source ./sessions/
吃一堆历史会话日志,挑出"agent 反复犯的错",把教训写进 CLAUDE.md / AGENTS.md。它产出的是 prompt 级别的规则,下次会话被自动加载。
三个东西分别在干啥
| 维度 | SharedContext | Headroom Learn | 我的 incident-kb |
|---|---|---|---|
| 存什么 | 工作杂事 / 偏好 | 失败教训 → 规则文本 | 故障 case 五件套 |
| 颗粒度 | 句子级 | 规则条款级 | 事件级 |
| 触发时机 | 实时(每次会话) | 离线批处理 | 实时(告警入口) |
| 注入位置 | user message 尾部 | system prompt | tool 返回结果之前 |
| 谁来写 | agent 自动 | 离线脚本自动 | 人工 review 后入库 |
| 上限 | 1024 token / 10 条 | 看 CLAUDE.md 体积 | 单 case 不限,按相似度 top-k |
我蹲完源码、画完这张表后才彻底想清楚——这三种"记忆"在 agent 系统里是三个不同的位置。SharedContext 是给 agent 留的便签条,Learn 是给 agent 立的规矩,incident-kb 是给 agent 准备的案例库。要有都得有,互不替代。
五、那为啥不直接用 SharedContext?
我认真考虑过这个问题。结论是 SharedContext 太轻,扛不住故障 case 的形态:
- 1024 token 预算对账号路径够用,对一份故障 case 不够。我那个 SRE agent 的一份 case 卡片,光是处置 SOP 就能写到 800 token,再加上原始告警截取、根因说明、相关链接,2-3k 起步。
- AUTO_TAIL 不动 system prompt 是为了保 prompt cache,但故障 case 注入的最佳位置其实不是 user message 尾部,而是工具返回告警内容之前——让 agent 在看到告警的瞬间就被提醒"似曾相识"。
- agent 自动写 在 SRE 场景里风险大。垃圾 case 入库会害死下次排查。我必须卡 review 这一步。
所以我的选择是:incident-kb 自己做,但把 SharedContext 那套"SQLite + 向量 + 本地嵌入"的组合直接抄过来——这套技术栈被它在生产里验证过,没必要换。
Headroom Learn 那一套我倒是真打算用——把我们 SRE agent 历史会话扔给它,让它生成一份初版 AGENTS.md,再人工梳一遍当成规则基线。Learn 产出规则,incident-kb 产出案例,两者刚好互补。
六、上线节奏(计划,不是已发生)
我打算分三步真上线:
- 第 1 周影子模式:只写不读。每次故障收尾让 agent 起草 case 卡片,我和组里另一个同事手动 review 入库。攒够 30 条再开下一步。
- 第 2-3 周灰度读取:5% 告警走"先查 kb"分支,对比命中率和误导率。设定一个回滚开关,命中后 agent 给出的判断如果跟最终结论南辕北辙,直接关闭 kb 注入。
- 第 4 周全量 + 接 Headroom Learn:把这四周的会话喂给
headroom learn,让它生成AGENTS.md规则。kb(案例)+ AGENTS.md(规则)+ crusher-lite(压缩)三套件齐活。
七、值不值得做:粗算一下
把这三层叠在一起的预期收益(按上一期同样的工作量基线估算):
| 改造项 | 主要节省 |
|---|---|
| crusher-lite | tool 输出压缩 ~80%,月省约 ¥6,200 |
| incident-kb(命中场景) | 单次排查 token 估降 30-50%,加快定位时间 |
| Headroom Learn 规则 | 减少 agent "犯过的错再犯一次",质量提升 > 成本降 |
钱的账接着上一期 ROI 表算就行,但真正的价值不在 token——是把 SRE 排查从"每次重新想"变成"先看历史"。这件事的杠杆比省钱大得多,只是说不出确切数字。
八、总结:记忆这事,要分层
写到这里我意识到一个比技术更重要的判断:
agent 的记忆从来不是一个东西。 它至少有三层——
- 杂事便签(SharedContext 干的活):高频、轻量、agent 自助。
- 规则约束(Headroom Learn 干的活):低频、稳定、离线生成。
- 案例库(我这个 incident-kb 干的活):中频、有结构、必须人工把关。
把这三层分开,每层用最匹配的载体,比想造一个"全能记忆系统"务实得多。Headroom 把前两层做好了,留给我的是第三层——这种"开源做底盘、自己盖业务层"的协作姿势,是我读完这个项目这几周最大的收获。