跳到主要内容

Star 插件系统与工具

这章讲什么: AstrBot 的扩展性来自「Star 插件系统」(Star = 插件)。第三方用一组装饰器,就能往系统里挂:聊天指令、生命周期钩子、给 LLM 用的工具。本章讲这些装饰器怎么把代码注册进系统、handler 怎么被流水线触发、以及「工具」怎么被上一章的 Agent 循环调用——内置工具和插件工具走的是同一套机制。

1. 它要解决的小问题

核心功能再全也满足不了所有人。有人想加「点歌」指令,有人想接「自己公司的 API」,有人想让 AI 能调「内部工具」。这些不该写进核心,而该让用户自己写代码挂上去

AstrBot 的插件机制要回答三个问题:

  1. 第三方代码怎么「告诉系统」自己想处理什么消息?
  2. 系统怎么在正确的时机调用这些代码?
  3. 怎么让插件给 LLM 提供「工具」?

2. 思路/直觉:装饰器登记 + 注册表

核心模式是装饰器登记到全局注册表。插件作者在自己的方法上贴一个装饰器(如 @register_command),装饰器就把这个方法包成一个 StarHandlerMetadata(handler 元数据)塞进全局的 star_handlers_registry。流水线在合适的阶段去这个注册表里查「谁要处理这种事件」。

插件代码 装饰器 全局注册表
┌─────────┐ @register_* ┌──────────────────┐ ┌────────────────────┐
│ async def│ ──────────────►│ StarHandler │─►│ star_handlers_ │
│ my_cmd()│ │ Metadata │ │ registry │
└─────────┘ │ (event_type + │ │ (按优先级排序) │
│ filters + 函数) │ └─────────┬──────────┘
└──────────────────┘ │ 流水线按 event_type 查

触发对应 handler

3. Handler 元数据:一切注册的统一形态

不管你注册的是指令、钩子还是别的,最终都变成一个 StarHandlerMetadata(astrbot/core/star/star_handler.py:248)。关键字段:

字段含义
event_type这个 handler 关心哪类事件(见下表)
handler真正的异步函数
event_filters过滤器列表(指令匹配/权限/正则…),AND 关系
handler_module_path所属插件模块,用于按插件启停
extras_configs["priority"]优先级,注册表按它排序

event_type 取自 EventType 枚举(star_handler.py:219),覆盖了系统全生命周期的「钩子点」:

EventType触发时机
AdapterMessageEvent收到一条消息(指令、消息监听走这个)
OnLLMRequestEvent / OnLLMResponseEventLLM 请求前 / 响应后
OnAgentBeginEvent / OnAgentDoneEventAgent 开始 / 结束
OnDecoratingResultEvent发送前装饰结果
OnCallingFuncToolEvent / OnLLMToolRespondEvent调工具前 / 后
OnAfterMessageSentEvent消息发出后
OnAstrBotLoadedEvent / OnPlatformLoadedEvent启动完成 / 平台就绪

注册表 StarHandlerRegistry(star_handler.py:14)的 get_handlers_by_event_type(star_handler.py:150)是核心查询:按事件类型筛,还会过滤「插件是否激活」「是否在会话白名单」。流水线各处就靠它拿 handler。

4. 装饰器一览

注册装饰器都在 astrbot/core/star/register/star_handler.py。挑几个常用的:

装饰器注册什么符号
register_command聊天指令(如 /help)star_handler.py:75
register_command_group指令组star_handler.py:205
register_event_message_type按消息类型监听star_handler.py:256
register_regex正则匹配消息star_handler.py:291
register_llm_tool给 LLM 用的工具star_handler.py:580
register_on_llm_requestLLM 请求前钩子star_handler.py:427
register_on_agent_doneAgent 完成钩子star_handler.py:501
register_agent注册一个(子)Agentstar_handler.py:691

插件类本身用 register_star(astrbot/core/star/register/star.py:8)登记。一个最小插件直观长这样:

# 示意,非源码 —— 一个最小插件的样子
from astrbot.api.star import Star, register
from astrbot.api.event import filter

@register("hello", "作者", "打招呼插件", "1.0.0")
class HelloPlugin(Star):
@filter.command("hello") # 注册 /hello 指令
async def hello(self, event):
yield event.plain_result("你好!") # 回一条文本

filter.command 这类门面最终落到 register_command,把方法包成一个 AdapterMessageEvent 类型、带 CommandFilter 的 handler。上一章讲的 WakingCheckStage 会跑这些 filter,命中就进 activated_handlers

5. 插件加载

PluginManager(astrbot/core/star/star_manager.py)负责扫描插件目录、import 模块(触发装饰器执行从而注册 handler)、实例化插件类。reload() 在启动时被调(core_lifecycle.py:243)。插件元数据进 star_registry / star_map(模块路径→插件,star.py),注册表里的 handler 通过 handler_module_path 关联回它所属的插件,用于「按插件启停/过滤」。

6. 工具:让 LLM 能「动手」

6.1 统一形态 FunctionTool

给 LLM 用的工具,统一是 FunctionTool(astrbot/core/agent/tool.py:41)。它继承 ToolSchema(名字+描述+JSON Schema 参数,tool.py:20),外加一个 handler(真正执行的异步函数)和一些 AstrBot 专属字段:

字段含义
name / description / parametersfunction calling 的 schema(参数会在构造时被 jsonschema 校验,tool.py:33)
handler工具的实现函数
active是否启用(可在 WebUI 关掉某工具)
is_background_task声明为后台任务:立即返回任务 ID,真实工作异步继续(tool.py:61)

一组工具装进 ToolSet(tool.py:78),它能转成 OpenAI / Anthropic / Google 各家的工具格式——又是一处「抹平厂商差异」。

6.2 内置工具和插件工具同源

关键洞察:内置工具(shell、Python、文件读写、网页搜索、知识库……)和插件用 register_llm_tool 注册的工具,是同一种 FunctionTool,走同一个 ToolSet 上一章 build_main_agentreq.func_tool 里加的全是这套(如 _apply_web_search_tools_apply_local_env_tools)。Agent 循环不区分「这个工具是内置的还是插件的」。

内置工具实现集中在 astrbot/core/tools/。其中 shell/python/文件等「计算机操作」工具在包目录 astrbot/core/tools/computer_tools/ 下(shell.pyExecuteShellToolpython.pyPythonTool/LocalPythonToolfs.py 的文件读写/grep 工具、cua.py 的 computer-use 工具);其余各类工具在同级单文件里:web_search_tools.pyknowledge_base_tools.pycron_tools.pymessage_tools.py

6.3 工具怎么被执行

上一章 step 里 _handle_function_tools 调的执行器是 FunctionToolExecutor(astrbot/core/astr_agent_tool_exec.py:54)。它的 execute(astr_agent_tool_exec.py:128)按工具类型分派:

  • 普通 FunctionTool:直接 await 它的 handler(可能是插件方法,也可能是内置工具)。
  • MCPTool(astrbot/core/agent/mcp_client.py):走 MCP 协议调用外部 MCP server 的工具。
  • HandoffTool(astrbot/core/agent/handoff.py:8):转交给子 Agent(见 §05)。

执行前后还会触发 OnCallingFuncToolEvent / OnLLMToolRespondEvent 钩子,让插件能拦截工具调用。

7. 巧妙之处(可借鉴)

  • 「一切皆 handler」:指令、钩子、消息监听全归一为 StarHandlerMetadata,用 event_type + filters 区分,注册表统一管理、统一排序、统一启停。新增一种扩展点只需加一个 EventType 和对应装饰器。
  • 内置工具与插件工具同构:Agent 循环对工具来源无感知,内置能力本质上就是「官方写的插件工具」,降低了心智负担也让两者享受同样的执行/钩子/超时机制。
  • filter 的 AND 组合:一个 handler 可叠加「指令 + 权限 + 平台类型」多个 filter,全过才触发(waking_check/stage.py:178),用小积木拼出复杂触发条件。

8. 边界与局限

  • 插件运行在主进程内(非沙箱),恶意/有 bug 的插件可能影响整个 AstrBot;沙箱机制是给「LLM 执行的代码」用的,不是给插件代码用的(见 §05)。
  • handler 的 event_type 和钩子点是固定枚举,插件不能定义全新的生命周期阶段,只能挂在既有点上。

9. 代码地图(导航索引)

主题文件路径符号名
Handler 元数据 + 注册表astrbot/core/star/star_handler.pyStarHandlerMetadataStarHandlerRegistryEventTypestar_handlers_registry
注册装饰器astrbot/core/star/register/star_handler.pyregister_commandregister_llm_toolregister_on_llm_requestregister_agent
插件类注册astrbot/core/star/register/star.pyregister_star
插件加载astrbot/core/star/star_manager.pyPluginManagerPluginManager.reload
插件映射astrbot/core/star/star.pystar_mapstar_registry
工具/工具集抽象astrbot/core/agent/tool.pyFunctionToolToolSchemaToolSet
工具执行器astrbot/core/astr_agent_tool_exec.pyFunctionToolExecutor.execute
MCP 工具astrbot/core/agent/mcp_client.pyMCPTool
子 Agent 转交工具astrbot/core/agent/handoff.pyHandoffTool
内置工具实现astrbot/core/tools/computer_tools/(shell.py/python.py/fs.py/cua.py)、web_search_tools.pyknowledge_base_tools.pycron_tools.py
唤醒阶段跑 filterastrbot/core/pipeline/waking_check/stage.pyWakingCheckStage.process