把"懂用户"拆成三层:偏好(你是谁)、工作流(你怎么干)、对齐(这次你要什么)。这三层的时间尺度不同,但共享同一种工程取舍——读时灵活、写时延迟,让 KV cache 不被频繁打断。
先用一张图把整套系统压成三行,后面三节分别拆解。结构上是从慢到快:偏好最稳,工作流次之,对齐发生在每一次任务开始的瞬间。
偏好层管的是跨会话、低频更新的信息:编码风格、工作语言、组织约束、代码评审口味。这一层最容易被忽视,因为它"不变"——但正因为它不变,它才适合放进 system prompt 的最前面,吃满 KV cache 的 prefix 命中率。
业界这几年有两个有代表性的实现:Anthropic 的 Claude Code 和 Nous Research 的 Hermes Agent。两者的方案有 80% 的重合,但 20% 的差异恰好暴露了"偏好层应该怎么写"的工程口味。
| 维度 | Claude Code | Hermes Agent |
|---|---|---|
| 数据格式 | CLAUDE.md · markdown,无强制结构 |
USER.md + MEMORY.md,分别记用户与 agent |
| 分层结构 | 组织 / 项目 / 用户 / 本地,4 层继承 | 单层,所有内容都在一个文件里 |
| 字符上限 | 无强制上限(实际由 context window 约束) | USER.md ≈ 500 token · MEMORY.md ≈ 800 token |
| 写入路径 | 用户手写 + auto memory(agent 在每个回合末抽取) | 仅通过 memory_tool 的 add / replace / remove |
| 编辑入口 | /memory 命令打开文件 · @path 跨文件 import |
对话中靠工具调用 · 文件直接落盘 |
| 注入与冻结策略 | 会话开始注入;session 内修改的可见性未明确 | 会话开始整段注入并冻结,session 内改动落盘但下个 session 才生效 |
| 设计动机 | 分层归档:项目共享 + 个人偏好 + 团队规范同时存在 | 明确为保 prefix cache hit rate 而冻结 |
但偏好层有一个隐性问题:用户不会主动写 CLAUDE.md。"配置自己的偏好"对绝大多数用户来说是一项需要先想清楚再动手的活,可偏好本身又是在工作中浮现的——你不知道这个 LLM 会怎么犯错,怎么提前定义"我不喜欢的写法"?
一个典型例子是 Karpathy Guidelines。这套规则是 Karpathy 公开吐槽 LLM 编码踩坑后被社区整理出来的——每一条规则都对应一类没有明示就会反复出现的反模式。比如一堆 try / except 包着一段根本不会失败的代码,只是为了"看起来稳"。这种偏好是用户用了 N 次 agent 之后才发现"哦原来我不喜欢这样",而不是 day 0 就能写下来。
把"反复给 LLM 改同一类毛病"这件事变成可注入的偏好规则。这套规则不是 day 0 拍脑袋写的,是从大量 LLM 编码踩坑里逐渐沉淀出来的——它的每一条都对应一种"不说就会再犯"的反模式。
memory_tool(agent 主动调用工具落盘)、Cursor 的 rules 文件——本质都是为了解决同一件事:让偏好可以被被动地、增量地积累。Karpathy Guidelines 这种"事后整理出来的反模式集合",正是它们的目标产物。
偏好层只能装一次会话内不变的东西。但用户真正的工作流——比如训练完一个模型,要走 merge → 部署 → 测试 → 结果统计这一整条流水线——是 多次任务后才浮现、并且每次都有细微差异的模式。这种东西写在 CLAUDE.md 里既写不全也写不对,它们应该被 agent 自己发现、自己写、自己维护。
举个每天都在重复的场景——训练完一个新 checkpoint 后的标准流程:
这正是 Skill 自动迭代 的舞台:把骨架沉淀成 skill,差异作为参数与示例追加进去,让 skill 随用随长。Anthropic 已经把"capability 打包"做成了 Skills——每个 skill 是一个 SKILL.md 加可选脚本,agent 按描述决定加载。但这套机制目前主要靠人工写。我想把它自动化——每隔 N 个回合启动一个 skill evaluator,扫描这段对话,判定是否出现了值得沉淀的工作流:
第二个工程问题来自规模:随着用户使用时间变长,skill 会越攒越多,但 system prompt 装不下所有 skill 的描述(光是 SKILL.md 的 name + description 字段就占 token)。这时需要一套类似缓存的汰换策略。
简单可用的策略:LRU + 高价值锁定。统计每个 skill 在过去 K 个 session 中的命中次数,长期未命中的 skill 进入"冷池"——它的 SKILL.md 描述不再注入 system prompt(节省 token),但脚本与参考文件仍保留在磁盘。如果用户后来又触发了相关任务,evaluator 会把它从冷池捞回来。
前两层解决"长期的我",对齐层解决"此时此刻的我"——不同任务里总有无法从历史推断的实现细节,这一层只能靠问。
对齐机制按场景并存,不是替代关系:早期靠 /clearify-task 这类命令显式触发 agent 抛选择题;新模型(如 Opus 4.7)已经会自己主动追问实现细节,这件事被一定程度内化进了模型;但对不关注实现细节的用户,问 API 选哪个没意义,他们要的是产品需求层面的对齐——这只能靠 harness 风格的 plan-eval loop 来做。三种方案各有适用边界,下面把其中两种最成熟的代表放在一起看:
| 对齐维度 | clearify-task 风格 | harness 风格 |
|---|---|---|
| 任务规模 | 分钟级到小时级 | 小时级到周级 |
| 对齐层级 | 实现细节里模棱两可的点 | 顶层需求 + Sprint 契约(feature × check × git) |
| 用户参与频率 | 开场一次 | 开场审 feature list + 关键 milestone 审 Sprint 契约 |
| 对齐产物 | 2–4 道针对性多选题 | feature list + 每个 feature 的 Sprint 契约(check + git 边界) |
| 承载形式 | slash 命令 / prompt 模板 | 子 agent / 工作流引擎 |
| 失败代价 | 低(重跑一次任务) | 高(中间产物难复用) |
这两种风格不是互斥关系。一个成熟的 agent 系统应当按任务复杂度自动选择:简单任务直接干、中等任务挂 clearify-task、复杂任务进 harness loop。判定逻辑可以放在主 agent 的开场 routing 里,也可以做成一个独立的 complexity classifier。
更进一步,这两种风格的底层共性都是把"还没说清楚的需求"显式化——前者用选择题、后者用 spec 文档。两者都遵循同一条直觉:提问的 token 远比重做的 token 便宜。
把三层放在同一次对话的时序里——「在哪个位置插什么机制」就清楚了。读取(注入)发生在 session 开头,写入(落盘)发生在 session 结尾,下次 session 才生效。这就是读时灵活、写时延迟的全部含义。