Files
AstrBot/ARCHITECTURE.md

985 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AstrBot SDK v4 架构与实现文档
## 目录
1. [架构概览](#架构概览)
2. [目录结构](#目录结构)
3. [核心模块详解](#核心模块详解)
- [协议层 (protocol/)](#协议层-protocol)
- [运行时层 (runtime/)](#运行时层-runtime)
- [客户端层 (clients/)](#客户端层-clients)
- [API 层 (api/)](#api层-api)
- [核心文件](#核心文件)
4. [五大硬性协议规则](#五大硬性协议规则)
5. [数据流与通信模型](#数据流与通信模型)
6. [扩展机制](#扩展机制)
---
## 架构概览
AstrBot SDK v4 采用分层架构设计,从上到下分为:
```
┌─────────────────────────────────────────────────────┐
│ 用户层 (User Layer) │
│ 插件开发者编写的 Star 类 │
├─────────────────────────────────────────────────────┤
│ API 层 (API Layer) │
│ Star, Context, decorators, filter, events │
├─────────────────────────────────────────────────────┤
│ 翻译层 (Translation Layer) │
│ HandlerDispatcher, Loader, LegacyAdapter │
├─────────────────────────────────────────────────────┤
│ 通信层 (Communication Layer) │
│ Peer, Transport, CapabilityRouter │
└─────────────────────────────────────────────────────┘
```
### 核心设计原则
1. **协议优先**: 所有通信通过标准化的协议消息
2. **能力抽象**: 通过 Capability 系统暴露核心功能
3. **双向通信**: Plugin ↔ Core 的对称通信模型
4. **向后兼容**: LegacyAdapter 提供 v3 兼容层
---
## 目录结构
```
src-new/astrbot_sdk/
├── __init__.py # 顶层导出
├── __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
│ ├── messages.py # 五种协议消息类型
│ └── legacy_adapter.py # v3 JSON-RPC ↔ v4 协议转换
├── runtime/ # 运行时层
│ ├── __init__.py
│ ├── peer.py # 核心通信端点
│ ├── transport.py # 传输层实现
│ ├── 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
├── components/ # 组件导出
│ ├── __init__.py
│ └── command.py # CommandComponent 导出
├── event/ # 事件相关
│ ├── __init__.py
│ └── filter.py # filter 命名空间
└── star/ # Star 相关
├── __init__.py
└── context.py # Legacy Context 导出
```
---
## 核心模块详解
### 协议层 (protocol/)
#### `descriptors.py` - 描述符定义
定义了 Handler 和 Capability 的元数据结构。
**核心类型:**
```python
# 权限配置
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
```
---
#### `messages.py` - 协议消息
定义五种协议消息类型,遵循**统一 id 字段**原则。
**消息类型:**
| 类型 | 用途 | 关键字段 |
|------|------|----------|
| `InitializeMessage` | 初始化握手 | `peer`, `handlers`, `metadata` |
| `InvokeMessage` | 调用 Capability | `capability`, `input`, `stream` |
| `ResultMessage` | 返回结果 | `success`, `output`, `error` |
| `EventMessage` | 流式事件 | `phase` (started/delta/completed/failed) |
| `CancelMessage` | 取消请求 | `reason` |
**核心函数:**
```python
def parse_message(payload: str | bytes | dict) -> ProtocolMessage:
"""解析 JSON 为协议消息对象"""
```
---
#### `legacy_adapter.py` - 协议适配器
实现 v3 JSON-RPC 与 v4 协议的双向转换。
**核心类:**
```python
class LegacyAdapter:
def legacy_to_v4(self, payload) -> LegacyToV4Message:
"""Legacy JSON-RPC → v4 Message"""
def legacy_request_to_message(self, payload) -> InitializeMessage | InvokeMessage | ...:
"""Legacy Request 转换"""
def initialize_to_legacy_handshake_response(self, message) -> dict:
"""v4 Initialize → Legacy Response"""
def invoke_to_legacy_request(self, message) -> dict:
"""v4 Invoke → Legacy Request"""
```
**常量:**
```python
LEGACY_JSONRPC_VERSION = "2.0"
LEGACY_CONTEXT_CAPABILITY = "internal.legacy.call_context_function"
LEGACY_HANDSHAKE_METADATA_KEY = "legacy_handshake_payload"
LEGACY_ADAPTER_MESSAGE_EVENT = 3
```
---
### 运行时层 (runtime/)
#### `peer.py` - 核心通信端点
实现 Plugin ↔ Core 的对称通信模型。
**核心方法:**
```python
class Peer:
# 生命周期
async def start(self) -> None: ...
async def stop(self) -> 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): ...
def set_invoke_handler(self, handler): ...
def set_cancel_handler(self, handler): ...
```
**内部状态:**
```python
self._pending_results: dict[str, asyncio.Future[ResultMessage]] # 普通调用
self._pending_streams: dict[str, asyncio.Queue] # 流式调用
self._inbound_tasks: dict[str, tuple[Task, CancelToken]] # 入站任务
```
---
#### `transport.py` - 传输层实现
抽象传输层,支持多种通信方式。
**类层次:**
```
Transport (ABC)
├── StdioTransport # 进程间通信 (stdin/stdout)
├── WebSocketServerTransport # WebSocket 服务端
└── WebSocketClientTransport # WebSocket 客户端
```
**StdioTransport 特性:**
- 支持作为父进程启动子进程 (`command` 参数)
- 支持直接读写 stdin/stdout
- 自动处理进程生命周期
**WebSocket 特性:**
- 心跳机制
- 单连接限制 (Server 端)
- 自动重连 (Client 端)
---
#### `loader.py` - 插件加载器
负责插件发现、环境准备和实例化。
**核心类型:**
```python
@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]
```
**核心函数:**
```python
def discover_plugins(plugins_dir: Path) -> PluginDiscoveryResult:
"""扫描插件目录,发现所有有效插件"""
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 的调用。
```python
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 调用。
```python
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.save` | 保存记忆 |
| `memory.delete` | 删除记忆 |
| `db.get` | 读取 KV |
| `db.set` | 写入 KV |
| `db.delete` | 删除 KV |
| `db.list` | 列出 KV |
| `platform.send` | 发送消息 |
| `platform.send_image` | 发送图片 |
| `platform.get_members` | 获取群成员 |
**Capability 命名规则:**
```python
RESERVED_CAPABILITY_PREFIXES = ("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 服务 |
```python
class WorkerSession:
"""Supervisor 管理的单插件会话"""
async def start(self) -> None: ...
async def invoke_handler(self, handler_id, event_payload, request_id) -> dict: ...
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/)
#### `_proxy.py` - Capability 代理
提供类型安全的 Capability 调用接口。
```python
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 客户端
```python
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]:
"""流式对话"""
class LLMResponse(BaseModel):
text: str
usage: dict[str, Any] | None = None
finish_reason: str | None = None
tool_calls: list[dict[str, Any]] = []
```
---
#### `db.py` - 数据库客户端
```python
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` - 记忆客户端
```python
class MemoryClient:
async def search(self, query: str) -> list[dict[str, Any]]: ...
async def save(self, key: str, value: dict[str, Any] | None = None, **extra) -> None: ...
async def delete(self, key: str) -> None: ...
```
---
#### `platform.py` - 平台客户端
```python
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/)
#### `star.py` - Star 基类
```python
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 发现机制:**
1. `@on_command` 等装饰器在方法上设置 `__astrbot_handler_meta__`
2. `Star.__init_subclass__` 遍历 MRO 收集带 meta 的方法名
3. Loader 读取 `__handlers__` 并构建 `HandlerDescriptor`
---
#### `decorators.py` - 装饰器
```python
# 命令触发
@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
```python
@dataclass
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):
self.llm = LLMClient(CapabilityProxy(peer))
self.memory = MemoryClient(CapabilityProxy(peer))
self.db = DBClient(CapabilityProxy(peer))
self.platform = PlatformClient(CapabilityProxy(peer))
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` - 事件定义
```python
@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:
"""创建纯文本结果"""
```
**依赖注入模式:**
```python
# HandlerDispatcher 中
event = MessageEvent.from_payload(message.input.get("event", {}))
event.bind_reply_handler(lambda text: ctx.platform.send(event.session_id, text))
```
---
#### `errors.py` - 错误模型
```python
@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` - 兼容层
```python
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 # 标识为旧版
```
---
#### `api/event/filter.py` - filter 命名空间
```python
ADMIN = "admin"
def command(name: str):
return on_command(name)
def regex(pattern: str):
return on_message(regex=pattern)
def permission(level):
if level == ADMIN:
return require_admin
return lambda func: func
class _FilterNamespace:
command = staticmethod(command)
regex = staticmethod(regex)
permission = staticmethod(permission)
filter = _FilterNamespace()
```
---
### 核心文件
#### `cli.py` - 命令行接口
```python
@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` 字段。
```python
class InitializeMessage(_MessageBase):
type: Literal["initialize"] = "initialize"
id: str # 必须有
...
class ResultMessage(_MessageBase):
type: Literal["result"] = "result"
id: str # 必须有
...
```
### 2. event 仅用于 stream=true
**规则**: `EventMessage` 只在流式调用中使用。
```python
# 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。
```python
# HandlerDispatcher 中
if message.capability != "handler.invoke":
raise AstrBotError.capability_not_found(message.capability)
```
### 4. cancel 作为 request-stop
**规则**: 取消请求发送 `CancelMessage`,等待终端事件。
```python
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 失败处理
**规则**: 初始化失败后连接进入不可用状态并关闭。
```python
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
1.`CapabilityRouter._register_builtin_capabilities()` 中注册:
```python
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 类型
1.`descriptors.py` 中定义新的 Trigger 类:
```python
class CustomTrigger(_DescriptorBase):
type: Literal["custom"] = "custom"
custom_field: str
```
2. 更新 `Trigger` 联合类型:
```python
Trigger = Annotated[
CommandTrigger | MessageTrigger | EventTrigger | ScheduleTrigger | CustomTrigger,
Field(discriminator="type"),
]
```
3.`decorators.py` 中添加装饰器:
```python
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
1. 继承 `Transport` 基类:
```python
class MyTransport(Transport):
async def start(self) -> None: ...
async def stop(self) -> None: ...
async def send(self, payload: str) -> None: ...
```
---
## 版本兼容性
| 组件 | v3 | v4 |
|------|----|----|
| 插件基类 | `CommandComponent` | `Star` |
| Context | `LegacyContext` | `Context` |
| 装饰器 | `@filter.command` | `@on_command` |
| 协议 | JSON-RPC 2.0 | 自定义协议 |
| 通信 | 单向 | 双向对称 |
**兼容策略**: `LegacyAdapter` 实现协议转换,`CommandComponent` 继承 `Star` 并标记 `__astrbot_is_new_star__ = False`