从 Obsidian 到 GitHub Pages:一套 Codex 协作的个人知识管理与发布工作流
8月 7, 2026
AI 参与说明(Agent:Codex;模型:gpt-5.6-terra;reasoning effort:ultra):本文基于作者正在使用的实际工作流,由 Codex 协助梳理、资料校验和撰写。它是一套个人实践,不代表 Obsidian、GitHub、Hugo、Pagefind 或 OpenAI 的官方推荐方案。示例已脱敏,不包含仓库标识、令牌、私有提交信息或本机路径;产品行为与配置请以文末原始资料为准。
先给结论#
我把“记笔记、写文章、部署博客”当作同一个知识系统,而不是三件孤立的事:
- Markdown 与 Git 是事实源:内容可迁移、可审阅、可追溯。
- Obsidian 是作者端:负责捕捉、链接、浏览、周刊与本地编辑;它不是托管平台,也不是 CI。
- Codex / AI 是受约束的协作作者和工程助手:协助调研、结构化、生成初稿、检查示例与构建,不跳过事实、公开性与发布条件的核验。
- 私有内容仓库与公开站点仓库分离:前者保存 Markdown,后者保存 Hugo 主题、站点配置与部署工作流。
- GitHub Actions、Hugo、Pagefind 与 GitHub Pages 负责确定性发布:内容变动生成静态站点和搜索索引,再通过 artifact 部署到自定义域名。
这套机制追求的不是“让 AI 无条件自动发文”,而是把低摩擦记录、可验证写作和可回溯发布连成一条链。本文记录的是 2026-08-07 的架构快照;工具和版本会变化,但职责边界与验收方法可以复用。
架构:四层系统,加一个发布核验环#
flowchart LR
author["作者"] --> obsidian["Obsidian Vault<br/>Markdown、链接、模板"]
codex["Codex / AI<br/>调研、起草、校验、测试"] -. 协作并按规则核验 .-> author
codex -. 读取规则与项目上下文 .-> rules["Agents.md / Skills"]
obsidian --> git["Git<br/>私有内容仓库"]
git -->|push main| notify["私有通知工作流<br/>仅发送不透明更新信号"]
notify --> site["公开站点仓库<br/>Hugo 主题、配置、工作流"]
site --> ci["公开构建工作流<br/>只读检出私有内容"]
ci --> build["Hugo + Pagefind<br/>静态 artifact"]
build --> pages["GitHub Pages<br/>自定义域名"]
r2["可选:R2 / CDN 媒体"] --> pages
pages --> giscus["可选:Giscus 讨论"]图中的实线是当前发布链;虚线是辅助工作。最重要的原则是:内容完成规定的事实、链接、代码与排版核验,并通过公开性检查后,即可用 draft: false 进入发布边界;由 Agent 一次性完成的内容也按相同标准处理。
| 层次 | 当前职责 | 不是它的职责 |
|---|---|---|
| Obsidian | 编辑 Markdown、知识链接、模板与本地浏览 | CI、静态站点托管、事实裁决 |
| Git / 私有内容仓库 | 内容历史、核验与发布边界、发布信号的起点 | 自动保证内容适合公开 |
| Codex / AI | 调研、写作、代码与链接校验、构建排查 | 跳过事实与公开性核验的发布、替作者承担事实责任 |
| 公开站点仓库 | Hugo 主题、模板、配置与 Pages workflow | 保存私有内容的 Git 历史 |
| GitHub Actions | 检出、构建、校验、部署 artifact | 替代内容核验 |
| Hugo + Pagefind | 生成静态页面与站内搜索 | 动态 CMS 或实时协作服务 |
Obsidian 官方将 vault 定义为本地文件系统中的文件夹,笔记是 Markdown 纯文本,因此同一组文件可以被编辑器、Git 和 Hugo 共同使用。Obsidian 的数据存储说明
1. 知识从哪里来:把内容类型和生命周期分开#
内容仓库不是只有“文章”一种对象。当前约定按照生命周期组织:
| 目录 | 适合放什么 | 发布与维护特征 |
|---|---|---|
docs/ | 可长期维护的知识条目、概览和实践手册 | 主题稳定,持续修订 |
posts/<year>/ | 有明确发布时间的文章、经验总结 | 保留当时的上下文与日期 |
weekly/<year>/ | 周刊、工作记录和收集 | 固定周期、低摩擦积累 |
timeline/<year>.md | 仅包含链接的轻量记录 | 不把一个链接强行扩写成文章 |
links/、portfolio/ | 独立导航或展示内容 | 按页面类型渲染 |
这种划分解决的是一个常见问题:所有笔记都放在一个平铺目录时,“长期知识”“临时灵感”和“已发布文章”会很快混在一起。这里的规则是,先判断内容未来是否值得维护,再决定它是 docs 还是 posts;单个链接先进入 Timeline,只有明确需要独立说明时才成为页面。
图片等公开媒体是另一条可选支路:Markdown 只引用公开 URL,上传凭据留在本地环境变量中,上传前执行检查、dry run 和 CDN 验证。这样媒体传输不必混进内容发布 token,也不会让二进制文件污染正文的 Git 历史。
2. Obsidian:知识工作台,而不是“自动发布器”#
Obsidian 在这套系统中的价值,不在于把笔记锁进某个专有数据库,而在于它直接操作 Markdown vault。作者可以用模板快速创建周刊或文章,用反向链接和图谱发现关联,并在本地预览 Hugo 页面时从页面跳回对应的笔记。
当前作者机还启用了 Obsidian Git 的本地自动备份:配置会在文件变更后及每 60 分钟创建 backup,且 push 没有被禁用。这能显著降低“忘记保存到远端”的概率,但它带来一个非常关键的工程结论:
当
main的推送会触发部署时,main不是普通备份分支,而是发布许可边界。
因此,自动备份并不等于内容已经满足发布条件。对于希望保留自动 Git 备份的个人工作流,draft 应当反映内容状态:
- 完成事实、链接、代码与排版核验,且通过公开性检查的内容,直接使用
draft: false;由 Agent 一次性完成的内容也一样,无需额外等待确认。 - 内容尚未完成、仍有待核实事实、未通过公开性检查,或作者明确要求暂不发布时,使用
draft: true。 - 需要多人审批或严格审稿的其他团队,可额外使用工作分支和 PR;本 Blog 知识库固定在
main分支工作,不以分支作为日常发布机制。
为体现“完成即发布”的默认边界,template/new-post.md 以 draft: false 创建新文章;若创建时内容尚未满足上述条件,应立即改为 true 并在核验完成前保持草稿。本文已按完成内容设置为 draft: false,并在发布前复核 front matter、事实来源和生成结果。Obsidian 的插件配置属于作者机器的 .obsidian/ 目录,不应假定其他人 clone 仓库后会获得完全相同的自动化行为。
3. Codex / AI:把“会写”变成可审计的协作#
AI 最容易被误用成无来源、无验证的自动写作器。我的做法是把它纳入项目规则,而不是把规则留在一次对话里。
仓库根部的 Agents.md 是一份持久化的协作约定。它可以要求代理:
# 以下为写作规则的简化示意,不是可直接替换的完整文件
- 易变的产品事实优先引用官方一手资料,并标注适用版本或整理日期。
- 技术文章说明前置条件、边界与失败场景,提供可复现的代码或命令。
- 发布前检查链接、配置键、构建结果和生成 HTML。
- 不把隐私、令牌、私有路径、原始聊天记录或未经验证的猜测发布出去。
- 内容完成上述核验且通过公开性检查后,直接使用 <code>draft: false</code>;由 Agent 一次性完成的内容也适用。Codex 的官方说明也建议通过仓库中的指令文件(如 AGENTS.md)提供代码库导航、测试命令和项目标准;本仓库使用的文件名为 Agents.md。其输出应结合日志与测试结果完成相应核验后再集成。OpenAI:Codex 与 AGENTS.md。
在具体执行上,Codex / AI 的职责可以拆成四类:
- 选题与结构化:把一次问答或零散笔记改写成面向读者的独立文档,先给结论,再补背景、例子、限制和验证方法。
- 资料校验:对于 API、版本、定价、配额、兼容性等易变信息,优先寻找官方文档;无法验证时明确写成推断或待确认项。
- 工程校验:检查 Markdown front matter、内部链接、Mermaid、代码示例;运行 Hugo 构建,必要时检查生成 HTML。
- 变更卫生:只改任务涉及的文件,保留用户已有的工作区改动;在提交、推送、修改 Secrets 等外部动作前请求明确授权。
这并不构成内容质量保证。人仍然负责:选题取舍、来源可信度的最终判断、涉及个人或公司信息的公开性审查,以及是否合并或推送。
4. 为什么使用双仓库#
这套实现把“内容”与“站点工程”分成两个不同权限域:
| 仓库 | 可见性 | 保存内容 | 为什么这样拆 |
|---|---|---|---|
<private-content-repo> | 私有 | Markdown、写作规则、模板、本地工具 | 不公开内容历史和作者端配置 |
<public-site-repo> | 公开 | Hugo 主题、配置、构建与 Pages workflow | 让站点工程可审阅、可复用、可独立演进 |
公开仓库忽略运行时检出的 content/ 目录。构建发生时,公开 workflow 使用只读凭据在临时工作目录检出私有内容;内容不会作为 Git 提交复制到公开站点仓库。
不过,这不是“私有内容永远不会泄露”的承诺:Hugo 会把被选中的 Markdown 渲染为公开页面。因此私有内容仓库应理解为作者源仓库,而不是存放任意机密的保险箱;任何会被检出参与生产构建的内容都必须先通过公开性审查。
双仓库也有成本:需要维护两个工作流、两个最小权限凭据和跨仓库排障路径。若站点很小、内容全部公开且不需要隔离历史,单仓库会更简单。若需要严格审稿流程,则应再加一个工作分支或 PR 阶段。
5. 从一次内容变更到线上页面#
5.1 私有仓库只发送一个不透明信号#
私有仓库在 main 推送后,不把提交 SHA、提交信息或文件列表带到公开仓库,而是发送一个仅含事件类型的 repository_dispatch。GitHub 说明该事件可通过 API 触发,workflow 可用 types 只接收约定的 event_type;如果发送 client_payload,它会暴露给目标 workflow 的事件上下文。GitHub Actions:repository_dispatch
下面是可运行的最小发送端示例。前提是:在私有仓库设置了 SITE_DISPATCH_TOKEN Secret,并在变量 SITE_REPOSITORY 中配置目标公开仓库的 owner/repo;令牌仅被授予向该目标仓库发送 dispatch 所需的最小权限。
name: Notify public site
on:
push:
branches: [main]
workflow_dispatch:
permissions: {}
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Send an opaque content-update signal
env:
DISPATCH_TOKEN: ${{ secrets.SITE_DISPATCH_TOKEN }}
SITE_REPOSITORY: ${{ vars.SITE_REPOSITORY }}
run: |
set -euo pipefail
test -n "$DISPATCH_TOKEN"
test -n "$SITE_REPOSITORY"
curl --fail --silent --show-error \\
--request POST \\
--header 'Accept: application/vnd.github+json' \\
--header 'Content-Type: application/json' \\
--header "Authorization: Bearer $DISPATCH_TOKEN" \\
--header 'X-GitHub-Api-Version: 2026-03-10' \\
"https://api.github.com/repos/$SITE_REPOSITORY/dispatches" \\
--data '{"event_type":"content-updated"}'目标公开仓库的 workflow 同时接受三种触发方式:
- 公开站点仓库默认分支的
push:用于主题、模板、配置或 workflow 的变更; repository_dispatch: types: [content-updated]:用于内容仓库的更新;workflow_dispatch:用于人工重试与排障。
GitHub 对 repository_dispatch 的运行上下文使用目标仓库默认分支的最新提交。因此如果短时间内连续推送多次,目标构建应被设计为“最终收敛到启动时的最新内容”,而不是承诺“一次内容提交严格对应一次部署”。这是一种有意的吞吐量取舍。
5.2 公开工作流构建什么#
公开 workflow 的实际步骤如下:
- 检出公开站点的 Hugo 主题和配置。
- 使用只读 token 解析私有内容仓库
main的 revision,并以 detached checkout 方式检出到临时content/。 - 删除内容 checkout 的 remote 和临时认证文件;将 Markdown 文件的修改时间归一化为各自最后一次 Git 提交时间,避免 CI checkout 时间影响“最近更新”。
- 使用公开站点配置和私有
content/运行 Hugo。 - 在生成的静态 HTML 上运行 Pagefind,写入
public/pagefind搜索索引。 - 验证首页与搜索索引存在、产物没有符号链接,并检查本次选定的私有 revision 没有出现在静态产物中。
- 将
public/上传为 GitHub Pages artifact,再由独立部署 job 发布。
GitHub Pages 的自定义 workflow 要求部署 job 使用 pages: write 和 id-token: write 等权限,且 Pages artifact 不能包含符号链接。GitHub Pages:自定义 workflow
Hugo 也建议由 GitHub Actions 持续部署,而不是把构建出的 publishDir 提交进仓库。Hugo:Host on GitHub Pages
5.3 为什么 Pagefind 必须在 Hugo 之后运行#
Pagefind 索引的是已经生成的静态 HTML,并把搜索 bundle 写回站点输出目录;它没有独立的服务端组件。因此顺序必须是 Hugo → Pagefind → 上传 artifact,而不能只运行 hugo server 后期待搜索索引出现。Pagefind:Getting Started
以下命令是本地构建的最小验证方式。前提是本机已安装与站点兼容的 Hugo 和 Node.js,且当前目录是公开 Hugo 站点仓库;<private-content-repo> 是相邻或可访问的 Markdown 目录。
cd <public-site-repo>
hugo \\
--contentDir "../<private-content-repo>" \\
--minify \\
--ignoreCache \\
--cleanDestinationDir \\
--gc
npx --yes pagefind@1.5.2 --site public
test -f public/index.html
test -d public/pagefind示例中的 Pagefind 1.5.2 是本文所述实现的固定版本快照,不是对未来版本的建议。生产 workflow 应显式固定并记录 Hugo、Node.js、Pagefind 和 GitHub Actions 的版本;升级时先在本地和 CI 中验收。
6. 两条日常发布路径#
内容与站点工程分离后,日常操作变得很清楚:
| 你修改了什么 | 在哪个仓库提交 | 触发什么 |
|---|---|---|
| Markdown、周刊、文章、长期文档 | 私有内容仓库 | 私有通知 → 公开构建工作流 |
| Hugo 模板、样式、配置、Pages workflow | 公开站点仓库 | 公开仓库自身的构建工作流 |
一个内容发布的最小操作如下。文章完成事实、链接、代码与排版核验并通过公开性检查后,即可设为 draft: false 并推送本 Blog 的 main 分支;由 Agent 一次性完成的内容同样按此标准执行,无需额外等待确认。
cd <private-content-repo>
git add posts/2026/<article>.md
git commit -m "docs: publish knowledge workflow article"
git push origin main首个端到端验收不应只看“页面看起来还在”。应选一处无敏感的微小内容改动,依次确认:
- 私有仓库的通知 workflow 成功;
- 公开仓库确实出现由
repository_dispatch触发的 build; - 新内容出现在目标页面,且
draft: true的内容没有出现; - Pagefind 能搜到新内容;
- 文章内链接、Mermaid、代码块、评论入口和移动端布局正常;
- workflow 失败时,线上仍保留上一次成功部署。
写作本文时,跨仓库的 dispatch 链路已经配置完成;任何团队在把它视为生产机制前,都应至少完成一次上述真实验收,而不是只验证公开仓库的 push 构建。
7. 安全和可靠性:把权限、产物和审阅拆开#
这套机制的安全重点不是“使用很多 token”,而是让每个 token 只拥有一个方向的最小能力:
| 凭据 | 放在哪里 | 用途 | 不应拥有的能力 |
|---|---|---|---|
| 内容只读凭据 | 公开站点仓库的 Secret | 临时检出私有 Markdown | 写公开站点、管理 Pages |
| 站点 dispatch 凭据 | 私有内容仓库的 Secret | 向指定公开仓库发送更新信号 | 读取私有内容 |
除此以外,还应贯彻以下实践:
- 事件不透明:通知只表达“内容更新了”,不包含私有 SHA、提交消息、文件清单或正文。
- 构建与部署分离:构建 job 不需要 Pages 写入权限;部署 job 只消费已经验证的 artifact。
- 产物可检查:上传前检查入口文件、搜索索引和符号链接;针对已知敏感标识做静态扫描。
- 依赖可复现:固定 Hugo、Node.js、Pagefind 和 GitHub Actions 的版本或提交。
- 内容先于技术:私有内容被检出用于生产构建,就应视为可能公开;
draft是渲染控制,不是机密分级。 - AI 透明标识:对 AI 辅助整理的内容保留清楚的说明和标签,并允许读者复核来源。
评论和媒体也要遵循同样的边界。Giscus 只挂在真实内容页,并使用稳定 pathname 映射;改 URL 前要考虑讨论会不会被分裂。R2/CDN 适合承载公开媒体,但不应成为保存写作凭据或私有原始素材的替代方案。
8. 已知限制,以及我会如何演进它#
这不是“一劳永逸”的架构。几个重要限制需要提前承认:
- 不保证逐提交一一部署:不透明 dispatch 不携带内容 revision;连续更新时,构建读取启动时最新的
main。如果业务需要“每次发布都可精确追溯到一个内容 SHA”,应把 revision 以受控方式传递、记录,并处理队列与重试。 - 自动备份不会替代发布条件核验:本项目的新文章模板以
draft: false表示完成即发布;正在编写、待核实或未通过公开性检查的内容必须主动设为draft: true。需要严格审稿的其他团队可使用工作分支与 PR;本 Blog 知识库则固定在main分支工作。 - 双仓库增加运维面:Secrets、默认分支、workflow 文件和错误定位都要维护。个人小站可以从单仓库开始,等有隔离需求再拆分。
- 静态站不适合所有产品:实时协作、复杂权限、服务端个性化或强审核流,应使用 CMS、应用后端或额外的审核系统。
- 运行手册会漂移:workflow、脚本和文档必须一起更新;以可执行 workflow 和构建验收为准,定期校对文字说明。
对我而言,下一步优先级是:完成一次真实的跨仓库 dispatch 验收、把发布规则从“约定”进一步变成模板和分支保护、并继续把可复用的运行手册写成长期维护的 docs/ 文档。
9. 给读者的最小复刻清单#
如果你想用自己的工具复刻这套思路,不必一次性实现所有组件。可以按以下顺序:
- 创建一个私有 Markdown 内容仓库和一个公开 Hugo 站点仓库。
- 让 Hugo 在本地读取私有仓库作为
contentDir,先通过本地构建。 - 将公开站点部署到 GitHub Pages,确认 artifact workflow 和自定义域名正常。
- 增加 Pagefind,并确认每次静态构建后都生成搜索索引。
- 增加私有 → 公开的
repository_dispatch,使用两枚相互分离的最小权限凭据。 - 对一次无敏感的内容改动完成端到端验收。
- 最后再接入 Obsidian Git 自动备份、R2/CDN、Giscus 或 AI 辅助写作;每加一层,都保留内容核验与回退路径。
如果你只需要一个轻量博客,单仓库 + Hugo + GitHub Pages 已经足够。如果你希望把私人知识工作台与公开站点工程隔离,并让 AI 参与但不越权,双仓库、明确的发布边界和可验证 CI 会更合适。
参考资料#
- Obsidian:How Obsidian stores data
- OpenAI:Introducing Codex(AGENTS.md、证据与人工复核)
- OpenAI:Custom instructions with AGENTS.md
- GitHub Actions:Events that trigger workflows — repository_dispatch
- GitHub Pages:Using custom workflows with GitHub Pages
- Hugo:Host on GitHub Pages
- Pagefind:Getting Started
- Giscus
整理日期:2026-08-07。本文中的工具版本、权限模型和部署行为均可能随上游产品更新而变化;发布或复刻前请重新核对官方文档。