04. Memory

让它「越写越懂你」:记忆不是一锅粥,是三个抽屉

来到第 4 篇,也是我个人觉得整个专栏里最能体现「垂直产品灵魂」的一篇。前面我们让「这一次写作任务」的历史跨越了关机、能恢复,但那始终是「同一篇」的记忆。SmartWriter 的目标是「越写越懂你」,靠的是另一种机制:跨越一次次写作、把你这个人记下来的长期记忆。这一篇就把这句 slogan 拆开,看它在技术上到底怎么落地,以及我为什么觉得,细节没有做到位的话,产品就没有灵魂了

👆你在这里
👆你在这里

🪧 「越写越懂你」,真正做好并不容易

这句 slogan 我很早就想喊出来:眼下的大模型虽然聪明,但产出的文字总带着一股挥之不去的「AI 味儿」,我想做一个能摸透我写作风格的 Agent 助手。真动手做起来,才被一串朴素的问题难住了:什么叫「懂」?到底该记什么、记到哪、谁来写、又怎么保证它别学歪、还能持续进化?

一个简单粗暴的想法是:管它什么记忆呢,反正都是「关于用户的信息」,都统一塞进一个地方。现在的模型不都号称百万级别上下文嘛,「放进去」这个动作应该不难

可这样行不通,举个直观的例子:假设用户写一篇吐槽职场的文章,为了烘托情绪,中间来了句「我这人是真受不了没完没了的会」。这本是这篇文章里的一句气话。可如果记忆系统不分层,把它当成了「关于这个用户的一条长期信息」记了下来。等下次他写一篇完全不相干的技术随笔,模型可能还惦记着「这人真讨厌开会」,时不时想往里带一句。这不就坏事了吗,它把一次性的「事实」记作长期的「偏好」,甚至还隐隐当成了要迎合的「风格」

「越写越懂你」的难点,不是「记不住」,而是 「张冠李戴」:把该忘的记成了永久的,把这篇的当成所有篇的,把一句话的事实当成一个人的风格。说白了,记忆混成一锅粥,就必然张冠李戴。解决办法在我看来并不新鲜,在古早的软件工程时代便流传至今:分而治之

🧭 基础概念:为什么记忆要分抽屉?

要分抽屉,先得知道按什么分。我个人理解,一个写作助手要记的东西,本质上是三类不同的东西

记忆的分类
记忆的分类

 

这三类东西性质各不相同,硬塞到一个抽屉里必然打架:

风格要求慢、稳、抗污染。一个人的文风是长期沉淀的,你不能因为他这篇文章里用了个不常用的词,就武断地改写他的画像。风格层最怕被一次性的噪声污染。哪怕污染一次,用户也会觉得「它怎么越来越不像懂我了」

事实恰恰相反,要求快、即时、可覆盖。他今天说「这个项目改叫 X 了」,就得立刻记下、随时能被明天的新说法覆盖。要是把事实也用「慢、稳、抗污染」那套去管,它就永远滞后

规则则是稳定的,不像风格那样持续演进,也不像事实那样易变,它就是一条条明确的写作规范

梳理清楚「三抽屉」的框架,接下来先看看 Claude Code CLI 现成提供了哪些,再看缺的我怎么自己造

🔩 Claude Code 怎么做:已经有两个抽屉,独缺「风格」那个

好消息是,Claude Code 本身就没往「一锅端」的路子上走,它天然就有两个抽屉。Learn Claude Code 的源码解读系列第九篇专门拆过 Claude Code 的记忆机制,这里顺着它的发现往下讲

一个是 CLAUDE.md,装的正是「📏 规则」。它是项目级的常驻规矩文件,你把「这个项目必须遵守什么」写进去,它就每次都作数。前两篇文章已经反复提起过

另一个是 Auto Memory(自动记忆),装的正是「📇 事实」。它是 Claude Code 内置的一层事实记忆,由模型自己判断「这条信息值得记住吗」,然后默默把「你是谁、你说过什么重要的、你上次交代过的约定」这类事实攒下来,下次自动带上

写代码场景需要的两个抽屉,Claude Code 都备好了:规矩(CLAUDE.md)和档案(Auto Memory)。它只是缺了第三个抽屉,「🎨 风格」。 这不怪它,写代码这件事,对「个人文风」的诉求本来就弱,代码写得对不对、清不清晰有客观标准,不太存在「这段代码要写得像我」的个性化需求

而这恰恰是写作场景最不能缺的一层。写作 Agent 服务于用户,要「写得像」才能起到正向效果。所以我这个写作 Agent 要补的关键一环,就是给它造一个 Claude Code 没有开箱提供的「风格抽屉」

🛠 SDK 提供了什么:两个抽屉的钥匙,和它们各自的使用说明书

先把两个 SDK 现成抽屉的「钥匙」摸清楚,尤其它们是「怎么进到模型脑子里」的通路,这决定了我后面把自造的风格层往哪塞

CLAUDE.md

一个容易搞混、却很关键的事实:它不进「系统提示词」。 我原本以为,常驻规则就是写进那个特殊的系统提示词位置的,其实不是

SDK 把 CLAUDE.md 的内容,注入到「你的第一句话」里(外面包一层 <system-reminder> 标签)。磁盘上的 CLAUDE.md 只在 CLI 子进程启动那一刻读一次,读完就缓存进内存,不会因为压缩就重新读盘。真正让它「抗压缩」的是,CLI 每次构建新的一句 user message,都会用内存里缓存的那份内容重新注入一遍 <system-reminder> 压缩摘得掉对话历史里的旧注入,但摘不掉下一句话里新注入的这一份(这个机制第 2 篇已经拆过),因此它能一直被记住

不过有个细节要注意:中途要是画像被后台悄悄改了,压缩本身并不会让这个会话读到新内容。得等这个会话的 CLI 子进程整个重启(比如你关掉页面很久后重新打开)才会读到磁盘上的最新版本

Auto Memory

有几个细节得记住:它默认开启;它按「代码仓库(repo)」派生一个存储路径,同一个 repo 下的子目录,共享同一份 Auto Memory;不想要可以一键关掉。最关键的一点是,它写什么、不写什么,是模型自主判断的,这既是它的方便(不用我操心),也是它的风险(它可能自作主张记下我不想让它记的东西,比如把风格判断混进来)

规则和事实的通路已经摸清了,风格这个抽屉还得我自己想办法送进模型上下文,这就是 SmartWriter 接下来要干的活

🧩 SmartWriter 怎么做:三权分立,且「灵魂」被单独看管

最后落地的方案,可以总结为:把记忆做成三权分立的三层,每层管什么、谁能写、放在哪,划得清楚。 尤其是「风格层」,是定制化最多的一部分。先看总分工:

抽屉 装什么 判据一句话 谁来写 · 放在哪
🎨风格层 = 画像 造词/句式/语气/忌讳 他「怎么写」 只能带外 worker 写,SDK 永不碰;profile/global.md + genres/*.md
📏规则层 = CLAUDE.md 写作规范/格式/流程 必须怎么做」 我们维护的运行期CLAUDE.md
📇事实层 背景/纠正/约定 他「是谁、说过什么」 见下,再劈成两半

 下面再把每层里的细节展开

个人写作风格是产品的灵魂,看管得最严

画像(也就是风格层)是整个产品的灵魂,所以给它定的规矩也最严格:

写作 Agent 自己,永远无权直接改画像

什么意思?模型在陪你写作的过程中,不管它多「觉得」自己摸到了你的新偏好,也没有一个工具能去写画像文件。所有对画像的修改,必须走一条带外(off-band)的后台流程:攒够N篇你的稿子,由一个专门的分析程序离线去看、去提炼,而且提炼出来的每一条建议,还得经过人工确认(你逐条点「采纳」或「不要」)才算数

为什么要如此谨慎?回想下开篇的 badcase:风格信号天生含噪。 某篇文章用了个生僻词,可能只是这篇需要,不代表你想改文风;或者你把 Agent 写的某句话删了,可能只是这句不合适,不代表你讨厌这个句式。要是让模型在写作过程中一边写一边「顺手」改画像,这些噪声就会源源不断地污染着核心,越污染越不像你。所以我宁可让画像更新慢一点、笨一点、要用户亲自点头,也不让它在写作的工具循环里被随手改动

画像怎么「进」到模型脑子里?

个人画像文件躺在磁盘上,模型看不见,得有一条通路把它送进去。在构建的过程中,对不同的方案也有过纠结,先来看两个被我否掉的:

  1. @import 直接把画像文件内联进来? 否。@import 是「无条件全量加载」,它表达不了「按当前题材,只加载对应那层画像,还要处理裁决优先级」。写随笔就该用随笔的画像,不该把产品技术文章的规矩也一股脑塞进来
  2. 用工具返回值(tool result)注入? 也要否。还记得第 2 篇那个关键洞察吗,压缩之后,自动重跑的是 hooks,不是任意一个工具。 要是画像靠某个工具的返回值注入,一旦压缩后,它不会被自动补回,画像就被摘没了。这等于把产品灵魂系在一根压缩后随时会剪断的线上

最终采取的做法是:任务一启动,通过一个叫 profile_reader 的模块,按你这篇的题材(随笔?书评?产品文?)把对应的画像组装好,写进 CLAUDE.md 的一个专门区块里。这样画像就搭上了 CLAUDE.md 那条「每次新消息自动重注、天然抗压缩」的顺风车

参考下图,「profile_reader 组装 + 写进 CLAUDE.md 区块」是 同时满足「按题材裁决」和「抗压缩」的干净解

画像装配
画像装配

事实层为什么还要再劈成两半

继续往下钻,在事实层,我做了个额外动作,把它分成了两块各自管理。因为「事实」里也藏着一个矛盾,有些事实该隔离,有些事实该共享

为了防止 Auto Memory 越界(它是模型自主写的,万一手抖把风格判断也记进来),我在 CLAUDE.md 里给它划了条明确的线:只准记事实背景、明确纠正、协作约定;绝不准记写作风格判断,风格一律归画像管。 Auto Memory 也会读 CLAUDE.md 的指引,但只能作为引导信号

这两半事实的写入,和风格层还不一样:风格层更新要 人工确认(灵魂,慎之又慎);而合集事实层是带外自动更新的(事实,时效优先,不必每条都拦着你点头)

📐 两个深挖:画像怎么防污染,档案怎么不臃肿

前面留了两个钩子:风格层的带外去噪具体怎么做、事实层到底记什么不记什么。以下分别展开,作为进阶细节补充

📐 深挖 · 风格层的「防污染」三件套:裁决、批处理、reject 记忆

画像既然是灵魂、又天生含噪,我给它配了三道闸:

① 裁决优先级(硬红线永不被覆盖):多层画像叠加时,按 全局禁忌 > 题材 > 全局偏好 裁决。「全局禁忌」是硬红线(比如你说过「绝不用某类比喻」),任何更具体的层都不能覆盖它;而「全局偏好」是软约束,可以被更具体的题材层细化。这保证了「你最在意的底线」永远说了算,不会被某个题材的临时偏好带偏

② 单轨批处理 + 强制 review:画像不是写一篇更一次(太颠簸),而是攒够 N 篇(默认 3,可调)统一分析一次,产出一批「建议这样调整你的画像」的条目,再由你逐条点采纳/否决。永远只有一个待确认批次,新的触发会并进旧的。慢,但稳,用户始终握着方向盘

③ reject 记忆(把「你不要什么」也记下来):你否决掉的建议,也会单独记进一个小本本(profile_feedback.json),滚动保留最近一批。下次分析时,把这些「你明确说过不要」的注入进去,让它别再重复产出类似建议。把负反馈也当资产存起来,也很重要:「否决」比「采纳」是更强的信号,因为「采纳」可能只是用户懒得改,而「明确否决」是真的不喜欢

风格层的所有巧思细节,都是为了让「越写越懂你」是真的越来越懂,而不是被噪声带着越跑越偏

📐 深挖 · 事实层的克制:不是「记住一切」,是「记住删掉会出事的」

合集事实(memory.md)我按 7 个主题组织(系列定位、已写篇目索引、系列立场与主张、作者背景、偏好厌恶、明确要求、上帝视角观察)。但真正的难点不在「分几类」,而在收录判据。早期踩过一个坑:让它记「这个系列用过哪些论点」,结果一篇文章能抽出十条论点,三十篇就是三百条,memory.md 直接爆炸

复盘发现是粒度错配:论点是「文章内部」的东西,而系列记忆该管的是「跨文章的连贯性」。把技术事实、机制说明这些「读者看文章就知道」的东西也记下来,纯属复述、纯属臃肿

后来我把判据收紧成一句话:删掉这条,下一篇会不会因此写得重复、矛盾或跑偏?会 → 记;不会 → 不记

按这个判据,只有「作者明确表达的立场主张」(下次若自打嘴巴,读者会觉得打脸)、「已写篇目索引」(防重复)这类真正影响后续连贯性的才记

⚖️ 收个尾:做到「真正懂你」,背后的技术含量

「越写越懂你」这五个字,用户读起来可能是温度,但真要它成立,背后全是分权、去噪、裁决这些冷冰冰的架构决策。也正是这些看不见的决策,决定了它到底是真的在懂你,还是像第一版那样乱成一锅粥,把用户随口一句吐槽郑重其事地记成了画像人设

⚖️ 取舍现场 · 记忆这件事,通用和垂直差在哪

「偷懒」的默认做法 SmartWriter 的选择 为什么这么选
记忆结构 一个通道,啥都往里记 三权分立:风格/规则/事实分抽屉 三类东西性质各异,混一起必然张冠李戴
谁能写「风格」 模型自主,顺手就记 画像只能带外 worker + 强制确认,SDK 永不碰 风格是灵魂,被一次性噪声污染就毁了
记忆哲学 倾向「尽量都记住」 只记「删掉会让后续跑偏的」,事实绝不复述 囤积一切等于臃肿加信噪比崩,懂你靠判断力不靠容量

 

记忆这一层解决的是「Agent 记住什么」。但我们还没碰它另一个更底层的东西,它到底以什么『人格』、守什么『纪律』在跟你干活? 是像个一板一眼的工程师,还是像个懂分寸的写作副驾?

 这个「行为底座」不归记忆管,是 System Prompt 负责的。下一篇起,我们就进入「驾驭模型」这个板块,去拆解这台机器真正的性格开关,咱们下一篇聊。