DSH 记忆模块源码解析:AI 是如何长期记住项目规范的
📝 本文首发于 栏轩·阁
欢迎访问阅读原文,获取更好的阅读体验。
用 DSH 有段时间了,我慢慢冒出一个挺具体的需求:项目里总有些东西希望 AI 一直记得,比如项目大纲、几条简短的编码规范、文档习惯。我不要每次开新对话都重新交代一遍,而是想让 AI 把它当成“早就知道的事”来对待。说白了就是想让 AI 长点记性。
DSH 最讨喜的地方是它开源。遇到这种疑问,与其去翻文档猜,不如直接把它装好的源码拆开,看它到底是怎么管记忆的。我花了一晚上跟着源码走了一遍,把它的记忆机制捋清楚了,这里记录一下。
什么是“记忆”
在 DSH 里,“记忆”并不是一个单一的东西,而是由一层又一层的东西叠起来的。最底层是一个事件溯源的会话日志:每一次交互都被追加成一条带序号的事件,从头到尾只增不改。AI 看到的那段“对话历史”,其实不是另存的一份,而是从这份日志实时派生出来的。
在这之上,才是我们真正关心的那部分——怎么让某些内容一直留得住。DSH 给“人”留了好几个口子,让我能把属于自己的长期内容喂给 AI:项目规范文件、技能、人设、还有工作区里那些普通文档。这次我主要盯的是项目规范这条线,也就是 AGENTS.md 那一套。
记忆存在哪、长什么样
DSH 的“家”在 ~/.dsh,也就是 $DSH_HOME(没设就用 ~/.dsh,也可以在启动配置里显式指定)。这里面东西分得很清楚:
- 设置:
settings.yaml - 凭证:
.credentials.yaml(权限 0600,只有自己能读) - 技能:
skills/目录,里面是一个个SKILL.md - 会话历史:独立的一套 JSONL 文件,默认还带 zstd 压缩
对话这条记忆留得最“讲究”。每个会话一个独立的仅追加文件,目录按项目和工作目录组织,文件名是 session.jsonl(或者压缩过的 session.jsonl.zstd)。第一行是会话头,记着版本、id、创建时间、工作目录这些元数据,后面每一行是一条事件。崩溃了也不怕,它靠临时文件加原子发布来写,读到一半的“撕裂尾巴”会被自动截掉,没闭合的回合还会被补一个“中断”标记。
底层那些 KV 数据(比如工作区记录、某条消息的反馈)走的是 JSON 存储,每个单元一个 <名字>.json 文件。
这一层解决的是“记忆落不落地、安不安全”,但它是被动的——它只负责把发生过的记下来,不负责“我指定要记住的东西”被送进上下文。真正干这活的,是 AGENTS.md 这一套注入机制。
有意思的地方:它连 CLAUDE.md 都认
翻源码的时候我发现一个挺贴心的细节:DSH 认的候选文件不只是 AGENTS.md。在 dsh-agent-instructions 的源码里,默认候选列表写的是 ['AGENTS.md', 'CLAUDE.md'],本地覆盖候选是 ['AGENTS.local.md', 'CLAUDE.local.md']。
也就是说,如果你从 Claude Code 那边迁过来,习惯用的 CLAUDE.md 直接就能被读到,不用改名。这一点不翻源码还真发现不了——文档里不一定写得那么细。
这套列表本身还是可以配置的:你甚至能给项目加个自定义名字的规则文件,只要填进 instructionFileCandidates 或者 localInstructionFileCandidates 就行。
优先级:从宽到严,逐级往上叠
DSH 不是只读一份 AGENTS.md,而是一整条链:
- 最顶层是用户全局:
~/.dsh/AGENTS.md,只有这一个,没有本地覆盖,也没 CLAUDE 版本。 - 往下是项目级:从项目根目录(默认按有没有
.git判断)一路到当前工作目录,沿途每一层目录都会找AGENTS.md、CLAUDE.md以及对应的.local版本。
这样一来,AGENTS.md 管全项目,packages/app/AGENTS.md 管那个子目录,AGENTS.local.md 又能做个人或机器本地的覆盖。越靠近当前工作目录的越“具体”,优先级也就越高。
还有个去重逻辑:同一层目录里,如果 AGENTS.md 和 CLAUDE.md 内容一模一样,就只渲染一份,不浪费 token。
注入机制:什么时候、怎么塞进上下文的
最值得讲的是注入时机。AGENTS.md 不是每轮对话都重新读一遍,而是在会话开始的时候注入一次,成为一条带 <system-reminder> 标记的持久用户消息,放进这段会话的历史里。之后整个会话都能看到它,但不用反复重复占 token。
改了文件怎么办?它没有文件监视器,不会实时感知。而是在这几件事发生时去对账:
- 下一次成功的
read/write/edit文件操作; - 会话恢复的时候;
- 历史被压缩折叠出可见范围之后重新组合。
而且它有个坑:用 bash 敲 cd 是不会触发发现的——因为每次 bash 调用都是全新 shell,DSH 只认第一方文件系统工具的动作。所以我动手改完 AGENTS.md,通常要等下一次文件操作,或用 read 碰一下,它才会把“更新后的指令”注入进来。
另外注意一点,注入内容是有预算上限的:总量受 maxBytes 约束,超出就先省略较宽泛的文件、最具体的才会被截断;单个源文件读取上限默认 1 MiB,太大的文件会被直接忽略。所以规范要写精,写成一整本大部头反而可能被砍。
最后说点我的判断
把这一套看完,我对自己那个“让 AI 长期记住”的需求就有了清晰答案:写进 AGENTS.md 是最省心的——每次会话自动带上、改动自动更新、还按目录支持分层覆盖。真正需要“时刻记住”的那几条铁律,可以再放一句进人设里;不需要常驻、按任务才用的规约,则适合写成 SKILL.md 技能,省 token 还不占上下文。
DSH 的“记忆”不是一个黑盒魔法,它就是“事件日志把发生过的事记牢 + 注入机制把你指定要记住的东西按时送进上下文”这么两件事的组合。弄明白它是怎么存的、怎么喂的,你就能决定该把自己的内容放到哪一层了。
更多推荐



所有评论(0)