Files
AstrBot/ARCHITECTURE.md

325 lines
13 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 当前架构文档
本文描述仓库 **当前实现**,是 `src-new/astrbot_sdk` / `src-new/astrbot` / `tests_v4` 的唯一主文档。
`refactor.md` 仅保留历史设计意图和演进说明,不再描述现状。
## 1. 目标与边界
AstrBot SDK v4 当前同时承担两件事:
1. 提供一套原生 v4 插件模型:`Star``Context``MessageEvent`、capability clients、v4 protocol。
2. 维持旧插件兼容:`astrbot_sdk.api.*``astrbot_sdk.compat``astrbot.api.*` 以及选定的 `astrbot.core.*` facade 继续可用。
因此compat 现在不是可忽略的旁路,而是一个受控的长期子系统。当前架构目标是:
- v4 原生 API 仍保持清晰、窄导出、协议优先。
- legacy 兼容逻辑尽量收口到私有边界,而不是扩散到 runtime 主干。
- 兼容导入路径继续可用,但不把旧应用整棵树重新复制进来。
- 文档明确区分“等价兼容”“降级兼容”“仅导入兼容”。
## 2. 当前分层模型
```text
插件作者
├─ 原生 v4: astrbot_sdk.{Star, Context, MessageEvent, decorators}
└─ legacy compat: astrbot_sdk.api.* / astrbot_sdk.compat / astrbot.api.*
高层 API
├─ 原生 clients: llm / memory / db / platform
└─ legacy facade: LegacyContext / LegacyStar / message components / filter namespace
执行边界
├─ runtime 主干: loader / bootstrap / handler_dispatcher / capability_router / peer
├─ compat 执行边界: _legacy_runtime.py
├─ legacy 行为承接: _legacy_api.py
└─ 会话等待器: _session_waiter.py
协议与传输
├─ protocol.messages / protocol.descriptors
├─ Peer
└─ StdioTransport / WebSocket transports
```
### 当前最重要的架构判断
- `astrbot_sdk.__init__` 只导出推荐的 v4 入口。
- `astrbot_sdk.runtime.__init__` 只导出高级运行时原语,不把 loader/bootstrap 等编排细节提升为根级稳定 API。
- `astrbot_sdk.protocol.__init__` 只导出 v4 原生协议模型legacy JSON-RPC 适配器留在 `protocol.legacy_adapter` 子模块。
- runtime 主干通过 `_legacy_runtime.py` 执行 compat filters / hooks / 生命周期桥接,不直接展开更多 legacy 细节。
## 3. 目录结构
```text
src-new/
├── astrbot_sdk/
│ ├── __init__.py # v4 推荐顶层入口
│ ├── context.py # Context / CancelToken
│ ├── decorators.py # on_command / on_message / provide_capability ...
│ ├── events.py # MessageEvent / PlainTextResult
│ ├── errors.py # AstrBotError / ErrorCodes
│ ├── star.py # Star 基类与 handler 收集
│ ├── cli.py # astr / astrbot-sdk CLI 入口
│ ├── testing.py # 本地开发与测试 harness
│ ├── compat.py # 旧顶层兼容重导出
│ ├── _legacy_api.py # LegacyContext / LegacyStar / CommandComponent
│ ├── _legacy_llm.py # legacy LLM/tool 兼容辅助
│ ├── _legacy_runtime.py # compat 执行边界
│ ├── _session_waiter.py # legacy session_waiter 兼容执行
│ ├── _shared_preferences.py # 共享偏好兼容辅助
│ │
│ ├── clients/
│ │ ├── _proxy.py # CapabilityProxy
│ │ ├── llm.py
│ │ ├── memory.py
│ │ ├── db.py # 包含 get_many / set_many / watch
│ │ └── platform.py
│ │
│ ├── protocol/
│ │ ├── __init__.py # 仅导出原生 v4 协议模型
│ │ ├── descriptors.py # handlers / capabilities / builtin schema registry
│ │ ├── messages.py # initialize / invoke / result / event / cancel
│ │ └── legacy_adapter.py # v3 JSON-RPC ↔ v4 适配
│ │
│ ├── runtime/
│ │ ├── __init__.py # Peer / Transport / CapabilityRouter / HandlerDispatcher
│ │ ├── peer.py
│ │ ├── transport.py
│ │ ├── capability_router.py
│ │ ├── handler_dispatcher.py
│ │ ├── loader.py
│ │ ├── environment_groups.py # 共享环境规划与分组环境管理
│ │ └── bootstrap.py
│ │
│ └── api/ # astrbot_sdk.api.* 兼容层
│ ├── basic/
│ ├── components/
│ ├── event/
│ ├── message/
│ ├── platform/
│ ├── provider/
│ └── star/
└── astrbot/ # 旧包名 facade受控兼容面
├── api/
└── core/
```
## 4. 核心执行链
### 4.1 插件发现与 worker 启动
1. `runtime.loader.discover_plugins()` 扫描插件目录,兼容 `plugin.yaml` 和 legacy `main.py` 插件。
2. `PluginEnvironmentManager.plan()` 基于 `runtime.python``requirements.txt` 规划共享环境分组。
3. `GroupEnvironmentManager` 负责准备分组环境worker 仍然保持“一插件一进程”,只是可共享同一个 Python 环境。
4. `load_plugin()` 加载组件v4 `Star` 直接实例化legacy 组件复用同一 `LegacyContext`
5. legacy component 注册通过 `_legacy_runtime` 把 compat hooks / LLM tools / context functions 绑定到共享 `LegacyContext`
6. `PluginWorkerRuntime` 创建 `Peer``HandlerDispatcher``CapabilityDispatcher`,初始化后向 supervisor 发送 `initialize`
7. worker 启动/停止时的 compat lifecycle hooks 统一由 `_legacy_runtime` 执行。
### 4.2 handler.invoke 调用链
1. 上游通过 capability `"handler.invoke"` 调 worker。
2. `HandlerDispatcher` 构造本地 `Context``MessageEvent`,先尝试把消息路由给 `_session_waiter`
3. 若命中 legacy compat handler则由 `_legacy_runtime` 应用 custom filters、结果装饰、发送后 hook、错误 hook。
4. handler 返回值支持:
- `MessageEventResult`
- `MessageChain`
- `PlainTextResult`
- `str`
- `{"text": ...}`
5. 发送链路优先使用 `ctx.platform.send_chain()``event.reply()`
### 4.3 capability 调用链
1. 插件代码通过 `ctx.llm.*``ctx.db.*``ctx.memory.*``ctx.platform.*` 访问上游能力。
2. clients 通过 `CapabilityProxy` 转成 `Peer.invoke()` / `Peer.invoke_stream()`
3. supervisor 侧 `CapabilityRouter` 处理内建能力worker 也可以通过 `@provide_capability()` 暴露插件自定义 capability。
4. 插件自定义 capability 由 `CapabilityDispatcher` 在 worker 内分发执行。
### 4.4 session_waiter
`_session_waiter.py` 提供 legacy `@session_waiter` 的最小可运行兼容实现。
它不是单纯导入桩,而是按 session 维度把后续消息重新路由给等待中的 compat 回调。
## 5. 协议契约
### 5.1 五条硬规则
1. 所有协议消息统一使用 `id` 关联请求与响应。
2. `EventMessage` 只用于 `stream=true` 的调用。
3. 插件 handler 回调统一走 capability `"handler.invoke"`
4. `CancelMessage` 表示“请求停止”,调用方仍需等待终止态。
5. `initialize` 失败后连接进入不可用状态。
### 5.2 版本语义
当前实现里必须区分两个概念:
- `protocol_version`**线协议版本**。当前 wire contract 使用 `"1.0"`
- `PeerInfo.version`**软件/实现版本标识**。当前 runtime 常用 `"v4"` 作为软件版本字符串。
二者不是同一个字段,也不应混写成同一含义。
### 5.3 主要消息
- `InitializeMessage`
- `InvokeMessage`
- `ResultMessage`
- `EventMessage`
- `CancelMessage`
`InitializeMessage``Peer.initialize()` 发起,成功响应是
`ResultMessage(kind="initialize_result", success=True, output=InitializeOutput(...))`
## 6. 当前内建 capabilities
当前协议注册表和 `CapabilityRouter` 内建 capability 一致,共 18 个:
| 命名空间 | Capability | 流式 |
|---|---|---|
| `llm` | `llm.chat` | 否 |
| `llm` | `llm.chat_raw` | 否 |
| `llm` | `llm.stream_chat` | 是 |
| `memory` | `memory.search` | 否 |
| `memory` | `memory.save` | 否 |
| `memory` | `memory.get` | 否 |
| `memory` | `memory.delete` | 否 |
| `db` | `db.get` | 否 |
| `db` | `db.set` | 否 |
| `db` | `db.delete` | 否 |
| `db` | `db.list` | 否 |
| `db` | `db.get_many` | 否 |
| `db` | `db.set_many` | 否 |
| `db` | `db.watch` | 是 |
| `platform` | `platform.send` | 否 |
| `platform` | `platform.send_image` | 否 |
| `platform` | `platform.send_chain` | 否 |
| `platform` | `platform.get_members` | 否 |
说明:
- `SessionRef` 是结构化发送目标 schema不是 capability。
- `internal.*``handler.*` 命名空间保留给框架内部使用,不属于公开内建 capability 列表。
## 7. 兼容层现状
### 7.1 等价或接近等价的兼容面
以下兼容面当前是实际可运行的,不只是 import stub
- `astrbot_sdk.api.*` 常用导入路径
- `astrbot_sdk.compat`
- `astrbot.api.*` 以及选定的 `astrbot.core.*` facade
- `LegacyContext` / `LegacyStar` / `CommandComponent`
- `filter.command` / `regex` / `permission`
- `event_message_type` / `platform_adapter_type`
- 常用 compat hooks
- `after_message_sent`
- `on_astrbot_loaded`
- `on_platform_loaded`
- `on_decorating_result`
- `on_llm_request`
- `on_llm_response`
- `on_waiting_llm_request`
- `on_using_llm_tool`
- `on_llm_tool_respond`
- `on_plugin_error`
- `on_plugin_loaded`
- `on_plugin_unloaded`
- message components 兼容导出、别名构造和常用工厂
- `session_waiter`
- 旧插件共享单一 `LegacyContext`
### 7.2 降级兼容
这些能力可以运行,但不保证与历史实现完全等价:
- `command_group`:当前会展平成普通命令名,不复刻旧的树状命令帮助与多层执行链。
- legacy JSON-RPC handshake 转 v4 handler 描述时,只能近似恢复旧触发信息,原始 payload 会保留在 metadata 里。
- `astrbot.core.*` 的深层 facade 只覆盖受支持的导入路径,不等于整个旧应用树。
- `tool_loop_agent()` 当前是 compat local tool loop并非完整复刻旧应用内部 agent 体系。
### 7.3 仅导入兼容或明确不支持
以下路径或能力要么只有导入兼容,要么明确不实现旧语义:
- `astrbot.api.agent()`:显式 `NotImplementedError`
- `astrbot.core.provider.provider` 中的 provider 基类与 embeddings/rerank 方法:导入可用,但方法是 stub
- 没有可映射执行链路的旧 `filter.*` helper显式 `NotImplementedError`
兼容原则是“尽量保留可运行的旧插件路径”,不是“重新实现整个旧 AstrBot 应用”。
## 8. 对插件作者的导入建议
### 推荐的新代码
```python
from astrbot_sdk import Star, Context, MessageEvent
from astrbot_sdk.decorators import on_command, on_message, provide_capability
```
### 仍受支持的旧代码
```python
from astrbot_sdk.api.event import AstrMessageEvent
from astrbot_sdk.api.star.context import Context
from astrbot_sdk.api.event.filter import filter
```
### 旧包名 facade
```python
from astrbot.api.star import Star
from astrbot.core.utils.session_waiter import session_waiter
```
只有在需要兼容现有旧插件时才应继续使用这些路径;新插件应直接使用 v4 顶层入口。
## 9. 本地开发与测试
当前仓库已经提供一条受控的本地开发路径:
- CLI
- `astr dev --local` / `astrbot-sdk dev --local`
- `astrbot-sdk init`
- `astrbot-sdk validate`
- `astrbot-sdk build`
- 稳定测试入口:`astrbot_sdk.testing`
`astrbot_sdk.testing` 当前公开的稳定面包括:
- `PluginHarness`
- `LocalRuntimeConfig`
- `MockContext`
- `MockMessageEvent`
- `MockLLMClient`
- `MockPlatformClient`
- `MockPeer`
- `MockCapabilityRouter`
- `InMemoryDB`
- `InMemoryMemory`
- `StdoutPlatformSink`
- `RecordedSend`
设计约束:
- 本地 harness 复用真实的 `load_plugin()``HandlerDispatcher``CapabilityDispatcher``_legacy_runtime.py``_session_waiter.py`
- `dev --local` 使用进程内 mock core而不是重新发明一套并行 runtime
- 同一次 `interactive` 会话会复用同一个 dispatcher / waiter manager / in-memory db / in-memory memory
- `astrbot_sdk.testing` 是插件测试依赖的公开 APIminor 版本内保持兼容稳定
## 10. 测试与维护约定
- 当前主测试目录是 `tests_v4/`,覆盖 protocol、runtime、clients、compat facade、legacy plugin integration、top-level imports 与 integration flows。
- 文档维护规则:
- capability 集合变化时,同时更新本文档与对应测试。
- compat 支持级别变化时,同时更新本文档、`CLAUDE.md` / `AGENTS.md` 备注以及相关契约测试。
- `refactor.md` 不再承载现状;出现冲突时,一律以本文档和代码/测试为准。
## 11. 当前建议的后续演进方向
1. 继续把 runtime 对 compat 的认知收口到 `_legacy_runtime.py`
2. 继续拆薄 `_legacy_api.py`,让 `LegacyContext` 更偏向 facade 和 orchestration。
3. 保持 `src-new/astrbot` 为受控 facade不要把旧应用整棵树重新复制进来。
4. 用契约测试保护 capability 注册表、compat hook 执行和 facade 导入矩阵,避免文档再次漂移。