Hermes Claw WordPress 博客集成实战:从 API 验证到双平台发布

发布于 2 小时前  7 次阅读


项目背景

我们使用 Hermes Agent 作为 AI 助手,已经有一个基于 Halo 的博客系统运行在 halo.jikai666.top。随着内容需求增长,我们决定在已有的 WordPress 站点(www.jikai666.top)上也开通技术博客发布能力。

目标:让 Hermes Agent 的 Discord 博客频道同时支持 Halo 和 WordPress 两个平台,用户只需在频道里发内容,Agent 自动完成润色、排版、图片上传和草稿保存。

环境信息

组件 Halo WordPress
站点地址 halo.jikai666.top www.jikai666.top
API 类型 Halo UC API (Kubernetes-style) WordPress REST API
认证方式 PAT (Bearer Token) Application Password (Basic Auth)
内容格式 Markdown + HTML annotation HTML
S3 图片前缀 / /

两个博客共用同一个雨云 S3 桶,按前缀区分文件夹。

  • Hermes Agent 版本:最新版(自部署)
  • S3 兼容存储:雨云
  • Discord 博客频道 ID:

实现过程

第一步:验证 WordPress REST API 连通性

使用 Application Password 进行 HTTP Basic Auth 认证。Application Password 不是 WordPress 登录密码,而是 WordPress 后台专为 REST API 生成的独立密码。

验证流程:

import requests

resp = requests.get(
    "https://www.jikai666.top/wp-json/wp/v2/categories",
    auth=("<api-user>@<domain>", "<app_password>"),
)
print(resp.status_code)  # 200

同时查询了当前用户信息,确认了两个关键用户 ID:

  • HermesClaw — API 调用身份
  • WaNG — 站点管理员,文章目标作者

第二步:凭据存储到环境变量

WordPress Application Password 存储在 ~/.hermes/.env 文件中:

WP_BASE_URL=https://www.jikai666.top
WP_API_USER=<api-user>@<domain>
WP_APP_PASSWORD=<application_password>
WP_S3_PREFIX=<wp-prefix>/

关键陷阱:os.environ 读不到 .env

Hermes Agent 的 gateway 子进程不会自动加载 .env 文件。直接用 os.environ.get("WP_APP_PASSWORD") 会返回 None,导致 Agent 陷入无限循环尝试各种认证方式。

正确做法是逐行读取文件:

def read_env_var(name: str) -> str:
    env_path = os.path.expanduser("~/.hermes/.env")
    with open(env_path, "r") as f:
        for line in f:
            line = line.strip()
            if line.startswith(f"{name}=") and not line.startswith("#"):
                return line.split("=", 1)[1].strip()
    raise ValueError(f"{name} not found in .env")

这个问题在 Halo 集成时已经踩过一次,WordPress 集成时直接复用了这个解决方案。

第三步:创建 wordpress-blog 技能

在 Hermes Agent 的技能系统中创建了一个新技能 wordpress-blog,包含:

  • 完整的 WordPress REST API 端点参考(posts、media、tags、categories)
  • Markdown 转 HTML 的代码模板(使用 markdown2 库)
  • S3 图片上传脚本(boto3,上传到 S3 桶的 / 前缀)
  • 草稿优先发布规则(禁止直接 publish)
  • 安全规则(禁止自动删除、修改前备份原内容)

第四步:更新 Discord 频道配置

博客频道的 prompt 原来只包含 Halo 的 API 配置。更新后同时包含两个平台的配置信息,并增加了平台选择逻辑:

  • 用户说 "WordPress" 或 "WP" → 发布到 WordPress
  • 用户说 "Halo" → 发布到 Halo
  • 未指定 → 询问用户

同时将 wordpress-blog 技能添加到频道的 skill 绑定列表中,与原有的 halo-blog 并列。

第五步:Markdown 转 HTML

WordPress REST API 的 content 字段接受 HTML 而非 Markdown。文章内容需要先用 markdown2 库转换:

import markdown2

html_content = markdown2.markdown(markdown_text, extras=[
    "fenced-code-blocks",
    "tables",
    "header-ids",
    "code-friendly",
])

这与 Halo 不同——Halo 同时接受 Markdown(raw)和 HTML(content),WordPress 只接受 HTML。

第六步:创建草稿

WordPress 的草稿创建比 Halo 简单得多。一个 POST 请求即可:

resp = requests.post(
    f"{WP_BASE}/wp-json/wp/v2/posts",
    auth=(WP_USER, WP_PASS),
    json={
        "title": "文章标题",
        "content": html_content,
        "status": "draft",  # 草稿,不直接发布
        "excerpt": "文章摘要",
    },
)

Halo 需要四步(POST 创建 → GET 草稿 → PUT 更新草稿 → PUT 发布),WordPress 只需一步创建 + 一步发布,流程更简洁。

遇到的问题

问题一:HermesClaw 用户权限不足,无法更改文章作者

文档要求"发布后更改文章作者为 WaNG",但测试发现返回 403 Forbidden:

{
  "code": "rest_cannot_edit_others",
  "message": "抱歉,您不能作为此用户更新文章。"
}

原因:HermesClaw 用户的角色权限不够,WordPress 要求编辑(editor)或管理员(administrator)角色才能修改文章作者。

问题二:max_tokens 未配置导致回复被截断

在创建技能文件时,Agent 的回复频繁被截断("Response truncated due to output length limit")。

排查发现:火山 CodingPlan 的 GLM-5.2 模型配置中只设了 context_length: 1000000(1M 上下文),但没有设 max_tokens。这导致 API 回退到默认值(约 4096 tokens),远不够输出完整的技能文件。

关键概念区分:
- 上下文窗口(context_length):模型能"读"多少输入,1M tokens
- 最大输出(max_tokens):模型能"写"多少输出,未配置时回退到 API 默认值

这两个参数完全独立,1M 的上下文窗口不意味着 1M 的输出能力。

问题三:YAML 数字键 vs 字符串键

更新 Discord 频道配置时,Python 的 yaml.safe_load 把频道 ID <channel-id> 解析成了整数(int)而非字符串。第一次更新脚本用了字符串键,导致旧配置没被替换而是新建了一个重复条目。

修复方法:统一使用整数键,并在写入前清理可能存在的字符串键。

解决方案

作者权限问题

两个方案供选择:

  • 方案 A:在 WordPress 后台把 HermesClaw 用户提升为编辑或管理员
  • 方案 B:保持现状,文章以 HermesClaw 名义发布,需要时手动改作者

当前选择方案 B,在技能文档中记录了这个限制。

max_tokens 修复

在 config.yaml 的火山 CodingPlan provider 配置中,给 glm-5.2 模型添加了 max_tokens: 16384

- name: 火山CodingPlan
  base_url: https://ark.cn-beijing.volces.com/api/coding/v3
  model: glm-5.2
  models:
    glm-5.2:
      context_length: 1000000
      max_tokens: 16384          # 添加这一行

端到端验证

使用 Python requests 完成了完整流程测试:

  1. 创建草稿(status=draft)→ ✅
  2. 读取文章(HTML 内容正确)→ ✅
  3. 发布文章(status=publish)→ ✅
  4. 删除文章(force=true)→ ✅

Halo vs WordPress API 对比

对比项 Halo 2.x WordPress
API 风格 Kubernetes-style (UC API) REST API
认证 Bearer Token (PAT) Basic Auth (App Password)
内容格式 Markdown + HTML annotation 纯 HTML
草稿创建 4 步(POST → GET → PUT → PUT) 1 步(POST)
发布方式 PUT /{name}/publish POST /{id} 改 status
分类/标签 slug 或 name 数字 ID
媒体上传 需单独处理 POST /wp/v2/media
文章作者 固定为创建者 固定为 hermesclaw,不切换

WordPress 的 API 流程比 Halo 更简洁,但 Halo 的 Markdown 原生支持更好(WordPress 需要额外转换 HTML)。

技能规范审计与修正

初版技能上线后,对照原始需求文档进行了逐项审计,发现并修正了 6 处问题。

修正一:get_post() 增加 context=edit

get_post() 未传 context=edit,只能拿到 rendered 字段(经过 WordPress 过滤的 HTML),无法可靠取得修改前的原始正文。

修正后增加 params={"context": "edit"},返回 title.rawcontent.rawexcerpt.raw 等原始字段,确保修改前的备份可用于精确回滚。

同时明确:禁止只保存 content.rendered 作为回滚源。修改文章前必须保存以下 6 个字段:title.raw、content.raw、excerpt.raw、status、categories、tags。

修正二:删除 status="any"

list_posts() 默认使用 status="any",这不是 WordPress REST API 的标准状态值,行为依赖服务器配置,存在不确定性。

修正后改为显式查询 5 种状态:publishdraftpendingfutureprivate,分别请求后合并结果,并按文章 ID 去重。

修正三:新增 snapshot_post() 函数

新增 snapshot_post(post) 函数,从 get_post(context=edit) 返回值中提取回滚所需的 6 个原始字段。所有字段使用 .get() 安全访问,避免 KeyError

update_post() 修改前必须先调用 get_post() 获取原文,再用 snapshot_post() 提取快照保存,修改失败时可据此回滚。

修正四:分类与标签创建限制

Hermes Claw 默认只允许查询现有分类/标签并设置到文章上。禁止自行创建新分类或新标签

创建分类或标签必须同时满足两个条件:

  1. 用户明确要求创建
  2. 已通过实际 API 测试确认 hermesclaw 具有对应权限

若权限未验证或返回 401/403,必须停止并报告,不得升级权限、改用管理员凭据或反复重试。

修正五:文章结构拆分环境信息

原技能规范中"环境信息"被放在"项目背景"的子项中。修正为两个独立的三级标题:### 项目背景### 环境信息,各自包含独立的内容要求。

修正六:图片处理逻辑明确

明确当前 WordPress 使用第三方主题,已实现自动生成文章封面和随机匹配二次元风格图片。因此:

  • Hermes Claw 不负责文章封面管理
  • 禁止主动上传封面图片或设置 featured_media
  • 正文插图优先上传 S3 用外链,不默认上传到 WordPress 媒体库

修正后的 API 参考表

操作 方法 路径
列出文章 GET /wp/v2/posts?status={status},分别查询 publish、draft、pending、future、private 后合并
获取文章 GET /wp/v2/posts/{id}?context=edit(获取 raw 字段用于回滚)

总结

这次集成让 Discord 博客频道具备了双平台发布能力。用户发内容时指定平台即可,Agent 自动处理润色、图片上传、Markdown 转换和草稿保存。

主要收获:
1. WordPress REST API 比 Halo 更简单,草稿创建只需一个请求
2. Application Password 认证方式安全可靠,不影响正常登录
3. .env 读取陷阱在两个平台间复用了解决方案
4. max_tokenscontext_length 是两个独立参数,都需要显式配置
5. WordPress 用户角色权限需要在后台提前配置好
6. status="any" 不是标准 API 参数,应显式查询各状态
7. 修改文章前必须用 context=edit 获取 raw 字段并保存快照
8. 分类和标签创建需要双条件:用户明确要求 + 权限已验证

S3 存储采用文件夹隔离策略(<halo-prefix>/<wp-prefix>/),共用一个桶,管理简单且成本可控。

阿巴阿巴
最后更新于 2026-08-06