37 KiB
AstrBot SDK v4 架构与实现文档
目录
架构概览
AstrBot SDK v4 采用分层架构设计,从上到下分为:
┌─────────────────────────────────────────────────────┐
│ 用户层 (User Layer) │
│ 插件开发者编写的 Star 类 │
├─────────────────────────────────────────────────────┤
│ API 层 (API Layer) │
│ Star, Context, decorators, filter, events │
├─────────────────────────────────────────────────────┤
│ 翻译层 (Translation Layer) │
│ HandlerDispatcher, Loader, LegacyAdapter │
├─────────────────────────────────────────────────────┤
│ 通信层 (Communication Layer) │
│ Peer, Transport, CapabilityRouter │
└─────────────────────────────────────────────────────┘
核心设计原则
- 协议优先: 所有通信通过标准化的协议消息
- 能力抽象: 通过 Capability 系统暴露核心功能
- 双向通信: Plugin ↔ Core 的对称通信模型
- 向后兼容: LegacyAdapter 提供 v3 兼容层
目录结构
src-new/astrbot_sdk/
├── __init__.py # 顶层导出 (Star, Context, decorators, events, errors)
├── __main__.py # CLI 入口点
├── cli.py # Click 命令行工具
├── star.py # Star 基类与 Handler 发现
├── context.py # 运行时 Context 与 CancelToken
├── decorators.py # 装饰器 @on_command, @on_message 等
├── events.py # MessageEvent 事件定义
├── errors.py # AstrBotError 错误模型
├── compat.py # 兼容层导出
├── _legacy_api.py # Legacy Context 与 CommandComponent
│
├── protocol/ # 协议层 (已完成)
│ ├── __init__.py # 公共入口,导出所有协议类型
│ ├── descriptors.py # HandlerDescriptor, CapabilityDescriptor
│ │ # 内置能力 JSON Schema 常量
│ ├── messages.py # 五种协议消息类型
│ └── legacy_adapter.py # v3 JSON-RPC ↔ v4 协议双向转换
│
├── runtime/ # 运行时层 (已完成)
│ ├── __init__.py # 公共入口
│ ├── peer.py # 核心通信端点
│ ├── transport.py # 传输层实现 (Stdio/WebSocket)
│ ├── loader.py # 插件加载器与环境管理
│ ├── handler_dispatcher.py # Handler 分发器
│ ├── capability_router.py # Capability 路由器
│ └── bootstrap.py # Supervisor/Worker 运行时
│
├── clients/ # 客户端层 (已完成)
│ ├── __init__.py # 导出所有客户端
│ ├── _proxy.py # CapabilityProxy 代理
│ ├── llm.py # LLM 客户端
│ ├── db.py # 数据库客户端
│ ├── memory.py # 记忆客户端
│ └── platform.py # 平台客户端
│
└── api/ # API 层 - 兼容层
├── __init__.py # 子模块导出
├── basic/ # 基础实体与配置
│ ├── astrbot_config.py
│ ├── conversation_mgr.py
│ └── entities.py
├── components/ # 组件导出
│ └── command.py # CommandComponent 导出
├── event/ # 事件相关
│ ├── astr_message_event.py
│ ├── astrbot_message.py
│ ├── event_result.py
│ ├── event_type.py
│ ├── filter.py # filter 命名空间
│ ├── message_session.py
│ └── message_type.py
├── message/ # 消息链
│ ├── chain.py
│ └── components.py
├── platform/ # 平台元数据
│ └── platform_metadata.py
├── provider/ # Provider 实体
│ └── entities.py
└── star/ # Star 相关
├── context.py # Legacy Context 导出
└── star.py
核心模块详解
协议层 (protocol/)
协议层负责消息格式定义和 legacy 兼容转换,是 v4 新引入的抽象层。
descriptors.py - 描述符定义
定义了 Handler 和 Capability 的元数据结构,以及内置能力的 JSON Schema 常量。
核心类型:
# 权限配置
class Permissions(_DescriptorBase):
require_admin: bool = False
level: int = 0
# 四种 Trigger 类型 (discriminated union)
class CommandTrigger:
type: Literal["command"] = "command"
command: str
aliases: list[str] = []
description: str | None = None
class MessageTrigger:
type: Literal["message"] = "message"
regex: str | None = None
keywords: list[str] = []
platforms: list[str] = []
class EventTrigger:
type: Literal["event"] = "event"
event_type: str
class ScheduleTrigger:
type: Literal["schedule"] = "schedule"
cron: str | None = None
interval_seconds: int | None = None
# Trigger 联合类型
Trigger = Annotated[
CommandTrigger | MessageTrigger | EventTrigger | ScheduleTrigger,
Field(discriminator="type"),
]
# Handler 描述符
class HandlerDescriptor(_DescriptorBase):
id: str
trigger: Trigger
priority: int = 0
permissions: Permissions
# Capability 描述符
class CapabilityDescriptor(_DescriptorBase):
name: str
description: str
input_schema: dict[str, Any] | None = None
output_schema: dict[str, Any] | None = None
supports_stream: bool = False
cancelable: bool = False
内置能力 Schema 常量:
# LLM 相关
LLM_CHAT_INPUT_SCHEMA
LLM_CHAT_OUTPUT_SCHEMA
LLM_CHAT_RAW_INPUT_SCHEMA
LLM_CHAT_RAW_OUTPUT_SCHEMA
LLM_STREAM_CHAT_INPUT_SCHEMA
LLM_STREAM_CHAT_OUTPUT_SCHEMA
# Memory 相关
MEMORY_SEARCH_INPUT_SCHEMA
MEMORY_SEARCH_OUTPUT_SCHEMA
MEMORY_SAVE_INPUT_SCHEMA
MEMORY_SAVE_OUTPUT_SCHEMA
MEMORY_GET_INPUT_SCHEMA
MEMORY_GET_OUTPUT_SCHEMA
MEMORY_DELETE_INPUT_SCHEMA
MEMORY_DELETE_OUTPUT_SCHEMA
# DB 相关
DB_GET_INPUT_SCHEMA
DB_GET_OUTPUT_SCHEMA
DB_SET_INPUT_SCHEMA
DB_SET_OUTPUT_SCHEMA
DB_DELETE_INPUT_SCHEMA
DB_DELETE_OUTPUT_SCHEMA
DB_LIST_INPUT_SCHEMA
DB_LIST_OUTPUT_SCHEMA
# Platform 相关
PLATFORM_SEND_INPUT_SCHEMA
PLATFORM_SEND_OUTPUT_SCHEMA
PLATFORM_SEND_IMAGE_INPUT_SCHEMA
PLATFORM_SEND_IMAGE_OUTPUT_SCHEMA
PLATFORM_GET_MEMBERS_INPUT_SCHEMA
PLATFORM_GET_MEMBERS_OUTPUT_SCHEMA
# 汇总字典
BUILTIN_CAPABILITY_SCHEMAS: dict[str, tuple[JSONSchema, JSONSchema]]
messages.py - 协议消息
定义五种协议消息类型,遵循统一 id 字段原则。
消息类型:
| 类型 | 用途 | 关键字段 |
|---|---|---|
InitializeMessage |
初始化握手 | peer, handlers, metadata |
InvokeMessage |
调用 Capability | capability, input, stream |
ResultMessage |
返回结果 | success, output, error |
EventMessage |
流式事件 | phase (started/delta/completed/failed) |
CancelMessage |
取消请求 | reason |
核心结构:
class ErrorPayload(_MessageBase):
code: str
message: str
hint: str = ""
retryable: bool = False
class PeerInfo(_MessageBase):
name: str
role: Literal["plugin", "supervisor", "core"]
version: str = "4.0"
class InitializeMessage(_MessageBase):
type: Literal["initialize"] = "initialize"
id: str
peer: PeerInfo
handlers: list[HandlerDescriptor] = []
metadata: dict[str, Any] = {}
class InitializeOutput(_MessageBase):
peer: PeerInfo
capabilities: list[CapabilityDescriptor] = []
metadata: dict[str, Any] = {}
class ResultMessage(_MessageBase):
type: Literal["result"] = "result"
id: str
kind: str # "initialize_result" 或 capability 名称
success: bool
output: dict[str, Any] | None = None
error: ErrorPayload | None = None
class InvokeMessage(_MessageBase):
type: Literal["invoke"] = "invoke"
id: str
capability: str
input: dict[str, Any] = {}
stream: bool = False
class EventMessage(_MessageBase):
type: Literal["event"] = "event"
id: str
phase: Literal["started", "delta", "completed", "failed"]
data: dict[str, Any] | None = None
output: dict[str, Any] | None = None
error: ErrorPayload | None = None
class CancelMessage(_MessageBase):
type: Literal["cancel"] = "cancel"
id: str
reason: str = "user_cancelled"
核心函数:
def parse_message(payload: str | bytes | dict) -> ProtocolMessage:
"""解析 JSON 为协议消息对象"""
legacy_adapter.py - 协议适配器
实现 v3 JSON-RPC 与 v4 协议的双向转换。
核心类型:
class LegacyRequest(_LegacyMessageBase):
jsonrpc: Literal["2.0"] = "2.0"
id: str | None = None
method: str
params: dict[str, Any] = {}
class LegacySuccessResponse(_LegacyMessageBase):
jsonrpc: Literal["2.0"] = "2.0"
id: str | None = None
result: Any
class LegacyErrorResponse(_LegacyMessageBase):
jsonrpc: Literal["2.0"] = "2.0"
id: str | None = None
error: LegacyErrorData
LegacyMessage = LegacyRequest | LegacySuccessResponse | LegacyErrorResponse
LegacyToV4Message = InitializeMessage | InvokeMessage | CancelMessage | None
核心函数:
def parse_legacy_message(payload: str | dict) -> LegacyMessage:
"""解析 legacy JSON-RPC 消息"""
def legacy_message_to_v4(legacy: LegacyMessage) -> LegacyToV4Message:
"""Legacy JSON-RPC → v4 Message"""
def initialize_to_legacy_handshake_response(message: InitializeMessage, output: InitializeOutput) -> dict:
"""v4 Initialize → Legacy Response"""
def invoke_to_legacy_request(message: InvokeMessage) -> dict:
"""v4 Invoke → Legacy Request"""
def result_to_legacy_response(message: ResultMessage) -> dict:
"""v4 Result → Legacy Response"""
def event_to_legacy_notification(message: EventMessage) -> dict:
"""v4 Event → Legacy Notification"""
def cancel_to_legacy_request(message: CancelMessage) -> dict:
"""v4 Cancel → Legacy Request"""
常量:
LEGACY_JSONRPC_VERSION = "2.0"
LEGACY_CONTEXT_CAPABILITY = "internal.legacy.call_context_function"
LEGACY_HANDSHAKE_METADATA_KEY = "legacy_handshake_payload"
LEGACY_PLUGIN_KEYS_METADATA_KEY = "legacy_plugin_keys"
LEGACY_ADAPTER_MESSAGE_EVENT = 3
运行时层 (runtime/)
运行时层负责把协议、传输、插件加载和生命周期管理拼成一条完整执行链。
peer.py - 核心通信端点
实现 Plugin ↔ Core 的对称通信模型。
核心方法:
class Peer:
# 生命周期
async def start(self) -> None: ...
async def stop(self) -> None: ...
async def wait_closed(self) -> None: ...
async def wait_until_remote_initialized(self, timeout: float = 30.0) -> None: ...
# 初始化
async def initialize(self, handlers, metadata) -> InitializeOutput: ...
# Capability 调用
async def invoke(self, capability, payload, stream=False) -> dict: ...
async def invoke_stream(self, capability, payload) -> AsyncIterator[EventMessage]: ...
# 取消
async def cancel(self, request_id, reason="user_cancelled") -> None: ...
# Handler 设置
def set_initialize_handler(self, handler: InitializeHandler): ...
def set_invoke_handler(self, handler: InvokeHandler): ...
def set_cancel_handler(self, handler: CancelHandler): ...
内部状态:
self._pending_results: dict[str, asyncio.Future[ResultMessage]] # 普通调用
self._pending_streams: dict[str, asyncio.Queue] # 流式调用
self._inbound_tasks: dict[str, tuple[Task, CancelToken]] # 入站任务
self._remote_initialized: asyncio.Event # 远端初始化状态
self._unusable: bool # 连接是否不可用
transport.py - 传输层实现
抽象传输层,支持多种通信方式。
类层次:
Transport (ABC)
├── StdioTransport # 进程间通信 (stdin/stdout)
├── WebSocketServerTransport # WebSocket 服务端
└── WebSocketClientTransport # WebSocket 客户端
StdioTransport 特性:
- 支持作为父进程启动子进程 (
command参数) - 支持直接读写 stdin/stdout
- 自动处理进程生命周期
WebSocket 特性:
- 心跳机制 (通过
heartbeat参数配置) - 单连接限制 (Server 端)
- 自动重连需要外部实现
loader.py - 插件加载器
负责插件发现、环境准备和实例化。
核心类型:
@dataclass
class PluginSpec:
name: str
plugin_dir: Path
manifest_path: Path
requirements_path: Path
python_version: str
manifest_data: dict[str, Any]
@dataclass
class LoadedHandler:
descriptor: HandlerDescriptor
callable: Any
owner: Any
legacy_context: Any | None = None
@dataclass
class LoadedPlugin:
plugin: PluginSpec
handlers: list[LoadedHandler]
instances: list[Any]
@dataclass
class PluginDiscoveryResult:
plugins: list[PluginSpec]
errors: dict[str, str]
核心函数:
def discover_plugins(plugins_dir: Path) -> PluginDiscoveryResult:
"""扫描插件目录,发现所有有效插件"""
def load_plugin_spec(plugin_dir: Path) -> PluginSpec:
"""从插件目录加载插件规范"""
def load_plugin(plugin: PluginSpec) -> LoadedPlugin:
"""加载插件,返回 Handler 列表"""
class PluginEnvironmentManager:
"""使用 uv 管理插件虚拟环境"""
def prepare_environment(self, plugin: PluginSpec) -> Path:
"""准备插件 Python 环境,返回 python 路径"""
Handler ID 格式:
{plugin_name}:{module}.{ClassName}.{method_name}
handler_dispatcher.py - Handler 分发器
处理 handler.invoke Capability 的调用。
class HandlerDispatcher:
async def invoke(self, message, cancel_token) -> dict[str, Any]:
"""调用指定 Handler"""
async def cancel(self, request_id: str) -> None:
"""取消正在执行的 Handler"""
async def _run_handler(self, loaded, event, ctx) -> None:
"""执行 Handler,处理同步/异步/生成器返回值"""
def _build_args(self, handler, event, ctx) -> list[Any]:
"""根据签名注入 event 和 ctx 参数"""
参数注入规则:
| 参数名 | 注入值 |
|---|---|
event |
MessageEvent 实例 |
ctx / context |
Context 实例 |
capability_router.py - Capability 路由器
管理和路由 Capability 调用。
class CapabilityRouter:
def register(self, descriptor, call_handler=None, stream_handler=None, exposed=True):
"""注册 Capability"""
async def execute(self, capability, payload, stream, cancel_token, request_id):
"""执行 Capability 调用"""
@dataclass
class StreamExecution:
iterator: AsyncIterator[dict[str, Any]]
finalize: Callable[[list[dict]], dict[str, Any]]
内置 Capabilities:
| Capability | 功能 |
|---|---|
llm.chat |
对话 (返回文本) |
llm.chat_raw |
对话 (返回完整响应) |
llm.stream_chat |
流式对话 |
memory.search |
搜索记忆 |
memory.get |
获取记忆 |
memory.save |
保存记忆 |
memory.delete |
删除记忆 |
db.get |
读取 KV |
db.set |
写入 KV |
db.delete |
删除 KV |
db.list |
列出 KV |
platform.send |
发送消息 |
platform.send_image |
发送图片 |
platform.get_members |
获取群成员 |
Capability 命名规则:
RESERVED_CAPABILITY_NAMESPACES = ("handler", "system", "internal")
CAPABILITY_NAME_PATTERN = r"^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$"
bootstrap.py - 运行时启动器
定义三种运行模式。
运行模式:
| 模式 | 类 | 用途 |
|---|---|---|
| Supervisor | SupervisorRuntime |
管理多插件,聚合 Handler |
| Worker | PluginWorkerRuntime |
单插件进程,处理 Handler 调用 |
| WebSocket | run_websocket_server() |
开发调试用 WebSocket 服务 |
class WorkerSession:
"""Supervisor 管理的单插件会话"""
async def start(self) -> None: ...
async def invoke_handler(self, handler_id, event_payload, request_id) -> dict: ...
async def cancel(self, request_id) -> None: ...
class SupervisorRuntime:
"""Supervisor 运行时"""
async def start(self) -> None:
# 1. 发现插件
# 2. 为每个插件启动 Worker 进程
# 3. 聚合 Handler 并向 Core 初始化
class PluginWorkerRuntime:
"""Worker 运行时"""
async def start(self) -> None:
# 1. 加载插件
# 2. 创建 Dispatcher
# 3. 向 Supervisor 初始化
客户端层 (clients/)
客户端层提供类型安全的 Capability 调用接口。
_proxy.py - Capability 代理
class CapabilityProxy:
async def call(self, name: str, payload: dict) -> dict[str, Any]:
"""普通调用"""
async def stream(self, name: str, payload: dict) -> AsyncIterator[dict[str, Any]]:
"""流式调用"""
llm.py - LLM 客户端
class ChatMessage(BaseModel):
role: Literal["user", "assistant", "system"]
content: str
class LLMResponse(BaseModel):
text: str
usage: dict[str, Any] | None = None
finish_reason: str | None = None
tool_calls: list[dict[str, Any]] = []
class LLMClient:
async def chat(self, prompt, system=None, history=None, model=None, temperature=None) -> str:
"""简单对话,返回文本"""
async def chat_raw(self, prompt, **kwargs) -> LLMResponse:
"""完整对话,返回结构化响应"""
async def stream_chat(self, prompt, system=None, history=None) -> AsyncGenerator[str, None]:
"""流式对话"""
db.py - 数据库客户端
class DBClient:
async def get(self, key: str) -> dict[str, Any] | None: ...
async def set(self, key: str, value: dict[str, Any]) -> None: ...
async def delete(self, key: str) -> None: ...
async def list(self, prefix: str | None = None) -> list[str]: ...
memory.py - 记忆客户端
class MemoryClient:
async def search(self, query: str) -> list[dict[str, Any]]: ...
async def get(self, key: str) -> dict[str, Any] | None: ...
async def save(self, key: str, value: dict[str, Any] | None = None, **extra) -> None: ...
async def delete(self, key: str) -> None: ...
platform.py - 平台客户端
class PlatformClient:
async def send(self, session: str, text: str) -> dict[str, Any]: ...
async def send_image(self, session: str, image_url: str) -> dict[str, Any]: ...
async def get_members(self, session: str) -> list[dict[str, Any]]: ...
API 层 (api/)
API 层作为兼容层,通过 thin re-export 方式暴露旧版 API。
兼容层设计
# api/__init__.py
from . import basic, components, event, message, platform, provider, star
# api/star/context.py - Legacy Context 导出
from ..._legacy_api import LegacyContext as Context
# api/components/command.py - CommandComponent 导出
from ..._legacy_api import CommandComponent
# api/event/filter.py - filter 命名空间
class _FilterNamespace:
command = staticmethod(command)
regex = staticmethod(regex)
permission = staticmethod(permission)
filter = _FilterNamespace()
核心文件
顶层导出 (__init__.py)
from .context import Context
from .decorators import on_command, on_event, on_message, on_schedule, require_admin
from .errors import AstrBotError
from .events import MessageEvent
from .star import Star
__all__ = [
"AstrBotError",
"Context",
"MessageEvent",
"Star",
"on_command",
"on_event",
"on_message",
"on_schedule",
"require_admin",
]
star.py - Star 基类
class Star:
__handlers__: tuple[str, ...] = ()
def __init_subclass__(cls, **kwargs):
"""收集子类的 Handler 方法名到 __handlers__"""
async def on_start(self, ctx) -> None:
"""生命周期钩子:启动时"""
async def on_stop(self, ctx) -> None:
"""生命周期钩子:停止时"""
async def on_error(self, error, event, ctx) -> None:
"""错误处理钩子"""
@classmethod
def __astrbot_is_new_star__(cls) -> bool:
return True # 新版 Star 返回 True
Handler 发现机制:
@on_command等装饰器在方法上设置__astrbot_handler_meta__Star.__init_subclass__遍历 MRO 收集带 meta 的方法名- Loader 读取
__handlers__并构建HandlerDescriptor
decorators.py - 装饰器
# 命令触发
@on_command("hello", aliases=["hi"], description="问候")
def hello_handler(event): ...
# 消息触发
@on_message(regex=r"^ping", keywords=["ping"], platforms=["qq"])
def ping_handler(event): ...
# 事件触发
@on_event("group_join")
def join_handler(event): ...
# 定时触发
@on_schedule(cron="0 9 * * *") # 或 interval_seconds=60
def scheduled_handler(): ...
# 权限
@on_command("admin")
@require_admin
def admin_handler(event): ...
context.py - 运行时 Context
@dataclass(slots=True)
class CancelToken:
def cancel(self) -> None: ...
@property
def cancelled(self) -> bool: ...
async def wait(self) -> None: ...
def raise_if_cancelled(self) -> None: ...
class Context:
def __init__(self, *, peer, plugin_id, cancel_token=None, logger=None):
proxy = CapabilityProxy(peer)
self.peer = peer
self.llm = LLMClient(proxy)
self.memory = MemoryClient(proxy)
self.db = DBClient(proxy)
self.platform = PlatformClient(proxy)
self.plugin_id = plugin_id
self.logger = logger or base_logger.bind(plugin_id=plugin_id)
self.cancel_token = cancel_token or CancelToken()
events.py - 事件定义
@dataclass
class PlainTextResult:
text: str
ReplyHandler = Callable[[str], Awaitable[None]]
class MessageEvent:
def __init__(self, *, text, user_id, group_id, platform, session_id, raw, context, reply_handler):
self.text = text
self.user_id = user_id
self.group_id = group_id
self.platform = platform
self.session_id = session_id or group_id or user_id or ""
self.raw = raw or {}
self._reply_handler = reply_handler
@classmethod
def from_payload(cls, payload, context=None, reply_handler=None) -> "MessageEvent":
"""从 payload 构造"""
def to_payload(self) -> dict[str, Any]:
"""序列化为 payload"""
async def reply(self, text: str) -> None:
"""回复消息 (依赖注入 reply_handler)"""
def bind_reply_handler(self, reply_handler: ReplyHandler) -> None:
"""绑定回复处理器"""
def plain_result(self, text: str) -> PlainTextResult:
"""创建纯文本结果"""
errors.py - 错误模型
@dataclass
class AstrBotError(Exception):
code: str
message: str
hint: str = ""
retryable: bool = False
@classmethod
def cancelled(cls, message="调用被取消") -> "AstrBotError": ...
@classmethod
def capability_not_found(cls, name: str) -> "AstrBotError": ...
@classmethod
def invalid_input(cls, message: str) -> "AstrBotError": ...
@classmethod
def protocol_version_mismatch(cls, message: str) -> "AstrBotError": ...
@classmethod
def protocol_error(cls, message: str) -> "AstrBotError": ...
@classmethod
def internal_error(cls, message: str) -> "AstrBotError": ...
def to_payload(self) -> dict[str, object]: ...
@classmethod
def from_payload(cls, payload) -> "AstrBotError": ...
_legacy_api.py - 兼容层
class LegacyContext:
"""v3 Context 兼容实现"""
def __init__(self, plugin_id: str):
self.plugin_id = plugin_id
self._runtime_context: NewContext | None = None
self.conversation_manager = LegacyConversationManager(self)
def bind_runtime_context(self, runtime_context: NewContext) -> None: ...
async def llm_generate(self, chat_provider_id, prompt, ...) -> LLMResponse: ...
async def tool_loop_agent(self, chat_provider_id, prompt, ...) -> LLMResponse: ...
async def send_message(self, session, message_chain) -> None: ...
async def put_kv_data(self, key, value) -> None: ...
async def get_kv_data(self, key) -> dict | None: ...
async def delete_kv_data(self, key) -> None: ...
class CommandComponent(Star):
"""v3 插件基类"""
@classmethod
def __astrbot_is_new_star__(cls) -> bool:
return False # 标识为旧版
cli.py - 命令行接口
@click.group()
def cli(): ...
@cli.command()
@click.option("--plugins-dir", default="plugins")
def run(plugins_dir: Path):
"""启动 Supervisor"""
asyncio.run(run_supervisor(plugins_dir=plugins_dir))
@cli.command(hidden=True)
@click.option("--plugin-dir", required=True)
def worker(plugin_dir: Path):
"""启动 Worker (内部命令)"""
asyncio.run(run_plugin_worker(plugin_dir=plugin_dir))
@cli.command(hidden=True)
@click.option("--port", default=8765)
def websocket(port: int):
"""启动 WebSocket 服务 (调试用)"""
五大硬性协议规则
1. 统一 id 字段
规则: 所有协议消息必须有 id 字段。
class InitializeMessage(_MessageBase):
type: Literal["initialize"] = "initialize"
id: str # 必须有
...
class ResultMessage(_MessageBase):
type: Literal["result"] = "result"
id: str # 必须有
...
2. event 仅用于 stream=true
规则: EventMessage 只在流式调用中使用。
# Peer._handle_result
if queue is not None: # stream=true 的 pending stream
await queue.put(AstrBotError.protocol_error("stream=true 调用不应收到 result"))
# Peer._handle_event
if future is not None: # stream=false 的 pending result
future.set_exception(AstrBotError.protocol_error("stream=false 调用不应收到 event"))
3. handler.invoke 用于回调
规则: 插件 Handler 调用通过 handler.invoke Capability。
# HandlerDispatcher 中
if message.capability != "handler.invoke":
raise AstrBotError.capability_not_found(message.capability)
4. cancel 作为 request-stop
规则: 取消请求发送 CancelMessage,等待终端事件。
async def cancel(self, request_id: str, reason: str = "user_cancelled") -> None:
await self._send(CancelMessage(id=request_id, reason=reason))
# Worker 收到后
token.cancel()
task.cancel()
5. initialize 失败处理
规则: 初始化失败后连接进入不可用状态并关闭。
async def _reject_initialize(self, message, error):
await self._send(ResultMessage(id=message.id, kind="initialize_result", success=False, error=...))
self._unusable = True
self._remote_initialized.set()
await self.stop()
数据流与通信模型
初始化流程
┌────────┐ ┌────────┐
│ Core │ │ Plugin │
└───┬────┘ └───┬────┘
│ │
│──── InitializeMessage ───────────────>│
│ {id, peer, handlers, metadata} │
│ │
│<─── ResultMessage ────────────────────│
│ {id, kind="initialize_result", │
│ success, output: {peer, │
│ capabilities, metadata}} │
│ │
Capability 调用流程 (普通)
┌────────┐ ┌────────┐
│ Core │ │ Plugin │
└───┬────┘ └───┬────┘
│ │
│──── InvokeMessage ───────────────────>│
│ {id, capability, input, stream=false}│
│ │
│<─── ResultMessage ────────────────────│
│ {id, success, output/error} │
│ │
Capability 调用流程 (流式)
┌────────┐ ┌────────┐
│ Core │ │ Plugin │
└───┬────┘ └───┬────┘
│ │
│──── InvokeMessage ───────────────────>│
│ {id, capability, input, stream=true}│
│ │
│<─── EventMessage(phase="started") ────│
│ │
│<─── EventMessage(phase="delta") ──────│
│ {data: {...}} │
│<─── EventMessage(phase="delta") ──────│
│ ... │
│ │
│<─── EventMessage(phase="completed") ──│
│ {output: {...}} │
│ │
取消流程
┌────────┐ ┌────────┐
│ Core │ │ Plugin │
└───┬────┘ └───┬────┘
│ │
│──── CancelMessage ───────────────────>│
│ {id, reason} │
│ │
│<─── EventMessage(phase="failed") ─────│
│ {error: {code: "cancelled"}} │
│ │
扩展机制
添加新 Capability
- 在
CapabilityRouter._register_builtin_capabilities()中注册:
self.register(
CapabilityDescriptor(
name="my.custom_action",
description="自定义操作",
input_schema={"type": "object", "properties": {...}, "required": [...]},
output_schema={"type": "object", "properties": {...}},
supports_stream=False,
cancelable=False,
),
call_handler=my_handler,
exposed=True, # 是否暴露给对端
)
添加新 Trigger 类型
- 在
descriptors.py中定义新的 Trigger 类:
class CustomTrigger(_DescriptorBase):
type: Literal["custom"] = "custom"
custom_field: str
- 更新
Trigger联合类型:
Trigger = Annotated[
CommandTrigger | MessageTrigger | EventTrigger | ScheduleTrigger | CustomTrigger,
Field(discriminator="type"),
]
- 在
decorators.py中添加装饰器:
def on_custom(custom_field: str):
def decorator(func):
meta = _get_or_create_meta(func)
meta.trigger = CustomTrigger(custom_field=custom_field)
return func
return decorator
添加新 Transport
- 继承
Transport基类:
class MyTransport(Transport):
async def start(self) -> None: ...
async def stop(self) -> None: ...
async def send(self, payload: str) -> None: ...
实现状态
已完成模块
| 模块 | 文件 | 状态 | 说明 |
|---|---|---|---|
| 协议层 | protocol/ |
✅ 完成 | |
descriptors.py |
✅ | Handler/Capability 描述符 + 内置 Schema 常量 | |
messages.py |
✅ | 5 种消息类型 + parse_message | |
legacy_adapter.py |
✅ | JSON-RPC ↔ v4 双向转换 | |
| 运行时层 | runtime/ |
✅ 完成 | |
peer.py |
✅ | 对称通信端点 + 取消 + 流式 | |
transport.py |
✅ | Stdio + WebSocket Server/Client | |
loader.py |
✅ | 插件发现 + 环境管理 + 加载 | |
handler_dispatcher.py |
✅ | Handler 分发 + 参数注入 | |
capability_router.py |
✅ | 能力路由 + 内置能力注册 | |
bootstrap.py |
✅ | Supervisor + Worker + WebSocket | |
| 客户端层 | clients/ |
✅ 完成 | |
_proxy.py |
✅ | CapabilityProxy 代理 | |
llm.py |
✅ | LLM 客户端 (chat/chat_raw/stream) | |
memory.py |
✅ | Memory 客户端 (search/get/save/delete) | |
db.py |
✅ | DB 客户端 (get/set/delete/list) | |
platform.py |
✅ | Platform 客户端 (send/send_image/get_members) | |
| API 层 | api/ |
✅ 完成 | 兼容层 |
star/context.py |
✅ | LegacyContext 导出 | |
components/command.py |
✅ | CommandComponent 导出 | |
event/filter.py |
✅ | filter 命名空间 | |
basic/ |
✅ | 基础实体与配置 | |
message/ |
✅ | MessageChain | |
platform/ |
✅ | 平台元数据 | |
provider/ |
✅ | Provider 实体 | |
| 核心文件 | 根目录 | ✅ 完成 | |
__init__.py |
✅ | 顶层导出 | |
star.py |
✅ | Star 基类 + Handler 发现 | |
context.py |
✅ | Context + CancelToken | |
decorators.py |
✅ | on_command/on_message/on_event/on_schedule | |
events.py |
✅ | MessageEvent | |
errors.py |
✅ | AstrBotError | |
_legacy_api.py |
✅ | LegacyContext + CommandComponent | |
cli.py |
✅ | Click 命令行工具 | |
__main__.py |
✅ | python -m astrbot_sdk 入口 |
测试覆盖
测试文件位于 tests_v4/ 目录,共 35+ 个测试文件:
tests_v4/
├── test_protocol.py # 协议层基础测试
├── test_protocol_descriptors.py # 描述符测试
├── test_protocol_messages.py # 消息类型测试
├── test_protocol_legacy_adapter.py # Legacy 适配器测试
├── test_peer.py # Peer 测试
├── test_transport.py # Transport 测试
├── test_capability_router.py # CapabilityRouter 测试
├── test_handler_dispatcher.py # HandlerDispatcher 测试
├── test_loader.py # 加载器测试
├── test_bootstrap.py # Bootstrap 测试
├── test_context.py # Context 测试
├── test_events.py # 事件测试
├── test_decorators.py # 装饰器测试
├── test_clients_module.py # 客户端模块测试
├── test_llm_client.py # LLM 客户端测试
├── test_memory_client.py # Memory 客户端测试
├── test_db_client.py # DB 客户端测试
├── test_platform_client.py # Platform 客户端测试
├── test_capability_proxy.py # CapabilityProxy 测试
├── test_api_modules.py # API 模块测试
├── test_api_decorators.py # API 装饰器测试
├── test_api_event_filter.py # filter 命名空间测试
├── test_api_legacy_context.py # Legacy Context 测试
├── test_api_contract.py # API 契约测试
├── test_runtime_integration.py # 运行时集成测试
├── test_script_migrations.py # 脚本迁移测试
├── test_supervisor_migration.py # Supervisor 迁移测试
└── ... # 更多测试文件
版本兼容性
| 组件 | v3 | v4 |
|---|---|---|
| 插件基类 | CommandComponent |
Star |
| Context | LegacyContext |
Context |
| 装饰器 | @filter.command |
@on_command |
| 协议 | JSON-RPC 2.0 | 自定义协议 |
| 通信 | 单向 | 双向对称 |
兼容策略: LegacyAdapter 实现协议转换,CommandComponent 继承 Star 并标记 __astrbot_is_new_star__ = False。
迁移指南:
# 旧版 (将在未来版本废弃)
from astrbot_sdk.api.event import AstrMessageEvent
from astrbot_sdk.api.star.context import Context
# 新版 (推荐)
from astrbot_sdk.events import MessageEvent
from astrbot_sdk.context import Context