<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.einverne.info/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.einverne.info/" rel="alternate" type="text/html" hreflang="zh" /><updated>2026-06-02T04:04:46-05:00</updated><id>https://blog.einverne.info/feed.xml</id><title type="html">Verne in GitHub</title><subtitle>博客内容涵盖多个主题，包括 Jekyll、Linux 命令、编程语言学习笔记、各种产品体验、经验总结等。还深入探讨 Git、Java、Vim、Linux、Android 等技术领域，分享关于 Docker、Go、Spring、开源项目的见解。</subtitle><author><name>Ein Verne</name><email>git@einverne.info</email></author><entry><title type="html">Surfingkeys：比 Vimium 更强大的浏览器键盘控制扩展</title><link href="https://blog.einverne.info/post/2026/06/surfingkeys-chrome-extension.html" rel="alternate" type="text/html" title="Surfingkeys：比 Vimium 更强大的浏览器键盘控制扩展" /><published>2026-06-02T00:00:00-05:00</published><updated>2026-06-02T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/06/surfingkeys-chrome-extension</id><content type="html" xml:base="https://blog.einverne.info/post/2026/06/surfingkeys-chrome-extension.html"><![CDATA[<p>最早接触 [[Vimium]] 是在学习 Vim 编辑器之后，那种能用键盘完全控制浏览器的感觉确实让人着迷。不用频繁移手到鼠标，链接跳转、页面滚动、标签切换全用键盘搞定，效率提升的体验是实实在在的。用了几年 Vimium 之后，我开始接触到 [[Surfingkeys]]，起初以为不过是另一个同类扩展，但深入用下来才发现这两者的差距远比我想象的大。</p>

<p><img src="https://pic.einverne.info/images/2026-06-02-10-00-00-surfingkeys-cover.png" alt="Surfingkeys 键盘控制浏览器" /></p>

<h2 id="为什么要用键盘控制浏览器">为什么要用键盘控制浏览器</h2>

<p>对于长期使用 [[Vim]] 的人来说，键盘操作的肌肉记忆已经根深蒂固。在编辑器里习惯了 hjkl 导航，切换到浏览器还要去抓鼠标总觉得很割裂。更重要的是，对于程序员、研究者或者任何需要长时间使用浏览器查阅资料的人，频繁移手到鼠标会带来明显的效率损失，甚至是手腕疲劳。</p>

<p>Vimium 在这个领域确立了基础范式：用 <code class="language-plaintext highlighter-rouge">f</code> 键激活链接提示、<code class="language-plaintext highlighter-rouge">hjkl</code> 滚动页面、<code class="language-plaintext highlighter-rouge">J</code>/<code class="language-plaintext highlighter-rouge">K</code> 切换标签页、<code class="language-plaintext highlighter-rouge">o</code>/<code class="language-plaintext highlighter-rouge">O</code> 打开链接等等。这套设计简洁而高效，是很多 Vim 用户的第一个浏览器键盘扩展。不过，随着使用深入，你会发现 Vimium 的局限性也相当明显：配置能力有限、不支持自定义 JavaScript、在某些页面上会失效、搜索功能比较基础。这时候 Surfingkeys 就显示出它的价值了。</p>

<h2 id="surfingkeys-是什么">Surfingkeys 是什么</h2>

<p>Surfingkeys 是一款同样基于 Vim 键位设计理念的浏览器扩展，支持 Chrome、Firefox 和 Safari。它的作者从 2015 年开始开发，目前仍在持续维护更新，在 GitHub 上有超过 13000 个星标。和 Vimium 相比，Surfingkeys 走的是功能更丰富、可定制性更强的路线，可以把它理解为浏览器键盘控制领域的”Neovim”，而 Vimium 则更像是轻量的”原版 Vim”。</p>

<p>核心上，两者都提供了类似的基础操作：用字母键激活页面上的链接、键盘滚动页面、标签页管理等。但 Surfingkeys 在这个基础上走得更远——它允许用户直接用 JavaScript 编写自定义快捷键逻辑，内置了一个独立的 Vim 编辑模式，甚至有自己的 PDF 查看支持和多引擎搜索系统。</p>

<h2 id="与-vimium-的核心差异">与 Vimium 的核心差异</h2>

<h3 id="配置的深度">配置的深度</h3>

<p>Vimium 的配置主要通过它的设置页面完成，支持自定义搜索引擎和重新映射键位，但能做的事情相对有限。Surfingkeys 则提供了一个完整的 JavaScript 配置接口，你可以在配置文件里写任意 JS 代码来定义行为。这意味着你可以创建复杂的按键序列，根据当前网站动态调整键位，甚至调用浏览器 API 做更高级的操作。</p>

<p>举个实际例子：我在 Surfingkeys 的配置里写了几个针对特定网站的快捷键，比如在 [[GitHub]] 上快速跳到文件树、在 Twitter/X 上快速点赞等。这种程度的定制在 Vimium 里根本无法实现。</p>

<h3 id="内置-vim-编辑器">内置 Vim 编辑器</h3>

<p>Surfingkeys 有一个非常实用的功能：在任何网页的文本输入框里，按 <code class="language-plaintext highlighter-rouge">Ctrl+i</code> 可以弹出一个内置的 [[CodeMirror]] 编辑器，用完整的 Vim 模式来编写文本。这对于需要在网页上写长文本的人来说非常有价值。写评论、填表单、甚至在 GitHub 上编辑文件，都可以在这个编辑器里用 Vim 操作完成。Vimium 完全不提供这个功能。</p>

<h3 id="多引擎搜索系统">多引擎搜索系统</h3>

<p>按 <code class="language-plaintext highlighter-rouge">o</code> 或 <code class="language-plaintext highlighter-rouge">O</code> 调出 Surfingkeys 的搜索栏之后，你可以在多个搜索引擎之间快速切换。配置文件里可以添加任意数量的搜索引擎，每个都绑定一个短前缀。比如输入 <code class="language-plaintext highlighter-rouge">g 关键词</code> 用 [[Google]] 搜索，输入 <code class="language-plaintext highlighter-rouge">y 关键词</code> 在 [[YouTube]] 搜索，输入 <code class="language-plaintext highlighter-rouge">gh 关键词</code> 在 GitHub 搜索。这个系统比 Vimium 的搜索引擎支持灵活得多，而且不需要每次都切换默认搜索引擎。</p>

<h3 id="视觉模式与文本选择">视觉模式与文本选择</h3>

<p>Surfingkeys 实现了比 Vimium 更完整的视觉模式，按 <code class="language-plaintext highlighter-rouge">v</code> 进入视觉模式后，可以用 Vim 的文本对象（如 <code class="language-plaintext highlighter-rouge">w</code>、<code class="language-plaintext highlighter-rouge">e</code>、<code class="language-plaintext highlighter-rouge">b</code>）来精确选择文字，然后按 <code class="language-plaintext highlighter-rouge">y</code> 复制。在需要精确复制页面文字的场景里，这个功能非常好用，比手动拖选鼠标准确得多。</p>

<h3 id="对-pdf-的支持">对 PDF 的支持</h3>

<p>Vimium 在 PDF 页面基本无效，而 Surfingkeys 对 PDF 有原生支持，可以用键盘翻页、搜索内容、跳转页码。这对于经常阅读 PDF 文档的用户来说是个实实在在的优势。</p>

<h2 id="实际使用体验与配置建议">实际使用体验与配置建议</h2>

<p>上手 Surfingkeys 的第一步建议先用默认配置熟悉两周。它的默认键位和 Vimium 有不少重合，但也有差异，比如链接提示默认用 <code class="language-plaintext highlighter-rouge">f</code> 激活（跟 Vimium 相同），但标签页切换是 <code class="language-plaintext highlighter-rouge">E</code>/<code class="language-plaintext highlighter-rouge">R</code>（Vimium 是 <code class="language-plaintext highlighter-rouge">J</code>/<code class="language-plaintext highlighter-rouge">K</code>）。这些差异需要时间适应。</p>

<p>配置文件可以直接在扩展设置页面里编辑，格式是纯 JavaScript。官方文档有完整的 API 说明，常用的配置项包括：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">api.map('newKey', 'oldKey')</code> — 重新映射键位</li>
  <li><code class="language-plaintext highlighter-rouge">api.mapkey('key', 'description', function() { ... })</code> — 创建新的自定义快捷键</li>
  <li><code class="language-plaintext highlighter-rouge">api.addSearchAlias('prefix', 'name', 'searchUrl')</code> — 添加搜索引擎</li>
</ul>

<p>有一点要注意：Surfingkeys 的键位激活逻辑偶尔会和某些单页应用的前端框架产生冲突，导致在某些网站的特定输入场景下快捷键失灵。这时候可以通过配置 <code class="language-plaintext highlighter-rouge">sites</code> 白名单或黑名单来处理。Vimium 也有类似问题，不过因为功能更简单，冲突概率相对低一些。</p>

<p>另外，如果你是从 Vimium 迁移过来的，可以在社区里找别人分享的仿 Vimium 配置，先让键位基本一致，然后再逐步加入 Surfingkeys 独有的功能。这样迁移成本会低很多，不用一次性重新学习所有键位。</p>

<h2 id="最后">最后</h2>

<p>用了 Vimium 几年再换到 Surfingkeys，感受最直接的提升是配置自由度带来的效率改善。Vimium 是一个非常好的入门工具，设计简洁、开箱即用、学习曲线低，对于刚开始探索键盘操控浏览器的人来说是更好的起点。但如果你已经把 Vimium 用熟了，想要更深度的定制、更强大的文本操作、或者让浏览器键盘体验更接近真实的 Vim 工作流，Surfingkeys 是值得花时间迁移的选择。</p>

<p>两者的核心差异不在于基础功能的好坏，而在于你愿意投入多少时间去配置和适应。Vimium 的 80% 功能几乎零配置就能用上，Surfingkeys 的 100% 功能需要你真正去读文档、写配置。如果你是那种会去给 Vim 写几百行配置的人，Surfingkeys 会让你觉得非常顺手。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="经验总结" /><category term="chrome-extension" /><category term="vim" /><category term="keyboard" /><category term="browser" /><category term="productivity" /><category term="surfingkeys" /><summary type="html"><![CDATA[深入介绍 Surfingkeys Chrome 扩展，与 Vimium 的功能对比，以及为什么它是键盘党更好的选择。]]></summary></entry><entry><title type="html">Denote 介绍 Emacs 下基于文件名的笔记系统</title><link href="https://blog.einverne.info/post/2026/05/introducing-denote-emacs-notes.html" rel="alternate" type="text/html" title="Denote 介绍 Emacs 下基于文件名的笔记系统" /><published>2026-05-28T00:00:00-05:00</published><updated>2026-05-28T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/05/introducing-denote-emacs-notes</id><content type="html" xml:base="https://blog.einverne.info/post/2026/05/introducing-denote-emacs-notes.html"><![CDATA[<p>最近在 GitHub 上闲逛的时候，我发现了 [[李继刚]] 整理的一批 [[Claude Skills]]，出于好奇就 clone 下来翻了翻仓库。打开目录一眼扫过去，几乎所有 Skill 输出的文档文件名都长得很特别，大致是这样的形式: <code class="language-plaintext highlighter-rouge">20260527162000==z--投资分析-ajinomoto-2802</code>。这种带着长串数字时间戳、夹杂 == 和 – 分隔符的写法，一眼看上去既不像我熟悉的 [[Jekyll]] 那种 <code class="language-plaintext highlighter-rouge">YYYY-MM-DD-title.md</code>，也不像 [[Obsidian]] 里常见的中文标题直接做文件名，反而有种”工程化”过头的味道。</p>

<p>我最初的猜测是 [[Zettelkasten]] 命名法，毕竟时间戳作为唯一 ID、之后跟着标题，这是卢曼卡片盒笔记法的经典做法之一。但仔细比对了一下我之前看过的几种 Zettelkasten 工具，发现这种带 等于等于 SIGNATURE 中间字段的命名方式其实并不是 Zettelkasten 通用做法，搜了一圈之后才确认，这是 [[Emacs]] 生态下一个叫做 [[Denote]] 的笔记包所使用的文件命名规则。既然好奇心已经被勾起来了，那干脆就花点时间系统性地了解一下 Denote，顺便看看作为一个长期使用 Obsidian 的人，我能从它的设计里借鉴到什么。</p>

<h2 id="从一个奇怪的文件名讲起">从一个奇怪的文件名讲起</h2>

<p>回到最开始那个文件名 <code class="language-plaintext highlighter-rouge">20260527162000==z--投资分析-ajinomoto-2802</code> ，如果不知道背后的命名规则，看起来就像是某种自动生成的临时缓存文件。但实际上，这个文件名里的每一段都承担着明确的语义。前面 14 位 <code class="language-plaintext highlighter-rouge">20260527162000</code> 是一个精确到秒的时间戳，等同于 ISO 8601 中 <code class="language-plaintext highlighter-rouge">20260527T162000</code> 这种写法的紧凑表达，代表这条笔记创建于 2026 年 5 月 27 日 16 点 20 分 00 秒。这串时间戳并不是给人看的，而是给文件本身一个不会重复、不会被改名影响的唯一标识符 (Identifier)。</p>

<p>接着是 等于等于 z 这一段，== 是 Denote 中专门用来分隔标识符与签名 (Signature) 的符号，而 z 则是这条笔记的签名内容。签名在 Denote 中是一个完全自由的字段，你可以用它来记录卡片在卢曼式 folgezettel 体系中的序号 (比如 1a、1a1、1a2)，也可以用它表达上下文 (比如 work、home、project-a)，甚至可以单纯地放一个版本号或者优先级标记。在李继刚的 Skill 输出里，z 看起来更像是某种约定俗成的分类前缀，具体语义需要看他自己的整理逻辑。</p>

<p>最后是 <code class="language-plaintext highlighter-rouge">--投资分析-ajinomoto-2802</code> 这一段，<code class="language-plaintext highlighter-rouge">--</code> 是签名与标题之间的分隔符，后面跟的就是标题本身，通常会做 slugify 处理，把空格、标点都换成短横线。完整解析之后，这个文件名实际上等同于一句完整的描述: “这是一条创建于 2026 年 5 月 27 日 16:20 的笔记，签名为 z，标题为’投资分析 ajinomoto 2802’“。从无意义的字符串到结构化信息，只需要知道分隔符的含义就能 100% 还原，这正是 Denote 文件命名设计的核心要点。</p>

<h2 id="denote-是什么以及它为何而生">Denote 是什么以及它为何而生</h2>

<p>[[Denote]] 是 [[Protesilaos Stavrou]] (社区里多数人简称他为 Prot) 开发的一个 Emacs 笔记管理包，首个公开版本发布于 2022 年，如今已经进入 [[GNU ELPA]] 仓库，可以通过 Emacs 自带的包管理器直接安装。Prot 同时也是 [[Modus Themes]] 、 [[Ef Themes]] 、 [[Logos]] 、 [[Tempel-Collection]] 等多个广为人知的 Emacs 包的作者，他的整个工具链有一以贯之的设计风格，那就是简洁、可读、尽量贴近 Emacs 原生能力，而不引入额外的运行时依赖。Denote 也完整继承了这种思路。</p>

<p>理解 Denote 的设计动机，需要先看看它出现之前的同类工具。在 Emacs 生态里，谈到笔记管理几乎绕不开 [[Org-roam]]，它基于 [[Org-mode]] 构建，通过 sqlite 数据库维护笔记之间的链接、反链以及节点元信息，功能强大但也带来不少代价: 数据库可能损坏需要重建，大量笔记下索引会变慢，而且这些笔记的语义信息一旦离开 Org-roam 的数据库就难以复现，可移植性受到影响。Prot 的看法是，如果笔记的核心价值是长期保存和自由迁移，那么任何外部数据库都是潜在的负担，真正应该被信任的只有文件本身。</p>

<p>基于这个判断，Denote 把所有的关键元数据都压进了文件名，数据库被彻底拿掉了。一条笔记的标识符、标题、关键词、签名，统统出现在文件名中，文件内部还会同步一份 YAML 或 Org 前置元数据 (front matter) 作为冗余备份。这样的结果是，即使你不打开 Emacs，只用 <code class="language-plaintext highlighter-rouge">ls</code>、<code class="language-plaintext highlighter-rouge">grep</code>、<code class="language-plaintext highlighter-rouge">find</code> 这些 Unix 标准工具，也能完成查找、过滤、批量改名等绝大多数管理操作。哪怕将来彻底不再使用 Emacs，这些文件搬到 [[VS Code]]、Obsidian 或者任何文本编辑器里，信息也不会丢。</p>

<h2 id="文件命名规则的完整拆解">文件命名规则的完整拆解</h2>

<p>Denote 的标准文件名格式可以用一个模式来概括: IDENTIFIER == SIGNATURE–TITLE__KEYWORDS.EXTENSION。每一段都有自己的分隔符，而且这些分隔符是被精心挑选过的，既不容易和正常的标题、标签内容冲突，也方便用正则表达式做切分。</p>

<p>IDENTIFIER 部分使用 <code class="language-plaintext highlighter-rouge">YYYYMMDDTHHMMSS</code> 这种紧凑的 ISO 风格时间戳，创建之后就不再变动，即使你重命名文件、修改标题、调整关键词，这个 ID 也始终保持不变。这一点至关重要，因为 Denote 的链接机制完全是基于 ID 的: 一条笔记内部引用另一条笔记时，记录的不是文件名，而是 14 位时间戳。这就意味着标题随便改、关键词随便加，链接永远不会失效。我个人觉得这是 Denote 整套设计里最优雅的部分之一，因为很多笔记系统的”链接腐烂”问题本质上都是把人类可读的标题当成了机器可识别的 ID，而 Denote 干脆把这两者分开来。</p>

<p>SIGNATURE 部分是可选的，夹在 == 之间。前面已经提到，它可以承载任意语义，Prot 自己也强调这是给用户自由发挥的字段。一个很有意思的用法是模拟 Niklas Luhmann 真正的卡片盒编号体系，比如 <code class="language-plaintext highlighter-rouge">1</code>、<code class="language-plaintext highlighter-rouge">1a</code>、<code class="language-plaintext highlighter-rouge">1a1</code>、<code class="language-plaintext highlighter-rouge">1a2</code>、<code class="language-plaintext highlighter-rouge">1b</code>，通过字符顺序就能在文件浏览器里看到笔记之间的逻辑关系。这种”folgezettel”结构在数字化 Zettelkasten 工具里其实长期缺乏好的实现，因为大多数工具只关心双向链接而忽略了顺序关系，Denote 通过签名字段把这件事重新带回了视野。</p>

<p>TITLE 部分跟在 <code class="language-plaintext highlighter-rouge">--</code> 之后，默认会被处理成全小写、用短横线连接的 slug。如果你的标题里有中文，Denote 会保留中文字符，只把空格和标点替换掉，所以李继刚那个例子里中文标题原样出现是完全合理的行为。KEYWORDS 部分跟在 <code class="language-plaintext highlighter-rouge">__</code> 之后，多个关键词用下划线连接，比如 <code class="language-plaintext highlighter-rouge">__emacs_productivity_zettelkasten</code>。最后是文件扩展名，Denote 支持 <code class="language-plaintext highlighter-rouge">.org</code>、<code class="language-plaintext highlighter-rouge">.md</code>、<code class="language-plaintext highlighter-rouge">.txt</code> 三种主流文本格式，你可以在配置里指定默认使用哪一种，不同格式对应的前置元数据写法也会自动调整。</p>

<p>值得强调的是，这五个组件的顺序并不是写死的，Denote 提供了 <code class="language-plaintext highlighter-rouge">denote-file-name-components-order</code> 这个变量，允许你按需调整顺序。比如有人喜欢把签名放在最前面方便排序，有人则希望关键词紧跟在标识符后面方便扫视，这些都可以在自己的配置里调整。但无论顺序如何变化，各段之间的分隔符 (– 、 __ 、 == ) 是固定不变的，这保证了不同顺序的文件名仍然可以被同一套解析逻辑识别。</p>

<h2 id="denote-和-zettelkasten-是什么关系">Denote 和 Zettelkasten 是什么关系</h2>

<p>我在调研过程中发现一个常见的误解，就是把 Denote 等同于 [[Zettelkasten]]，或者反过来认为 Denote 仅仅是 Zettelkasten 的 Emacs 实现之一。事实上两者关系更微妙: Denote 借鉴了 Zettelkasten 的一些核心理念 (比如唯一 ID、纯文本、原子化笔记、链接为主)，但它本身并不强制你使用 Zettelkasten 方法论。</p>

<p>Zettelkasten 的精髓在于卡片之间的网络结构、每张卡片只表达一个想法、通过持续地建立连接来涌现知识，这是方法论层面的事情。Denote 提供的只是一组工具: 文件名生成、链接管理、反链显示、关键词过滤，至于你怎么组织内容，完全是个人选择。你可以用 Denote 写日记、做项目笔记、记会议纪要、整理引用文献，这些场景全部都不需要遵守 Zettelkasten 的任何规则。</p>

<p>但如果你确实想严肃地实践 Zettelkasten，Denote 提供的工具组合恰好非常合适。基于时间戳的唯一标识符天然适合做原子化笔记的 ID，签名字段可以承载卢曼式的层级编号，关键词字段对应 Zettelkasten 中的索引词，链接和反链则实现了卡片之间的网络结构。Prot 还专门开发了一个可选扩展 <code class="language-plaintext highlighter-rouge">denote-sequence.el</code>，专门用来管理 folgezettel 序列，对深度 Zettelkasten 用户来说是个加分项。</p>

<h2 id="与-obsidian-的核心差异">与 Obsidian 的核心差异</h2>

<p>作为一个 Obsidian 重度用户，我自然会把 Denote 和 Obsidian 做对比。两者表面上看起来很像: 都是基于纯文本，都强调链接，都不锁定厂商，但深入到具体的使用体验，差异其实非常明显。</p>

<p>Obsidian 的核心是 wiki-link 风格的双向链接，你通过 <code class="language-plaintext highlighter-rouge">[[笔记标题]]</code> 来引用另一条笔记，链接的解析完全依赖于标题。这种方式人类可读、操作直观，但缺点也很清楚: 一旦你改了笔记标题，所有引用都需要同步更新，Obsidian 会自动帮你做这件事，但代价是它必须维护一份反向索引。Denote 走的是另一条路，链接的目标永远是 14 位时间戳 ID，标题只是用来展示的，改标题对链接没有任何影响。从健壮性角度看，Denote 的方式更稳妥，但从可读性角度看，Obsidian 的 <code class="language-plaintext highlighter-rouge">[[wiki-link]]</code> 更友好。</p>

<p>文件命名上，Obsidian 把命名权完全交给用户，可以用中文、英文、长句子、emoji，只要文件系统支持都行。Denote 则是命名权交给工具，你输入标题和关键词，工具按规则生成文件名。前者灵活但容易混乱，后者整齐但需要适应。我自己用 Obsidian 这么多年，文件命名上确实存在一些不一致，比如有的笔记用中文标题、有的用英文，有的是动词开头、有的是名词开头，真正想搜索的时候不得不依赖全文搜索而不是文件名。Denote 这种”工具兜底”的方式在长期管理上确实能避免很多混乱。</p>

<p>元数据上，Obsidian 把所有信息放在 YAML frontmatter 里，文件名只是个标题。Denote 则把标识符、签名、标题、关键词全部塞进文件名，frontmatter 只是备份。这意味着 Denote 的文件名比 Obsidian 的更”重”，但也带来一个好处: 只看文件列表就能掌握全部关键信息，不需要打开文件，也不需要任何外部工具的帮助。在脱离 Emacs 或 Obsidian 的环境下，比如在终端、在 [[rclone]] 同步过程中、在文件管理器里浏览，这种差异会变得很明显。</p>

<p>生态层面，Obsidian 拥有庞大的第三方插件市场，移动端、桌面端体验完善，新手上手成本极低。Denote 则深度绑定 Emacs，如果你不是 Emacs 用户，引入 Denote 的成本几乎等同于学习一个新的编辑器，这是它最大的门槛。所以对绝大多数人来说，Denote 并不是 Obsidian 的替代品，但它的设计思路完全值得借鉴。</p>

<h2 id="在-obsidian-中借鉴-denote-的思路">在 Obsidian 中借鉴 Denote 的思路</h2>

<p>调研到这里，我开始思考一个更实际的问题: 我能不能在不放弃 Obsidian 的前提下，把 Denote 那些好用的设计抽出来，应用到自己的工作流里。答案是大部分都能借鉴，而且并不需要太复杂的改造。</p>

<p>第一个可以借鉴的是文件名结构。我之前给 Obsidian 笔记起名比较随性，完全靠当时的心情和标题灵感。试着改用 Denote 风格的 <code class="language-plaintext highlighter-rouge">ID--TITLE__TAGS.md</code> 之后，我发现两个明显的好处: 一是按文件名排序就等同于按创建时间排序，不需要依赖 frontmatter 里的 <code class="language-plaintext highlighter-rouge">create_time</code> 字段; 二是在 [[git]] 历史里看 commit diff 时，文件名本身就携带了足够的上下文，不需要打开文件就能知道改的是哪一类笔记。当然如果你完全照搬 Denote 的命名规则，Obsidian 的双链解析会有点别扭，因为 <code class="language-plaintext highlighter-rouge">[[ID--TITLE__TAGS]]</code> 这种写法既长又难看，这时候可以借助 [[Obsidian]] 的 alias 字段，在 frontmatter 里把人类可读的标题作为别名，链接时仍然用 <code class="language-plaintext highlighter-rouge">[[标题]]</code> 即可。</p>

<p>第二个可以借鉴的是签名字段的概念。Obsidian 默认没有 SIGNATURE 这个层级，但你完全可以用文件名前缀或者 frontmatter 字段来模拟。比如我可以约定所有读书笔记前缀为 <code class="language-plaintext highlighter-rouge">book-</code>，所有日记前缀为 <code class="language-plaintext highlighter-rouge">diary-</code>，所有项目笔记前缀为 <code class="language-plaintext highlighter-rouge">proj-</code>，这就相当于一个简化版的 signature 系统。结合 Obsidian 的全局搜索和 Dataview 查询，这种前缀分类的检索效率非常高。</p>

<p>第三个可以借鉴的是”文件即数据库”的思路。Obsidian 本身就是基于纯文本的，但很多用户依赖大量插件来管理元数据，等于又把数据迁移回了某种”数据库”。Denote 的极简思路提醒我，任何依赖插件才能解读的信息，都是有失效风险的，真正可靠的信息应该写在文件名和 frontmatter 这种通用结构里。我打算把自己一些散在插件状态里的标签和分类，统一迁移到 frontmatter 里去。</p>

<p>第四个可以借鉴的是链接的稳定性。虽然 Obsidian 不支持基于 ID 的链接，但我们可以利用 Obsidian 的 <code class="language-plaintext highlighter-rouge">aliases</code> 字段来缓解标题改名带来的链接腐烂问题。把笔记之前用过的所有标题都加到 aliases 里，即使现在的主标题变了，通过别名也能正常链接，这在一定程度上接近了 Denote 的”ID 永远稳定”的效果。</p>

<h2 id="适合谁使用以及不适合谁">适合谁使用以及不适合谁</h2>

<p>把研究的结果落到决策层面，我自己的判断是: 如果你已经是 Emacs 用户，并且对笔记的长期可移植性、不依赖数据库、纯文本至上这些原则非常认同，Denote 几乎是当前最优雅的选择。它的代码风格清晰、文档质量极高 (Prot 写的 manual 在 Emacs 包里算得上一流)、社区也比较活跃，可以放心地长期使用。</p>

<p>但如果你是 Obsidian 用户、Notion 用户，或者完全没有 Emacs 经验，我不会建议你切换到 Denote。门槛不在 Denote 本身，而在 Emacs 这个底座，从快捷键到配置文件到调试方式，都是一整套需要时间投入的体系。更现实的做法是，把 Denote 当作一个设计案例来研究，从中提炼出适合自己工具链的部分。本文前面提到的几个借鉴思路，都不需要你真的去装 Emacs。</p>

<p>对于像李继刚那样使用 Denote 来组织 Skill 输出的场景，我觉得是一个非常合理的选择。AI 生成的笔记、卡片、文档天然需要一个唯一标识符 (避免重复)、一个可机器解析的元数据结构 (方便后续处理)、以及对未来工具变迁的鲁棒性 (不绑定特定平台)，Denote 的命名规则恰好把这三件事一次性解决。我看完之后也开始思考是不是把自己的 [[Claude Code]] 输出、[[ChatGPT]] 对话归档也用这套规则整理起来，把 AI 时代产生的大量”中间态文本”变成可长期检索的资产。</p>

<h2 id="最后">最后</h2>

<p>研究完 Denote 之后，我最大的感受是，工具的价值不仅仅在于它能不能解决问题，更在于它的设计哲学能不能给你新的视角。我并不打算从 Obsidian 切换到 Denote，但 Denote 那种”信任文件名、不信任数据库”的思路，确实让我对自己当前的笔记管理方式做了一次反思。文件名里能放进去的信息，我之前是不是都用到了; 标题和 ID 的分离，我能不能借助 alias 字段实现; 跨工具的可移植性，我有没有低估它的长期价值。</p>

<p>回到那个最初引发好奇的文件名 <code class="language-plaintext highlighter-rouge">20260527162000==z--投资分析-ajinomoto-2802</code>，现在再看它，已经不再是一串”工程化过头”的随意字符，而是一份非常克制、非常自律的信息组织方式。每一段都承担明确的语义，每一段都为长期保存而设计，每一段都尽量减少对外部工具的依赖。这种克制本身就是一种值得学习的态度。</p>

<p>下一步我想做的事情是: 在自己的 Obsidian vault 里挑一个子目录，比如 [[Zettelkasten]] 这个文件夹，试着改用 Denote 风格的命名规则跑一段时间，看看长期下来的检索效率、链接稳定性、心智负担是不是真的有改善。如果效果确实好，也许会扩展到整个仓库。这种”借鉴而不照搬”的方式，大概是面对优秀工具时最务实的态度。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="学习笔记" /><category term="denote" /><category term="emacs" /><category term="zettelkasten" /><category term="obsidian" /><category term="note-taking" /><category term="knowledge-management" /><summary type="html"><![CDATA[系统介绍 Emacs 下的 Denote 笔记包,讲清楚它的文件命名规则 ID==SIGNATURE--TITLE__KEYWORDS.EXT,以及它与 Zettelkasten 的关系,并探讨 Obsidian 用户能从中借鉴什么。]]></summary></entry><entry><title type="html">Ghostty 和 xterm-ghostty 是什么</title><link href="https://blog.einverne.info/post/2026/05/ghostty-and-xterm-ghostty.html" rel="alternate" type="text/html" title="Ghostty 和 xterm-ghostty 是什么" /><published>2026-05-23T00:00:00-05:00</published><updated>2026-05-23T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/05/ghostty-and-xterm-ghostty</id><content type="html" xml:base="https://blog.einverne.info/post/2026/05/ghostty-and-xterm-ghostty.html"><![CDATA[<p>最近我在 macOS 上重新整理终端工具链的时候，又一次认真看了 [[Ghostty]]。很多人第一次接触 Ghostty，会先被它的界面、速度或者作者背景吸引，但只要打开 Shell 跑一个 <code class="language-plaintext highlighter-rouge">echo $TERM</code>，很快就会看到一个更陌生的名字：<code class="language-plaintext highlighter-rouge">xterm-ghostty</code>。这个名字看起来像是传统 <code class="language-plaintext highlighter-rouge">xterm</code> 的变种，实际上它背后代表的是 Ghostty 对终端兼容性的一整套设计思路。很多人把它当成一个无关紧要的环境变量，但如果你经常使用 [[SSH]]、[[tmux]]、[[Vim]]、[[Neovim]]、[[less]] 或者各种 TUI 工具，理解 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 会让你少踩很多坑。</p>

<p><img src="https://pic.einverne.info/images/2026-05-23-ghostty-xterm-ghostty-cover.png" alt="Ghostty 与 xterm-ghostty" /></p>

<p>这篇文章我就集中聊两件事：第一，Ghostty 到底是一款什么样的终端；第二，<code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 到底是什么，它相比传统的 <code class="language-plaintext highlighter-rouge">xterm</code>、<code class="language-plaintext highlighter-rouge">xterm-256color</code> 又多了哪些能力，为什么 Ghostty 要专门为自己定义一个终端类型。</p>

<h2 id="ghostty-是什么">Ghostty 是什么</h2>

<p>Ghostty 是 [[Mitchell Hashimoto]] 主导开发的开源终端模拟器，项目目标很明确，就是做一个现代的、跨平台的、面向开发者工作流的终端。它不是那种只强调“极简”的终端，也不是把大量功能堆成一个庞杂 GUI 的终端，而是尽量把渲染性能、终端兼容性、原生平台体验和现代特性放在一起做平衡。按照官方文档的定位，Ghostty 在 macOS 上提供原生体验，在 Linux 上也尽量贴近各自桌面环境的习惯，同时保持统一的终端行为。</p>

<p>我自己看 Ghostty 最顺眼的一点，是它没有把“现代化”理解成重做一套封闭工作流。很多新终端喜欢把命令历史、AI、会话管理、团队协作都做进产品里，结果是表面上功能很多，底层兼容性却经常拖后腿。Ghostty 走的是另外一条路：先把终端模拟器本身做好，再把标签页、分屏、Shell 集成、图形协议、快捷终端这些真正和终端本体相关的能力打磨到位。对我这种长期泡在命令行里的人来说，这种思路更踏实。</p>

<p>从体验上看，Ghostty 有几个很明显的特征。第一个是渲染流畅，它本身就是围绕现代图形渲染路径来构建的，滚动大段日志、跑持续刷新的命令或者长时间开多个分屏，窗口依然很跟手。第二个是原生能力足够完整，它内置标签页、分屏和 Quick Terminal，不需要像以前那样先在 [[iTerm2]]、[[Kitty]]、[[tmux]] 之间反复权衡。第三个是它的配置模型比较干净，文本配置足够直观，适合直接纳入 dotfiles 管理。</p>

<h2 id="xterm-ghostty-到底是什么">xterm-ghostty 到底是什么</h2>

<p><code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 本质上是 Ghostty 对外声明的终端类型，也就是 <code class="language-plaintext highlighter-rouge">TERM</code> 环境变量里的值。终端程序、[[Shell]]、[[ncurses]] 应用、全屏 TUI 程序并不会直接“认识”某个终端窗口长什么样，它们通常只会看 <code class="language-plaintext highlighter-rouge">TERM</code>，然后去 terminfo 数据库里查这个终端支持哪些控制序列、键盘事件、颜色能力、鼠标模式和光标控制方式。换句话说，<code class="language-plaintext highlighter-rouge">TERM=xterm-ghostty</code> 并不是装饰品，而是 Ghostty 告诉整个命令行生态：“请按这一份能力说明和我对话。”</p>

<p>很多人会疑惑，为什么不直接沿用 <code class="language-plaintext highlighter-rouge">xterm-256color</code>。原因很简单，历史上的 <code class="language-plaintext highlighter-rouge">xterm-256color</code> 是一个兼容性很强、但能力描述也相对保守的通用终端身份。只要 Ghostty 的行为超出了这个公共最小集合，比如支持更多扩展键位、更准确的真彩色能力、更现代的粘贴模式、鼠标事件或者同步更新语义，那么继续伪装成 <code class="language-plaintext highlighter-rouge">xterm-256color</code> 反而会让应用程序无法正确利用这些能力。Ghostty 用 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 这个名字，本质上是在继承 xterm 家族兼容基础的同时，声明“我还有自己的增强能力”。</p>

<p>从命名上看，这个选择也很聪明。前缀保留 <code class="language-plaintext highlighter-rouge">xterm</code>，意味着它仍然站在最广泛兼容的 VT/xterm 生态上，不是另起炉灶；后缀加上 <code class="language-plaintext highlighter-rouge">ghostty</code>，表示这是 Ghostty 自己维护的一份终端定义。对多数类 Unix 工具来说，这种命名既熟悉又明确，兼容老工具的概率更高，也给新特性留下了清晰的扩展空间。</p>

<h2 id="xterm-ghostty-的核心特性">xterm-ghostty 的核心特性</h2>

<p>如果只看名字，<code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 很容易被理解成“换皮的 xterm”。但我本地用 <code class="language-plaintext highlighter-rouge">infocmp -x xterm-ghostty</code> 看过 Ghostty 自带的 terminfo 定义之后，会发现它包含的内容其实相当完整，远不只是 256 色这么简单。结合 Ghostty 官方文档，可以把它的能力理解成下面几层。</p>

<h3 id="更明确的颜色能力">更明确的颜色能力</h3>

<p><code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 声明了 <code class="language-plaintext highlighter-rouge">colors#256</code>，同时还包含 <code class="language-plaintext highlighter-rouge">Tc</code> 和 <code class="language-plaintext highlighter-rouge">setrgbf</code>、<code class="language-plaintext highlighter-rouge">setrgbb</code> 这类真彩色相关能力。这意味着依赖 terminfo 的应用不只知道“这个终端至少支持 256 色”，还可以更可靠地判断它支持 24-bit true color。对于现代编辑器、语法高亮器、Git diff 工具和各种 Dashboard 来说，这一点非常关键。很多人平时觉得主题显示不对，只会去怀疑配色方案，实际上问题常常出在终端身份过于保守，导致程序主动降级输出。</p>

<p>Ghostty 之所以要把这件事写进自己的终端定义，是因为现代终端的显示能力早就不止 16 色或者 256 色。今天我们在终端里使用 Catppuccin、Tokyo Night、Gruvbox 或者各种自定义主题时，往往默认期待颜色过渡准确、暗部层次足够细腻、选中态和光标色也能正确显示。<code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 的存在，就是让应用层不用猜，直接知道这些能力是可以依赖的。</p>

<h3 id="更完整的键盘修饰键和功能键映射">更完整的键盘修饰键和功能键映射</h3>

<p>另一个特别实用的点，是 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 对扩展功能键和修饰键组合描述得更完整。通过 <code class="language-plaintext highlighter-rouge">infocmp</code> 可以看到它不仅覆盖普通的 F1-F12，还列出了 Shift、Alt、Ctrl 等组合后的大量变体，比如 <code class="language-plaintext highlighter-rouge">kf13</code> 到更高位的功能键序列，以及 Home、End、Insert、Delete、方向键配合修饰键时的不同编码。对于只写普通命令的用户来说，这些东西平时几乎感知不到，但对 [[Vim]]、[[Emacs]]、[[Helix]]、[[tmux]]、[[fzf]] 这类大量依赖快捷键的工具而言，这类定义越完整，键位识别就越稳定。</p>

<p>这也是为什么有时候你会遇到一种很奇怪的情况：本地终端里某个快捷键能用，一进远程主机就失灵，或者进了 tmux 之后组合键被吃掉。很多人会先怀疑是 Shell、tmux 或编辑器配置的问题，但根子常常在于远程机器根本不认识 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code>，于是只能按更保守的方式理解输入。终端身份一旦退化，键盘能力也会跟着退化。</p>

<h3 id="现代终端常见的交互语义">现代终端常见的交互语义</h3>

<p>Ghostty 的 terminfo 里还包含了很多现代终端日常会用到的交互能力，比如 bracketed paste、焦点事件、鼠标追踪、备用屏幕切换、下划线样式、下划线颜色、同步更新一类的控制序列。对普通用户来说，这些术语不一定熟，但你每天其实都在享受它们带来的好处。举个最直接的例子，bracketed paste 能让 Shell 和编辑器知道“这是一整段粘贴进来的内容”，从而避免因为自动缩进、自动执行或者逐字符解释而引发意外。焦点事件会让某些程序在窗口切换时刷新状态，鼠标追踪会让终端里的选择、点击和滚动行为更自然。</p>

<p>尤其是同步更新能力，这是一类很容易被忽略，但对观感影响很大的细节。终端程序在快速刷新界面时，如果没有同步更新机制，就可能出现闪烁、撕裂或者中间态短暂可见的问题。支持这类能力之后，TUI 程序可以把一整批画面更新更一致地提交给终端。你平时会觉得某个终端“看起来更稳”，背后往往就是这些协议细节在起作用。</p>

<h3 id="为图形和链接等扩展特性打基础">为图形和链接等扩展特性打基础</h3>

<p>Ghostty 官方文档还明确列出了对外部协议和终端扩展的支持，比如 OSC 8 hyperlinks、图形协议、剪贴板相关 OSC 52 以及 shell integration 所依赖的一些语义。这里要注意一件事：并不是所有扩展特性都会完整体现在 terminfo 里，因为 terminfo 主要描述的是一套传统的终端能力接口，很多较新的协议是通过额外 escape sequence 实现的。但 Ghostty 既提供了自己的终端定义，也在文档里明确说明支持哪些扩展，这就让应用开发者既能通过传统方式探测基础能力，也能为高级场景做更有把握的适配。</p>

<p>对终端爱好者来说，这一点很有吸引力。你可以把 Ghostty 理解成一个尽量和传统 Unix 生态兼容，但又不拒绝现代终端创新的实现。它并没有为了新特性去抛弃旧世界，而是努力让两边对齐，这也是 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 这个命名比单纯叫 <code class="language-plaintext highlighter-rouge">ghostty</code> 更有现实意义的原因。</p>

<h2 id="什么时候你会真正碰到-xterm-ghostty">什么时候你会真正碰到 xterm-ghostty</h2>

<p>如果你只在本机开终端跑命令，<code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 基本不会给你制造麻烦。Ghostty 会自带对应的 terminfo 数据，本地程序通常都能正常识别，绝大多数时候你甚至不需要知道它的存在。真正容易出问题的场景，通常出现在跨机器连接之后，尤其是你通过 SSH 连接到一台比较旧的 Linux、BSD、路由器、容器或者公司内网服务器时。</p>

<p>在这些机器上，如果系统没有安装 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 的 terminfo 条目，应用拿到 <code class="language-plaintext highlighter-rouge">TERM=xterm-ghostty</code> 之后就可能直接报错，比如最常见的 <code class="language-plaintext highlighter-rouge">unknown terminal type</code>，或者退化成不完整的显示和键盘行为。Ghostty 官方帮助文档也专门解释了这一点：本地没问题不代表远程环境一定认识这个名字，必要时需要把 Ghostty 提供的 terminfo 安装到远程机器上。</p>

<p>这件事在 2026 年依然很现实，因为终端本身更新得很快，但很多服务器环境极度保守。你本地已经在用一个支持真彩色、扩展键位和现代协议的新终端，远端可能还是一套几年没升级过的 ncurses 数据库。只要 TERM 值传过去而 terminfo 没跟上，兼容性问题就会立刻暴露出来。</p>

<h2 id="我会怎么处理远程兼容性问题">我会怎么处理远程兼容性问题</h2>

<p>如果远程机器提示不认识 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code>，我通常优先走“安装 terminfo”这条路，而不是直接把 <code class="language-plaintext highlighter-rouge">TERM</code> 改成 <code class="language-plaintext highlighter-rouge">xterm-256color</code>。因为后一种做法虽然省事，但本质上是让 Ghostty 主动降级伪装，很多增强能力也就跟着消失了。官方文档给出的建议也类似：尽量在远程环境安装正确的 terminfo 定义，这样本地和远端对终端能力的理解是一致的。</p>

<p>如果你只是临时登录一台权限受限的机器，没法安装 terminfo，那么把 <code class="language-plaintext highlighter-rouge">TERM</code> 临时切成 <code class="language-plaintext highlighter-rouge">xterm-256color</code> 是一个可以接受的兜底方案。只是你要知道，这不是“正确配置完成了”，而是“为了兼容老系统先退一步”。一旦你发现颜色不对、快捷键异常、鼠标行为不一致或者 tmux 中部分功能失灵，就应该优先回头检查 terminfo，而不是在应用层做越来越多的补丁。</p>

<p>平时我还会保留一个习惯：出问题时直接看 <code class="language-plaintext highlighter-rouge">echo $TERM</code>，再配合 <code class="language-plaintext highlighter-rouge">infocmp $TERM</code> 或者远程检查 terminfo 是否存在。终端问题看起来都像是编辑器问题、tmux 问题或者 shell 问题，但如果从终端身份开始排查，很多时候能很快找到根源。</p>

<h2 id="ghostty-值不值得用">Ghostty 值不值得用</h2>

<p>如果你想找的是一款现代、快速、又尽量不破坏传统命令行生态的终端，我觉得 Ghostty 很值得试。它的吸引力不只在于“快”或者“好看”，更在于它对底层兼容性足够认真。很多终端喜欢在营销层面强调新功能，但 Ghostty 连 <code class="language-plaintext highlighter-rouge">TERM</code> 和 terminfo 这种多数普通用户不会主动关注的细节都处理得很扎实，这反而让我更愿意长期用它。</p>

<p>而 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 这个名字，看似不起眼，其实正是 Ghostty 技术取向的缩影。它既承认自己站在 <code class="language-plaintext highlighter-rouge">xterm</code> 这条漫长的兼容链条上，又明确把自己的现代能力表达出来。对开发者来说，这样的设计比“神秘地能用”更重要，因为只有能力被准确声明，工具链才能稳定协同。</p>

<h2 id="最后">最后</h2>

<p>Ghostty 是一个现代终端模拟器，而 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code> 是它在 Unix 终端生态里的正式身份证。前者解决的是你每天实际看到和操作的体验问题，后者解决的是编辑器、Shell、TUI 程序和远程服务器应该如何正确理解它的问题。把这两件事放在一起看，Ghostty 的价值就不只是一个“新终端”，而是一种尽量不牺牲传统兼容性的现代化实现。</p>

<p>如果你最近正准备从 [[iTerm2]]、[[Warp]]、[[Kitty]] 或 [[Alacritty 终端]] 切换，我会建议你不仅去试试 Ghostty 的界面和速度，也顺手看一眼 <code class="language-plaintext highlighter-rouge">echo $TERM</code>。当你真正开始理解 <code class="language-plaintext highlighter-rouge">xterm-ghostty</code>，你会发现终端体验好不好，很多时候不是主题和字体决定的，而是这些平时不怎么显眼的底层协议和能力声明决定的。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="学习笔记" /><category term="ghostty" /><category term="terminal" /><category term="xterm-ghostty" /><category term="terminfo" /><category term="shell" /><summary type="html"><![CDATA[介绍 Ghostty 终端模拟器，以及 xterm-ghostty 这个终端名背后的 terminfo、兼容性设计与实际使用建议。]]></summary></entry><entry><title type="html">Antigravity CLI：Google 推出的下一代终端 AI 编程智能体</title><link href="https://blog.einverne.info/post/2026/05/antigravity-cli-google-terminal-ai-agent.html" rel="alternate" type="text/html" title="Antigravity CLI：Google 推出的下一代终端 AI 编程智能体" /><published>2026-05-22T00:00:00-05:00</published><updated>2026-05-22T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/05/antigravity-cli-google-terminal-ai-agent</id><content type="html" xml:base="https://blog.einverne.info/post/2026/05/antigravity-cli-google-terminal-ai-agent.html"><![CDATA[<p>最近在关注 Google I/O 2026 的动态时，看到了一个让我颇感兴趣的发布——[[Antigravity CLI]]，Google 官方宣布用它来取代已经积累了超过 10 万 GitHub Stars 的 [[Gemini CLI]]。这个时机有点微妙：个人版 Gemini CLI 用户必须在 2026 年 6 月 18 日前完成迁移，否则将失去模型访问权限。作为一个长期在终端里工作、也在持续关注 AI 编程工具演进的开发者，我觉得有必要认真研究一下这个新工具到底带来了什么改变。</p>

<p><img src="https://pic.einverne.info/images/antigravity-cli-cover.png" alt="Antigravity CLI cover" /></p>

<h2 id="gemini-cli-为什么需要被替代">Gemini CLI 为什么需要被替代</h2>

<p>要理解 Antigravity CLI 的价值，首先得弄清楚 Gemini CLI 为什么走到了被替代这一步。Gemini CLI 诞生于 AI 助手的早期，那时候人们对 AI 编程工具的期待还停留在”能帮我写代码、能帮我解释函数”这个层面，一个对话框加上文件上下文，基本就够用了。</p>

<p>但随着时间推移，开发者们开始尝试更复杂的工作流：跨多个文件的大规模重构、同时推进多个独立任务、在 CI 流水线里嵌入自动化代理……Gemini CLI 底层是 Node.js，在这些场景下暴露出了性能瓶颈；而更根本的问题是，它的架构根本没为多智能体协作设计。代码库越来越臃肿，扩展也越来越难。所以与其打补丁，Google 选择了从头重建。</p>

<p>Antigravity CLI 的背后是一个更大的平台——Antigravity 2.0，这是 Google 重新定义的”智能体优先”开发环境，核心理念是把 AI 智能体从”回答问题的助手”升级为”主动参与开发的协作者”。CLI 只是这个平台的终端入口。</p>

<h2 id="核心特性深度解析">核心特性深度解析</h2>

<h3 id="从-nodejs-到-go不只是语言的替换">从 Node.js 到 Go：不只是语言的替换</h3>

<p>Antigravity CLI 用 Go 重写了整个二进制文件，命令从 <code class="language-plaintext highlighter-rouge">gemini</code> 变成了 <code class="language-plaintext highlighter-rouge">agy</code>。这个变化带来的不只是启动速度的提升，更重要的是并发模型的根本性改变。Go 的 goroutine 天然适合异步任务调度，这为下面要说的异步子智能体模式奠定了基础。</p>

<p>在实际使用中，<code class="language-plaintext highlighter-rouge">agy</code> 的冷启动明显比 Node.js 版本快，在 SSH 远程会话或资源受限的环境里感知尤为明显。对于需要频繁启动 CLI 的自动化脚本来说，这个差异会随着调用次数累积成相当可观的效率提升。</p>

<h3 id="异步子智能体最值得关注的新能力">异步子智能体：最值得关注的新能力</h3>

<p>如果说 Antigravity CLI 有一个让我眼前一亮的特性，那一定是异步子智能体模式（Async Subagent Mode）。在 TUI 界面里，你可以用 <code class="language-plaintext highlighter-rouge">/agent</code> 命令启动一个后台智能体，它会在不阻塞主会话的情况下并行执行任务：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/agent refactor <span class="s2">"Convert all callback-based handlers in @internal/api to use context.Context"</span>
</code></pre></div></div>

<p>这意味着你可以同时跑多个独立的大型任务——一个在重构 API 层，另一个在整理测试文件，第三个在查找潜在的安全漏洞，你则继续在主会话里做其他工作。对于中大型代码库来说，这种并行化能力能显著压缩整体完成时间。当然，副作用是多个并行智能体会同时消耗 API 配额，在免费层用户那里需要注意别烧得太快。</p>

<h3 id="三种操作模式覆盖不同场景">三种操作模式覆盖不同场景</h3>

<p>工具设计得很务实，提供了三种明确定位的操作模式。交互式智能体模式（直接运行 <code class="language-plaintext highlighter-rouge">agy</code>）是完整的 TUI 界面，支持 <code class="language-plaintext highlighter-rouge">@filename</code> 和 <code class="language-plaintext highlighter-rouge">@**/*.ts</code> 这样的 glob 模式向上下文注入文件，适合日常的对话式开发。</p>

<p>无头脚本模式适合自动化集成：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>agy <span class="nt">-p</span> <span class="s2">"List all TODOs in this codebase"</span> <span class="nt">--output-format</span> json
</code></pre></div></div>

<p>加上 <code class="language-plaintext highlighter-rouge">--output-format json</code> 后，输出可以直接被下游脚本解析，嵌进 CI/CD 流水线里毫无压力。</p>

<p>异步子智能体模式前面已经说过，是最有差异化的能力，适合那些”扔进去、等结果”的大型任务。</p>

<h3 id="agentsmd项目级的智能体行为定制">AGENTS.md：项目级的智能体行为定制</h3>

<p>这个设计思路和我在用 [[Claude Code]] 时见到的 <code class="language-plaintext highlighter-rouge">CLAUDE.md</code> 非常相似。在项目根目录放一个 <code class="language-plaintext highlighter-rouge">AGENTS.md</code> 文件，里面用自然语言描述项目规范，智能体在每次启动时会自动读取并遵循：</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Always use TypeScript. Prefer functional patterns over class-based ones.
Run <span class="sb">`npm test`</span> after every file modification.
</code></pre></div></div>

<p>更进一步，你还可以在 <code class="language-plaintext highlighter-rouge">.agents/skills/</code> 目录下创建 Markdown 文件，它们会自动变成 TUI 里的 <code class="language-plaintext highlighter-rouge">/slash</code> 命令，相当于把常用的操作流程封装成了可复用的技能。全局技能则放在 <code class="language-plaintext highlighter-rouge">~/.gemini/antigravity-cli/skills/</code> 下跨项目复用。</p>

<h3 id="hooks-和-mcp深度集成开发工作流">Hooks 和 MCP：深度集成开发工作流</h3>

<p>Hooks 是 JSON 格式的生命周期拦截器，可以在工具调用前、文件编辑后或会话开始时触发自定义脚本。比如每次写完 Go 文件后自动运行 <code class="language-plaintext highlighter-rouge">gofmt</code>，或者在每次会话开始时加载最新的项目文档——这类需求过去要靠额外的包装脚本实现，现在内置支持了。</p>

<p>MCP（Model Context Protocol）服务器通过 <code class="language-plaintext highlighter-rouge">mcp_config.json</code> 配置，支持本地 stdio 和远程 HTTP 两种接入方式，和目前主流的 AI 工具生态对接没有障碍。</p>

<h3 id="多模型支持">多模型支持</h3>

<p>Antigravity CLI 不再局限于 Gemini 模型家族，内置支持了：</p>

<ul>
  <li>Gemini 3.5 Flash</li>
  <li>Gemini 3.1 Pro</li>
  <li>Claude Sonnet / Opus</li>
  <li>GPT-OSS 120B</li>
</ul>

<p>在 TUI 里用 <code class="language-plaintext highlighter-rouge">/model</code> 命令可以随时切换，这对需要根据任务类型选择最优模型的开发者来说很友好。</p>

<h2 id="安装和初上手">安装和初上手</h2>

<p>安装很简单，官方提供了一键脚本：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># macOS / Linux</span>
curl <span class="nt">-fsSL</span> https://antigravity.google/cli/install.sh | bash

<span class="c"># Windows PowerShell</span>
irm https://antigravity.google/cli/install.ps1 | iex

<span class="c"># 验证安装</span>
agy <span class="nt">--version</span>
</code></pre></div></div>

<p>认证方面，桌面环境会自动打开浏览器完成 Google OAuth；SSH/无头环境会打印授权 URL 和一次性验证码，在本地完成授权后远程会话自动激活。也支持直接设置 API Key：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">ANTIGRAVITY_API_KEY</span><span class="o">=</span>your_api_key_here
</code></pre></div></div>

<p>安装完成后，运行 <code class="language-plaintext highlighter-rouge">agy inspect</code> 可以查看当前加载的配置、技能、插件、Hooks 和已连接的 MCP 服务器，相当于一个配置状态的快速诊断命令。如果你原来是 Gemini CLI 用户，官方提供了迁移命令：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>agy plugin import gemini
</code></pre></div></div>

<p>它会把旧版扩展转换为新的 Plugin 格式，同时保留原始文件备份，迁移过程相对安全。</p>

<h2 id="和-claude-code-以及其他工具的对比">和 Claude Code 以及其他工具的对比</h2>

<p>从我目前的使用体验来看，这几个工具的定位其实各有侧重，很难说谁全面碾压谁。</p>

<p>Antigravity CLI 的核心优势在于异步并行子智能体和与 Antigravity 2.0 桌面端的无缝同步——你可以把一个终端会话直接推送到桌面 GUI 里继续处理，这种跨界面的上下文保留是独家的。对于需要批量处理大型任务、或者在 SSH 环境里重度工作的开发者，它有不可替代的价值。</p>

<p>[[Claude Code]] 在我这边用得最多的场景是需要深度推理的复杂任务。Opus 4.7 的推理能力、百万 token 上下文窗口、加上精细的用户审批工作流，在处理需要仔细思考的架构决策或微妙的 Bug 时更让人放心。</p>

<p>[[Cursor]] 则继续是日常快节奏开发的首选——IDE 集成太紧密了，补全速度快，对普通的功能开发来说效率最高。</p>

<p>我现在的模式基本是：日常功能开发用 Cursor，复杂推理任务用 Claude Code，需要并行跑多个大型重构任务时用 Antigravity CLI。三个工具并不互斥，反而能形成互补。</p>

<h2 id="最后">最后</h2>

<p>Antigravity CLI 的出现标志着 AI 终端工具从”单线程对话”向”多智能体并发编排”的明确转变。Google 用 Go 重写、引入异步子智能体、打通桌面端，这些选择都指向同一个判断：未来的开发工作流是多个智能体协作完成的，不是一个智能体串行执行的。</p>

<p>对于个人开发者来说，如果你现在还在用 Gemini CLI，迁移截止日期是 6 月 18 日，建议尽早完成迁移以免影响日常工作。如果你对多智能体并发这个方向感兴趣，Antigravity CLI 值得认真试用一段时间，特别是那个异步子智能体模式，一旦用顺了，回不去了。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="产品体验" /><category term="ai" /><category term="cli" /><category term="terminal" /><category term="google" /><category term="gemini" /><category term="antigravity" /><category term="ai-agent" /><summary type="html"><![CDATA[Google 在 2026 年 Google I/O 发布了 Antigravity CLI，作为 Gemini CLI 的官方继任者。本文介绍其核心特性、安装使用方式，以及与 Claude Code 等工具的对比分析。]]></summary></entry><entry><title type="html">终端开发的新范式：从 Gemini CLI 到 Antigravity CLI</title><link href="https://blog.einverne.info/post/2026/05/introducing-antigravity-cli.html" rel="alternate" type="text/html" title="终端开发的新范式：从 Gemini CLI 到 Antigravity CLI" /><published>2026-05-22T00:00:00-05:00</published><updated>2026-05-22T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/05/introducing-antigravity-cli</id><content type="html" xml:base="https://blog.einverne.info/post/2026/05/introducing-antigravity-cli.html"><![CDATA[<h2 id="终端智能体的新旅程">终端智能体的新旅程</h2>

<p>作为一个长期泡在终端里的开发者，我的工作流几乎完全是由命令行和各类快捷键支撑起来的。从早期的命令行自动补全，到后来各种基于大模型的命令行聊天助手，AI 在终端里的角色一直在发生改变。然而，大多数时候我依然觉得有些割裂。当我们需要 AI 帮我们重构一个复杂的跨文件模块、运行构建命令并根据报错自动迭代时，传统的聊天式工具就显得力不从心了。</p>

<p>我最近一直在深度使用 Google 推出的新一代命令行开发工具 [[Antigravity CLI]]。在实际体验了一段时间之后，我深刻地感受到，它不仅仅是又一个命令行 AI 助手，而是代表了一种全新的人机协同开发范式。今天我想和大家聊聊，为什么 Google 决定淘汰掉原有的 [[Gemini CLI]]，并全面转向以智能代理为核心的 [[Antigravity CLI]]，以及它是如何重塑我们的命令行开发体验的。</p>

<p><img src="/assets/antigravity-cli-cover.png" alt="Antigravity CLI Cover" /></p>

<h2 id="从传统对话走向代理自治">从传统对话走向代理自治</h2>

<p>在探讨新工具之前，我们有必要先回看一眼已经被替代的 [[Gemini CLI]]。早期的 [[Gemini CLI]] 本质上是一个以聊天和命令补全为核心的终端助手。它的交互模式非常单一：用户输入一个问题或命令意图，大模型返回一段生成的 shell 命令或代码片段，然后用户通过复制粘贴或者手动确认来执行。这种单次对话的模式在面对简单查询时表现尚可，但在处理真实的软件工程任务时，它的局限性就暴露无遗。</p>

<p>当面临需要分析项目整体架构、修改多个相互关联的文件、运行本地测试并根据测试报错进行修复的场景时，传统的单次对话模式会让开发者陷入无休止的“提问、复制、运行、报错、再次提问”的循环中。频繁的上下文切换不仅极大地消耗了开发者的精力，也限制了 AI 在复杂任务中的输出质量。</p>

<p>这就是 Google 推出 [[Antigravity CLI]] 的根本动力。相较于前代产品，新工具实现了从单纯的“自然语言转命令行工具”到“自主规划与工具执行智能体”的跨越。它引入了主动式的规划循环，智能体在接收到任务指令后，不仅会生成解决方案，还会自主使用内置的读写文件、代码检索以及终端运行工具来步步逼近目标。这种代理自治的模式，让 AI 能够真正以一个协作者的身份深入到工程项目的内部。</p>

<h2 id="安全沙箱与异步多智能体协同">安全沙箱与异步多智能体协同</h2>

<p>要实现代理自治，让智能体在开发者的本地机器上自主执行各种操作，首先要解决的就是安全和效率两大痛点。为此，[[Antigravity CLI]] 带来了两项颠覆性的技术设计。</p>

<h3 id="跨平台的轻量级沙箱隔离">跨平台的轻量级沙箱隔离</h3>

<p>给一个 AI 智能体开放本地系统的命令执行权限是极其危险的。即使是再聪明的模型，也可能因为环境差异或理解幻觉而执行一些灾难性的命令，比如误删关键文件。为了从根本上消除这一风险，该工具在不同的操作系统上都集成了原生的内核级隔离技术。</p>

<p>在 Linux 系统上，它采用 nsjail 技术，结合命名空间和系统调用过滤限制网络与文件访问；在 macOS 上则利用 Seatbelt 机制通过内置沙箱来隔离文件与网络；在 Windows 下则基于 AppContainer 进行原生包隔离。这套沙箱机制默认对智能体执行的所有终端命令进行审查 and 限制，只允许其读写当前工作区的文件。这种设计既解放了智能体的执行力，又牢牢守住了开发者本地系统的安全底线。</p>

<h3 id="异步多智能体管理">异步多智能体管理</h3>

<p>在开发大型特性时，我们常常需要同时处理多个子任务，比如一边编写核心业务逻辑，一边为辅助函数补充单元测试，同时还要更新对应的文档。如果使用传统的命令行 AI，我们只能排队等候一个任务结束。</p>

<p>而 [[Antigravity CLI]] 的另一大亮点在于支持后台并行的子智能体管理。通过内置的会话派生机制，我们可以直接在提示词中指示它在后台启动一个新的子智能体分支去处理文档或测试。所有的后台运行状态都可以通过特定的面板集中监控，主会话则保持畅通，这极大地提升了多任务处理的连贯性。</p>

<h2 id="极客的日常终端实战">极客的日常终端实战</h2>

<p>在实际的开发使用中，熟练掌握一些进阶的命令行指令和状态控制，可以让这套智能体系统发挥出更大的威力。</p>

<h3 id="精细化的权限策略调整">精细化的权限策略调整</h3>

<p>当我们在处理自己非常熟悉的安全本地项目时，可以通过命令实时修改智能体的权限级别。输入特定斜杠指令可以唤出权限设置，系统通常提供三种策略：第一种是命令运行前必须人工逐一审计，第二种是由智能体根据命令的危险程度自主决定是否请示，第三种是允许智能体在当前会话中完全自治。这让我们可以在安全可控与高效率之间自由切换。</p>

<h3 id="巧妙的会话与分支管理">巧妙的会话与分支管理</h3>

<p>长时间的连续开发会在上下文窗口中累积大量的历史对话，这往往会导致智能体产生记忆混乱或废话增多。我的习惯是每当完成一个独立模块的开发或修复后，就使用清空命令重置上下文，让智能体带着干净的历史开始下一个任务。</p>

<p>此外，当面临一些实验性的重构想法时，分支功能也非常实用。它会指示智能体在底层的版本控制系统上拉出一个临时分支去进行破坏性修改和测试。如果最终效果不理想，可以直接丢弃该分支，完全不用担心污染主分支的代码。</p>

<h2 id="最后">最后</h2>

<p>从单纯的命令行问答外壳走向深度集成的沙箱智能代理，[[Antigravity CLI]] 的演进正是近年来 AI 辅助开发工具发展的一个缩影。它不仅极大地降低了开发者在日常构建和多文件重构中的重复劳动，更通过沙箱隔离为智能体在宿主机上的自动执行撑起了一把保护伞。</p>

<p>对于像我这样热爱终端环境的开发者来说，这种键盘友好、安全且支持高并发的工作流设计，无疑是将人机协同的生产力推向了一个新的高度。如果你也是命令行工具的拥趸，不妨在自己的终端里体验一下这种全新的代理开发范式，或许它能给你的开发日常带来不一样的启发。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="产品体验" /><category term="developer-tools" /><category term="command-line" /><category term="ai-agent" /><category term="google-gemini" /><category term="sandboxing" /><summary type="html"><![CDATA[介绍 Google 推出的命令行智能体开发平台 Antigravity CLI，分析其核心功能、沙箱机制以及多任务异步子智能体设计，探讨为什么以智能代理为核心的开发工具必将取代传统的聊天式终端助手。]]></summary></entry><entry><title type="html">我购买了一台零刻 ME Pro 用来存放视频素材</title><link href="https://blog.einverne.info/post/2026/05/i-bought-beelink-me-pro.html" rel="alternate" type="text/html" title="我购买了一台零刻 ME Pro 用来存放视频素材" /><published>2026-05-20T00:00:00-05:00</published><updated>2026-05-20T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/05/i-bought-beelink-me-pro</id><content type="html" xml:base="https://blog.einverne.info/post/2026/05/i-bought-beelink-me-pro.html"><![CDATA[<p>随着我<a href="https://youtube.com/@einverne">频道</a>录制的视频越攒越多，加上平时用<a href="https://blog.einverne.info/post/2025/12/insta360-go-ultra-review.html">相机</a>拍摄的原始素材，存储问题开始变得越来越头疼。一个 4K 视频的原始素材少则几个 GB，多则上百 GB，几次拍摄下来一块移动硬盘就见底了。而且随着视频数量的增加，想快速找到某段素材或者回翻旧成片的成本越来越高。这种情况下，我开始认真考虑搭建一套本地存储方案，于是目光落到了[[零刻]] ME Pro 这台小型 NAS 主机上。</p>

<p><img src="https://pic.einverne.info/images/2026-05-20-14-00-00-beelink-me-pro.png" alt="零刻 ME Pro 小型 NAS 主机" /></p>

<h2 id="为什么需要一台-nas">为什么需要一台 NAS</h2>

<p>说起来这个问题其实积累了很久。之前拍视频的素材都临时存在移动硬盘里，拍完一期就整理一次，但随着时间推移，手头的移动硬盘越来越多，哪块盘存了哪段素材根本理不清楚。有几次要找几个月前某个项目的 B-roll 素材，翻了好几块硬盘才找到，实在太低效了。</p>

<p>另外一个更让我担心的问题是频道视频的存档。YouTube 上传的视频都经过了压缩，画质相比原始文件已经损失了不少，但我一直没有一个稳妥的地方把原始成片统一保管起来。万一哪天账号出现问题，或者视频被误删，手头还要有一份完整备份才放心。这种隐患一直悬在那里，直到我真正动手解决之前都让人不舒服。</p>

<p>这些问题都指向同一个方向：搭建一套可靠的本地存储，最好还能配合软件实现自动整理和备份。传统品牌 NAS 价格偏高，而且存储之外的计算能力有限，很难跑额外的服务。几番权衡之后，我把目光投向了国内的小主机品牌，最终锁定了[[零刻]]的 ME Pro。</p>

<h2 id="零刻-me-pro-是什么">零刻 ME Pro 是什么</h2>

<p>[[零刻]]（Beelink）是国内比较知名的迷你主机品牌，这几年在 NAS 方向推出了一些有意思的产品。ME Pro 是他们专门面向存储扩展场景设计的小主机，整体尺寸控制得很紧凑，但内部塞入了相当可观的存储接口。</p>

<table>
  <thead>
    <tr>
      <th>项目</th>
      <th>规格</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CPU</td>
      <td>Intel N95</td>
    </tr>
    <tr>
      <td>内存</td>
      <td>12GB LPDDR5</td>
    </tr>
    <tr>
      <td>硬盘位</td>
      <td>上部 2× 3.5” HDD + 下部 3× M.2 2280 SSD</td>
    </tr>
    <tr>
      <td>最大存储</td>
      <td>HDD 2×20TB + SSD 3×4TB，理论约 52TB</td>
    </tr>
    <tr>
      <td>网口</td>
      <td>1× 5GbE + 1× 2.5GbE</td>
    </tr>
    <tr>
      <td>视频输出</td>
      <td>HDMI 2.1</td>
    </tr>
    <tr>
      <td>USB</td>
      <td>前 1×USB-A 10Gbps / 后 2×USB-A 480Mbps + 1×USB-C 10Gbps</td>
    </tr>
    <tr>
      <td>系统</td>
      <td>支持[[飞牛 fnOS]]</td>
    </tr>
    <tr>
      <td>价格</td>
      <td>基础版 1848 元 / 商业版 1999 元（含 128GB SSD + 5 年会员）</td>
    </tr>
  </tbody>
</table>

<p>两个 3.5 寸硬盘位是这台机器最吸引我的地方。对于视频存储来说，机械硬盘的容量密度和价格比固态硬盘友好太多了，塞两块 8TB 或 16TB 的西数红盘，就能获得相当充裕的空间。三个 M.2 插槽可以用来装系统盘和高频访问的缓存，让整体读写性能更均衡。双网口的设计也很实用，5GbE 加上 2.5GbE，在局域网内传输大文件的速度不会成为瓶颈，尤其是未来如果升级到万兆交换机，5GbE 这个口直接接上去就好了。</p>

<h2 id="购买和开箱">购买和开箱</h2>

<p>我选择了商业版，1999 元包含一块 128GB 的 SSD 用作系统盘，以及五年的[[飞牛 fnOS]]会员。硬盘另外单独购买，选了两块 8TB 的西数红盘，在二手市场淘到了不错的价格。</p>

<p>机器到手打开包装，机身是金属一体机身，手感和质感比我想象中要好，不像某些小主机那样有明显的塑料感。涡轮风扇采用定向风道设计，噪音控制得还可以，放在书架上日常基本听不到。硬盘安装很简单，上盖一拧就开，两块 3.5 寸盘插进去接好数据线和电源线，盖上机器就完成了，完全不需要工具。</p>

<h2 id="安装飞牛-fnos">安装飞牛 fnOS</h2>

<p>[[飞牛 fnOS]] 是国内专门为 NAS 和家庭服务器开发的操作系统，界面设计类似 [[Synology]] DSM，但在国内生态适配上做了很多工作。商业版附赠的会员可以解锁一些高级功能，比如更多的用户数和一些进阶的备份选项。</p>

<p>安装过程很顺畅，系统镜像预先写入附赠的 128GB SSD，插进 M.2 口然后开机，按照引导一步步来，大概 15 分钟左右就可以通过浏览器访问后台了。两块 8TB 硬盘在存储管理里组成了 RAID 1，这样任何一块盘损坏数据都不会丢失，代价是可用空间只有 8TB，对于我目前的需求已经绰绰有余。</p>

<h2 id="用来存视频素材">用来存视频素材</h2>

<p>系统搭好之后，第一件事就是把分散在各个移动硬盘里的素材集中迁移过来。通过局域网的 SMB 共享，ME Pro 直接挂载成网络驱动器，拖拽就能复制，5GbE 的速度让这个过程比我想象中快不少，一块 4TB 移动硬盘大概两个小时左右就能传完。</p>

<p>视频素材的目录结构我按照「年份/月份/项目名」的层级来组织，每个项目文件夹下面分别放原始素材、粗剪片段、成品文件以及缩略图，这样以后要找某个项目的任何素材都一目了然。[[飞牛 fnOS]]自带的文件管理界面可以直接在浏览器里预览视频，不用把文件下载下来就能快速确认内容，这个功能在翻找素材时省了不少时间。</p>

<p>频道视频的存档也按类似思路整理，每条视频单独一个文件夹，成品 MP4、字幕文件、封面图和相关素材放在一起。以后视频需要重新剪辑或者输出成其他格式，素材随时可以调出来，不用再担心丢失或者找不到的问题。</p>

<h2 id="额外跑了什么服务">额外跑了什么服务</h2>

<p>既然 ME Pro 本质上是一台 Intel N95 的小型 Linux 主机，在跑 NAS 之外顺带跑一些服务也是自然而然的选择。我在[[飞牛 fnOS]]里启用了 Docker，额外部署了几个常用服务：</p>

<ul>
  <li>[[Jellyfin]] 用来在局域网内看存档视频，手机和电视上都能访问，效果非常好</li>
  <li>[[Syncthing]] 实现电脑和 NAS 的自动同步，新素材拍完了接上网络就自动传输，不用手动操作</li>
  <li>一个简单的 [[WebDAV]] 服务，配合 [[Tailscale]] 可以在外出时访问 NAS 上的文件</li>
</ul>

<p>ME Pro 的性能面对这些轻量级服务完全绰绰有余，平时 CPU 占用基本维持在很低的水平，即使同时在转码视频也没有出现明显卡顿。</p>

<h2 id="最后">最后</h2>

<p>搭建这套存储方案之后，视频素材管理的效率提升非常明显。以前要找素材靠记忆和运气，现在打开浏览器按目录搜索，几秒钟就能定位到。更重要的是，再也不用担心移动硬盘磕碰或者丢失导致数据消失，RAID 1 的冗余加上机器稳定的散热设计，让这套方案在可靠性上有了基本保障。</p>

<p>1999 元的商业版对于一套可以稳定运行的家用 NAS 方案来说性价比相当合理，尤其是对于有视频创作需求、同时又不想在存储管理上花太多时间折腾的人来说，是一个很值得考虑的选项。如果你也在面临素材越堆越多、找文件费时费力的问题，不妨认真考虑一下这个方向。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="产品体验" /><category term="nas" /><category term="mini-pc" /><category term="零刻" /><category term="beelink" /><category term="fnos" /><category term="飞牛" /><category term="视频存储" /><category term="存储" /><category term="home-server" /><summary type="html"><![CDATA[购买零刻 ME Pro 小型 NAS 主机，解决视频素材和频道存档的本地存储难题]]></summary></entry><entry><title type="html">试遍所有 Navidrome 客户端，我最终选择了 Narjo</title><link href="https://blog.einverne.info/post/2026/05/narjo-music-player-review.html" rel="alternate" type="text/html" title="试遍所有 Navidrome 客户端，我最终选择了 Narjo" /><published>2026-05-19T00:00:00-05:00</published><updated>2026-05-19T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/05/narjo-music-player-review</id><content type="html" xml:base="https://blog.einverne.info/post/2026/05/narjo-music-player-review.html"><![CDATA[<p>我是一个对音乐播放体验有点执念的人。自从搭建了 [[Navidrome]] 自托管音乐服务器，我就开始了一段漫长的客户端寻觅之旅。在 iOS 上，我几乎把能找到的 [[Navidrome]] 客户端都试了一遍，甚至为了体验更好而付费购买了 [[音流]]。但最终，我还是删掉了它们，把 [[Narjo]] 固定在了屏幕上。</p>

<p><img src="https://pic.einverne.info/images/2026-05-19-14-00-00-narjo-music-player.png" alt="Narjo 音乐播放器界面" /></p>

<h2 id="自托管音乐服务器的现状">自托管音乐服务器的现状</h2>

<p>在流媒体服务大行其道的今天，还在折腾自建音乐库的人，往往有些相似的执念：想要拥有自己的音乐，不依赖平台，不受版权下架的困扰，或者单纯就是喜欢把喜欢的 FLAC 文件存放在自己的硬盘上。[[Navidrome]] 就是这样一个开源的自托管音乐服务器，轻量、稳定，支持 Subsonic API，所以理论上所有兼容 Subsonic 的客户端都可以连接它。</p>

<p>问题在于，”兼容”只是入场门票，而体验才是核心。iOS 上能用的 Navidrome 客户端并不少，但真正做到让人用得顺手的寥寥无几。大多数客户端要么界面陈旧，停留在十年前的设计语言里；要么功能堆砌，导航逻辑混乱；还有一些干脆就是把 Web 界面套了个壳，交互完全不像一个原生 iOS 应用该有的感觉。</p>

<h2 id="试用之路从免费到付费">试用之路：从免费到付费</h2>

<p>在我的寻觅过程中，[[Amperfy]] 是我第一个认真使用的客户端。它功能完整，支持离线缓存，也有基本的播放控制，作为一个免费的开源项目，确实值得称道。但它的界面让我觉得有些拥挤，信息层级不够清晰，切换专辑和浏览曲库的流程不够顺滑。[[Substreamer]] 的情况类似，功能可以用，但交互逻辑让我时不时需要多按几次才能找到想要的东西。</p>

<p>后来，我入手了<a href="https://blog.einverne.info/post/2024/07/stream-music-navidrom-subsonic.html">音流</a>。作为一款专门面向中国用户的 Navidrome iOS 客户端，它在本地化和 UI 打磨上明显花了心思。首次打开时，它的界面确实漂亮，对齐感强，颜色搭配也舒服。我用它有一段时间，也觉得够用。但用着用着，我开始注意到一些小摩擦：某些操作需要多一步确认，某些列表加载有时显得不够流畅，以及一些我说不清楚的”不对劲”——就是那种打开 App 之后，感觉操作动线与自己的直觉有微妙偏差的感受。</p>

<p>就在这个阶段，我发现了 [[Narjo]]。</p>

<h2 id="narjo-的第一印象">Narjo 的第一印象</h2>

<p>打开 Narjo 的第一眼，我就感觉到有些不一样。它的界面用的是深色主题，专辑封面被放大展示，字体选择和排版间距都是那种”照顾过的”状态，不是为了塞更多信息而牺牲视觉呼吸感。最重要的是，常用操作都在拇指能自然触达的区域，这是很多音乐播放器容易忽视的细节。</p>

<p>Narjo 对 iOS 生态的集成让我印象深刻。它支持锁屏小组件，可以在桌面直接看到当前播放的曲目；支持 CarPlay，开车时不需要掏出手机；支持 Siri 指令控制，以及 iOS Shortcuts 的自动化接入。这些不是”有聊胜于无”的功能点，而是真正能在日常使用中减少摩擦的设计决策。这背后体现的是开发者把 Narjo 当成一个认真的 iOS 公民来打造，而不是单纯移植一套功能表。</p>

<h2 id="那些让我留下来的细节">那些让我留下来的细节</h2>

<p>在音质和播放控制层面，Narjo 支持交叉淡入淡出（crossfade）和无缝播放（gapless playback），这对于听专辑的完整体验来说很重要；内置的 EQ 调节功能可以针对不同耳机调整音色偏好；歌词同步显示的效果也很精准，配合深色界面，有一种恰到好处的沉浸感。</p>

<p>离线缓存的逻辑做得比较聪明。可以设置缓存上限，Narjo 会根据播放记录自动管理哪些内容需要保留，不需要手动管理一堆离线文件。这个细节省掉了我不少麻烦——之前用其他客户端，时不时需要手动清理缓存，Narjo 让这件事变得透明。</p>

<p>UPNP/DLNA 输出的支持也是我没想到的惊喜。家里有一台支持 DLNA 的音箱，以前需要通过其他 App 才能推送，现在直接在 Narjo 里就能选择输出设备，切换播放端的体验变得完整了。</p>

<h2 id="对比音流的一些思考">对比音流的一些思考</h2>

<p>我没有贬低 [[音流]] 的意思，对于很多用户来说，它是一个非常成熟的选择，中文界面和对国内用户习惯的针对性设计也有其价值。但对我个人来说，Narjo 在两个维度上胜出：一是与 iOS 原生能力的深度整合，Narjo 更像是”为 iPhone 设计的”，而不是”在 iPhone 上可以用的”；二是整体交互流的顺畅程度，我很难用一两个功能点来描述这种差异，但就是那种打开 App、找到想听的歌、按下播放的整个流程里，Narjo 让我产生的阻力更少。</p>

<h2 id="最后">最后</h2>

<p>折腾自托管音乐服务的人，通常不缺耐心，但真正好的工具应该让人花时间在音乐本身，而不是操作界面上。Narjo 目前还在 TestFlight 测试阶段，这意味着它还在持续迭代，也意味着它有可能在未来引入订阅或一次性付费。但就目前的体验而言，它已经是我用过的所有 iOS Navidrome 客户端里最让我满意的一个。</p>

<p>如果你也在用 [[Navidrome]]，还没有找到一个用起来顺手的 iOS 客户端，<a href="https://narjomusic.com/">Narjo</a> 值得一试。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="产品体验" /><category term="navidrome" /><category term="narjo" /><category term="music-player" /><category term="ios" /><category term="self-hosted" /><category term="streaming" /><category term="music" /><summary type="html"><![CDATA[在试遍了 iOS 上几乎所有 Navidrome 客户端，甚至购买了付费的音流之后，我还是回到了 Narjo——一款在细节和交互上都让我感到舒适的播放器。]]></summary></entry><entry><title type="html">Trellis：让 AI 编码代理真正投入生产的框架</title><link href="https://blog.einverne.info/post/2026/05/trellis-ai-coding-agent-framework.html" rel="alternate" type="text/html" title="Trellis：让 AI 编码代理真正投入生产的框架" /><published>2026-05-16T00:00:00-05:00</published><updated>2026-05-16T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/05/trellis-ai-coding-agent-framework</id><content type="html" xml:base="https://blog.einverne.info/post/2026/05/trellis-ai-coding-agent-framework.html"><![CDATA[<p>最近我一直在思考一个问题：AI 编码工具越来越多，但为什么每次切换工具或开启新会话，都感觉像是从零开始？我用 [[Claude Code]] 写了一段时间，又想试试 [[Gemini]] CLI，但每次都要重新解释项目背景、编码规范、当前任务进度。这种重复性的”上下文喂养”工作，慢慢变成了一种隐性负担。</p>

<p>直到我发现了 Trellis，才意识到这个问题其实已经有人在认真解决了。</p>

<p><img src="https://pic.einverne.info/images/2026-05-16-trellis-ai-coding-framework.png" alt="Trellis AI 编码代理框架" /></p>

<h2 id="什么是-trellis">什么是 Trellis</h2>

<p>Trellis 是由 Mindfold AI 开发的一个开源框架，定位非常明确：让 AI 编码代理真正具备生产就绪能力（production-ready）。它的核心理念是把开发规范、任务上下文和项目记忆统一存储在代码库中，让任何支持的 AI 平台都能自动获取这些信息，而不是每次手动复制粘贴。</p>

<p>从本质上说，Trellis 是一个”AI 代理的工作协议层”。它不替代任何具体的 AI 工具，而是在这些工具之上建立一套共享的约定和存储机制。开发者写一次规范，团队中所有人的 AI 代理都能受益；记录一次任务背景，下次换平台或换人接手时，AI 依然能理解当前进度。</p>

<p>目前 Trellis 声称支持 14 个 AI 编码平台，涵盖 [[Claude Code]]、Gemini CLI、OpenAI Codex、Cursor、Windsurf、Cline 等主流工具，基本覆盖了现在开发者常用的选择。</p>

<h2 id="为什么-ai-辅助开发需要一个框架">为什么 AI 辅助开发需要一个框架</h2>

<p>在没有 Trellis 这类工具之前，AI 辅助开发存在几个非常实际的痛点，相信很多人都踩过。</p>

<p>第一个是上下文丢失问题。每次新开会话，AI 都不记得上次的内容。你需要重新解释项目结构、告诉它你们团队不用 <code class="language-plaintext highlighter-rouge">var</code>、提醒它接口命名要用驼峰式。这些碎片化的重复沟通，看似小事，累积起来却非常耗时。</p>

<p>第二个是跨平台一致性问题。假设你用 Claude Code 开发，同事用 Cursor，另一个同事用 Copilot，那三个人的 AI 助手都在按各自的”理解”写代码，最终合并时风格差异、规范冲突在所难免。</p>

<p>第三个是任务追踪缺失。AI 写代码很快，但它不知道”整个功能的实现计划是什么”、”这个 PR 的审查意见有哪些”、”上次做到哪一步了”。没有结构化的任务信息，AI 的输出往往是局部正确但全局脱节的。</p>

<p>Trellis 针对这三个问题都给出了对应的解决方案。</p>

<h2 id="trellis-的核心机制">Trellis 的核心机制</h2>

<p>Trellis 在项目根目录下创建一个 <code class="language-plaintext highlighter-rouge">.trellis/</code> 文件夹，里面分三个子目录各司其职，设计非常清晰。</p>

<p><code class="language-plaintext highlighter-rouge">.trellis/spec/</code> 存放编码约定和项目规范。你可以在这里定义代码风格、架构决策、命名规范、禁止使用的模式等内容。Trellis 会在每次 AI 会话启动时自动注入这些规范，让 AI 始终按照你们团队的标准工作，而不是按照它自己的默认偏好。</p>

<p><code class="language-plaintext highlighter-rouge">.trellis/tasks/</code> 是任务中心。你可以把产品需求文档（PRD）、功能实现计划、代码审查意见存储在这里。当 AI 开始处理一个任务时，它能先读取完整的任务背景，而不是盲目地从当前文件猜测意图。这让 AI 的工作更有方向感，输出的代码也更符合整体设计。</p>

<p><code class="language-plaintext highlighter-rouge">.trellis/workspace/</code> 保存项目记忆。它会记录历史会话中的重要决策、已解决的问题和关键背景信息。这样下一次会话，不管是你自己接着做，还是换个 AI 平台，都能基于真实历史而不是空白状态开始工作。</p>

<p>这三个目录的内容都是普通文本文件，可以纳入版本控制，团队成员都能贡献和修改。一个人优化了规范，所有人的 AI 助手都同步受益，这是 Trellis 设计中我最欣赏的一点。</p>

<h2 id="安装与上手">安装与上手</h2>

<p>Trellis 的安装非常简单，通过 npm 全局安装即可：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span> <span class="nt">-g</span> @mindfoldhq/trellis@latest
</code></pre></div></div>

<p>然后在你的项目目录下初始化：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>trellis init <span class="nt">-u</span> your-name
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">-u</code> 参数指定你的用户名，用于在项目记忆中标记是谁添加了哪些信息。初始化完成后，<code class="language-plaintext highlighter-rouge">.trellis/</code> 目录会被创建，里面有基础的模板文件供你填写。</p>

<p>之后的工作流很自然：在 <code class="language-plaintext highlighter-rouge">spec/</code> 下写你们的编码规范（可以从现有的 <code class="language-plaintext highlighter-rouge">.cursorrules</code> 或 CLAUDE.md 迁移过来），在 <code class="language-plaintext highlighter-rouge">tasks/</code> 下创建功能任务卡，然后打开你喜欢的 AI 编码工具开始工作。Trellis 负责在会话开始时把相关信息注入给 AI，你只需要专注于具体问题本身。</p>

<p>对于已经有 <code class="language-plaintext highlighter-rouge">CLAUDE.md</code> 或者 <code class="language-plaintext highlighter-rouge">.cursorrules</code> 的项目来说，迁移成本很低。可以把现有的规范内容直接复制到 <code class="language-plaintext highlighter-rouge">.trellis/spec/</code> 下，Trellis 会统一管理这些信息并适配到不同平台。</p>

<h2 id="在团队协作中的价值">在团队协作中的价值</h2>

<p>我觉得 Trellis 真正发光的场景是多人协作。个人开发者用它能省去不少重复沟通，但团队使用时带来的价值会被放大几倍。</p>

<p>想象一个典型场景：产品经理把需求写成一个 <code class="language-plaintext highlighter-rouge">tasks/feature-xxx.md</code> 文件提交到仓库，后端工程师的 Claude Code 读取后按照 <code class="language-plaintext highlighter-rouge">spec/backend-conventions.md</code> 中的规范实现接口，前端工程师的 Cursor 读取同样的任务背景后按照 <code class="language-plaintext highlighter-rouge">spec/frontend-conventions.md</code> 写组件，代码审查的意见也记录回 task 文件。整个过程中 AI 始终在同一个”知识库”下工作，而不是各自为战。</p>

<p>这种模式下，团队对 AI 工具的依赖不再是个人行为，而是变成了一种可以持续演进的集体能力。规范越来越完善，记忆越来越丰富，新成员入职后 AI 助手也能快速提供高质量的辅助，因为它已经”学到”了这个团队的工作方式。</p>

<h2 id="最后">最后</h2>

<p>Trellis 解决的问题听起来不算惊天动地，但它戳中了 AI 辅助开发工作流中一个真实而长期被忽视的薄弱环节：AI 工具本身越来越强，但工具之间、会话之间的连接性一直很弱。把规范和记忆从每个工具的私有配置中解放出来，存到代码库里统一管理，这个思路既简单又务实。</p>

<p>对我来说，Trellis 最大的启示不只是这个工具本身，而是它背后反映的一种工程思维：AI 代理不应该是用完即弃的临时助手，而应该是可以积累知识、与团队共同成长的协作者。把这种积累机制做好，才是 AI 辅助开发真正成熟的标志。</p>

<p>如果你也在使用多个 AI 编码工具，或者在团队中推广 AI 辅助开发，Trellis 值得认真了解一下。项目地址在 GitHub：<a href="https://github.com/mindfold-ai/trellis">mindfold-ai/trellis</a>。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="学习笔记" /><category term="ai" /><category term="coding-agent" /><category term="developer-tools" /><category term="workflow" /><category term="claude-code" /><summary type="html"><![CDATA[Trellis 是一个开源框架，帮助开发者在 Claude Code、Gemini、Codex 等 AI 编码平台间共享上下文和工作流规范，让 AI 辅助开发真正可持续。]]></summary></entry><entry><title type="html">tiptop：用图表重新定义命令行系统监控</title><link href="https://blog.einverne.info/post/2026/05/tiptop-command-line-system-monitor.html" rel="alternate" type="text/html" title="tiptop：用图表重新定义命令行系统监控" /><published>2026-05-13T00:00:00-05:00</published><updated>2026-05-13T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/05/tiptop-command-line-system-monitor</id><content type="html" xml:base="https://blog.einverne.info/post/2026/05/tiptop-command-line-system-monitor.html"><![CDATA[<p>最近在排查一台服务器的性能问题时，我习惯性地打开了 [[htop]]，盯着那一列列滚动的数字，试图从里面读出 CPU 负载的变化趋势。说实话，数字本身没什么问题，但当你需要判断”过去几分钟内 CPU 是否有明显的周期性抖动”时，一屏幕的百分比实在不如一条折线来得直观。就在那个时候，我发现了 tiptop 这个工具，用了之后感觉有点相见恨晚。</p>

<p><img src="https://pic.einverne.info/images/2026-05-13-16-00-00-tiptop-system-monitor.png" alt="tiptop 终端系统监控工具界面示意" /></p>

<h2 id="从-top-到-tiptop">从 top 到 tiptop</h2>

<p>[[top]] 是 Unix/Linux 系统里最经典的进程监控命令，存在了几十年，几乎人人都用过。后来出现的 [[htop]] 在交互体验上做了很大的改进，支持鼠标操作、彩色显示、树形进程视图，成为很多开发者和运维工程师的首选替代品。但不管是 top 还是 htop，它们的核心展示方式都是”表格+数字”——实时刷新的数字列表。</p>

<p>tiptop 走了一条不同的路。它的核心理念是把这些数字变成图表，让趋势可见。与其告诉你”当前 CPU 使用率 47%”，不如展示一条过去几分钟内 CPU 使用率的折线图，让你一眼就能看出现在是上升趋势、下降趋势还是保持稳定。这个思路其实很简单，但在命令行工具里做出来效果非常实用。</p>

<p>tiptop 由开发者 Nico Schlömer 用 Python 编写，项目托管在 GitHub（<code class="language-plaintext highlighter-rouge">nschloe/tiptop</code>），采用 MIT 许可证开源。底层依赖两个库：[[Textual]] 负责终端 UI 的布局和渲染，[[psutil]] 负责跨平台地抓取系统数据。这个技术选型让 tiptop 在 Windows、macOS 和 Linux 上都能运行，不需要针对不同平台做特殊配置。</p>

<h2 id="实时图表带来的不同体验">实时图表带来的不同体验</h2>

<p>打开 tiptop 之后，界面分为上下两个区域。上半部分是多个图表面板，默认展示 CPU 使用率、内存占用、网络收发速率和磁盘 I/O，每个面板都是持续更新的折线图，横轴代表时间，纵轴代表指标数值。下半部分是进程列表，和 htop 类似，可以按 CPU 或内存占用排序。</p>

<p>图表面板的价值在排查间歇性问题时体现得最为明显。比如你怀疑某个任务每隔一段时间会触发一次 CPU 峰值，用普通的 top 你只能守在屏幕前等待，碰巧刷新到那一刻才能看到。而 tiptop 的图表会把历史趋势保留下来，哪怕你回头看，也能从曲线的波峰位置推断出问题的规律。内存的图表同样有用，缓慢上升的曲线往往是内存泄漏的早期信号，远比”当前使用 3.2GB”这个数字更能引起警觉。</p>

<p>网络流量的可视化对于需要监控带宽使用的场景也很有帮助。折线图能清楚地展示流量的突增和骤降，结合下方的进程列表，定位到是哪个进程在消耗带宽相对容易。</p>

<h2 id="安装和基本使用">安装和基本使用</h2>

<p>tiptop 的安装非常简单，通过 [[pip]] 一行搞定：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>tiptop
</code></pre></div></div>

<p>安装完成之后直接运行 <code class="language-plaintext highlighter-rouge">tiptop</code> 即可启动，无需任何配置文件。工具会自动检测系统环境并选择合适的网络接口显示流量数据，如果需要指定特定网络接口，可以通过 <code class="language-plaintext highlighter-rouge">--net</code> 参数传入：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tiptop <span class="nt">--net</span> eth0
</code></pre></div></div>

<p>交互方面，tiptop 提供了一些常用的键盘快捷键。<code class="language-plaintext highlighter-rouge">q</code> 退出，<code class="language-plaintext highlighter-rouge">c</code> 按 CPU 使用率排序进程列表，<code class="language-plaintext highlighter-rouge">m</code> 按内存排序，<code class="language-plaintext highlighter-rouge">p</code> 暂停和恢复数据更新。操作逻辑和 htop 很接近，熟悉 htop 的人几乎不需要学习成本。</p>

<p>在 macOS 上也可以通过 [[Homebrew]] 或 [[MacPorts]] 安装：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># MacPorts</span>
<span class="nb">sudo </span>port <span class="nb">install </span>tiptop
</code></pre></div></div>

<p>如果你用 [[pipx]] 管理命令行 Python 工具（这是我个人更推荐的方式，避免污染全局环境），安装命令是：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pipx <span class="nb">install </span>tiptop
</code></pre></div></div>

<h2 id="适合哪些场景">适合哪些场景</h2>

<p>tiptop 最适合几类使用场景。第一是 SSH 到远程服务器做临时巡检，一条命令启动，图表直接告诉你服务器当前的负载趋势，比看数字效率高得多。第二是在本地做性能测试的时候，把 tiptop 开在一个终端窗口里，另一个窗口跑压测脚本，CPU 和内存的变化曲线实时可见，结果比截图一列数字更有说服力。第三是日常在终端里保持一个 tiptop 窗口，充当轻量级的系统仪表盘，随时能看到机器的运行状态。</p>

<p>和 htop 相比，tiptop 并不是要取代它。htop 在进程管理方面更完善，支持直接 kill 进程、设置 nice 值、查看进程树，这些是 tiptop 没有的功能。两者更像是互补关系：需要深入管理进程时用 htop，需要直观感知系统负载趋势时用 tiptop。</p>

<p>有一点值得注意，tiptop 目前的版本（0.2.8）功能还比较精简，自定义面板布局的能力有限。如果你有非常复杂的监控需求，可能需要搭配专业的监控系统来用。但作为一个轻量、开箱即用的命令行工具，它的定位非常清晰，在这个定位内做得相当好。</p>

<h2 id="最后">最后</h2>

<p>tiptop 让我意识到，有时候改变信息的呈现方式，比增加信息本身更有价值。同样是系统监控数据，用折线图表达出来的洞察力远超数字列表。对于经常在终端里工作的开发者和运维人员来说，这是一个值得加入工具箱的小工具，安装成本几乎为零，但在需要判断趋势的场景下能节省不少脑力。如果你已经习惯了 top 和 htop，不妨花五分钟试试 tiptop，感受一下图表化监控带来的不同体验。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="经验总结" /><category term="linux" /><category term="macos" /><category term="cli" /><category term="system-monitor" /><category term="terminal" /><category term="python" /><category term="tui" /><summary type="html"><![CDATA[介绍 tiptop 这款基于 Python Textual 框架的命令行系统监控工具，通过实时折线图可视化 CPU、内存、网络等关键指标，让系统状态一目了然。]]></summary></entry><entry><title type="html">PostHog：一站式开源产品分析平台</title><link href="https://blog.einverne.info/post/2026/05/posthog-introduction.html" rel="alternate" type="text/html" title="PostHog：一站式开源产品分析平台" /><published>2026-05-13T00:00:00-05:00</published><updated>2026-05-13T00:00:00-05:00</updated><id>https://blog.einverne.info/post/2026/05/posthog-introduction</id><content type="html" xml:base="https://blog.einverne.info/post/2026/05/posthog-introduction.html"><![CDATA[<p><img src="https://pic.einverne.info/images/2026-05-13-10-00-00-posthog-cover.png" alt="PostHog 产品分析平台" /></p>

<p>做产品的人都绕不开一个问题：用户到底在用我的产品做什么？他们在哪一步流失，哪个功能最受欢迎，新版本上线后行为有没有变化。回答这些问题需要数据，而收集和分析这些数据，往往需要堆砌一大堆工具——用 [[Mixpanel]] 做事件分析，用 [[FullStory]] 录制会话，用 [[LaunchDarkly]] 管理功能开关，用 [[Optimizely]] 跑 A/B 测试。每个工具都要单独集成 SDK，单独管理账单，数据还分散在各处，关联分析几乎不可能。</p>

<p>我在寻找一个能把这些能力整合起来、又不让数据流向第三方的解决方案时，发现了 [[PostHog]]。</p>

<h2 id="posthog-是什么">PostHog 是什么</h2>

<p>PostHog 是一个开源的产品分析平台，2020 年创立于英国，目标是把产品团队需要的数据工具全部放进一个产品里。它的 GitHub 仓库完全公开，代码量巨大，背后有真实的商业化运营——他们提供云端托管版本，同时也支持你把整套系统部署在自己的服务器上。</p>

<p>与 Mixpanel、Amplitude 这类纯 SaaS 产品分析工具相比，PostHog 最大的差异点有两个：一是功能广度，它把过去需要多个工具才能覆盖的能力都内置了；二是数据主权，自托管模式意味着用户行为数据不需要离开你自己的基础设施。对于需要处理敏感数据的企业，或者对数据隐私有要求的团队，这一点非常关键。</p>

<h2 id="核心功能全景">核心功能全景</h2>

<p>PostHog 的功能体系比大多数竞品都要宽，理解它的方式是把它当成一个产品数据基础设施平台，而不是单一的分析工具。</p>

<p>产品分析是它的核心。你可以通过埋点或者无埋点自动捕获用户行为，然后用漏斗（Funnel）分析转化路径，用留存图追踪用户的回访规律，用路径图看用户在不同页面或功能之间的流转情况。这部分的体验和 Mixpanel 非常接近，对熟悉主流产品分析工具的人来说几乎没有学习成本。</p>

<p>会话录制（Session Recording）让你能够回放真实用户的操作过程。不同于一般的屏幕录制工具，PostHog 的会话录制是事件驱动的，可以直接和分析数据关联起来——比如你发现某个漏斗步骤流失率很高，可以直接点击查看这批用户的会话录像，看他们究竟遇到了什么问题。热图功能也集成在这里，点击热图和滚动热图都支持。</p>

<p>功能开关（Feature Flags）是另一个让我印象深刻的模块。你可以基于用户属性、百分比、地区等条件精细化控制某个功能的可见范围，灰度发布时极为有用。功能开关可以直接与分析事件绑定，不需要额外配置就能看到开关不同状态下的用户行为差异。</p>

<p>A/B 测试（Experiments）建立在功能开关的基础上。你定义实验组和对照组，指定目标指标，系统会自动进行统计显著性计算，告诉你哪个变体表现更好。整套流程内置在 PostHog 里，不需要像以前那样把实验平台和分析工具分开管理。</p>

<p>用户调研（Surveys）是相对较新的功能，支持在产品内弹出问卷，收集 NPS 评分或开放式反馈，数据同样汇入 PostHog 的数据仓库，可以和行为数据一起分析。</p>

<p>数据管道（Data Pipelines）功能支持把 PostHog 采集到的事件实时同步到外部数据仓库，比如 BigQuery、Snowflake、Redshift，或者从外部系统导入用户属性数据。这让 PostHog 能够融入更复杂的数据栈，而不是成为数据孤岛。</p>

<h2 id="自托管体验">自托管体验</h2>

<p>PostHog 提供两套部署方案：PostHog Cloud（官方托管）和自托管版本。</p>

<p>自托管走的是 Docker Compose 路线，官方提供了一套完整的配置文件，在配置合理的 VPS 上一条命令就能跑起来。数据库用 ClickHouse，事件处理走 Kafka，整体架构相当现代化。ClickHouse 在列式存储上的优势让 PostHog 能以较低的资源消耗处理大量事件数据。</p>

<p>我在一台 4 核 8GB 的机器上跑过自托管版本，每天处理几十万事件问题不大，查询响应速度也挺快。真正对服务器要求高的是 ClickHouse 的内存消耗——事件量上来之后这块的资源需求会明显增加，官方建议生产环境至少 4 核 16GB 起步。</p>

<p>自托管的另一个好处是成本可控。PostHog Cloud 的定价是按事件量计费，免费层每月有 100 万事件，超出后按阶梯计费。对于用户量不小的产品，云端费用会积累得相当快。自托管虽然需要运维投入，但边际成本主要是服务器资源。</p>

<h2 id="与主流工具的对比">与主流工具的对比</h2>

<p>相比 Mixpanel 和 Amplitude，PostHog 在分析深度上没有明显短板，但在某些高级分析功能上（比如预测性分析、机器学习驱动的洞察）目前还没有跟上。不过对于大多数产品团队的日常需求——漏斗、留存、路径、用户分群——PostHog 完全够用。</p>

<p>功能开关和 A/B 测试对比 LaunchDarkly 和 Optimizely，后者更专业，支持更复杂的实验设计和更精细的流量管理，但价格也相当不菲。PostHog 的这两个功能对于中小规模的团队来说足够，省去了另外集成专用工具的麻烦。</p>

<p>会话录制和 FullStory、Hotjar 比起来，易用性稍逊，但核心功能完备，更重要的是数据不出去。如果你之前用 Hotjar 主要是为了看录像和热图，PostHog 完全可以替代。</p>

<h2 id="最后">最后</h2>

<p>PostHog 解决的核心问题是工具碎片化——产品分析、会话录制、功能开关、A/B 测试，四件事在一个平台里全做了，数据天然打通，不需要靠导出 CSV 再手动关联。对于一个快速迭代的产品团队来说，这种集成度带来的效率提升是很实际的。</p>

<p>开源加自托管的组合则解决了另一个越来越被重视的问题：数据去哪了。不管是出于合规要求还是对数据资产的珍视，把用户行为数据放在自己可控的地方，是一个值得认真考虑的选择。</p>

<p>如果你正在为产品选型数据工具，PostHog 值得认真评估一番，尤其是你有自托管能力、团队在乎数据自主权的情况下。它不是在某一个维度做到极致的专用工具，但作为覆盖面最广的开源产品分析平台，目前没有什么竞争者。</p>]]></content><author><name>Ein Verne</name><email>git@einverne.info</email></author><category term="产品体验" /><category term="posthog" /><category term="product-analytics" /><category term="open-source" /><category term="self-hosted" /><summary type="html"><![CDATA[PostHog 是一款开源的产品分析平台，集产品分析、会话录制、功能开关、A/B 测试于一体，支持自托管，让你对数据拥有完整的掌控权。]]></summary></entry></feed>