这段时间陆续改了几个插件和 OpenClaw 侧的小能力,感受最明显的一点是:插件二改不是简单堆功能,也不是把 prompt 写得更长,而是把原来依赖“模型刚好会这么做”的链路,改成更确定、更可观测、更容易兜底的工程流程。
很多插件刚接上时,其实已经“能用”。但线上跑久一点就会发现,能用和可靠之间差了很远:
- 模型有时不调用工具;
- 第三方网站偶尔风控;
- API 返回了看似正常但实际错配的数据;
- 图片、文件、cookie、路径都没问题,但最终就是没发出去;
- 失败日志散在框架、插件、适配器、容器几层里,不查源码很难定位。
这篇就记录一下最近二改插件时形成的一些思路。
一、先分清:是模型没做,还是插件做失败了
最典型的是收款码插件。
一开始的现象是:用户在群里提到“打钱”“收款码”之类的话,但机器人没有发二维码图片。直觉上很容易怀疑是图片路径错了、权限错了、OneBot/NapCat 发图失败,或者插件没加载。
但查完以后发现:
- 图片文件存在;
- PNG 文件头正常;
- AstrBot 插件加载成功;
- LLM tool
send_payment_qr已经注册; - 适配器的图片发送链路也没明显问题;
- 日志里没有任何一次
send_payment_qr工具调用。
所以真正的问题不是“插件发图失败”,而是“模型没有调用工具”。
这个判断很关键。因为如果判断错了,就会在图片路径、权限、适配器里反复打转;但真正该改的是触发机制。
我的处理方式是保留原来的 LLM tool,同时增加一层确定性关键词监听:
- 如果模型主动调用
send_payment_qr,继续走原逻辑; - 如果消息里出现“打钱、转账、收款码、付款、V我、打赏”等关键词,并且消息是 at 或唤醒场景,就直接发送二维码;
- 发送后
stop_event(),避免后面再接一段无意义的 LLM 回复。
这就是所谓“双触发”:LLM 触发用于灵活表达,规则触发用于稳定兜底。
我觉得这类插件非常适合这种结构:
用户消息 ├─ 明确关键词/命令 → 直接执行确定动作 └─ 语义模糊场景 → 交给 LLM 判断是否调用工具这样既不牺牲自然语言交互,又不会把关键动作完全押宝在模型的 tool calling 上。
二、关键词触发要有边界,不能为了可靠变成骚扰
加规则触发的时候,有一个很容易犯的错误:只要群里有人说到关键词,机器人就响应。
这在群聊里很危险。比如别人闲聊一句“二维码坏了”,机器人突然甩一个收款码,就很像发疯。
所以我加了一个边界:只在 event.is_at_or_wake_command 为真时触发。
也就是说,必须是用户明确唤醒机器人,规则才会生效。这样可靠性提高了,但不会破坏群聊氛围。
插件二改里,这类边界比功能本身更重要。一个插件如果“太积极”,比不工作还麻烦。
三、不要只看报错,要复现最小链路
B站视频解析这次也很典型。
最开始看到的是 HTTP 412,于是自然会问:是不是 cookie 过期了?
但 412 在 B站语境下不一定是 cookie 过期,更常见是安全风控。于是我没有直接换 cookie,而是拆成几步验证:
- 用 cookie 调
nav接口,看登录态是否正常; - 调
x/web-interface/view,看基础视频信息是否能拿; - 用 yt-dlp 提取,观察它具体在哪个接口报警;
- 单独请求
wbi/view/detail,确认 412 来源; - 再回到 OpenClaw 的
web_fetch,验证通用网页抓取是否会被 B站直接风控。
最后结论很清楚:
- cookie 没过期;
- B站 API 的基础信息能拿;
- yt-dlp 主流程也能拿到标题、格式等信息;
- 412 主要来自某些附加元数据接口或通用网页抓取;
- OpenClaw 默认
web_fetch是普通 HTTP 抓取,不带 B站登录 cookie,也不是浏览器环境,所以会被 B站视频页风控。
这就不是“换 cookie”能根治的问题,而是解析策略错了。
四、通用抓网页不适合所有站点,要给高频站点做专用解析
OpenClaw 的 web_fetch 适合抓普通网页,但 B站视频页不是普通网页。
它有几个特点:
- 视频页本身对非浏览器请求容易 412;
- 真正有价值的信息在 API 和字幕接口里;
- 字幕接口需要 aid/cid;
- b23 短链还需要先解析跳转;
- yt-dlp 能兜底,但也会遇到局部 412 警告;
- B站 AI 字幕偶尔会返回“标题是 A,字幕内容却是 B”的错配情况。
所以最后没有去硬改 web_fetch,而是加了一个本地 skill:bilibili-video。
它的思路是:遇到 B站链接时,不走通用网页抓取,而是走专用流程:
输入 URL/BV/b23 → 提取 BV/av → 调 view API 拿 aid/cid/标题/简介/UP/播放数据 → 调 player subtitle API 拿字幕地址 → 拉取 AI 字幕 → 用标题/简介和字幕做相关性校验 → 必要时重试或 yt-dlp 兜底 → 输出结构化 JSON/Markdown这里最有意思的是“字幕相关性校验”。
实际测试时,B站偶发返回过完全不相关的字幕:标题是 Linux Dirty Frag 漏洞,字幕内容却是汽车、潜水、小说剧情。这种返回不是 HTTP 失败,状态码甚至可能是 200。如果只看接口成功,就会总结出一篇完全错误的内容。
所以二改时不能只判断“有没有返回”,还要判断“返回的东西像不像我要的东西”。
简单做法就是从标题和简介里提取关键词,比如:
- dirty frag
- copy fail
- CVE
- Linux
- kernel
- exploit
然后看字幕里是否出现这些词。只要完全不沾边,就不要直接相信,宁可重试或提示字幕错配。
五、可靠性来自多层兜底,而不是一个万能 prompt
很多插件问题表面上可以通过 prompt 缓解,比如:
当用户提到收款时,请调用 send_payment_qr 工具。
或者:
当用户发 B站链接时,请总结视频。
但 prompt 只能提高概率,不能保证执行。
更可靠的结构通常是分层:
- 规则层:明确命令、关键词、URL 模式,直接进入确定流程;
- 工具层:把脆弱操作封装成脚本/API,而不是让模型临场拼请求;
- 模型层:负责理解、总结、组织语言;
- 校验层:检查文件是否存在、接口返回是否相关、输出是否明显错配;
- 兜底层:主路径失败时换 API、换 yt-dlp、降级到元信息总结,或者明确告诉用户哪里失败。
模型适合做理解和表达,不适合承担所有流程控制。
二改的目标就是把“流程控制”尽量从模型嘴里拿出来,放进代码和可观测日志里。
六、日志比猜测重要,源码比印象重要
这几次排查都印证了一点:不要猜。
比如收款码插件,如果不看日志,很容易以为是发图失败;但日志里没有 tool call,就说明根因在触发。
比如 B站 412,如果不拆接口,很容易以为 cookie 过期;但 nav 是登录态,view 是 200,就说明问题不是 cookie 本身。
比如 OpenClaw 解析失败,如果只说“B站风控”,还不够;必须直接用 OpenClaw 的 web_fetch 复现 412,才能确认是 OpenClaw 通用抓取链路不适合这个站点。
我现在比较喜欢的排查顺序是:
现象 → 找最小复现 → 查日志确认有没有进入目标分支 → 查源码确认框架怎么调插件 → 单独测试外部依赖/API → 再决定改触发、改链路、还是改配置这比直接“多加几个 prompt”慢一点,但改出来的东西更稳。
七、二改时尽量少侵入核心,多做可回滚扩展
还有一个原则:能不改核心就不改核心。
比如 B站解析,我没有直接改 OpenClaw 的 web_fetch 核心逻辑。因为通用 web_fetch 面向所有网站,如果为了 B站加 cookie、加特殊 UA、加重试,很容易影响其它网页抓取,也不好维护。
更合适的是做成一个专用 skill:
- 触发范围清楚:B站/BV/b23;
- 脚本位置清楚:
skills/bilibili-video/scripts/bili_parse.py; - 行为可测试:输入一个 BV,输出 JSON/Markdown;
- 失败可降级:没字幕就拿元信息,接口风控就 yt-dlp 兜底;
- 回滚简单:删掉 skill 或不用它即可。
这类改法比“在框架核心里打补丁”更适合个人助手环境。
八、好的插件二改,最后应该变成一套产品判断
插件二改到最后,其实不是在写代码,而是在做产品判断:
- 什么情况下机器人应该主动响应?
- 什么情况下应该沉默?
- 哪些动作必须确定执行,不能依赖模型心情?
- 哪些失败可以静默重试,哪些必须告诉用户?
- 外部数据看起来成功但可能是错的,如何识别?
- 功能增强会不会带来骚扰、误触发、隐私或安全风险?
比如收款码:可靠发送很重要,但不能群里一提“二维码”就发。
比如 B站总结:能抓字幕很重要,但字幕错配时不能硬总结。
比如服务器漏洞检查:能查状态很重要,但升级内核、重启、blacklist 模块都属于有风险操作,必须先报告、再确认。
这些边界感,才是插件从“玩具”变成“可长期运行的助手能力”的关键。
结语
最近这些二改给我的最大感受是:
不要把插件做成“模型可能会用的能力”,要把它做成“一条可靠的业务链路”。
LLM 很强,但它不应该背所有锅。
该确定的地方用规则,该稳定的地方用脚本,该检查的地方加校验,该外部依赖的地方做兜底。模型负责理解上下文、生成解释和总结;插件负责把事情做成。
这样改出来的助手,才会从“偶尔很聪明”变成“多数时候都靠谱”。
部分信息可能已经过时