最近在 GitHub 上闲逛的时候,我发现了 [[李继刚]] 整理的一批 [[Claude Skills]],出于好奇就 clone 下来翻了翻仓库。打开目录一眼扫过去,几乎所有 Skill 输出的文档文件名都长得很特别,大致是这样的形式: 20260527162000==z--投资分析-ajinomoto-2802。这种带着长串数字时间戳、夹杂 == 和 – 分隔符的写法,一眼看上去既不像我熟悉的 [[Jekyll]] 那种 YYYY-MM-DD-title.md,也不像 [[Obsidian]] 里常见的中文标题直接做文件名,反而有种”工程化”过头的味道。
我最初的猜测是 [[Zettelkasten]] 命名法,毕竟时间戳作为唯一 ID、之后跟着标题,这是卢曼卡片盒笔记法的经典做法之一。但仔细比对了一下我之前看过的几种 Zettelkasten 工具,发现这种带 等于等于 SIGNATURE 中间字段的命名方式其实并不是 Zettelkasten 通用做法,搜了一圈之后才确认,这是 [[Emacs]] 生态下一个叫做 [[Denote]] 的笔记包所使用的文件命名规则。既然好奇心已经被勾起来了,那干脆就花点时间系统性地了解一下 Denote,顺便看看作为一个长期使用 Obsidian 的人,我能从它的设计里借鉴到什么。
从一个奇怪的文件名讲起
回到最开始那个文件名 20260527162000==z--投资分析-ajinomoto-2802 ,如果不知道背后的命名规则,看起来就像是某种自动生成的临时缓存文件。但实际上,这个文件名里的每一段都承担着明确的语义。前面 14 位 20260527162000 是一个精确到秒的时间戳,等同于 ISO 8601 中 20260527T162000 这种写法的紧凑表达,代表这条笔记创建于 2026 年 5 月 27 日 16 点 20 分 00 秒。这串时间戳并不是给人看的,而是给文件本身一个不会重复、不会被改名影响的唯一标识符 (Identifier)。
接着是 等于等于 z 这一段,== 是 Denote 中专门用来分隔标识符与签名 (Signature) 的符号,而 z 则是这条笔记的签名内容。签名在 Denote 中是一个完全自由的字段,你可以用它来记录卡片在卢曼式 folgezettel 体系中的序号 (比如 1a、1a1、1a2),也可以用它表达上下文 (比如 work、home、project-a),甚至可以单纯地放一个版本号或者优先级标记。在李继刚的 Skill 输出里,z 看起来更像是某种约定俗成的分类前缀,具体语义需要看他自己的整理逻辑。
最后是 --投资分析-ajinomoto-2802 这一段,-- 是签名与标题之间的分隔符,后面跟的就是标题本身,通常会做 slugify 处理,把空格、标点都换成短横线。完整解析之后,这个文件名实际上等同于一句完整的描述: “这是一条创建于 2026 年 5 月 27 日 16:20 的笔记,签名为 z,标题为’投资分析 ajinomoto 2802’“。从无意义的字符串到结构化信息,只需要知道分隔符的含义就能 100% 还原,这正是 Denote 文件命名设计的核心要点。
Denote 是什么以及它为何而生
[[Denote]] 是 [[Protesilaos Stavrou]] (社区里多数人简称他为 Prot) 开发的一个 Emacs 笔记管理包,首个公开版本发布于 2022 年,如今已经进入 [[GNU ELPA]] 仓库,可以通过 Emacs 自带的包管理器直接安装。Prot 同时也是 [[Modus Themes]] 、 [[Ef Themes]] 、 [[Logos]] 、 [[Tempel-Collection]] 等多个广为人知的 Emacs 包的作者,他的整个工具链有一以贯之的设计风格,那就是简洁、可读、尽量贴近 Emacs 原生能力,而不引入额外的运行时依赖。Denote 也完整继承了这种思路。
理解 Denote 的设计动机,需要先看看它出现之前的同类工具。在 Emacs 生态里,谈到笔记管理几乎绕不开 [[Org-roam]],它基于 [[Org-mode]] 构建,通过 sqlite 数据库维护笔记之间的链接、反链以及节点元信息,功能强大但也带来不少代价: 数据库可能损坏需要重建,大量笔记下索引会变慢,而且这些笔记的语义信息一旦离开 Org-roam 的数据库就难以复现,可移植性受到影响。Prot 的看法是,如果笔记的核心价值是长期保存和自由迁移,那么任何外部数据库都是潜在的负担,真正应该被信任的只有文件本身。
基于这个判断,Denote 把所有的关键元数据都压进了文件名,数据库被彻底拿掉了。一条笔记的标识符、标题、关键词、签名,统统出现在文件名中,文件内部还会同步一份 YAML 或 Org 前置元数据 (front matter) 作为冗余备份。这样的结果是,即使你不打开 Emacs,只用 ls、grep、find 这些 Unix 标准工具,也能完成查找、过滤、批量改名等绝大多数管理操作。哪怕将来彻底不再使用 Emacs,这些文件搬到 [[VS Code]]、Obsidian 或者任何文本编辑器里,信息也不会丢。
文件命名规则的完整拆解
Denote 的标准文件名格式可以用一个模式来概括: IDENTIFIER == SIGNATURE–TITLE__KEYWORDS.EXTENSION。每一段都有自己的分隔符,而且这些分隔符是被精心挑选过的,既不容易和正常的标题、标签内容冲突,也方便用正则表达式做切分。
IDENTIFIER 部分使用 YYYYMMDDTHHMMSS 这种紧凑的 ISO 风格时间戳,创建之后就不再变动,即使你重命名文件、修改标题、调整关键词,这个 ID 也始终保持不变。这一点至关重要,因为 Denote 的链接机制完全是基于 ID 的: 一条笔记内部引用另一条笔记时,记录的不是文件名,而是 14 位时间戳。这就意味着标题随便改、关键词随便加,链接永远不会失效。我个人觉得这是 Denote 整套设计里最优雅的部分之一,因为很多笔记系统的”链接腐烂”问题本质上都是把人类可读的标题当成了机器可识别的 ID,而 Denote 干脆把这两者分开来。
SIGNATURE 部分是可选的,夹在 == 之间。前面已经提到,它可以承载任意语义,Prot 自己也强调这是给用户自由发挥的字段。一个很有意思的用法是模拟 Niklas Luhmann 真正的卡片盒编号体系,比如 1、1a、1a1、1a2、1b,通过字符顺序就能在文件浏览器里看到笔记之间的逻辑关系。这种”folgezettel”结构在数字化 Zettelkasten 工具里其实长期缺乏好的实现,因为大多数工具只关心双向链接而忽略了顺序关系,Denote 通过签名字段把这件事重新带回了视野。
TITLE 部分跟在 -- 之后,默认会被处理成全小写、用短横线连接的 slug。如果你的标题里有中文,Denote 会保留中文字符,只把空格和标点替换掉,所以李继刚那个例子里中文标题原样出现是完全合理的行为。KEYWORDS 部分跟在 __ 之后,多个关键词用下划线连接,比如 __emacs_productivity_zettelkasten。最后是文件扩展名,Denote 支持 .org、.md、.txt 三种主流文本格式,你可以在配置里指定默认使用哪一种,不同格式对应的前置元数据写法也会自动调整。
值得强调的是,这五个组件的顺序并不是写死的,Denote 提供了 denote-file-name-components-order 这个变量,允许你按需调整顺序。比如有人喜欢把签名放在最前面方便排序,有人则希望关键词紧跟在标识符后面方便扫视,这些都可以在自己的配置里调整。但无论顺序如何变化,各段之间的分隔符 (– 、 __ 、 == ) 是固定不变的,这保证了不同顺序的文件名仍然可以被同一套解析逻辑识别。
Denote 和 Zettelkasten 是什么关系
我在调研过程中发现一个常见的误解,就是把 Denote 等同于 [[Zettelkasten]],或者反过来认为 Denote 仅仅是 Zettelkasten 的 Emacs 实现之一。事实上两者关系更微妙: Denote 借鉴了 Zettelkasten 的一些核心理念 (比如唯一 ID、纯文本、原子化笔记、链接为主),但它本身并不强制你使用 Zettelkasten 方法论。
Zettelkasten 的精髓在于卡片之间的网络结构、每张卡片只表达一个想法、通过持续地建立连接来涌现知识,这是方法论层面的事情。Denote 提供的只是一组工具: 文件名生成、链接管理、反链显示、关键词过滤,至于你怎么组织内容,完全是个人选择。你可以用 Denote 写日记、做项目笔记、记会议纪要、整理引用文献,这些场景全部都不需要遵守 Zettelkasten 的任何规则。
但如果你确实想严肃地实践 Zettelkasten,Denote 提供的工具组合恰好非常合适。基于时间戳的唯一标识符天然适合做原子化笔记的 ID,签名字段可以承载卢曼式的层级编号,关键词字段对应 Zettelkasten 中的索引词,链接和反链则实现了卡片之间的网络结构。Prot 还专门开发了一个可选扩展 denote-sequence.el,专门用来管理 folgezettel 序列,对深度 Zettelkasten 用户来说是个加分项。
与 Obsidian 的核心差异
作为一个 Obsidian 重度用户,我自然会把 Denote 和 Obsidian 做对比。两者表面上看起来很像: 都是基于纯文本,都强调链接,都不锁定厂商,但深入到具体的使用体验,差异其实非常明显。
Obsidian 的核心是 wiki-link 风格的双向链接,你通过 [[笔记标题]] 来引用另一条笔记,链接的解析完全依赖于标题。这种方式人类可读、操作直观,但缺点也很清楚: 一旦你改了笔记标题,所有引用都需要同步更新,Obsidian 会自动帮你做这件事,但代价是它必须维护一份反向索引。Denote 走的是另一条路,链接的目标永远是 14 位时间戳 ID,标题只是用来展示的,改标题对链接没有任何影响。从健壮性角度看,Denote 的方式更稳妥,但从可读性角度看,Obsidian 的 [[wiki-link]] 更友好。
文件命名上,Obsidian 把命名权完全交给用户,可以用中文、英文、长句子、emoji,只要文件系统支持都行。Denote 则是命名权交给工具,你输入标题和关键词,工具按规则生成文件名。前者灵活但容易混乱,后者整齐但需要适应。我自己用 Obsidian 这么多年,文件命名上确实存在一些不一致,比如有的笔记用中文标题、有的用英文,有的是动词开头、有的是名词开头,真正想搜索的时候不得不依赖全文搜索而不是文件名。Denote 这种”工具兜底”的方式在长期管理上确实能避免很多混乱。
元数据上,Obsidian 把所有信息放在 YAML frontmatter 里,文件名只是个标题。Denote 则把标识符、签名、标题、关键词全部塞进文件名,frontmatter 只是备份。这意味着 Denote 的文件名比 Obsidian 的更”重”,但也带来一个好处: 只看文件列表就能掌握全部关键信息,不需要打开文件,也不需要任何外部工具的帮助。在脱离 Emacs 或 Obsidian 的环境下,比如在终端、在 [[rclone]] 同步过程中、在文件管理器里浏览,这种差异会变得很明显。
生态层面,Obsidian 拥有庞大的第三方插件市场,移动端、桌面端体验完善,新手上手成本极低。Denote 则深度绑定 Emacs,如果你不是 Emacs 用户,引入 Denote 的成本几乎等同于学习一个新的编辑器,这是它最大的门槛。所以对绝大多数人来说,Denote 并不是 Obsidian 的替代品,但它的设计思路完全值得借鉴。
在 Obsidian 中借鉴 Denote 的思路
调研到这里,我开始思考一个更实际的问题: 我能不能在不放弃 Obsidian 的前提下,把 Denote 那些好用的设计抽出来,应用到自己的工作流里。答案是大部分都能借鉴,而且并不需要太复杂的改造。
第一个可以借鉴的是文件名结构。我之前给 Obsidian 笔记起名比较随性,完全靠当时的心情和标题灵感。试着改用 Denote 风格的 ID--TITLE__TAGS.md 之后,我发现两个明显的好处: 一是按文件名排序就等同于按创建时间排序,不需要依赖 frontmatter 里的 create_time 字段; 二是在 [[git]] 历史里看 commit diff 时,文件名本身就携带了足够的上下文,不需要打开文件就能知道改的是哪一类笔记。当然如果你完全照搬 Denote 的命名规则,Obsidian 的双链解析会有点别扭,因为 [[ID--TITLE__TAGS]] 这种写法既长又难看,这时候可以借助 [[Obsidian]] 的 alias 字段,在 frontmatter 里把人类可读的标题作为别名,链接时仍然用 [[标题]] 即可。
第二个可以借鉴的是签名字段的概念。Obsidian 默认没有 SIGNATURE 这个层级,但你完全可以用文件名前缀或者 frontmatter 字段来模拟。比如我可以约定所有读书笔记前缀为 book-,所有日记前缀为 diary-,所有项目笔记前缀为 proj-,这就相当于一个简化版的 signature 系统。结合 Obsidian 的全局搜索和 Dataview 查询,这种前缀分类的检索效率非常高。
第三个可以借鉴的是”文件即数据库”的思路。Obsidian 本身就是基于纯文本的,但很多用户依赖大量插件来管理元数据,等于又把数据迁移回了某种”数据库”。Denote 的极简思路提醒我,任何依赖插件才能解读的信息,都是有失效风险的,真正可靠的信息应该写在文件名和 frontmatter 这种通用结构里。我打算把自己一些散在插件状态里的标签和分类,统一迁移到 frontmatter 里去。
第四个可以借鉴的是链接的稳定性。虽然 Obsidian 不支持基于 ID 的链接,但我们可以利用 Obsidian 的 aliases 字段来缓解标题改名带来的链接腐烂问题。把笔记之前用过的所有标题都加到 aliases 里,即使现在的主标题变了,通过别名也能正常链接,这在一定程度上接近了 Denote 的”ID 永远稳定”的效果。
适合谁使用以及不适合谁
把研究的结果落到决策层面,我自己的判断是: 如果你已经是 Emacs 用户,并且对笔记的长期可移植性、不依赖数据库、纯文本至上这些原则非常认同,Denote 几乎是当前最优雅的选择。它的代码风格清晰、文档质量极高 (Prot 写的 manual 在 Emacs 包里算得上一流)、社区也比较活跃,可以放心地长期使用。
但如果你是 Obsidian 用户、Notion 用户,或者完全没有 Emacs 经验,我不会建议你切换到 Denote。门槛不在 Denote 本身,而在 Emacs 这个底座,从快捷键到配置文件到调试方式,都是一整套需要时间投入的体系。更现实的做法是,把 Denote 当作一个设计案例来研究,从中提炼出适合自己工具链的部分。本文前面提到的几个借鉴思路,都不需要你真的去装 Emacs。
对于像李继刚那样使用 Denote 来组织 Skill 输出的场景,我觉得是一个非常合理的选择。AI 生成的笔记、卡片、文档天然需要一个唯一标识符 (避免重复)、一个可机器解析的元数据结构 (方便后续处理)、以及对未来工具变迁的鲁棒性 (不绑定特定平台),Denote 的命名规则恰好把这三件事一次性解决。我看完之后也开始思考是不是把自己的 [[Claude Code]] 输出、[[ChatGPT]] 对话归档也用这套规则整理起来,把 AI 时代产生的大量”中间态文本”变成可长期检索的资产。
最后
研究完 Denote 之后,我最大的感受是,工具的价值不仅仅在于它能不能解决问题,更在于它的设计哲学能不能给你新的视角。我并不打算从 Obsidian 切换到 Denote,但 Denote 那种”信任文件名、不信任数据库”的思路,确实让我对自己当前的笔记管理方式做了一次反思。文件名里能放进去的信息,我之前是不是都用到了; 标题和 ID 的分离,我能不能借助 alias 字段实现; 跨工具的可移植性,我有没有低估它的长期价值。
回到那个最初引发好奇的文件名 20260527162000==z--投资分析-ajinomoto-2802,现在再看它,已经不再是一串”工程化过头”的随意字符,而是一份非常克制、非常自律的信息组织方式。每一段都承担明确的语义,每一段都为长期保存而设计,每一段都尽量减少对外部工具的依赖。这种克制本身就是一种值得学习的态度。
下一步我想做的事情是: 在自己的 Obsidian vault 里挑一个子目录,比如 [[Zettelkasten]] 这个文件夹,试着改用 Denote 风格的命名规则跑一段时间,看看长期下来的检索效率、链接稳定性、心智负担是不是真的有改善。如果效果确实好,也许会扩展到整个仓库。这种”借鉴而不照搬”的方式,大概是面对优秀工具时最务实的态度。