把临时操作变成可复用能力:为 OpenClaw 设计一个安全的 WordPress 管理 Skill
让 AI Agent 管理 WordPress,看起来只需要“给它一个 API,然后让它发文章”。真正开始长期使用之后,很快会发现事情没有这么简单。
发布文章只是最表层的能力。实际还会遇到分类、标签、草稿、私密文章、Slug、特色图片、批量更新、权限控制、备份、回滚、异常中止等问题。如果这些规则只存在于某一次聊天里,那么下一次任务仍然要重新解释,执行结果也容易漂移。
更可靠的做法,是把已经验证过的规则整理成一个可复用的 Skill。这样,模型负责理解任务,Skill 负责约束行为,底层脚本负责与 WordPress API 交互。
为什么需要 Skill
一次性的提示词适合临时任务,但不适合作为长期运维规范。
例如,同样是“更新一篇文章”,实际可能有完全不同的风险:
- 修改正文可能意外覆盖原始 HTML;
- 更新分类时可能把原来的标签一起清空;
- 重新提交整篇 Post 对象可能改变 Slug、时间或状态;
- 批量操作时,一篇文章失败可能影响后续数百篇;
- REST API 中某些统计字段并不能完整反映 private 内容;
- 分类和标签虽然都属于 taxonomy,但它们的业务语义并不相同。
如果每次都让模型临场决定,很容易出现“技术上能执行,但业务上不应该执行”的情况。
Skill 的价值,就是把这些已经确认的规则固定下来。
一个合适的职责分层
比较稳妥的结构可以分成三层。
第一层是自然语言层。用户只需要描述目标,例如:
将一批文章重新分类,但不要改变正文、Slug 和发布状态。
第二层是 Skill。Skill 明确告诉 Agent:
- 哪些字段允许修改;
- 哪些字段必须冻结;
- 什么时候需要先备份;
- 哪些 API 可以调用;
- 哪些操作必须串行;
- 哪些异常必须立即停止。
第三层才是 API 脚本。脚本只负责:
- GET Post;
- GET Categories / Tags;
- 创建必要的 taxonomy term;
- 发送最小化更新 payload;
- 回读验证结果。
这样,决策、规则和传输逻辑不会混在一起。
Skill 目录应该包含什么
一个简单的 WordPress 管理 Skill,可以采用类似下面的结构:
wordpress-manager/
├── SKILL.md
└── scripts/
└── wp_api.py
SKILL.md 负责描述行为规范,wp_api.py 负责 REST API 调用。
认证信息则不应写进 Skill,也不应出现在命令历史或日志里。更合适的方式是通过环境变量提供,例如:
WP_URL
WP_USERNAME
WP_APP_PASSWORD
Skill 只知道“认证信息从环境变量读取”,不需要知道具体值。
最重要的原则:最小写入
WordPress REST API 返回的 Post 对象通常包含很多字段,但这并不意味着更新时应该把整个对象再提交回去。
例如,只修改分类时,理想的 payload 应该只有:
{
"categories": [123]
}
如果只修改标签:
{
"tags": [12, 34, 56]
}
如果两个都要修改:
{
"categories": [123],
"tags": [12, 34, 56]
}
不应顺便提交:
- title
- content
- excerpt
- slug
- status
- date
- author
- featured_media
这种“最小 payload”策略,是防止批量任务意外扩大影响范围的关键。
配置文件修改必须可回退
Agent 经常需要调整自己的配置,例如新增环境变量引用、插件设置或 API 参数。配置修改本身也应该受到约束。
比较安全的流程是:
读取当前配置
→ 确认需要修改的字段
→ 创建带时间戳的原地备份
→ 执行最小修改
→ diff
→ 重新读取验证
不建议随手生成大量 .bak、.old 文件,也不应直接覆盖配置后再考虑如何恢复。
备份本身应当成为 Skill 的固定规则,而不是每次靠模型记忆。
让 Agent 认识“默认系统对象”
WordPress 中有一些对象虽然当前可能没有文章,但不应该被自动删除。
例如默认分类通常承担系统级兜底作用。即使当前引用数为零,也应该明确写入 Skill:
保留默认分类,不删除、不改名、不改 Slug。
这是一个很典型的例子:API 看见的只是“一个 count 为 0 的 term”,但业务规则知道它是系统对象。
AI Agent 如果没有这层约束,很容易把“当前没用”误判成“可以删除”。
不要盲信 REST API 的 count
在包含公开、私密、草稿等不同状态的站点中,taxonomy 接口返回的 count 不一定适合作为唯一依据。
更可靠的方法是:
- 获取实际需要管理的全部 Post;
- 读取每篇文章的 Category / Tag ID;
- 在本地重新统计真实引用关系。
尤其是在执行删除操作之前,应该根据实际 Post 引用确认 term 是否真的为空。
这类规则也值得固定进 Skill,因为它不是某一次任务的临时经验,而是长期可靠性要求。
批量操作一定要有停止条件
如果要处理大量文章,不应该“一口气跑到底”。
更安全的策略是小批量串行:
生成计划
→ 修改一小批
→ GET 回读
→ 验证
→ 下一批
一旦出现以下情况,就停止后续写入:
- HTTP 异常;
- 返回的 Category 集合与计划不同;
- 返回的 Tag 集合与计划不同;
- Slug 发生变化;
- Status 发生变化;
- 任何受保护字段出现变化。
Agent 不应该在这时“自己想一个补救方案继续跑”。最安全的动作通常是停止,然后报告差异。
Category 和 Tag 要按集合比较
还有一个很容易踩坑的细节:taxonomy term ID 数组的返回顺序通常没有业务意义。
例如:
计划: [12, 34, 56]
返回: [56, 12, 34]
这两个结果实际上完全相同。
验证时应该比较:
sorted(set(actual_ids))
==
sorted(set(expected_ids))
而不是直接比较数组顺序。
如果把顺序误认为语义,就可能把一次完全正确的更新误判成失败,甚至触发不必要的回滚。
让 Skill 管规则,不让 Skill 猜内容
一个重要边界是:Skill 很适合约束执行方式,但不应该假装自己拥有稳定的内容判断能力。
例如下面这些事情可以由 Skill 固定:
- 不修改正文;
- 不修改 Slug;
- 更新前备份;
- 每批最多多少篇;
- 验证方式;
- 出错时停止。
但下面这些事情不应该偷偷写成脆弱的字符串规则:
- 看到某个单词就判断文章属于某个 Category;
- 短字符串出现在正文里就自动加 Tag;
- 仅因为运行环境是 Linux 就添加 Linux Tag;
- 仅因为命令通过 SSH 执行就添加 SSH Tag。
内容决策和执行规则应该分开。
Skill 最终带来的变化
在没有 Skill 时,AI Agent 更像一个拥有 API 权限的临时操作员。
建立 Skill 之后,它更接近一个受约束的自动化系统:
用户意图
↓
Agent 理解任务
↓
Skill 限定可做与不可做
↓
API 脚本执行最小操作
↓
回读验证
↓
结果或停止报告
这种结构最重要的价值并不是“自动化更多”,而是让自动化的边界清晰、行为可重复、结果可审计、错误可回退。
对于拥有长期自托管服务、博客、服务器和自动化任务的环境来说,这比一次聪明的提示词更加重要。