Vibe Coding 插件开发指南
本文面向使用 AI 辅助编写 MaiBot 插件的开发者。目标是把插件需求拆成 AI 容易理解、容易验证、不会误改主程序的工作单元。
AI 任务简报
把下面这段作为给 AI 的首段上下文,再补充你的具体需求:
text
你正在为 MaiBot 编写第三方插件。插件必须放在 plugins/<plugin-name>/ 下,不要修改 MaiBot 主程序代码,除非我明确许可。请使用 maibot-plugin-sdk,入口文件为 plugin.py,元信息文件为 _manifest.json。必须实现 on_load、on_unload、on_config_update 和 create_plugin。优先使用 @Tool、@Command、@HookHandler、@EventHandler、@API、@MessageGateway;不要给新插件使用 @Action。所有用户可见文本优先使用简体中文。请保持改动边界清晰,并给出测试方式。开发边界
- 插件目录固定为
plugins/<plugin-name>/;如果插件需要自己的忽略规则,在插件目录内新增.gitignore,不要修改仓库根目录.gitignore。 - 插件入口固定为
plugin.py,工厂函数固定为create_plugin()。 - 插件元信息固定为
_manifest.json,运行配置放在config.toml。 - 新插件不要修改
src/、dashboard/、config/等主程序目录;确实需要主程序能力时,先说明原因、影响面和替代方案,再请求许可。 - 不要把本地实验数据、日志、临时脚本、个人密钥、token、cookie、数据库文件提交进插件。
- 依赖以
_manifest.json的dependencies为准;只有在维护 MaiBot 主程序依赖时才同步pyproject.toml和requirements.txt。
最小目录结构
text
plugins/my-plugin/
├── _manifest.json
├── plugin.py
├── config.toml
├── README.md
└── .gitignore推荐额外添加:
text
plugins/my-plugin/
├── tests/
├── docs/
└── assets/Manifest 要点
_manifest.json 用于让 Host 在加载前校验插件。AI 生成时必须注意:
manifest_version当前固定为2。id使用小写反向域名或作者前缀,例如com.example.my-plugin。version使用严格三段式语义版本,例如1.0.0。author.url、urls.repository等 URL 必须以http://或https://开头。host_application和sdk都要声明min_version与max_version。- Python 包依赖写入
dependencies,类型为python_package。 - 插件间依赖写入
dependencies,类型为plugin。 capabilities只声明确实需要的能力。i18n.default_locale推荐使用zh-CN。
json
{
"manifest_version": 2,
"id": "com.example.my-plugin",
"version": "1.0.0",
"name": "我的插件",
"description": "一个 MaiBot 插件",
"author": {
"name": "开发者",
"url": "https://github.com/developer"
},
"license": "MIT",
"urls": {
"repository": "https://github.com/developer/my-plugin"
},
"host_application": {
"min_version": "1.0.0",
"max_version": "1.99.99"
},
"sdk": {
"min_version": "2.5.1",
"max_version": "2.99.99"
},
"dependencies": [],
"capabilities": ["send_message"],
"i18n": {
"default_locale": "zh-CN"
}
}代码骨架
python
from typing import Any
from maibot_sdk import Command, Field, MaiBotPlugin, PluginConfigBase, Tool
from maibot_sdk.types import ToolParameterInfo, ToolParamType
class PluginSectionConfig(PluginConfigBase):
"""插件基础配置。"""
__ui_label__ = "插件"
__ui_icon__ = "package"
__ui_order__ = 0
enabled: bool = Field(default=False, description="是否启用插件")
config_version: str = Field(default="1.0.0", description="配置版本")
class MyPluginConfig(PluginConfigBase):
"""插件配置。"""
plugin: PluginSectionConfig = Field(default_factory=PluginSectionConfig)
class MyPlugin(MaiBotPlugin):
"""我的插件。"""
config_model = MyPluginConfig
async def on_load(self) -> None:
"""插件加载时执行。"""
self.ctx.logger.info("插件已加载")
async def on_unload(self) -> None:
"""插件卸载时执行。"""
self.ctx.logger.info("插件已卸载")
async def on_config_update(self, scope: str, config_data: dict[str, Any], version: str) -> None:
"""配置热重载时执行。"""
del scope
del config_data
del version
@Tool(
"echo_text",
description="复述用户提供的文本。适合测试插件是否能被 LLM 调用。",
parameters=[
ToolParameterInfo(
name="text",
param_type=ToolParamType.STRING,
description="要复述的文本",
required=True,
),
],
)
async def echo_text(self, text: str, **kwargs: Any) -> dict[str, str]:
del kwargs
return {"content": text}
@Command("ping", description="测试插件是否在线", pattern=r"^/ping$")
async def ping(self, stream_id: str = "", **kwargs: Any) -> tuple[bool, str, int]:
del kwargs
await self.ctx.send.text("pong", stream_id)
return True, "pong", 2
def create_plugin() -> MyPlugin:
"""创建插件实例。"""
return MyPlugin()组件选择
- 让 LLM 主动调用能力 →
@Tool— 默认工具进入 deferred 池,需要被搜索发现;高频低风险工具才考虑core_tool=True - 用户输入命令触发 →
@Command— 使用正则pattern匹配,例如/ping - 拦截或观察主流程 →
@HookHandler— 适合消息改写、发送前审计、流程拦截 - 监听消息或生命周期事件 →
@EventHandler— 适合统计、记录、异步辅助任务 - 给其他插件调用能力 →
@API— 只把稳定接口设为公开 - 接入外部聊天平台 →
@MessageGateway— 适配器类插件使用 - 维护旧插件 →
@Action— 仅兼容旧代码,新插件不要使用
Tool 编写要点
description要写清楚何时使用、参数含义、限制和副作用。- 参数优先用
ToolParameterInfo声明,类型来自ToolParamType。 - 返回值优先使用
dict,把给 LLM 阅读的文字放在content。 - 需要返回图片等媒体时,使用文档约定的
content_items,不要把 base64 直接塞进长文本。 - 默认不要设置
core_tool=True;过多核心工具会增加模型选择成本。 - 工具内部要处理失败并返回可读错误,不要让异常泄漏成难懂堆栈。
发送消息要点
- 已有
stream_id时,使用await self.ctx.send.text(content, stream_id)发送文本。 - 发送图片、表情、转发、混合消息时,优先使用
self.ctx.send和self.ctx.emoji提供的能力代理。 - 不要自行计算会话 ID;插件应使用上下文传入的
stream_id或 SDK 提供的消息上下文。 - 用户可见文本默认使用简体中文。
配置要点
- 配置模型继承
PluginConfigBase,字段使用Field(default=..., description="...")。 - 推荐保留
[plugin]分组,并包含enabled与config_version。 - WebUI 展示信息可以通过
__ui_label__、__ui_icon__、__ui_order__补充。 - 配置读取优先使用
self.config.<section>.<field>;确实需要原始字典时再用self.get_plugin_config_data()。 - 配置热重载逻辑放入
on_config_update()。
AI 生成代码检查清单
要求 AI 完成后逐项自检:
_manifest.json字段完整,版本号和 URL 格式合法。plugin.py只从标准库、第三方库和maibot_sdk导入;导入顺序符合项目规范。- 插件类继承
MaiBotPlugin,并声明config_model。 - 已实现
on_load()、on_unload()、on_config_update()。 - 已实现
create_plugin()并返回插件实例。 - 新功能优先使用
@Tool或@Command,没有给新代码使用@Action。 - 没有修改主程序代码和根目录
.gitignore。 - 没有硬编码 token、绝对路径、个人 QQ 号、群号或私有 URL。
- 所有网络请求都有超时和异常处理。
- 所有定时任务、后台任务、连接和文件句柄能在卸载时清理。
- 用户可见文本是简体中文。
- README 写明安装、启用、配置、命令和常见问题。
推荐提示词
生成新插件
text
请在 plugins/<plugin-name>/ 创建一个 MaiBot 第三方插件,实现 <功能描述>。
要求:
1. 只改插件目录,不改 MaiBot 主程序。
2. 使用 maibot-plugin-sdk 和 plugin.py / _manifest.json 结构。
3. 实现 on_load、on_unload、on_config_update、create_plugin。
4. 配置用 PluginConfigBase + Field,用户可见文本使用简体中文。
5. 给出 README、config.toml、必要的 .gitignore。
6. 完成后说明如何在 MaiBot 中启用和测试。修改已有插件
text
请只修改 plugins/<plugin-name>/ 内的文件,为现有插件增加 <功能描述>。
先阅读 _manifest.json、plugin.py、config.toml 和 README,沿用现有风格。
不要重构无关代码,不要整理全仓库格式。
如果需要主程序改动,先停止并说明原因。排查插件问题
text
请排查 plugins/<plugin-name>/ 的加载或运行问题。
优先检查 _manifest.json 校验、依赖声明、生命周期方法、create_plugin、配置模型、日志中的异常。
请给出最小修复,不要做无关重构。测试建议
- 静态检查:确认
_manifest.json是合法 JSON,plugin.py可以被 Python 解释器导入。 - 本地加载:把插件放入
plugins/,启动 MaiBot,观察插件加载日志。 - WebUI 检查:在插件管理中确认插件出现、可启用、配置可编辑。
- 命令测试:用
/ping一类命令确认@Command可触发。 - Tool 测试:通过正常聊天让 LLM 发现并调用工具,观察工具返回是否可读。
- 卸载测试:禁用或卸载插件后,确认后台任务、连接和缓存被清理。
发布前检查
- 插件仓库根目录包含
_manifest.json、plugin.py、README.md、config.toml示例。 - README 包含功能说明、安装方式、配置项、命令、权限/能力说明、故障排查。
- 许可证与
_manifest.json中的license一致。 - 依赖声明完整,避免要求用户手动安装未声明依赖。
- 没有提交
.venv/、__pycache__/、日志、数据库、密钥或本地配置。 - 如果准备提交到插件仓库,阅读插件仓库贡献指南并按其要求整理元信息。