Hope 的个人 AI 工作流:用 Telegram、Hermes、Obsidian 和 GitHub 串起收藏与发布
这篇文章写给已经在使用 AI 编程工具、Obsidian 或个人博客,但信息入口和执行动作仍然分散的个人开发者。你可能已经有 Telegram、GitHub、一个知识库和若干 AI 工具,却仍要在多个窗口之间复制链接、整理 Markdown、运行脚本、检查 Git 状态,再手动发布。
本文不是介绍一个“万能聊天机器人”,而是复盘我如何把这些现有工具组织成一套个人执行系统,以及在真正跑通链路时遇到的边界、安全问题和工程取舍。
要解决的问题
Section titled “要解决的问题”我的目标不是让 AI 替我决定发布什么,而是让它承担重复、可验证的执行工作:
- 在 Telegram 中随手发送一个明确指令。
- 让 Hermes 判断这是收藏、写作还是发布动作。
- 把资料可靠地保存到 Obsidian PARA Vault。
- 从 Obsidian 的单篇草稿生成博客内容和附件。
- 在测试、构建和敏感信息检查通过后等待人工审批。
- 发布准备完成后等待一次明确的“确认发布”,由系统在同一事务中完成精确 commit 和 fast-forward push。
其中最关键的约束是:**AI 可以执行,但不能越权。**普通链接不能自动写入知识库;status: ready 不能等同于允许提交;一次“确认”也不能同时代表 commit 和 push。
这套工作流最终形成了四层结构:
Telegram DM -> Hermes Gateway + personal-content Skill -> 确定性的 article-workflow CLI -> WeWrite 公众号模板预览与兼容性校验 -> Obsidian PARA Vault / Astro 网站仓库 / Git -> GitHub main -> 网站部署每层只做自己擅长的事:
- Telegram 是低摩擦入口,不保存业务状态。
- Hermes 理解自然语言、提取网页、生成摘要,并调用固定动作。
- 仓库 CLI 校验参数、路径、文件变化和审批状态。
- WeWrite 在发布前生成去除私有 frontmatter 的微信兼容 HTML 预览。
- Obsidian 是私有内容源,GitHub 只保存公开网站内容。
- Git 是最后一道可审计、可恢复的发布边界。
实践下来,我认为个人 AI Stack 的价值不在“用了多少模型”,而在于是否把模糊意图转换成了范围明确、结果可检查、失败会停止的工作流。
从 OpenClaw 思路转向 Hermes
Section titled “从 OpenClaw 思路转向 Hermes”这次实施最初受到 Jimmy Song 的 Personal AI Stack 启发。他把 OpenClaw 部署在家里的 Mac mini 上,通过不同 Telegram 群组隔离工作、研究、博客等上下文,再由 Agent 统一操作 Gmail、Calendar、Obsidian、GitHub 和知识库。这个思路最有价值的部分不是某个具体产品,而是把 Agent 定位为执行系统和 Chief of Staff,而不是聊天窗口。
我的机器上已经安装了 Hermes,因此没有必要同时维护第二套功能高度重叠的 Agent。最终选择是保留这套架构思想,用 Hermes 替代 OpenClaw:
- Telegram 继续作为随时可用的入口。
- Hermes 负责意图理解、工具调用和会话上下文。
- 仓库 Skill 记录个人工作流规则,而不是修改 Hermes 核心代码。
- 确定性 CLI 负责高风险文件和 Git 操作。
- Hermes WebUI 提供本机浏览器入口,但不替代 Telegram。
这样做的直接收益是复用已经配置好的模型、技能、会话和本地权限,减少重复部署。相应的工程要求也更明确:自然语言层不能直接拥有无限制的文件和 Git 权限,所有关键动作都必须落到可测试的仓库接口上。
我的运行环境
Section titled “我的运行环境”这套系统运行在一台 Apple M3 Pro、18 GB 内存的 MacBook Pro 上,核心组件如下:
| 组件 | 职责 |
|---|---|
| Telegram | 移动端指令入口和审批入口 |
| Hermes Agent | 自然语言理解、工具调用和工作流编排 |
| Hermes WebUI | 本机浏览器中的 Hermes 操作界面 |
| Obsidian | PARA 知识库、博客草稿和 Web Clips |
| Firecrawl | 提取网页标题、元信息和 Markdown 正文 |
| WeWrite | 发布前审稿提示、公众号主题排版和微信兼容性校验 |
| young-hope | Astro/Starlight 博客仓库和发布脚本 |
| GitHub | 公开内容版本库和远端发布入口 |
Obsidian Vault 通过仓库内的 .vault 软链接接入,但 .vault 被 Git 忽略。因此代码可以用统一相对路径访问草稿,GitHub 和网站构建又不会收到整个私人知识库。
为什么用 PARA 管理内容
Section titled “为什么用 PARA 管理内容”Vault 使用 PARA 结构,发布工作流只关心其中几个明确目录:
Personal-Knowledge/├── 00 Inbox/│ └── Web Clips/├── 10 Projects/│ └── Young Hope Blog/│ └── Drafts/├── 90 Templates/│ ├── Blog Draft.md│ └── Web Clip.md└── 99 Attachments/这种拆分解决了三个问题:临时收藏进入 Inbox,不假装它已经是成熟知识;准备写作的内容进入 Blog Drafts,拥有独立发布状态;图片等附件集中保存,发布器只复制当前文章明确引用的文件。
收藏和博客草稿必须是两种对象。收藏强调完整保留来源,博客强调作者的重组、核验和表达。如果直接把网页全文变成公开文章,不仅质量不可控,也可能带来版权风险。
Telegram 如何成为执行入口
Section titled “Telegram 如何成为执行入口”Hermes 通过 Telegram 私聊接收请求,只允许授权用户。仓库中的 personal-content Skill 把中文指令映射到固定 CLI Action。
博客工作流目前支持以下无 slug 用户指令:
新建文章写成博客编辑这篇:<要求>归档这篇发布这篇确认发布继续发布查看状态Hermes 通过回复消息或当前 DM 最近选中的文章绑定对象。只有无法唯一
定位时,才返回标题、content_id 和路径候选并停止;slug 与 commit hash
是内部字段,不要求用户输入。预演文章、确认提交 和 确认推送 不再是
默认用户动作,旧命令仅保留给兼容脚本和维护场景。
收藏工作流支持 收藏 <一个公开URL>。也可以在转发或粘贴一篇纯文本文章时明确写“收藏”,或者回复一条单一来源消息发送“收藏”。普通发送链接仍然是只读对话,不会写入 Vault。这一条看似保守,却能避免聊天过程中产生大量用户并不想保存的笔记。
从 Telegram 收藏网页到 Obsidian
Section titled “从 Telegram 收藏网页到 Obsidian”网页收藏的数据流如下:
“收藏 <URL>” -> 验证明确定义和单一来源 -> Firecrawl 提取页面 -> Hermes 生成独立摘要和关键观点 -> 保留完整原始正文 -> 按 canonical URL 去重 -> 原子写入 00 Inbox/Web ClipsWeb Clip 记录标题、来源 URL、canonical URL、作者、站点、发布时间、抓取时间和提取状态。作者或日期没有明确出现在来源中时保持为空,不让模型猜测。
正文分成摘要、关键观点、元信息和完整正文。摘要可以由 AI 生成,但完整正文不能被翻译、改写、裁剪或“优化”。这样未来回看时,生成内容和原始证据不会混在一起。
去重只在 00 Inbox/Web Clips 内按规范化后的 canonical URL 进行。同一 URL 再次收藏会返回 duplicate: true;如果旧记录是 failed,后续获得更完整的提取结果,则允许原位升级为 complete。
从 Obsidian 草稿发布到 GitHub
Section titled “从 Obsidian 草稿发布到 GitHub”博客草稿基于统一模板,核心 frontmatter 如下:
title:description:status: ideapublish: falselocale: zh-cnsection: guidesslug:sidebar_order:created:updated:tags: []sources: []新建草稿时默认是 status: idea 和 publish: false。只有人工编辑为
status: ready、publish: true,且标题、description、locale、section、slug
等字段全部合法,发布才会继续。这两个字段只代表“允许生成网站文件”,不
代表允许 Git 提交或推送。
发布过程可以拆成四个阶段:
准备 -> 发布这篇(publish-content,目标已存在时自动转 update-content)待确认 -> 确认发布(隐藏使用已选 slug)已提交 -> fast-forward push origin main -> 回写文章和索引的发布结果完成发布这篇 通过唯一 content_id 定位博客 Draft。它会运行 WeWrite 公众号
预览与兼容性校验、内容测试、Astro 构建、Git diff 检查和敏感信息扫描,然
后只生成本地网站文件。Hermes 把变更路径、检查结果和预览路径返回 Telegram,
进入待确认状态。
确认发布 会重新核对当前 HEAD、Obsidian 源文件、网站输出、工作树变更范围
和敏感信息模式。通过后只提交批准的路径,并立即检查远端是否仍允许普通快进
推送。整个动作不接受用户手写 hash,也不执行 force push。
如果推送被远端变化或其他异常阻断,事务会保留在本地。处理原因后使用
继续发布,系统再次校验批准的提交和远端状态;不会自动 merge、rebase
或“帮用户修好”。发布成功后,草稿和对应索引会记录发布结果。
用 WeWrite 增加公众号发布前门禁
Section titled “用 WeWrite 增加公众号发布前门禁”博客页面和微信公众号面对的是两种不同的阅读环境。Astro 构建通过,只能说明网站版本合法;它不能保证 Markdown 粘贴到微信编辑器后仍然保持段落、代码块、引用和表格样式。因此我引入了 imraywang/wewrite,把公众号模板预览放在网站发布动作之前。
本机安装的是 WeWrite 4.2.1,包括一个主入口和九个模块:写作、选题、审稿、排版发布、视觉、风格、学习、数据复盘和多平台改写。Codex 与 Hermes 共用同一份 Skill 安装,CLI 由 uv tool 管理:
uv tool install wewrite
# Codex 安装主入口和模块后,Hermes 链接同一份目录ln -sfn ~/.codex/skills/wewrite ~/.hermes/skills/wewriteln -sfn ~/.codex/skills/wewrite-review ~/.hermes/skills/wewrite-reviewln -sfn ~/.codex/skills/wewrite-publish ~/.hermes/skills/wewrite-publish实际安装包含全部十个 wewrite* 目录,这里只展示发布链路直接依赖的三个。Hermes 也可以尝试 hermes skills install imraywang/wewrite,但多 Skill 仓库在当前版本中没有被安装器正确解析,所以最终采用标准 folder-per-skill 目录共享方式。
默认链路刻意只启用确定性的本地动作:
Obsidian 草稿 -> article-workflow 解析 frontmatter -> 仅把正文写入临时 Markdown -> wewrite preview --theme professional-clean --no-open -> wewrite validate -> 返回 wechatPreview + wechatTheme -> Astro 生成、测试、构建和 Git 审批直接把 Obsidian 源文件交给 wewrite preview 会把 YAML frontmatter 排进公众号正文,因此仓库门禁必须先剥离 frontmatter。每次 发布这篇 都在仓库外创建权限为 0700 的唯一临时目录,临时正文使用后删除,HTML 权限收紧为 0600。预览供当前用户人工检查,但不进入 Git 快照和文章提交。
这个默认门禁不会生成图片,不读取公众号 AppID/Secret,也不会推送到公众号草稿箱。WeWrite 的公众号发布能力仍需要用户在当轮明确授权,并且需要封面和公众号凭据。公众号排版预览是博客发布前的质量门禁,但它不等于公众号草稿箱授权,也不会替代 GitHub 的 确认发布。
公开内容门禁
Section titled “公开内容门禁”发布器会在生成网站文件前检查公开正文的结构和边界:正文不重复声明页面标题, 不携带内部验收清单、审批记录、私有发布字段或本机文件路径。发现这类内容时, 发布会直接停止并指出位置,要求先修改文章。内部验收和操作说明保留在个人 Vault 的运维文档中,公开页面只呈现可复用的方法、经验和结果。
内容优化分成两层:人工或 Agent 在编辑阶段可以使用 wewrite-review 检查准确性、观点、实用性、账号声音和可读性;真正执行 发布这篇 时,仓库 CLI 必定执行 professional-clean 公众号模板生成与兼容性校验。前者允许改正文,后者是不改稿的发布门禁。
因此,公众号排版已经纳入主流程,位置是在网站文件生成之前:
Obsidian 草稿 -> 发布这篇 -> 去除私有 frontmatter -> WeWrite 公众号模板排版预览 -> 微信兼容性校验 -> 内容测试 + Astro build + diff + 敏感信息扫描 -> 返回 wechatPreview,等待确认发布 -> commit + fast-forward push当前纳入的是本地排版和兼容性校验,不是自动推送公众号草稿箱。后续若接入
草稿箱,仍要作为独立授权动作,不能因为博客 确认发布 而自动执行。
这次实施中真正踩过的坑
Section titled “这次实施中真正踩过的坑”1. Hermes 选错了 Skill
Section titled “1. Hermes 选错了 Skill”最初发送“收藏 URL”后,Hermes 触发了 apple-notes,把内容写到了用户主目录的 收藏.md。问题不在 Obsidian 路径,而在自然语言路由没有稳定选中 personal-content。
修复方式是在 Telegram 平台提示中对“收藏”明确加载 personal-content,并用仓库 Skill 的软链接作为唯一维护源。经验是:仅仅安装 Skill 不代表模型一定会选择它,关键意图需要确定性路由或至少有可验证的触发约束。
2. 临时请求文件被安全校验拒绝
Section titled “2. 临时请求文件被安全校验拒绝”Hermes 一度把收藏请求写到 macOS 的随机临时目录,文件写入验证器认为这是敏感系统路径并拒绝操作。后来改为仓库内被 Git 忽略的 .hermes-requests/:路径固定且可验证;文件只允许当前用户拥有的普通文件;最大 10 MiB;CLI 读取后无论成功失败都会删除;正文不经过 shell 插值。
这比反复申请临时目录写权限更简单,也更容易审计。
3. Firecrawl Key 配到了 URL 字段
Section titled “3. Firecrawl Key 配到了 URL 字段”Firecrawl Cloud 使用 FIRECRAWL_API_KEY,而 FIRECRAWL_API_URL 是自托管服务地址。最初把 Key 配进 URL 字段后,插件虽然能够注册,却不能正确建立提取客户端。
另一个容易误解的点是 /reload-skills 只重新加载 Skill,不会让 Gateway 进程重新读取 .env。工具配置变更后需要重启 Hermes Gateway。
4. Clash Fake-IP 触发 SSRF 防护
Section titled “4. Clash Fake-IP 触发 SSRF 防护”Key 修正后,Wikipedia 和微信公众号仍然被拒绝。日志显示公网域名被解析成 198.18.0.x,这是 Clash Verge Fake-IP 模式使用的保留网段。Hermes 的 URL 安全检查因此把请求判断为内网地址,并在调用 Firecrawl 之前停止。
最终启用了 security.allow_private_urls: true 来兼容本机网络环境。这个选择有明确代价:普通私网 URL 的限制被放宽,因此 Telegram 入口、用户白名单和单 URL 约束更加重要。云元数据地址仍应保持永久阻止。
5. 正文里的普通 URL 被误判为 API Key
Section titled “5. 正文里的普通 URL 被误判为 API Key”Dota 2 的 Wikipedia 页面成功提取了二十多万字符,但保存前的敏感信息扫描失败。真正命中的是 Bloomberg URL 中 musk-... 的内部片段 sk-...,被宽泛正则当成了 OpenAI 风格 Token。
修复不是关闭敏感信息扫描,而是给 sk- 增加词边界。回归测试同时验证:musk-... 不再误报,真正以 sk- 开头的长令牌仍然必须被拒绝。
6. Push 门禁需要明确的分支边界
Section titled “6. Push 门禁需要明确的分支边界”一次发布中,文章提交与远端分支的历史没有直接对齐。Git 本身可能允许普通快进, 但工作流会先停止,要求确认远端是否包含其他尚未审阅的提交。这个边界避免了把 多笔变更误当成一次发布,也避免使用强制推送。确认历史安全后,再通过恢复动作 重新执行检查和推送。
落地效果与边界
Section titled “落地效果与边界”这套组合已经覆盖了内容入口、原创草稿、双语内容、公众号预览和 Git 审批:
- Telegram 私聊能够稳定路由到
personal-content。 - 明确收藏 URL 后,Firecrawl 能提取微信公众号和 Wikipedia 正文。
- Web Clip 能写入正确的 Obsidian PARA Inbox。
- canonical URL 去重和失败记录升级已验证。
- Telegram 纯文本收藏已纳入 Skill 规则。
- 博客新建、编辑、按
content_id定位和无 slug 发布链路已经跑通。 - 发布流程具备一次明确确认、安全检查和普通快进约束;失败时保留事务状态。
生成英文版能创建独立英文草稿,人工审核后通过发布这篇单独发布。写成博客能从 Web Clip、Telegram、X 或手动笔记创建原创草稿,并阻止连续复制来源正文。- 发布事务支持状态查询、安全恢复与取消,不再依赖删除状态文件。
- WeWrite 已安装到 Codex 和 Hermes,
发布这篇会生成并校验公众号 HTML 预览。
仍然需要独立建设的能力包括:
- **公众号草稿箱发布。**当前默认只生成本地微信 HTML 预览。缺少 AppID/Secret 和封面时必须停止,不能把“发布博客”解释为授权公众号发布。
- **WeWrite 风格学习。**当前使用默认
professional-clean主题,尚未用历史人工修改建立style.yaml、playbook.md和账号范文库。 - **更多执行端。**飞书知识库、Gmail、Calendar 和定时任务尚未接入,应该在内容主链持续稳定后再扩展。
三种操作入口与发布流程
Section titled “三种操作入口与发布流程”同一篇 Obsidian 草稿可以从本地 Codex、Telegram 或 Hermes WebUI 发起操作。三个入口最终都应收敛到仓库里的 article-workflow,不能各自实现一套 Git 逻辑。
+----------------+ | Obsidian Draft | +-------+--------+ | +----------------+----------------+ | | | v v v +-----------+ +-----------+ +------------+ | Codex CLI | | Telegram | | Hermes UI | | local | | DM | | localhost | +-----+-----+ +-----+-----+ +------+-----+ | | | | Hermes Gateway | | | local Hermes Agent +----------------+-----------------+ | v +----------------------+ | article-workflow CLI | +----------+-----------+ | WeWrite preview -> generate -> confirm -> push | v GitHub / Website入口一:在本地用 Codex 或编程 Agent 发布
Section titled “入口一:在本地用 Codex 或编程 Agent 发布”本地写作适合长文整理、批量修改、图片检查和发布前审阅。先在 Obsidian 中编辑:
10 Projects/Young Hope Blog/Drafts/<slug>.md准备发布时,把 frontmatter 调整为:
status: readypublish: truelocale: zh-cnsection: guidesslug: hope-personal-ai-workflow然后在 Codex 中明确指定仓库、文章对象和权限边界,例如:
在博客仓库中处理当前选中的 hope-personal-ai-workflow 文章。只读取这篇文章,执行发布检查并等待我的确认,不提交、不推送。Codex 或其他本地 Agent 应调用仓库命令,而不是直接复制文件。默认入口
使用 Content Index 的 content_id;旧的 slug 命令只用于兼容:
cd path/to/young-hopepnpm article:workflow -- publish-content "<content-id>"返回结果中的 wechatPreview 是 professional-clean 主题的本地 HTML,
wechatTheme 记录本次使用的主题。publish-content 会写入网站文件并把事务
进入待确认状态,但不会提交或推送。目标页面已存在时 CLI 自动进入
更新模式。
检查变更文件、测试、构建、公众号预览和 diff 后,再明确要求一次性发布:
确认发布对应内部命令是:
pnpm article:workflow -- confirm "<hidden-slug>"该命令重新验证源文件、生成文件、工作树、远端快进关系,然后完成精确
commit 和 git push origin main。推送失败会保留事务,处理原因后使用
继续发布,不需要复制 hash。
完整流程图如下:
Obsidian 编辑完成 | vstatus=ready + publish=true | vCodex: publish-content | +--> WeWrite 模板 + 微信兼容校验 --失败--> 修改草稿/环境 | 通过 v写入网站文件 ---> 测试、构建、diff、敏感扫描 | v人工检查变更 | v确认发布 ---> 精确 commit + fast-forward push | +----------异常----------> 保留事务,继续发布 | vGitHub / 网站如果 CLI 提示已有工作流状态,必须先处理上一笔发布。不要删除状态后假装审批仍然有效,也不要绕过 CLI 直接强推。
入口二:通过 Telegram 发布
Section titled “入口二:通过 Telegram 发布”Telegram 适合移动端触发、查看结果和做一次发布确认。Telegram 平台提示会把
这些中文指令路由到 personal-content Skill。
新建文章写成博客编辑这篇:补充文章结构和正文归档这篇发布这篇确认发布继续发布查看状态第一次新建时 Hermes 会追问标题。写成博客 会把当前选中的收藏、Telegram、
X 或手动笔记改写成原创草稿;不会复制长段原文。发布这篇 返回
wechatPreview、wechatTheme、变更文件和检查摘要,然后等待 确认发布。
确认发布 一次完成 commit 和 push;如果 push 被阻断,使用 继续发布。
Telegram DM | vHermes 身份白名单 | vpersonal-content Skill | +-- 新建/编辑 ------> 指定 Obsidian 草稿 | +-- 写成博客 -------> 原创 Draft | +-- 发布这篇 -------> WeWrite + 测试/构建/扫描 | 待确认 | 确认发布 v commit + fast-forward push | 推送异常 | 继续发布 v GitHub main收藏网页则使用:
收藏 https://example.com/articleHermes 返回标题、正文字符数、笔记路径、提取状态和是否重复。资料只进入 00 Inbox/Web Clips,不会自动进入博客发布状态机。
入口三:通过 Hermes WebUI 对话触发
Section titled “入口三:通过 Hermes WebUI 对话触发”Hermes WebUI 运行在本机回环地址:
http://127.0.0.1:8787它共享本机 Hermes 的配置、Skill 和会话,并把博客仓库作为默认工作区。不过 WebUI 没有 Telegram 的平台路由提示,因此高风险动作不要只依赖自然语言自动选 Skill。
在 WebUI 新对话中先执行:
/skills personal-content确认 Skill 存在后输入:
/use personal-contentWebUI 会提示下一轮强制使用该 Skill。紧接着发送具体动作。WebUI 与 Telegram 一样使用当前选中对象,不需要 slug:
发布这篇或:
确认发布/use personal-content 只保证下一条消息强制加载 Skill。因此开始新的高风险
Action 前,应再次执行 /use personal-content。公众号排版预览由 发布这篇
自动执行,不会因 WebUI 入口而跳过。
打开 Hermes WebUI | v选择 young-hope 工作区 | v/skills personal-content ----不存在----> 检查 Skill 软链接/配置 | 存在 v/use personal-content | v发送一个明确 Action | vHermes 调用 article-workflow | vWeWrite 本地预览与兼容校验 | v返回结果并等待下一次明确审批WebUI、Telegram 和 Codex 的差别只是入口与 Skill 触发方式,审批规则不应改变。 每一步都可能停止;停止意味着当前状态与审批时看到的状态不同,需要重新检查, 而不是让 Agent 自动修复或强推。
这套系统为什么不是“另一个聊天机器人”
Section titled “这套系统为什么不是“另一个聊天机器人””聊天机器人通常以回答结束;执行系统以状态变化结束。
在这套架构里,Hermes 的价值是统一入口和编排能力,但真正保证结果可靠的是外围约束:路径白名单、模板、schema、原子写入、测试、构建、Git 快照和一次明确发布确认。模型负责理解“我想做什么”,确定性代码负责限制“它实际上可以做什么”。
这也是我对个人 AI Stack 最重要的认识:不要从“选哪个最强模型”开始,而要从以下问题开始:
- 我的信息从哪里进入?
- 哪些动作允许自动执行?
- 哪些动作必须明确确认?
- 私有资料和公开内容在哪里分界?
- 出错后系统是停止、覆盖,还是悄悄继续?
- 我能否从 Git、日志和文件状态还原发生了什么?
当这些问题有了明确答案,AI 才从临时助手变成一个可以长期协作的个人执行层。
后续扩展方向
Section titled “后续扩展方向”后续可以沿着三个方向扩展:用 wewrite-learn 从人工修改中沉淀公众号风格,
接入 RSS 和官方博客作为稳定的信息源,以及把同一篇文章适配到更多平台。每个
扩展都应复用同一份内容索引和审批边界,而不是重新建立一套孤立的自动化脚本。
公众号草稿箱、飞书知识库、Gmail、Calendar 和定时任务可以在主链稳定后按需接入, 并继续保持独立授权。