我给AI助手做了个微创手术

2026年5月12日,晴。
今天下午干了一件有意思的事——把Hermes(我的AI助手)的底层记忆机制做了个手术。
说起来挺丢人的。这个bug我用了大半年才发现。每次我跟AI说"记住这个"之后,它都变笨一点——反应慢了,回答长了,我一开始还以为是模型问题。直到前两天翻了翻源码,真相让我一愣。
原来记忆模块一直嵌在系统提示词里。系统提示词是什么?就是每次对话开始前,后台喂给大模型的那套固定指令。放在最前面,一直不变。问题是——放最后一条指令的时候也放在这里面。每次我写一条新记忆,系统提示词就会被重新拼一遍,所有之前缓存的内容全作废。下一轮对话得从头算起。
我打个比方你就明白了。你天天去同一家兰州拉面馆,老板每次都重新和面、揉面、醒面——不是因为你换了口味,是因为你昨天说了句"少放点辣",他就以为你今天要吃完全不一样的东西。
改造前后到底差在哪
改造前长这样
system: [Hermes系统指令] [MEMORY.md内容] ← 每次写新记忆就变 [USER.md内容] ← 每次写新记忆就变 [工具列表] user: 张总你好,今天要干嘛?
每次你让它记住点东西,MEMORY.md的内容就变了,system prompt被重写,缓存在这一瞬间归零。
改造后长这样
system: ← 完全不变 [Hermes系统指令] [工具列表] user: [CACHED SYSTEM STATE] [MEMORY.md内容] ← 只在这里变 [USER.md内容] ← 只在这里变 张总你好,今天要干嘛?
system prompt锁死不重建。记忆改没改,都不影响缓存。
别小看这点改动
改动本身不大,前后就改了4个地方,加起来大约多写了60行代码:
1.
_build_system_prompt()—— 把MEMORY.md和USER.md从system prompt里拆出来,改存到实例变量里2. 主循环用户消息注入处 —— 每次发消息时,在第一条用户消息前面加上[记忆块]
3. flush调用和summary调用 —— 同样的逻辑,同样的注入位置
4.
_invalidate_system_prompt()—— 以前压缩事件后会清空整个system prompt缓存,现在只重读记忆,缓存原地不动
效果对比
| 场景 | 改造前缓存命中率 | 改造后 |
|---|---|---|
| 写新记忆之后的第一轮对话 | 0%(全量重建) | 90%以上 |
| 上下文压缩之后 | 0%(全量重建) | 95%以上 |
| 长会话(10轮之后)token成本 | 每5-8轮支付一次全额 | 首轮全额,后续仅增量和+记忆增量 |
你想想看,这个bug藏了大半年——系统提示词缓存这个东西,大模型厂商已经支持好久了。但因为记忆嵌错了位置,缓存功能等于从来没生效过。改成了,没加任何新功能,省下来的却是每天真金白银的token钱。
我跟你说句实话,这个事给我的触动还挺大的。
做企业也是一样的道理。有时候系统跑得慢、成本高,不是因为功能不够多,而是基础架构上挂了一堆不该挂的东西。你做灵活用工、做企业服务,功能再好用,如果最底层的税务合规模块没搭对,上面越堆越重,早晚出事。
我自己也还在摸索。今天改的这60行,明年回头看可能也是幼稚的。但先让能跑的东西跑得更稳一点,比天天琢磨新功能靠谱。
顺便聊两句"改代码"这件事
今天改的时候,发现文件里居然有注释写着"系统提示词修改会破坏缓存前缀,所以只往用户消息里注入"——意思是我要改的地方,写代码的人早就想到了,只是另外一条路径(记忆模块)没同步上。
这其实是常见的架构问题:同一个原则在A处写得很清楚,B处没跟上。就像你公司里定了规矩"所有客户报价统一走审批流程",销售部照做了,市场部忘了。不是谁不认真,是系统大了之后,一致性很难维护。
最好的办法是什么?我自己的习惯是:定一条规则,然后用代码/工具把规则锁死。今天改完之后,我顺手把记忆写入也检查了一遍——确保未来不管谁加新功能,都不会再把可变数据塞进固定前缀里。
这个思路跟你做企业服务也是通的:合规、流程、标准化——不是靠人盯出来的,是靠制度和技术锁出来的。
发布于随趣科技 · Hermes项目改造日志
2026年5月12日