ClawdBot 插件开发:为 Telegram 机器人添加新快捷命令的实践
1. ClawdBot 是什么:一个可扩展的本地 AI 助手底座
ClawdBot 不是一个开箱即用的'功能型机器人',而是一个面向开发者设计的可插拔 AI 网关平台。它不像传统 Telegram Bot 那样只做单一任务,而是像一个轻量级的'AI 操作系统'——你可以在自己的设备(笔记本、树莓派、云服务器)上运行它,然后按需加载不同能力模块:文本推理、多模态理解、消息路由、频道接入、快捷命令等。
它的核心定位很清晰:把大模型能力封装成可编排的服务单元,再通过标准化接口暴露给各类前端渠道(Telegram、Web UI、CLI、API)。这正是它能支撑 MoltBot 这类复杂翻译机器人的底层原因——不是靠硬编码所有功能,而是靠插件机制动态组合能力。
ClawdBot 的后端由 vLLM 提供高性能推理支持,这意味着它不依赖云端 API,所有模型调用都在本地完成,响应快、隐私强、无调用限制。你看到的 /weather、/fx、/wiki 这些命令,并非写死在 Telegram 代码里,而是作为独立插件注册进 ClawdBot 的命令总线,由统一的路由层分发执行。
换句话说:ClawdBot 是'引擎',MoltBot 是'跑车',而你写的每一个新命令,就是给这辆跑车加装的新功能模块——比如一键生成会议纪要、自动归档群聊关键词、根据截图查商品价格……只要你想得到,就能插得上。
2. 为什么是插件开发:告别硬编码,拥抱模块化扩展
很多开发者第一次接触 ClawdBot 时会疑惑:'我已经有 Telegram Bot Token,为什么不直接写个 Python 脚本监听消息?' 答案很简单:可维护性、复用性、隔离性。
- 可维护性:当你要修改
/weather的城市解析逻辑时,只需改weather-plugin目录下的代码,不影响翻译、OCR 或汇率模块; - 复用性:同一个天气插件,既能被 Telegram 渠道调用,也能被 Web 控制台或 CLI 命令复用,无需重复实现;
- 隔离性:某个插件崩溃(比如 OCR 模型加载失败),不会导致整个机器人宕机,ClawdBot 会自动降级并记录日志。
更重要的是,ClawdBot 的插件系统采用声明式注册 + 函数式执行模式:你不需要关心消息怎么从 Telegram 接入、怎么序列化、怎么鉴权——你只用专注写一个干净的 Python 函数,接收结构化输入,返回结构化输出。其余所有胶水逻辑,都由框架兜底。
这种设计让'加一个新命令'这件事,从过去需要改路由、加 handler、修测试、部署服务的 2 小时工程,压缩成:新建文件 → 写 20 行函数 → 注册配置 → clawdbot plugins reload —— 全程 5 分钟,且零停机。
3. 动手实践:从零编写一个 /summary 插件
我们以一个真实高频需求为例:为群聊中长消息自动生成摘要。用户发送一段 500 字的产品需求描述,希望机器人一键返回 3 句精炼要点。
3.1 创建插件目录结构
ClawdBot 插件必须放在 ~/.clawdbot/plugins/ 下(或通过 CLAWDBOT_PLUGINS_DIR 环境变量指定)。我们新建一个 summary 插件:
mkdir -p ~/.clawdbot/plugins/summary
touch ~/.clawdbot/plugins/summary/__init__.py
touch ~/.clawdbot/plugins/summary/main.py
提示:ClawdBot 会自动扫描该目录下含
__init__.py的子目录,将其识别为插件包。
3.2 编写核心逻辑(main.py)
typing , ,
clawdbot.plugins Plugin, CommandContext, CommandResult
():
():
().__init__(config)
.model = config.get(, )
.max_length = config.get(, )
() -> [, ]:
{
: ,
: ,
:
}
() -> CommandResult:
text = ctx.get_reply_text() ctx.get_arg_text()
text (text.strip()) < :
CommandResult.error()
(text) > .max_length:
text = text[:.max_length] +
prompt =
:
response = ctx.llm.chat(
model=.model,
messages=[{: , : prompt}],
temperature=,
max_tokens=
)
summary_text = response.choices[].message.content.strip()
CommandResult.success(summary_text)
Exception e:
CommandResult.error()
plugin = SummaryPlugin({})

