一直想做个人知识管理,拖了很久没有动手。到了 AI 时代,整理和记录的成本变得很低,于是今年 9 月正式开工:先搭了一个私人的知识库(LLM-Wiki 的方式,AI 辅助维护,笔记互相链接),博客则是它的公开出口——库里的笔记积累到一定程度,就改写出一篇文章发到这里。这篇文章记录的是博客本身的搭建过程:选型、踩坑,以及它和知识库怎么配合。这个博客目前主要是方便自己用的,个人整理难免有纰漏——如果读者发现了错漏,欢迎批评指正,也欢迎随时交流。
一、技术选型:最后用的组合
最终组合:Hugo(静态生成器)+ PaperMod(主题)+ Cloudflare Pages(托管)+ GitHub Actions(博客园同步),总成本 0 元。
| 决策点 | 候选 | 选择 | 理由 |
|---|---|---|---|
| 生成器 | Hexo / Hugo / Astro | Hugo | 单二进制、构建毫秒级;Hexo 生态成熟但近年动能放缓;Astro 的组件化对个人博客偏重 |
| 托管 | GitHub Pages / Cloudflare Pages | Cloudflare Pages | 全球 CDN、PR 自动预览部署、一键回滚 |
| 图片 | 图床 / 进仓库 | 进仓库 | 零外部依赖,仓库体积可控 |
| 国内平台 | 手动复制 / 自动同步 | Actions 自动同步博客园 | push 一次,双平台同时更新 |
说实话,选型时我没有逐个仔细研究这些方案,主要依赖了 AI 的判断。一是不想在新时代还用旧技术;二是 AI 时代学新东西的成本很低,不了解的技术借助 AI 也能很快上手。另外用新栈也有一个现实考虑:现在选定,可以避免老技术将来淘汰后的一次迁移。
还有一个放心点:文章全是 markdown,生成器以后想换随时能换。
二、架构总览
本地写作 (markdown) → git push → GitHub 仓库
│
├─→ Cloudflare Pages:构建 + 部署 → https://<名>.pages.dev
└─→ GitHub Actions:检测文章变更 → 自动同步到博客园
站点构建部署完全交给 Cloudflare;GitHub Actions 只负责把变更的文章同步一份到博客园。两边互相独立:博客园没配好不影响主站,主站随便换也不影响同步脚本。
三、搭建实录
1. Hugo 与主题
Hugo 是单文件程序,下载即用,机器上装好 Git 就行。主题选了 PaperMod:社区最大、中文 i18n 完善、自带搜索/目录/字数统计。主题文件直接放进仓库 themes/ 目录(vendored),升级主题等于替换目录,不引入 submodule。
2. 新版 Cloudflare 部署的三个坑
Cloudflare 已经把 Pages 并入 Workers 体系,新项目的部署流程和旧版不一样了。这次踩了三个坑,记录如下。
坑 1:引导页没有"输出目录"字段。 旧版流程里这一项填 public;新流程没有这项,输出目录改由仓库根目录的 wrangler.jsonc 声明:
{
"name": "wiki",
"compatibility_date": "2026-09-18",
"assets": {
"directory": "./public"
}
}
name 必须和 Cloudflare 项目名完全一致(小写),不一致会直接部署失败。
坑 2:缺 compatibility_date。 wrangler 强制要求的兼容性日期。报错信息里会给出建议值,照抄即可。
坑 3:向导里要填 Deploy command。 新流程多出来的字段,静态站也要填。我最终的向导配置:
| 向导字段 | 值 |
|---|---|
| Framework preset | Hugo |
| Build command | hugo --gc --minify |
| Deploy command | npx wrangler deploy |
| 环境变量(advanced 展开) | HUGO_VERSION=0.166.0 |
HUGO_VERSION 必须显式设置,否则 Cloudflare 用自带的旧版 Hugo 构建,会直接失败。
3. 改名陷阱
xxx.pages.dev 的子域名 = 项目名,项目名创建后不能改;想换名字只能删项目重建(内容都在 GitHub 上,重建 5 分钟)。所以创建项目时定的名字就是最终域名。另外账号设置里的 workers.dev 子域是另一套东西,和博客地址无关,这次没有动它。
4. baseURL:写给机器看的地址
config.yml 里的 baseURL 不影响页面访问,但它写进搜索引擎收录、RSS、站点地图里的每一条链接。部署拿到真实域名后要把它改成正式地址再 push 一次——上线初期改成本最低,此时还没被收录、没人订阅。
5. 双平台:自动同步博客园
push 一次同时发博客园,需要一个 GitHub Actions workflow。社区里常见的做法是抓 cookie 调博客园的非官方接口,这次用的是博客园官方的 MetaWeblog API(访问令牌认证,标准 XML-RPC,Python 标准库即可实现):
- 博客园后台 → 设置 → 其他设置 → 允许 MetaWeblog 博客客户端访问 → 设置访问令牌
- GitHub 仓库 Secrets 存三个值:
CNBLOGS_BLOGNAME(博客地址里的标识)/CNBLOGS_USERNAME/CNBLOGS_TOKEN - 同步脚本按变更文件增量工作:新文章自动创建,并把
postId回写到文章 front-matter;之后修改这篇文章,就自动更新博客园那一篇
一个前置:博客园现在开通博客需要人工审核,申请理由写"发布原创软件开发实践文章"这类即可,几小时到两天通过。
四、内容从哪来:和知识库联动
博客搭好之后是空的,接下来是内容。我的做法是把博客挂进私人知识库的工作流(思路来自 Karpathy 的 llm-wiki 模式):
平时:资料进知识库,AI 帮忙维护成互链的笔记(知识库私有)
↓ 每月一次
复盘:让 AI 挑"养熟"的页面(多来源支撑、被反复用到、观点稳定)
↓
改写:AI 基于笔记起草成文章 → 我补充观点和经历 → 发布
↓
回链:发布记录写回知识库原页面,闭环
这样每篇博客背后都是积累了一段时间的笔记,而不是对着空白编辑器硬憋。这篇文章本身就是这个流程的第一个产物。
五、成本与耗时
- 金钱:0 元(Hugo / Cloudflare / GitHub Actions / 博客园全部免费,域名 optional)
- 时间:从零到上线约 2 小时(不含等博客园审核),其中约一半花在第三节那几个坑上
- 日常写作:本地
hugo server -D预览,写完 push 即发布
六、结尾
整套东西搭下来:一个二进制(Hugo)、一个 git 仓库、两个托管平台(Cloudflare + 博客园),没有服务器、没有数据库、没有 deploy key。内容上,目前主要来自对自己已有认知的整理和学习记录;新技术的研究会陆续写进来。整个知识库用 LLM-Wiki 的方式维护,由 AI 辅助梳理、学习和补全知识框架——这部分等跑顺了再单独写一篇。
七、后记:上线之后又踩了两个坑
文章发出去之后,管线又暴露了两个问题,记录在这里。
坑 4:博客园同步报 301。 第一次触发同步,GitHub Actions 直接失败,报错是 ProtocolError ... 301 Moved Permanently。原因是博客园的 MetaWeblog 入口已经从 www.cnblogs.com/<博客名>/services/metaweblog.aspx 迁到了 rpc.cnblogs.com/metaweblog/<博客名>,旧路径只剩一个跳转,而 Python 的 XML-RPC 客户端默认不跟随跳转。脚本里的端点换成新地址就通了。同一步还暴露了一个 CI 环境问题:脚本回写 front-matter 后要自动提交,报 fatal: empty ident name——CI 机器上没有 git 身份,需要先配置 github-actions[bot] 的 user.name 和 user.email。
坑 5:pages.dev 绑定脱落。 当时的症状很迷惑:博客园那边文章已经能打开,主站首页看着也正常,唯独文章页 522。排查记录如下:构建日志全绿(文章文件确认上传了)、部署历史显示 100% 流量、workers.dev 直链完全正常——三条都排除后只剩一个可能:当时的 pages.dev 地址没有绑在当前项目上。到 Domains 页一看,“No custom domains”。来源是调试期间项目被重建成了普通 Worker,而 pages.dev 子域只有 Pages 项目才有资格持有;Worker 的 Add Domain 只接受自己拥有的域名,这个绑定接不回来。最终走旧版 Pages 向导重建项目解决,新地址换成了另一个带随机后缀的 pages.dev 子域,真实地址不在文中列出。这个坑的狡猾之处在于:首页一度"能看"其实是边缘缓存,让人误以为只有文章页坏了。
这类问题的排查顺序也留在这里:构建日志 → workers.dev 直链 → Domains 绑定。前两项正常而主域 522,基本就是绑定掉了。