Fat Skill 实战:用 Claude Code + Playwright 造一个路由器黑名单管理技能

实践记录 | 2026 年 4 月 | 约 8 分钟阅读
起因
昨天刚写了 Thin Harness, Fat Skills 的翻译和解读,今天就遇到一个实践场景——家里有台 MacBook 总在不该上网的时候上网,我想用 AI 管管路由器的黑名单。
Garry Tan 说:不是更聪明的模型,而是 Thin Harness, Fat Skills,以及把一切固化为能力的纪律。
"拉黑一台设备"这件事我今天做了一次,明天可能还要做。如果每次都对 AI 说"帮我登录路由器,找到 MacBook,点拉黑",AI 需要每次重新探索页面结构,效率低、不稳定、还可能把自己拉黑。把它固化为一个 Skill,这件事从此只需要一句话。
于是我用 Claude Code(claude-sonnet-4-6)从零开发了这个工具,再部署为 Fat Skill。
做了什么
开发:Claude Code 完成全部编码——探索锐捷路由器 eWeb 页面结构、编写 Playwright 自动化脚本、调试 Element UI 选择器、实现自我保护机制、编写两份 SKILL.md(Claude Code + OpenClaw)。开发完成后推送到 GitHub。
安装:从 GitHub 克隆仓库 → npm install + npm run setup 安装 Playwright Chromium → 从已有路由器配置读取地址和密码写入 .env → 整个目录复制到 ~/.openclaw/skills/router-blacklist/。中间发现脚本没有自动加载 .env,在 SKILL.md 中用 source .env 解决。
执行:通过飞书对话框向 OpenClaw 发送指令。
| 操作 | 飞书指令 | 耗时 | 结果 |
|---|---|---|---|
| 拉黑 MacBook-laptop | 路由器中拉黑macbook | ~3 秒 | 一次成功 |
| 移除 MacBook-laptop | 将macbook从黑名单中移除 | ~2 秒 | 一次成功 |
在飞书里打一句话,Playwright 无头浏览器就自动完成登录、解析在线终端表格、模糊匹配设备名、检查自我保护、点击拉黑/解除、确认弹窗——全部流程不到 3 秒,零人工干预。
最终结构——根目录本身就是 OpenClaw 技能包,.claude/skills/ 下同时提供 Claude Code 入口。一份代码,两个 harness:
router-blacklist/
├── SKILL.md # OpenClaw 技能入口
├── .claude/skills/router/SKILL.md # Claude Code /router 技能
├── scripts/blacklist.js # Playwright 自动化(377 行)
├── package.json # 唯一依赖:playwright
├── AI_LOG.md # 构建过程决策记录
└── .env.example设计决策
代码已开源在 GitHub,这里只聊 why。
Playwright 操作 UI,而不是逆向 API
锐捷 EG105G-P-L 没有公开 REST API。逆向 CGI 接口意味着 stok token 管理、加密参数、版本变更——逆向成本高,维护成本更高。Playwright 操作 UI 是"最笨但最稳"的方式,UI 变了改选择器就行。Garry Tan 也提到过:Playwright CLI 每个操作 100ms,Chrome MCP 做同样的事要 15 秒——快 75 倍。
确定性导航,而不是依赖 UI 时序
路由器后台的侧边栏菜单是 Vue 动态渲染的,headless 下展开时序不稳定。解决方案:从当前 URL 正则提取 stok token,直接构造目标页 URL 跳转。同理,Element UI 按钮在 headless 下有时被 Playwright 判为 "not visible",通过 page.evaluate() 直接调 DOM .click() 绕过可见性检查。能用确定性手段解决的,就不依赖 UI 渲染时序。
.env 交给 harness,而不是脚本自己加载
脚本只关心 process.env.ROUTER_PASSWORD 存不存在,不关心它怎么来的。环境准备是 harness 层的职责。不加 dotenv 依赖,在 SKILL.md 里 source .env 就够了——Thin Harness 的分工。
双层自我保护:Latent + Deterministic
这个 Skill 会操作路由器拉黑设备,必须确保永远不会把运行工具的本机拉黑。
| 层级 | 机制 | 特征 |
|---|---|---|
| 脚本层(确定性) | assertNotSelf() 运行时获取本机所有 IPv4,与目标 IP 比对,匹配则 throw Error | 硬约束,调用方无法绕过 |
| AI 层(Latent) | SKILL.md 写明"永远不得将本机加入黑名单",并列出主机名和 IP | 软约束,依赖模型遵守 |
为什么两层都要?只有 AI 层,模型可能幻觉或被 prompt 注入绕过;只有脚本层,AI 可能在执行前就做出危险操作。AI 层在意图阶段阻止,脚本层在执行阶段兜底。
一个细节:为什么用 IP 而不是 MAC?macOS 从 Ventura 起默认对每个 Wi-Fi 使用随机私有 MAC,路由器看到的 MAC 和硬件 MAC 完全不同。IP 在 DHCP 租期内稳定,是更可靠的自我标识。
SKILL.md 里还特意写了"脚本层已有 IP 检测作为兜底"——这是告诉 AI "即使你判断失误,系统也不会真的执行",降低模型尝试绕过规则的概率。
收尾
关键不在于这次拉黑省了多少时间。关键在于——这个 Skill 从此存在了。
做过一次的事,不应该再做第二次。把它固化为能力,就是 Garry Tan 说的那个纪律。这个 Skill 天然具备 Fat Skill 的特征:参数化调用(ban / unban / list)、确定性工具下沉(Playwright + JS 脚本)、安全约束内置(双层防护)、永久升级(模型变强 Skill 自动变强)。
你有哪些重复性操作,值得固化为一个 Fat Skill?