Skip to content
0xlic
Go back

我的知识库 + 博客系统是怎么跑起来的

Edit page

为什么做这个系统

我想要的不是「再搭一个博客」,而是把四件事统一在一个仓库里:

  1. AI Agent 友好操作 —— 让模型能直接读、写、改我的笔记和文章,不需要我做格式转换
  2. 个人知识库积累 —— 长期沉淀,单向只增不减,按主题而不是按时间组织
  3. 从笔记到成品文章的输出 —— 草稿、卡片、文献笔记最终能凝练成可发布的内容
  4. 发布到公网 —— 让一部分内容走出私域,对外可读

传统做法是「笔记软件 + 博客系统」两套割裂的工具,写完笔记还要复制、改格式、再 push 一次。中间这层手工搬运是低价值的劳动,也是我最容易放弃更新的地方。

这个系统的核心目标,就是消除中间这层搬运

整体数据流

Obsidian / AI Agent 写作
    -> knowledge/06-Posts/*.md   (单一内容源)
    -> Astro 构建静态站点
    -> git push origin main
    -> Cloudflare Pages 自动部署
    -> 浏览器访问

整条链路只有一个内容源、一次构建、一次发布。我在 Obsidian 里做的所有改动,最终都会以同一份 Markdown 的身份出现在公网上 —— 这是最重要的设计前提。

知识库的设计:分层而不是分类

知识库目录采用 PARA 风格的编号结构:

这套分层不是按「话题」分的,是按「信息的成熟度」分的:

一条信息从 Inbox 进入,逐步精炼成卡片(Notes),多张卡片在 MOC 里被组织起来,最终被提炼成一篇 Posts。这个流向是单向的,写作不再是从空白页开始,而是从已有卡片中拼装

我做的取舍是:不允许「话题文件夹」。比如不会有一个叫 Programming/ 的目录把所有编程相关都塞进去。话题归属交给 tag 和 wikilink,目录只负责「这条信息处于什么阶段」。这样同一条笔记不会在多个目录之间纠结放哪。

博客只读 06-Posts/,其他目录不参与发布

所有可能公开的内容必须明确进入 06-Posts/,其他目录天然在公网视野之外。这条规则带来两个直接好处:

publish 护栏:默认不发布

即使文章已经在 06-Posts/ 里,也不会自动公开。每篇文章 frontmatter 必须显式声明:

「默认 false」这个选择是有意的。它的反面 ——「默认 true,要藏才设 false」—— 看起来更省事,但它把出错方向定在了「误公开」上。一旦写出来才发现不合适,回收的成本远高于多打几个字 publish: true

这是一道配合目录隔离的双层护栏:先决定「是否搬进 Posts」,再决定「是否打开开关」。两步都需要明确动作,没有任何一步可以靠默认行为完成发布。

Wikilink:保留 Obsidian 的连接性

Obsidian 的灵魂是 [[这种语法]],让笔记之间形成图谱。如果发布时把 wikilink 当作未知语法降级成纯文本,等于砍掉了 Obsidian 在公网这一侧的延伸。

所以构建时做了一层处理:

这样我在 Obsidian 里写文章时不需要切换语法,所有笔记里的链接习惯可以无缝带到博客里。同时这层处理保证了「未发布的笔记永远不会从已发布的文章里被链接到」—— 这又是一道护栏。

构建前检查:让发布意图显式化

每次构建前会自动跑一个脚本,扫描 knowledge/06-Posts/,列出所有即将发布的文章 —— 标题、文件路径、字数。

它不修改任何东西,只是把发布意图打印出来

这个脚本的存在不是为了发现错误,而是为了在我即将把内容推到公网前,给我一次「看一眼,是这些吗?」的机会。设计上很轻量,但它消除了一种特定的恐慌:「我刚才那次 push 到底发了什么出去?」

目录结构:blog/ 和 knowledge/ 平级,不嵌套

仓库根目录长这样:

/
├── blog/        # Astro 项目,包含 node_modules
├── knowledge/   # Obsidian vault
├── scripts/     # 构建检查脚本
└── ...

最初的直觉是把 blog/ 放进 knowledge/ 里面,这样「整个 vault 就是仓库」。但这会带来一个非常具体的问题:Obsidian 启动时会扫描 vault 下的所有文件,遇到 blog/node_modules/ 那十几万个文件会卡到不可用。

所以二者分开放,knowledge/ 是 Obsidian 的世界,blog/ 是 Node.js 的世界,互不干扰。Astro 在构建时跨目录读取 ../knowledge/06-Posts/,这种「跨目录读取」看起来不优雅,但解决了一个真实的体验问题。

这是一个典型的「为现实让步」的取舍:不追求结构上的纯净,而追求每天打开 Obsidian 的那 200 毫秒体验。

Cloudflare 这一段:从 push 到上线全自动

发布侧用 Cloudflare Pages,关键的几次配置:

Pages 授权 GitHub —— 一次性。授权时建议只选这一个仓库,不开放整个 GitHub 账号的访问权。

构建配置 —— 三件事讲清楚就够了:根目录是 blog/,构建命令是 npm install && npm run build,输出目录是 dist。Cloudflare 的免费额度对个人博客来说是溢出的。

域名 NS 迁移 —— 把域名的 nameservers 从原 DNS 服务商(我这里是腾讯云)切到 Cloudflare 自己的两个 NS 上。这不是改一条解析记录,是把整个域名的解析权交给 Cloudflare。代价是「不能再在原服务商面板上改解析」,收益是「DNS、CDN、SSL 全部一站式」,对单人写作来说这笔交换很划算。

SSL —— 全自动签发 Let’s Encrypt 证书。唯一要主动确认的是 SSL/TLS 模式必须是 Full(或 Full strict),不能是 Flexible,否则会出现奇怪的重定向死循环。

设置完成后,发布的全部动作就是:

git push origin main

GitHub webhook 通知 Cloudflare → clone 仓库 → 进 blog/ 跑 build → 部署到全球边缘节点。整个过程 1–3 分钟,没有手动步骤,也没有可以出错的地方。

这套系统故意不做的事

回过头看,这套系统在「最小可用」的边界上停得很早:

每砍掉一项,就少一份维护成本。当我自己也是这套系统的唯一用户时,克制比丰富更有价值

写在最后

这个系统的设计目标,从头到尾只有一个:让我能像写笔记一样去发文章,让 AI Agent 能像编辑文档一样去编辑公开内容,而不需要在「私域笔记」和「公开博客」之间维持两套心智模型。

它不是为了成为一个更好的博客系统,而是为了让我多写。


Edit page
Share this post on: