feat: 集成 AstrBot SDK vendored runtime 与 bridge 运行时#6810
Open
whatevertogo wants to merge 480 commits intoAstrBotDevs:masterfrom
Open
feat: 集成 AstrBot SDK vendored runtime 与 bridge 运行时#6810whatevertogo wants to merge 480 commits intoAstrBotDevs:masterfrom
whatevertogo wants to merge 480 commits intoAstrBotDevs:masterfrom
Conversation
- Added new utility functions for memory management in _memory_utils.py. - Refactored memory capability mixin methods to utilize the new utility functions for better readability and maintainability. - Updated PROJECT_ARCHITECTURE.md to reflect changes in documentation and structure.
208bc59 Merge pull request AstrBotDevs#30 from united-pooh/refactor/unify-legacy-injected-params d86534a docs: add TODO for documentation content in _command_model.py 090724a refactor(injection): centralize legacy injected parameter filtering git-subtree-dir: astrbot-sdk git-subtree-split: 208bc59
…TECTURE.md Co-authored-by: whatevertogo <149563971+whatevertogo@users.noreply.github.com>
docs: fix PROJECT_ARCHITECTURE.md inaccuracies (path, Python version, client API table)
Refactor memory utility functions and enhance memory capability mixin
ad5e8d1 Merge pull request AstrBotDevs#37 from united-pooh/sdk/whatevertogo 5751701 Merge pull request AstrBotDevs#38 from united-pooh/copilot/sub-pr-37 e21acba docs: fix path, Python version, and client API table in PROJECT_ARCHITECTURE.md ee67cab Initial plan 7d92157 Refactor memory utility functions and enhance memory capability mixin git-subtree-dir: astrbot-sdk git-subtree-split: ad5e8d1
…rbot-sdk into sdk/whatevertogo
Sdk/whatevertogo
5003da5 Merge pull request AstrBotDevs#40 from united-pooh/sdk/whatevertogo b5084c4 Merge branch 'sdk/whatevertogo' of https://github.com/united-pooh/astrbot-sdk into sdk/whatevertogo 7559edf docs: fix TODO comment formatting in _command_model.py git-subtree-dir: astrbot-sdk git-subtree-split: 5003da5
fix(testing): use public session waiter probe in PluginHarness
…BotDevs#33) * fix(testing): route session waiter followups through dispatcher * fix(testing): preserve waiter context and completion state * fix(runtime): preserve session waiter plugin identity * fix(runtime): scope session waiters by plugin * fix(testing): isolate waiter replacement and followup drains * fix(runtime): normalize waiter routing inputs
- Updated MEMORY_SEARCH_INPUT_SCHEMA to include `namespace` and `include_descendants`. - Modified MEMORY_SEARCH_OUTPUT_SCHEMA to allow nullable `namespace`. - Added `namespace` to MEMORY_GET_INPUT_SCHEMA, MEMORY_DELETE_INPUT_SCHEMA, MEMORY_SAVE_WITH_TTL_INPUT_SCHEMA, MEMORY_GET_MANY_INPUT_SCHEMA, and MEMORY_DELETE_MANY_INPUT_SCHEMA. - Enhanced MEMORY_STATS_INPUT_SCHEMA to support `namespace` and `include_descendants`. - Updated MEMORY_GET_OUTPUT_SCHEMA and MEMORY_STATS_OUTPUT_SCHEMA to include `namespace` and `namespace_count`. - Introduced `_memory_backends` in CapabilityRouterHost and CapabilityRouterBridgeBase for better memory management. - Refactored MemoryCapabilityMixin to utilize memory backends for plugin-specific memory operations. - Improved memory search functionality to respect namespaces and include descendants based on input parameters. - Added tests to validate memory operations across different namespaces and ensure persistence across restarts. - Implemented error handling in the handler dispatcher to manage exceptions gracefully.
…rbot-sdk into sdk/whatevertogo
Contributor
Author
|
当前 v4 的实际实现,我的分歧点不是“JSON-RPC 能不能做底座”,而是它并不会明显减少 AstrBot 现在这层协议最核心的工作量。最重要的是,除了"标准",理解难度反而上升了而不是下降了 目前 v4 线协议本身就是一套按消息类型分派的扁平模型。消息入口在 _PROTOCOL_MESSAGE_MODELS = {
"initialize": InitializeMessage,
"result": ResultMessage,
"invoke": InvokeMessage,
"event": EventMessage,
"cancel": CancelMessage,
}
收到 payload 后,运行时是按 type 直接选择对应模型并做 Pydantic 校验,而不是先走一层通用 RPC 信封,再按 method 去拆 params:
def parse_message(payload):
message_type = payload.get("type")
model = _PROTOCOL_MESSAGE_MODELS.get(str(message_type))
return model.model_validate(payload)
更关键的是,当前协议真正复杂的部分不在“消息外壳”,而在这些已经被显式建模的上层语义:
initialize 负责初始化握手,同时交换 protocol_version、supported_protocol_versions、handlers、provided_capabilities、metadata`
invoke 负责能力调用
event 负责流式生命周期
result 负责非流式结果
cancel 负责取消传播
流式调用尤其说明问题。它不是普通 request/response,而是显式的 started -> delta -> completed / failed 生命周期,并且每个 phase 的字段组合都有模型约束。也就是说,这套协议语义本身就是 AstrBot 自己定义和维护的,不是 transport 层自动提供的能力。
如果换成 JSON-RPC 做底座,这些语义层仍然要我们自己重新定义和维护。最终得到的仍然会是 AstrBot 自己的协议语义,只是外层从现在的 type + 扁平字段 变成了 method + params 的嵌套形式。初始化、流式生命周期、取消、能力声明、版本协商这些核心复杂度并不会因此消失。
从实现角度看,当前模型是“按 type 直接反序列化为强类型消息”;如果换成 JSON-RPC,就会变成“先按 method 分发,再解析不同的 params 子 schema”。这不是不能做,而是协议复杂度没有被消掉,只是换了一种组织方式。
所以我想的是:JSON-RPC 能统一外层 request / response / notification / batch 的格式,但不会替 AstrBot 定义和简化现在这层真正关键的协议语义。它解决的是外壳统一,不是协议设计本身。
另外,当前插件开发者实际接触的是 SDK 的装饰器和 Context API,而不是 wire format。协议层改造成 JSON-RPC,对插件开发体验没有直接收益。除非近期目标已经明确是开放跨语言客户端/插件实现,或者要强依赖现成 JSON-RPC 工具链,否则这个改造的收益有限。即使未来要做多语言支持,initialize、streaming、cancel、capability discovery 这些语义层仍然要完整实现,工作量不会因为换成 JSON-RPC 而显著下降。
代码依据在这里:
- [messages.py](AstrBot/astrbot-sdk/src/astrbot_sdk/protocol/messages.py#L57)
- [messages.py](AstrBot/astrbot-sdk/src/astrbot_sdk/protocol/messages.py#L223)
- [peer.py](AstrBot/astrbot-sdk/src/astrbot_sdk/runtime/peer.py#L314)
- [peer.py](AstrBot/astrbot-sdk/src/astrbot_sdk/runtime/peer.py#L631)
- [decorators.py](AstrBot/astrbot-sdk/src/astrbot_sdk/decorators.py#L1103)
- [context.py](AstrBot/astrbot-sdk/src/astrbot_sdk/context.py#L230)
下次你觉得不好的地方麻烦给一下代码位置,我认为"标准"两个字不够充分,别人看得懂更重要,除非粉毛说了算,请你根据实际代码考虑周全狠狠的喷我谢谢
Current v4 protocol
Worker (Plugin) Supervisor (Core)
| |
| InitializeMessage |
| { |
| protocol_version, |
| peer, |
| handlers, |
| provided_capabilities, |
| metadata[supported_protocol_versions,...] |
| } |
|--------------------------------------------->|
| |
| ResultMessage(kind="initialize_result") |
| { success, output{peer, protocol_version, |
| capabilities, metadata} } |
|<---------------------------------------------|
| |
| InvokeMessage |
| { capability, input, stream=false } |
|<---------------------------------------------|
| |
| ResultMessage { success, output } |
|--------------------------------------------->|
| |
| InvokeMessage |
| { capability, input, stream=true } |
|<---------------------------------------------|
| EventMessage { phase="started" } |
|--------------------------------------------->|
| EventMessage { phase="delta", data=... } |
|--------------------------------------------->|
| EventMessage { phase="completed", output } |
|--------------------------------------------->|
| |
| CancelMessage { id, reason } |
|<---------------------------------------------|
JSON-RPC 2.0:
Worker (Plugin) Supervisor (Core)
| |
| {"jsonrpc":"2.0","id":"1", |
| "method":"initialize", |
| "params":{protocol_version, peer, handlers, |
| provided_capabilities, metadata}} |
|--------------------------------------------->|
| |
| {"jsonrpc":"2.0","id":"1","result":{...}} |
|<---------------------------------------------|
| |
| {"jsonrpc":"2.0","id":"2", |
| "method":"capability/invoke", |
| "params":{capability,input,stream:false}} |
|<---------------------------------------------|
| {"jsonrpc":"2.0","id":"2","result":{...}} |
|--------------------------------------------->|
| |
| {"jsonrpc":"2.0","id":"3", |
| "method":"capability/invoke", |
| "params":{capability,input,stream:true}} |
|<---------------------------------------------|
| {"jsonrpc":"2.0","method":"event/started", |
| "params":{id:"3"}} |
|--------------------------------------------->|
| {"jsonrpc":"2.0","method":"event/delta", |
| "params":{id:"3",data:...}} |
|--------------------------------------------->|
| {"jsonrpc":"2.0","method":"event/completed", |
| "params":{id:"3",output:...}} |
|--------------------------------------------->|
| {"jsonrpc":"2.0","method":"cancel", |
| "params":{id:"3",reason:"user_cancelled"}} |
|<---------------------------------------------|
关键区别一眼就看出来了:
信息量完全一样,JSON-RPC 版本多了 "jsonrpc": "2.0" 和 "method" 的重复,但该定义的语义一个没少
流式是最大的问题——JSON-RPC 的 notification 没有 id 字段,所以得在 params 里手动塞 request_id 来关联请求,而 EventMessage 天然带 id
JSON-RPC 的 response 只有 result 或 error 二选一, ResultMessage 有 kind 字段区分初始化结果、调用结果等,JSON-RPC 做不到,得塞进 result 里 |
…ventConverter - Replace dual truth model (legacy AstrMessageEvent + SDK payload) with single canonical overlay state; both sides now project from overlay - Delete EventConverter; split into InboundEventSnapshot (frozen dataclass), materialize path, and standalone sanitize helpers in event_payload.py - Promote sdk_message_type() to core/message/message_types.py so core layer no longer depends on bridge internals - Bridge AstrMessageEvent result APIs to overlay via runtime binding (no reverse import, no circular dependency) - Add result_stopped control bit separate from result_payload; stop forces should_call_llm=False, continue does not restore it - Layer serialization: json round-trip at SDK worker boundary, copy.deepcopy for internal overlay reads - Enhance sanitize_sdk_extra_value with datetime/bytes/UUID support
62c890abe chore: refresh vendor snapshot [skip ci] d77cbc524 Merge pull request AstrBotDevs#95 from united-pooh:dev dece75adb refactor: 使用 TypeVar 优化装饰器类型注解,提高代码可读性和灵活性 REVERT: 2f1402fd4 chore: refresh vendor snapshot [skip ci] git-subtree-dir: astrbot-sdk git-subtree-split: 62c890abe70b0d03d3afd7924ab8f79c5d356a2b
629bf307c chore: refresh vendor snapshot [skip ci] 970ca3367 docs: 在 messages.py 添加 Batch Invoke 协议 TODO 设计备忘 REVERT: 62c890abe chore: refresh vendor snapshot [skip ci] REVERT: d77cbc524 Merge pull request AstrBotDevs#95 from united-pooh:dev REVERT: dab2c7706 Merge pull request AstrBotDevs#94 from united-pooh:dev REVERT: a9a953aef Merge pull request AstrBotDevs#93 from united-pooh:dev git-subtree-dir: astrbot-sdk git-subtree-split: 629bf307cd4c2fead29f02d5b4523a9d5072fa5b
f23721f24 chore: refresh vendor snapshot [skip ci] 1805f6d5c Merge pull request AstrBotDevs#97 from united-pooh:dev ae6b2a1f9 fix: 优化 PluginHarness 中的帮助文本发送逻辑,简化条件判断 1d4cd212d fix: 将 AstrBotError.protocol_error 替换为 ValueError,增强错误处理一致性 feat: 新增 settings.local.json 文件以配置权限 feat: 添加命令归属名称提取功能,优化命令匹配逻辑 033c88025 fix: 将 ValueError 替换为 AstrBotError.protocol_error,增强错误处理一致性 REVERT: 629bf307c chore: refresh vendor snapshot [skip ci] git-subtree-dir: astrbot-sdk git-subtree-split: f23721f24cfd87183f7c4b70eb617347a0d251dc
955470aee chore: refresh vendor snapshot [skip ci] 6da1db9a7 Merge pull request AstrBotDevs#98 from united-pooh:dev 4e6d9590d fix: 更新 set_extra 和 get_extra 方法的文档,增加关于数据序列化的说明 fix: 修改 HandlerDispatcher 中的事件类型判断,支持 streaming_delta REVERT: f23721f24 chore: refresh vendor snapshot [skip ci] git-subtree-dir: astrbot-sdk git-subtree-split: 955470aeef93f2fd8c6354c001777c46e9d98835
…d runtime_store - Added SdkRegistryManager for managing SDK plugins, including listing, registering, and unregistering skills and HTTP APIs. - Introduced SdkRequestRuntime to handle request overlays, dispatch tokens, and event results. - Created runtime_store to maintain the state of SDK plugins, request contexts, and HTTP routes. - Implemented data classes for structured management of plugin records, request overlays, and dynamic command routes. - Enhanced error handling and logging for better debugging and operational visibility.
b817715b9 chore: refresh vendor snapshot [skip ci] 231bd5da2 Merge pull request AstrBotDevs#99 from united-pooh:dev 2637cbbdb feat: 添加 register_file_url 方法以支持文件 URL 注册,并更新相关文档 bb4a57aec fix: 更新 MessageEvent 类中的方法文档,增加中文说明以提升可读性 REVERT: 955470aee chore: refresh vendor snapshot [skip ci] git-subtree-dir: astrbot-sdk git-subtree-split: b817715b9179b359ea1a1ea5a7d1bf198f0be798
378257db7 chore: refresh vendor snapshot [skip ci] f8dab1de5 Merge pull request AstrBotDevs#100 from united-pooh:dev 5492735cd feat: 添加 repo 字段到插件元数据,增强插件信息的完整性 REVERT: b817715b9 chore: refresh vendor snapshot [skip ci] git-subtree-dir: astrbot-sdk git-subtree-split: 378257db7da5e75fb99449ab1266f33e101a6922
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
动机 / Motivation
这个 PR 的目标,不是简单"把一个新 SDK 目录拷进主仓库",而是把 AstrBot SDK v4 的运行时能力 以一种可以在主仓库长期维护、逐步落地、并且不破坏旧插件生态的方式接入 AstrBot。
要解决的核心问题有 4 类:
插件运行边界不清晰
旧插件路径与主进程深度耦合,插件直接共享宿主进程内对象,插件异常、运行时污染、依赖冲突都更容易放大到整个系统。
插件开发接口过于依赖宿主内部实现
旧接口里很多能力是"直接拿宿主对象调用"的风格,虽然能用,但边界不稳定、类型约束弱、难以做清晰的能力声明,也不利于长期演进。
主仓库和 SDK 仓库的职责边界需要明确
独立的
astrbot-sdk仓库需要作为 SDK 的 source of truth 持续演进;AstrBot 主仓库需要的是一个可消费、可同步、可测试的 vendored 运行时快照,而不是把整个 SDK 源仓库原样搬进来。迁移必须渐进,不能推翻现有插件体系
AstrBot 现有大量逻辑仍然基于 legacy Star 插件体系。SDK 的接入必须保证:
因此,这个 PR 的真实定位是:
sdk_bridge宿主桥接层变更规模 / Change Statistics
按模块统计:
astrbot-sdk/astrbot/core/sdk_bridge/tests/astrbot/core/astrbot/dashboard/astrbot/core/pipeline/astrbot/core/platform/sources/scripts/这次 PR 实际做了什么
1. 在主仓库中引入
astrbot-sdk/的 vendored snapshot这个 PR 在主仓库下新增了
astrbot-sdk/目录,但这里必须明确:astrbot-sdk/是 给 AstrBot 主仓库消费的 vendored subtree snapshotastrbot-sdk源仓库的完整镜像这里保留的主要内容包括:
src/astrbot_sdk/:SDK 运行时包主体pyproject.toml:让主仓库可以按本地 path dependency 使用 SDKREADME.md/VENDORED.md/LICENSE:说明 vendoring 约定与快照边界templates/project_notes/AGENTS.md/CLAUDE.md:因为astr init仍然会生成这些模板testing.py、_testing_support.py、_internal/testing_support.py等最小测试辅助:因为主仓库与模板仍然依赖这些约定同时,很多内容是 刻意不 vendoring 进主仓库的:
这样设计的原因是:
主仓库要消费稳定快照,不是承载 SDK 全量研发现场
AstrBot 主仓库关注的是"可运行、可集成、可回归"的 SDK 版本,而不是承接 SDK 仓库的所有开发噪音。
source of truth 必须单一
SDK 行为、文档、测试体系的第一来源仍然应该是独立
astrbot-sdk仓库。主仓库里的astrbot-sdk/只是同步产物,不应该再被误解为主要开发位置。便于后续同步与审查
scripts/sync-sdk.ps1/scripts/sync-sdk.sh明确要求:这保证了变更路径清晰,减少"主仓库偷偷改 SDK vendored 文件"的维护风险。
2. 把 SDK 的运行时模型正式接入 AstrBot 主仓库
主仓库引入的不只是一个 Python package,而是一整套 SDK 运行时入口:
protocol/:协议消息模型与描述符runtime/:loader / peer / transport / supervisor / handler_dispatcher / capability_router 等运行时骨架clients/:ctx.llm、ctx.memory、ctx.db、ctx.platform等高层客户端decorators.py:声明式 handler/capability 注册入口(20+ 装饰器,涵盖 on_command、on_message、on_event、on_schedule、on_provider_change、validate_config、http_api、mcp_server、register_skill、background_task 等)context.py/events.py/message/*:插件作者直接面对的主要 APISDK 本身的设计是比较清晰的一条链路:
也就是说,SDK 不是让插件"直接摸宿主对象",而是让插件通过 显式能力调用 与宿主通信。
这套设计的核心思想有三点:
协议优先(protocol-first)
先定义清楚消息模型、调用边界、错误结构,再谈功能。
能力优先(capability-first)
宿主暴露什么能力,要通过 capability 名称和 payload schema 明确表达,而不是让插件到处拿宿主内部对象直接调用。
声明式优先(descriptor-first)
handler、trigger、capability 都先变成可序列化描述符,再进入运行时分发。这让跨进程、热重载、命令面板、平台命令注册这些事情都更可控。
3. SDK CLI 工具链
SDK 包含完整的命令行工具(
astr命令),为插件开发者提供端到端的工作流支持:astr init--agents claude,codex自动生成 AI 编程助手 skill 模板astr validateastr buildastr dev --local--watch文件变更热重载、--interactive交互调试astr runastr worker4. 插件持久化记忆系统
SDK 内置
PluginMemoryBackend,提供插件级别的持久化键值记忆后端:save_with_ttl()支持带过期时间的记忆条目include_descendants递归查询5. SDK 客户端面(Capability Surface)
SDK 通过
Context暴露 15+ 类型化客户端,每个客户端背后是 capability proxy:LLMClientchat,chat_raw,stream_chatMemoryClientsearch,save,get,delete,list_keysDBClientget,set,delete,list,get_many,set_many,watchPlatformClientsend,send_stream,get_member_info,get_group_member_listProviderClientchat_for_provider,get_llm_profileProviderManagerClientlist_providers,get_providerPersonaManagerClientlist,create,update,deleteConversationManagerClientlist,create,update,delete,set_activeKnowledgeBaseManagerClientlist,create,update,delete,upload,retrieveMessageHistoryManagerClientlist,get_pageMCPManagerClientlist_servers,register_global_server,enable_server,session_callPermissionClientcheckPermissionManagerClientlist,add,removeSkillClientregister_skill,unregister_skillHTTPClientregister_api,unregister_apiMetadataClientget_plugin_config,get_plugin_metadataFileServiceClientregister_file,handle_fileSessionServiceManager6. SDK 测试基础设施
SDK 提供完善的测试辅助(
astrbot_sdk.testing),支持插件作者编写单元测试和集成测试:MockContext:完整 mock 的 Context,内置 InMemoryDB、InMemoryMemory、MockCapabilityRouterMockMessageEvent:可配置 user_id / group_id / platform / session 的 mock 事件MockLLMClient:支持预设响应(mock_response/mock_stream_response)MockPlatformClient:记录发送历史,支持assert_sent断言PluginHarness:从 plugin.yaml 加载插件并执行完整 dispatch 流程StdoutPlatformSink:将平台输出捕获到内存或 stdoutSDK 为什么这样设计
1. 为什么要做 Worker / Supervisor 运行时边界
SDK 的核心架构是把插件执行从主进程逻辑里抽出来,形成更明确的执行边界:
Supervisor管理 Worker 生命周期WorkerSession代表一个被宿主管理的插件运行单元Peer + Transport负责协议收发HandlerDispatcher/CapabilityDispatcher负责把协议调用分发到真实 Python 对象这样做的原因是:
隔离故障域
插件执行失败、卡死、局部污染时,不必直接把主进程也拖下水。
隔离依赖与环境
SDK 运行时天然更适合做环境分组、虚拟环境规划和后续多运行边界扩展。
让宿主和插件关系从"对象耦合"变成"协议耦合"
这会让系统更难写一点,但长期维护成本更低,因为边界明确以后,兼容性和演进策略才可控。
2. 为什么是 Capability Router,而不是直接暴露宿主 API
Context下的llm/memory/db/platform/provider/permission/kb/mcp等客户端,本质上都是 capability 的 typed facade。这样做的好处是:
调用边界稳定
插件只需要知道 capability 名称和参数,不需要知道宿主内部对象结构。
更适合做 schema 验证与错误治理
Capability 本身就是"一个命名的、结构化的远程调用点",天然适合做 JSON Schema、错误码、重试语义和 docs_url。
便于宿主分层实现
主仓库里真正干活的代码仍然可以分散在 ProviderManager、PlatformManager、ConversationManager、DB、KB 等各处,但对 SDK 来说只看见统一 capability surface。
3. 为什么 handler / trigger 要走描述符,而不是靠反射硬猜
HandlerDescriptor、CommandTrigger、MessageTrigger、EventTrigger、ScheduleTrigger这一层是 SDK 的一个重要设计点。它解决的是:
如果没有这一层,宿主要做的事情会很混乱:
描述符层的意义,本质上是把"运行时行为"先降维成"静态声明",这样宿主侧才有机会在真正执行前做规划、注册、展示和校验。
为什么 AstrBot 主仓库还需要一层
sdk_bridge如果 SDK 已经有 protocol、peer、capability_router,为什么主仓库还要额外实现
astrbot/core/sdk_bridge/?因为 SDK 是通用插件运行时,AstrBot Core 则是一个已经存在多年、已有完整 legacy 体系、已有平台适配器、已有消息流水线和 Dashboard 的具体宿主。两者之间不可能无缝直接拼上,必须有一层宿主适配桥。
sdk_bridge做的事情,本质上是把:做一次明确的对接。
可以把它理解为:
sdk_bridge决定"在 AstrBot 这个具体宿主里,这些事情到底接到哪里去、怎么兼容现有流水线"sdk_bridge的设计与职责1.
SdkPluginBridge:宿主侧总入口(3,793 行)astrbot/core/sdk_bridge/plugin_bridge.py是这一层的核心。它承担了几类关键职责:
插件发现与运行管理
WorkerSessionhandler 匹配与分发
请求级状态管理
_RequestOverlayState跟踪单次消息分发的 overlay 状态sent_message/stop/call_llm宿主侧面板与管理接口
宿主事件桥接
decorating_result、agent_begin、agent_done等这样设计的原因是:
SdkPluginBridge里,能够避免这种适配逻辑散落到全项目2.
CoreCapabilityBridge:把 AstrBot Core 能力整理成 SDK capability surfaceastrbot/core/sdk_bridge/capability_bridge.py和其 mixins 负责把 AstrBot Core 已有能力重新整理成 SDK 世界可理解的 capability 集合。这里不是重新实现业务,而是把宿主现有能力做一层 协议化、能力化、结构化暴露。
当前拆分出的能力组包括(对应
sdk_bridge/capabilities/下的 mixin 文件):_host.pybasic.pyllm.pyprovider.pyplatform.pysession.pypersona.pyconversation.pyknowledgebase.py/kb.pymessage_history.pypermission.pymcp.pyskill.pysystem.py这么拆的原因是:
按领域拆分,比按技术层拆更稳定
对插件作者来说,最稳定的心智模型不是"这个函数在宿主的哪个 manager 里",而是"这是 provider 能力、platform 能力、kb 能力还是 session 能力"。
便于逐步扩展 capability 面
后续某个领域能力增长时,只需要扩对应 mixin,而不是把一个巨型 bridge 类继续堆大。
更利于测试
主仓库当前新增的大量
tests/test_sdk/unit/测试,本质上就是在回归这些 capability contract 是否稳定。3.
TriggerConverter:trigger/filter/permission 的宿主侧匹配TriggerConverter的职责看似简单,但实际上很关键:HandlerDescriptor中声明的 trigger/filter/permission 规则映射到 AstrBot 当前消息事件模型上normalize_message_type()统一规范化(group/private/other)它存在的原因是:
SDK 描述符与 AstrBot 当前事件模型不是同一套类型系统
需要一层稳定的 host-side matcher。
命令与消息匹配逻辑必须宿主可控
这样 Dashboard 命令展示、平台原生命令注册、运行时触发行为才会保持一致。
本地 filter ref 必须 fail-open 处理
某些 filter 指向插件进程内 callable,宿主不能直接执行,所以桥接层要明确知道哪些规则能在 host 侧判断,哪些不能。
4.
event_payload.py:payload 归一化层AstrBot Core 事件对象和 SDK 侧
MessageEvent不是同一个对象体系。event_payload.py的设计目标是:AstrMessageEvent的关键信息抽成InboundEventSnapshot为什么这层必要:
5.
_RequestOverlayState:请求覆盖层这是这次 bridge 设计里最容易被忽略、但实际上非常关键的部分。
AstrBot 原有流水线不是为"SDK 插件在另一套 runtime 里执行,并返回 stop/call_llm/result 覆盖行为"而设计的。为了在不重写整个流水线的前提下把 SDK 接进去,必须引入一层 请求级 overlay state。
它解决的核心问题是:
SDK handler 返回的结果要能影响宿主后续流水线
比如:
这些影响必须是"本次请求级"的,而不是全局状态
否则不同消息请求会互相污染。
宿主现有 stage 必须能读取"effective result"而不是只读
event.get_result()所以你会看到 pipeline 的多个 stage 被改造成:
换句话说,overlay 设计是在尽量少改动旧流水线的前提下,把 SDK 的"跨进程 handler 决策"投影回宿主当前请求上下文里。
这是一个很典型的增量架构设计:
这个 bridge 是如何嵌入 AstrBot 现有生命周期的
1. Core Lifecycle 接入
astrbot/core/core_lifecycle.py里,这个 PR 增加了:SdkPluginBridgestar_contextplatform_managerstart()它这样做的原因是:
2. Pipeline 接入(9 文件,243 行变更)
改动点主要在:
astrbot/core/pipeline/process_stage/stage.pyastrbot/core/pipeline/respond/stage.pyastrbot/core/pipeline/result_decorate/stage.py这些改动的核心不是"重写 pipeline",而是让 pipeline 在关键节点具备以下能力:
call_llm的最终决策不能再只看 legacy event 状态,还要看 SDK overlay 决策decorating_result这类宿主事件也需要反向派发给 SDK这里的设计重点是 共存而非替换。
也就是说,当前实现不是把旧 pipeline 砍掉换成 SDK pipeline,而是让:
这也是为什么这个 PR 的迁移风险可控得多。
3. Dashboard 接入(10 文件,547 行变更)
Dashboard 相关改动主要在:
astrbot/dashboard/routes/command.pyastrbot/dashboard/routes/tools.pyastrbot/dashboard/routes/plugin.pyastrbot/dashboard/routes/config.pyastrbot/dashboard/routes/skills.pyastrbot/dashboard/routes/session_management.pyastrbot/dashboard/routes/stat.pyastrbot/dashboard/server.py这里的设计策略也很明确:
SDK 命令要可见,但不一定要全量可编辑
当前
command.py明确把 SDK commands 标记为只读。这是合理的,因为 SDK command 的 source of truth 在插件描述符,不在 Dashboard 本地手工改表单。SDK tools 可以纳入统一工具面板
只要 tool 是桥接层能管理的,就应该与 legacy tools 一样出现在工具视图里,并支持激活/停用。
配置 schema 通过 bridge 获取,而不是复制一份配置逻辑
这样 Dashboard 能显示 SDK 插件的配置,又不会把 plugin config contract 分裂成两套。
这种设计说明 Dashboard 的目标不是"自己管理一套 SDK 元数据",而是作为 bridge 的展示和操作前端。
4. 平台原生命令接入
在 Telegram / Discord 适配器里,这个 PR 增加了从 bridge 读取 native command candidates 的逻辑。
这背后的设计原因是:
这再次体现了"声明式描述符"的价值:
同一份元数据既能驱动运行时触发,也能驱动 Dashboard 展示,还能驱动平台命令注册。
除了 bridge,本 PR 还做了哪些必要配套
这个 PR 里还有一些配套改动,单看可能不显眼,但它们其实都是 SDK integration 能落地的必要条件。
1. 配置、技能、工具、MCP、权限等周边能力统一纳入 capability 面
CoreCapabilityBridge不只是桥一下llm.chat这么简单,它把:都整理进了 capability world。
这说明这个 PR 做的不是"给 SDK 开两个测试用接口",而是认真地把 AstrBot Core 能力重新投影成 SDK 的宿主契约。
2. Star / Skill 管理器增强
star_manager.py(+269 行):增强插件发现、加载、重载逻辑,支持 SDK 插件与 legacy Star 共存skill_manager.py(+435 行):增强技能安装、注册、同步逻辑,支持 SDK 技能纳入统一管理3. 新增工具函数
media_utils.py(94 行):媒体处理工具storage_cleaner.py(271 行):存储清理工具4. 同步脚本与 vendoring 约束
scripts/sync-sdk.ps1/scripts/sync-sdk.sh的价值不只是方便同步,而是把维护约束落到了脚本里:这相当于把"vendored SDK 不该坏掉"的约束做成了工具化的 guardrail。
5. SDK 事件分发钩子
新增
agent_begin和agent_done系统事件分发支持,允许 SDK 插件监听 Agent 执行生命周期。测试覆盖
本 PR 新增 17,926 行测试代码,分布在以下区域:
tests/test_sdk/unit/tests/test_sdk/tests/test_db_backward_compat.pytests/test_dashboard.pytests/test_plugin_manager.pytests/test_platform_register.pytests/test_computer_config.pytests/unit/test_astr_agent_hooks.pytests/unit/test_astr_agent_tool_exec.pytests/unit/test_tool_conflict_resolution.pytests/unit/test_tool_google_schema.pytests/unit/test_faiss_vec_db.py兼容性与迁移结论
Breaking Changes
预期兼容策略是:
换句话说,这个 PR 更像是一次 宿主能力扩展与架构铺路,而不是一次激进替换。
从迁移角度看,它建立的是一个可逐步迁移的双轨体系:
验证方式 / Verification
Checklist / 检查清单
astrbot-sdk/在主仓库中是 vendored snapshot,而不是完整 SDK 源仓库sdk_bridge在 AstrBot 宿主中的职责、边界和存在必要性