一个本地优先工具的设计记录
记录一个本地优先写作工具的设计过程:为什么本地优先、数据怎么存、同步怎么取舍,以及目前仍未解决的问题。
目录 · Contents
这是一个正在构建的小工具的设计记录——一个本地优先的写作环境,附带一个能在本地语料上工作的 AI 助手。写下来是为了强迫自己把决策讲清楚。
为什么是本地优先
动机很朴素:写下来的东西应该比任何一家公司活得久。云服务提供的便利是真实的,但它的代价是你的语料以别人的存亡为前提。
本地优先在这个工具里意味着三条硬约束:
- 断网时功能完整可用(AI 功能除外,但要有明确降级);
- 数据是纯文件,离开这个工具依然可读、可搜索、可迁移;
- 任何「同步」都只是复制,不是迁移——本地副本永远是权威版本。
数据怎么存
内容用 Markdown 纯文件,元信息放在 frontmatter。目录即结构,不引入数据库。唯一允许的索引是一个可以随时重建的缓存:
notes/
├── 2026/
│ ├── 07-28-agent-boundaries.md
│ └── 07-21-local-first-design.md
└── .index/cache.json # 可随时删除重建
检索用倒排索引,构建一次几百篇笔记在毫秒级,不需要更重的方案:
function buildIndex(docs: Doc[]): Map<string, Set<string>> {
const index = new Map<string, Set<string>>();
for (const doc of docs) {
for (const term of tokenize(doc.body)) {
if (!index.has(term)) index.set(term, new Set());
index.get(term)!.add(doc.id);
}
}
return index;
}
工具应该像水:装满什么形状的容器都可以,倒出来还是水。
同步的取舍
多设备同步用「文件级最后写入胜出 + 冲突副本」。这意味着偶尔会产生 note (conflict 2026-07-21).md 这样的文件。我接受这个代价,因为:
- 写作场景的冲突本来就极少;
- 冲突以可见的副本呈现,比静默合并出错好得多;
- 它不需要一个中心服务器,任何文件夹同步工具都能胜任。
AI 助手放在哪一层
AI 助手不碰原始文件,只读写一个 drafts/ 目录。它生成的任何东西,都要经过人显式「采纳」才进入正式笔记。这是上一篇关于行动边界的思考在自家工具上的直接应用。
还没解决的问题
- 移动端查看体验目前很粗糙;
- 全文检索对中文分词的处理还很幼稚;
- 「采纳」这一步的交互还不够轻,用多了会烦。
先这样。工具是给自己用的,能在真实使用里长出来的设计才是活的设计。