Skip to content

feat: 集成 AstrBot SDK vendored runtime 与 bridge 运行时#6810

Open
whatevertogo wants to merge 497 commits intoAstrBotDevs:masterfrom
whatevertogo:feat/sdk-integration
Open

feat: 集成 AstrBot SDK vendored runtime 与 bridge 运行时#6810
whatevertogo wants to merge 497 commits intoAstrBotDevs:masterfrom
whatevertogo:feat/sdk-integration

Conversation

@whatevertogo
Copy link
Copy Markdown
Contributor

@whatevertogo whatevertogo commented Mar 22, 2026

动机 / Motivation

这个 PR 的目标,不是简单"把一个新 SDK 目录拷进主仓库",而是把 AstrBot SDK v4 的运行时能力 以一种可以在主仓库长期维护、逐步落地、并且不破坏旧插件生态的方式接入 AstrBot。
与其说他是sdk,不如说他是为了新旧插件都兼容的新插件扩展平台

要解决的核心问题有 4 类:

  1. 插件运行边界不清晰
    旧插件路径与主进程深度耦合,插件直接共享宿主进程内对象,插件异常、运行时污染、依赖冲突都更容易放大到整个系统。

  2. 插件开发接口过于依赖宿主内部实现
    旧接口里很多能力是"直接拿宿主对象调用"的风格,虽然能用,但边界不稳定、类型约束弱、难以做清晰的能力声明,也不利于长期演进。

  3. 主仓库和 SDK 仓库的职责边界需要明确
    独立的 astrbot-sdk 仓库需要作为 SDK 的 source of truth 持续演进;AstrBot 主仓库需要的是一个可消费、可同步、可测试的 vendored 运行时快照,而不是把整个 SDK 源仓库原样搬进来。

  4. 迁移必须渐进,不能推翻现有插件体系
    AstrBot 现有大量逻辑仍然基于 legacy Star 插件体系。SDK 的接入必须保证:

    • 旧插件继续按旧路径工作
    • 新插件可以走 SDK 路径
    • 两者在同一条主消息流水线、Dashboard、平台命令注册体系里共存

因此,这个 PR 的真实定位是:

  • 在 AstrBot 主仓库中 vendoring 一份 SDK 运行时快照
  • 在主仓库中新增一层 sdk_bridge 宿主桥接层
  • 让 SDK 插件能够通过协议和 capability 调用 AstrBot Core
  • 同时保持 legacy 路径继续可用,形成一个 增量迁移架构

变更规模 / Change Statistics

指标 数值
变更文件 241
新增行数 65,371
删除行数 437
提交数 459

按模块统计:

模块 文件数 新增行数 说明
astrbot-sdk/ 107 ~32,377 SDK vendored snapshot(含 runtime、clients、CLI、testing、memory backend 等)
astrbot/core/sdk_bridge/ 21 10,526 宿主桥接层(plugin_bridge、capability_bridge + 13 个 mixin、event_payload、trigger_converter)
tests/ 73 ~17,926 SDK 集成测试、单元测试、兼容性测试
astrbot/core/ 28 ~2,324 star_manager、skill_manager、provider、utils 等核心模块增强
astrbot/dashboard/ 10 547 命令/工具/插件/配置/技能路由扩展
astrbot/core/pipeline/ 9 243 流水线 stage 接入 SDK handler 分发
astrbot/core/platform/sources/ 12 ~2,315 新增 weixin_oc 适配器 + telegram/wecom 增强
scripts/ 3 368 SDK 同步脚本(sync-sdk.sh / .ps1)+ CI 测试脚本

这次 PR 实际做了什么

1. 在主仓库中引入 astrbot-sdk/ 的 vendored snapshot

这个 PR 在主仓库下新增了 astrbot-sdk/ 目录,但这里必须明确:

  • 这里的 astrbot-sdk/给 AstrBot 主仓库消费的 vendored subtree snapshot
  • 它不是独立 astrbot-sdk 源仓库的完整镜像
  • 它保留的是 AstrBot 运行时真正需要的 SDK 包布局和最小配套文件

这里保留的主要内容包括:

  • src/astrbot_sdk/:SDK 运行时包主体
  • pyproject.toml:让主仓库可以按本地 path dependency 使用 SDK
  • README.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 源仓库完整 docs
  • 大量仅用于 SDK 仓库自身演进的测试与开发元数据

这样设计的原因是:

  1. 主仓库要消费稳定快照,不是承载 SDK 全量研发现场
    AstrBot 主仓库关注的是"可运行、可集成、可回归"的 SDK 版本,而不是承接 SDK 仓库的所有开发噪音。

  2. source of truth 必须单一
    SDK 行为、文档、测试体系的第一来源仍然应该是独立 astrbot-sdk 仓库。主仓库里的 astrbot-sdk/ 只是同步产物,不应该再被误解为主要开发位置。

  3. 便于后续同步与审查
    scripts/sync-sdk.ps1 / scripts/sync-sdk.sh 明确要求:

    • 先更新独立 SDK 仓库
    • 再通过 subtree 同步 vendor branch 到主仓库
      这保证了变更路径清晰,减少"主仓库偷偷改 SDK vendored 文件"的维护风险。

2. 把 SDK 的运行时模型正式接入 AstrBot 主仓库

主仓库引入的不只是一个 Python package,而是一整套 SDK 运行时入口:

  • protocol/:协议消息模型与描述符
  • runtime/:loader / peer / transport / supervisor / handler_dispatcher / capability_router 等运行时骨架
  • clients/ctx.llmctx.memoryctx.dbctx.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/*:插件作者直接面对的主要 API

SDK 本身的设计是比较清晰的一条链路:

插件作者 API
  -> Context / Decorators / MessageEvent
  -> 各种 typed clients
  -> CapabilityProxy / Peer
  -> Protocol messages
  -> 宿主侧 capability router

也就是说,SDK 不是让插件"直接摸宿主对象",而是让插件通过 显式能力调用 与宿主通信。

这套设计的核心思想有三点:

  1. 协议优先(protocol-first)
    先定义清楚消息模型、调用边界、错误结构,再谈功能。

  2. 能力优先(capability-first)
    宿主暴露什么能力,要通过 capability 名称和 payload schema 明确表达,而不是让插件到处拿宿主内部对象直接调用。

  3. 声明式优先(descriptor-first)
    handler、trigger、capability 都先变成可序列化描述符,再进入运行时分发。这让跨进程、热重载、命令面板、平台命令注册这些事情都更可控。

3. SDK CLI 工具链

SDK 包含完整的命令行工具(astr 命令),为插件开发者提供端到端的工作流支持:

子命令 功能
astr init 创建新插件骨架,生成 plugin.yaml、main.py、README.md、测试模板;支持 --agents claude,codex 自动生成 AI 编程助手 skill 模板
astr validate 校验 plugin.yaml 清单、导入路径、handler 发现是否正常
astr build 将插件打包为 .zip 发布包,自动排除 .git、pycache 等无关文件
astr dev --local 本地开发模式,支持 --watch 文件变更热重载、--interactive 交互调试
astr run 启动插件 supervisor 进程,通过 stdio 与 AstrBot 核心通信
astr worker 内部命令,由 supervisor 调用以启动单个插件 worker 进程

4. 插件持久化记忆系统

SDK 内置 PluginMemoryBackend,提供插件级别的持久化键值记忆后端:

  • 存储引擎:SQLite(WAL 模式),自动管理过期条目清理
  • 全文搜索:FTS5 虚拟表,支持 Unicode tokenizer
  • 向量搜索:可选 FAISS 集成(IndexFlatIP + IndexIDMap2),无 FAISS 时退化为精确余弦相似度
  • 混合搜索:keyword + vector 混合评分(0.65 × vector + 0.35 × keyword + 0.05 bonus)
  • TTL 支持save_with_ttl() 支持带过期时间的记忆条目
  • 命名空间:层级命名空间隔离,支持 include_descendants 递归查询
  • 嵌入管理:自动检测缺失嵌入、按需批量计算、脏标记驱动的增量索引构建

5. SDK 客户端面(Capability Surface)

SDK 通过 Context 暴露 15+ 类型化客户端,每个客户端背后是 capability proxy:

客户端 能力域 关键方法
LLMClient LLM 调用 chat, chat_raw, stream_chat
MemoryClient 记忆管理 search, save, get, delete, list_keys
DBClient 键值存储 get, set, delete, list, get_many, set_many, watch
PlatformClient 平台消息 send, send_stream, get_member_info, get_group_member_list
ProviderClient Provider 代理 chat_for_provider, get_llm_profile
ProviderManagerClient Provider 管理 list_providers, get_provider
PersonaManagerClient 人格管理 list, create, update, delete
ConversationManagerClient 对话管理 list, create, update, delete, set_active
KnowledgeBaseManagerClient 知识库管理 list, create, update, delete, upload, retrieve
MessageHistoryManagerClient 消息历史 list, get_page
MCPManagerClient MCP 管理 list_servers, register_global_server, enable_server, session_call
PermissionClient 权限校验 check
PermissionManagerClient 权限管理 list, add, remove
SkillClient 技能注册 register_skill, unregister_skill
HTTPClient Web API register_api, unregister_api
MetadataClient 插件元数据 get_plugin_config, get_plugin_metadata
FileServiceClient 文件服务 register_file, handle_file
SessionServiceManager 会话服务 会话级 MCP/Provider 管理

6. SDK 测试基础设施

SDK 提供完善的测试辅助(astrbot_sdk.testing),支持插件作者编写单元测试和集成测试:

  • MockContext:完整 mock 的 Context,内置 InMemoryDB、InMemoryMemory、MockCapabilityRouter
  • MockMessageEvent:可配置 user_id / group_id / platform / session 的 mock 事件
  • MockLLMClient:支持预设响应(mock_response / mock_stream_response
  • MockPlatformClient:记录发送历史,支持 assert_sent 断言
  • PluginHarness:从 plugin.yaml 加载插件并执行完整 dispatch 流程
  • StdoutPlatformSink:将平台输出捕获到内存或 stdout

SDK 为什么这样设计

1. 为什么要做 Worker / Supervisor 运行时边界

SDK 的核心架构是把插件执行从主进程逻辑里抽出来,形成更明确的执行边界:

  • Supervisor 管理 Worker 生命周期
  • WorkerSession 代表一个被宿主管理的插件运行单元
  • Peer + Transport 负责协议收发
  • HandlerDispatcher / CapabilityDispatcher 负责把协议调用分发到真实 Python 对象

这样做的原因是:

  1. 隔离故障域
    插件执行失败、卡死、局部污染时,不必直接把主进程也拖下水。

  2. 隔离依赖与环境
    SDK 运行时天然更适合做环境分组、虚拟环境规划和后续多运行边界扩展。

  3. 让宿主和插件关系从"对象耦合"变成"协议耦合"
    这会让系统更难写一点,但长期维护成本更低,因为边界明确以后,兼容性和演进策略才可控。

2. 为什么是 Capability Router,而不是直接暴露宿主 API

Context 下的 llm / memory / db / platform / provider / permission / kb / mcp 等客户端,本质上都是 capability 的 typed facade。

这样做的好处是:

  1. 调用边界稳定
    插件只需要知道 capability 名称和参数,不需要知道宿主内部对象结构。

  2. 更适合做 schema 验证与错误治理
    Capability 本身就是"一个命名的、结构化的远程调用点",天然适合做 JSON Schema、错误码、重试语义和 docs_url。

  3. 便于宿主分层实现
    主仓库里真正干活的代码仍然可以分散在 ProviderManager、PlatformManager、ConversationManager、DB、KB 等各处,但对 SDK 来说只看见统一 capability surface。

3. 为什么 handler / trigger 要走描述符,而不是靠反射硬猜

HandlerDescriptorCommandTriggerMessageTriggerEventTriggerScheduleTrigger 这一层是 SDK 的一个重要设计点。

它解决的是:

  • 插件注册了什么 handler
  • 这个 handler 由什么触发
  • 它属于命令、消息、系统事件还是定时任务
  • 它需要什么权限、优先级、过滤器、参数模型

如果没有这一层,宿主要做的事情会很混乱:

  • Dashboard 很难正确列命令
  • 平台原生命令注册很难同步
  • 热重载后很难稳定恢复状态
  • 跨进程后宿主几乎拿不到完整元信息

描述符层的意义,本质上是把"运行时行为"先降维成"静态声明",这样宿主侧才有机会在真正执行前做规划、注册、展示和校验。


为什么 AstrBot 主仓库还需要一层 sdk_bridge

如果 SDK 已经有 protocol、peer、capability_router,为什么主仓库还要额外实现 astrbot/core/sdk_bridge/

因为 SDK 是通用插件运行时,AstrBot Core 则是一个已经存在多年、已有完整 legacy 体系、已有平台适配器、已有消息流水线和 Dashboard 的具体宿主。两者之间不可能无缝直接拼上,必须有一层宿主适配桥。

sdk_bridge 做的事情,本质上是把:

  • AstrBot Core 的内部对象与流程
  • SDK runtime 所要求的协议与能力边界

做一次明确的对接。

可以把它理解为:

  • SDK 决定"插件应该怎么描述自己、怎么调用宿主、怎么收消息、怎么返回结果"
  • sdk_bridge 决定"在 AstrBot 这个具体宿主里,这些事情到底接到哪里去、怎么兼容现有流水线"

sdk_bridge 的设计与职责

1. SdkPluginBridge:宿主侧总入口(3,793 行)

astrbot/core/sdk_bridge/plugin_bridge.py 是这一层的核心。

它承担了几类关键职责:

  1. 插件发现与运行管理

    • 调用 SDK runtime loader 发现插件
    • 启动/维护 WorkerSession
    • 处理 reload、turn on/off、失败重试、运行状态切换
  2. handler 匹配与分发

    • 收集 SDK 插件暴露出来的 handler 描述符
    • 在宿主消息到来时做 trigger matching
    • 把命中的 handler 调到对应 Worker 执行
  3. 请求级状态管理

    • 通过 _RequestOverlayState 跟踪单次消息分发的 overlay 状态
    • 保存 SDK handler 返回的 sent_message / stop / call_llm
    • 管理 SDK-local extras、effective result、handler whitelist 等请求级数据
  4. 宿主侧面板与管理接口

    • 给 Dashboard 提供命令、工具、插件元信息、配置 schema
    • 给平台适配器提供 native command candidates
    • 接 SDK HTTP API 路由分发
  5. 宿主事件桥接

    • 分发 message event
    • 分发 system event,比如 decorating_resultagent_beginagent_done
    • 处理 event/result payload 与宿主消息模型之间的转换

这样设计的原因是:

  • 宿主只需要一个明确入口来管理所有 SDK 插件
  • SDK runtime 是通用的,但 AstrBot 的 pipeline / dashboard / platform command surface 是宿主特定的
  • 把这些 glue code 集中在 SdkPluginBridge 里,能够避免这种适配逻辑散落到全项目

2. CoreCapabilityBridge:把 AstrBot Core 能力整理成 SDK capability surface

astrbot/core/sdk_bridge/capability_bridge.py 和其 mixins 负责把 AstrBot Core 已有能力重新整理成 SDK 世界可理解的 capability 集合。

这里不是重新实现业务,而是把宿主现有能力做一层 协议化、能力化、结构化暴露

当前拆分出的能力组包括(对应 sdk_bridge/capabilities/ 下的 mixin 文件):

Mixin 职责
_host.py 宿主基础信息与插件运行环境
basic.py 基础 CRUD 与状态查询
llm.py LLM 聊天、流式、结构化调用
provider.py Provider 管理(1,340 行,最大的 mixin)
platform.py 平台消息发送与成员管理
session.py 会话级状态与服务管理
persona.py 人格管理
conversation.py 对话管理
knowledgebase.py / kb.py 知识库管理
message_history.py 消息历史查询
permission.py 权限校验与管理
mcp.py MCP 服务器注册与调用
skill.py 技能注册与同步
system.py 系统级文件、路径、token、日志等能力

这么拆的原因是:

  1. 按领域拆分,比按技术层拆更稳定
    对插件作者来说,最稳定的心智模型不是"这个函数在宿主的哪个 manager 里",而是"这是 provider 能力、platform 能力、kb 能力还是 session 能力"。

  2. 便于逐步扩展 capability 面
    后续某个领域能力增长时,只需要扩对应 mixin,而不是把一个巨型 bridge 类继续堆大。

  3. 更利于测试
    主仓库当前新增的大量 tests/test_sdk/unit/ 测试,本质上就是在回归这些 capability contract 是否稳定。

3. TriggerConverter:trigger/filter/permission 的宿主侧匹配

TriggerConverter 的职责看似简单,但实际上很关键:

  • HandlerDescriptor 中声明的 trigger/filter/permission 规则映射到 AstrBot 当前消息事件模型上
  • 支持命令、关键词、正则、平台过滤、消息类型过滤、权限要求
  • 为 handler 构造参数注入前的 args
  • 消息类型通过 normalize_message_type() 统一规范化(group/private/other)

它存在的原因是:

  1. SDK 描述符与 AstrBot 当前事件模型不是同一套类型系统
    需要一层稳定的 host-side matcher。

  2. 命令与消息匹配逻辑必须宿主可控
    这样 Dashboard 命令展示、平台原生命令注册、运行时触发行为才会保持一致。

  3. 本地 filter ref 必须 fail-open 处理
    某些 filter 指向插件进程内 callable,宿主不能直接执行,所以桥接层要明确知道哪些规则能在 host 侧判断,哪些不能。

4. event_payload.py:payload 归一化层

AstrBot Core 事件对象和 SDK 侧 MessageEvent 不是同一个对象体系。

event_payload.py 的设计目标是:

  • 把宿主 AstrMessageEvent 的关键信息抽成 InboundEventSnapshot
  • 把消息链、sender、session、message_type、admin、wake 状态等信息稳定序列化
  • 管理 host extras、sdk local extras、raw payload updates
  • 对 extras 做 JSON-safe 清洗,避免任意 Python 对象透传进协议层

为什么这层必要:

  1. 跨进程必须传 payload,而不是传 Python 对象引用
  2. 宿主 extras 往往带各种复杂对象,直接透传会破坏协议稳定性
  3. 消息回放、事件重建、测试回归都需要稳定 payload 形态

5. _RequestOverlayState:请求覆盖层

这是这次 bridge 设计里最容易被忽略、但实际上非常关键的部分。

AstrBot 原有流水线不是为"SDK 插件在另一套 runtime 里执行,并返回 stop/call_llm/result 覆盖行为"而设计的。为了在不重写整个流水线的前提下把 SDK 接进去,必须引入一层 请求级 overlay state

它解决的核心问题是:

  1. SDK handler 返回的结果要能影响宿主后续流水线
    比如:

    • 这个 handler 已经发消息了
    • 这个 handler 要求 stop
    • 这个 handler 仍然希望宿主继续 call LLM
    • 这个 handler 改写了最终 result
  2. 这些影响必须是"本次请求级"的,而不是全局状态
    否则不同消息请求会互相污染。

  3. 宿主现有 stage 必须能读取"effective result"而不是只读 event.get_result()
    所以你会看到 pipeline 的多个 stage 被改造成:

    • 优先读 bridge 的 effective result
    • 在 SDK dispatch 后决定是否继续 call LLM
    • 在 result decorate / respond 阶段继续尊重 SDK 结果

换句话说,overlay 设计是在尽量少改动旧流水线的前提下,把 SDK 的"跨进程 handler 决策"投影回宿主当前请求上下文里。

这是一个很典型的增量架构设计:

  • 不硬推翻旧流水线
  • 不把 SDK 行为偷偷塞进全局变量
  • 而是在请求粒度建立一个明确的覆盖层

这个 bridge 是如何嵌入 AstrBot 现有生命周期的

1. Core Lifecycle 接入

astrbot/core/core_lifecycle.py 里,这个 PR 增加了:

  • 初始化 SdkPluginBridge
  • 把 bridge 注入 star_context
  • 把 bridge 暴露给 platform_manager
  • 在核心启动阶段 start()

这样做的原因是:

  • SDK 插件也是插件系统的一部分,应该跟核心生命周期同进同退
  • 平台管理器、Dashboard、pipeline 都会用到 bridge
  • 放在 lifecycle 初始化阶段最符合宿主组件装配方式

2. Pipeline 接入(9 文件,243 行变更)

改动点主要在:

  • astrbot/core/pipeline/process_stage/stage.py
  • astrbot/core/pipeline/respond/stage.py
  • astrbot/core/pipeline/result_decorate/stage.py

这些改动的核心不是"重写 pipeline",而是让 pipeline 在关键节点具备以下能力:

  1. 在 legacy handlers 执行后,继续调度 SDK handlers
  2. 如果 SDK handler 已经发送消息或终止传播,宿主要知道
  3. 如果 SDK handler 改写了 result,后续 stage 应该读取 effective result
  4. call_llm 的最终决策不能再只看 legacy event 状态,还要看 SDK overlay 决策
  5. decorating_result 这类宿主事件也需要反向派发给 SDK

这里的设计重点是 共存而非替换

也就是说,当前实现不是把旧 pipeline 砍掉换成 SDK pipeline,而是让:

  • legacy handler 路径继续跑
  • SDK 路径在合适阶段接入
  • 最终结果由 bridge 的请求级状态协调

这也是为什么这个 PR 的迁移风险可控得多。

3. Dashboard 接入(10 文件,547 行变更)

Dashboard 相关改动主要在:

  • astrbot/dashboard/routes/command.py
  • astrbot/dashboard/routes/tools.py
  • astrbot/dashboard/routes/plugin.py
  • astrbot/dashboard/routes/config.py
  • astrbot/dashboard/routes/skills.py
  • astrbot/dashboard/routes/session_management.py
  • astrbot/dashboard/routes/stat.py
  • astrbot/dashboard/server.py

这里的设计策略也很明确:

  1. SDK 命令要可见,但不一定要全量可编辑
    当前 command.py 明确把 SDK commands 标记为只读。这是合理的,因为 SDK command 的 source of truth 在插件描述符,不在 Dashboard 本地手工改表单。

  2. SDK tools 可以纳入统一工具面板
    只要 tool 是桥接层能管理的,就应该与 legacy tools 一样出现在工具视图里,并支持激活/停用。

  3. 配置 schema 通过 bridge 获取,而不是复制一份配置逻辑
    这样 Dashboard 能显示 SDK 插件的配置,又不会把 plugin config contract 分裂成两套。

这种设计说明 Dashboard 的目标不是"自己管理一套 SDK 元数据",而是作为 bridge 的展示和操作前端。

4. 平台原生命令接入

在 Telegram / Discord 适配器里,这个 PR 增加了从 bridge 读取 native command candidates 的逻辑。

这背后的设计原因是:

  • SDK plugin 的 command metadata 已经存在于 descriptor 中
  • 平台原生命令注册需要的也是结构化命令元信息
  • 最合理的路径就是由 bridge 汇总 descriptor,再交给具体平台适配器消费

这再次体现了"声明式描述符"的价值:

同一份元数据既能驱动运行时触发,也能驱动 Dashboard 展示,还能驱动平台命令注册。


除了 bridge,本 PR 还做了哪些必要配套

这个 PR 里还有一些配套改动,单看可能不显眼,但它们其实都是 SDK integration 能落地的必要条件。

1. 配置、技能、工具、MCP、权限等周边能力统一纳入 capability 面

CoreCapabilityBridge 不只是桥一下 llm.chat 这么简单,它把:

  • provider 查询与管理
  • platform 发送与管理
  • persona / conversation / kb 管理
  • session 级开关
  • skill 注册与同步
  • MCP 本地 / session / global 能力
  • permission 校验
  • system 级文件、路径、token 等能力

都整理进了 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 的价值不只是方便同步,而是把维护约束落到了脚本里:

  • 要求 worktree clean
  • 要求远端 snapshot 仍然满足本地 path dependency 所需布局
  • 要求 subtree 同步后关键路径仍然存在

这相当于把"vendored SDK 不该坏掉"的约束做成了工具化的 guardrail。

5. SDK 事件分发钩子

新增 agent_beginagent_done 系统事件分发支持,允许 SDK 插件监听 Agent 执行生命周期。


测试覆盖

本 PR 新增 17,926 行测试代码,分布在以下区域:

测试目录/文件 行数 覆盖范围
tests/test_sdk/unit/ ~6,000+ capability bridge 各 mixin 的单元回归
tests/test_sdk/ ~11,000+ SDK 集成测试(runtime、peer、capability dispatch、worker)
tests/test_db_backward_compat.py - 旧数据库后端兼容性回归
tests/test_dashboard.py - Dashboard 路由扩展回归
tests/test_plugin_manager.py - 插件管理器增强回归
tests/test_platform_register.py - 平台注册增强回归
tests/test_computer_config.py - Computer Use 配置回归
tests/unit/test_astr_agent_hooks.py 158 SDK 事件钩子测试
tests/unit/test_astr_agent_tool_exec.py 49 Agent 工具执行测试
tests/unit/test_tool_conflict_resolution.py 233 工具冲突解决测试
tests/unit/test_tool_google_schema.py 77 Google Schema 工具测试
tests/unit/test_faiss_vec_db.py 20 FAISS 向量数据库测试

兼容性与迁移结论

Breaking Changes

  • 这个 PR 不以破坏 legacy Star 插件兼容性为目标

预期兼容策略是:

  • legacy Star 插件继续走现有宿主逻辑
  • SDK 插件走新的 runtime + bridge 逻辑
  • 两者共用同一个 AstrBot Core 生命周期与主消息流水线

换句话说,这个 PR 更像是一次 宿主能力扩展与架构铺路,而不是一次激进替换。

从迁移角度看,它建立的是一个可逐步迁移的双轨体系:

  • 老插件不必立刻重写
  • 新插件可以开始按 SDK v4 的方式开发
  • 宿主逐步把新能力纳入 capability surface

验证方式 / Verification

# AstrBot 主仓库侧 SDK 集成与兼容性测试
uv run pytest tests/test_sdk/ -q
uv run pytest tests/test_dashboard.py tests/test_db_backward_compat.py tests/test_computer_config.py -q
uv run pytest tests/test_plugin_manager.py tests/test_platform_register.py -q

# 可选:独立 astrbot-sdk 源仓库侧回归
cd ../astrbot-sdk
uv run pytest tests/test_runtime_peer.py tests/test_mcp_runtime.py tests/test_sync_vendor_script.py -q

Checklist / 检查清单

  • 已将 PR 描述改为基于当前仓库真实结构的中文版本
  • 已明确 astrbot-sdk/ 在主仓库中是 vendored snapshot,而不是完整 SDK 源仓库
  • 已解释 SDK 采用 protocol-first / capability-first / descriptor-first 的设计原因
  • 已解释 sdk_bridge 在 AstrBot 宿主中的职责、边界和存在必要性
  • 已说明当前集成策略是与 legacy Star 共存,而不是直接替换旧体系
  • 已补充变更规模统计(241 文件、65,371 行新增、459 commits)
  • 已补充 SDK CLI 工具链说明(init/validate/build/dev/run/worker)
  • 已补充插件持久化记忆系统说明(SQLite + FTS5 + FAISS)
  • 已补充 SDK 客户端面详细列表(15+ 类型化客户端)
  • 已补充测试覆盖范围说明(17,926 行测试代码)
  • 已补充新平台适配器(weixin_oc)、新 Provider(MIMO API)、管理器增强等配套变更

Li-shi-ling and others added 30 commits March 20, 2026 10:23
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.
- Implemented `validate_plugin_id` to ensure safe plugin identifiers.
- Added `resolve_plugin_data_dir` to resolve plugin data directories securely.
- Updated memory and system capabilities to utilize new plugin ID validation.
- Refactored session waiter management to simplify plugin ID handling.
- Enhanced tests for plugin ID validation and data directory resolution.
feat(memory): 增强内存功能并修复测试与注入问题
…tion-review

fix(runtime): resolve issue AstrBotDevs#24 review regressions and add coverage
- Moved message result and session classes to internal modules while preserving legacy import paths for compatibility.
- Updated imports across the SDK to reflect the new internal structure.
- Enhanced session waiter management to support multiple plugins and improve error handling.
- Added tests to ensure LLM tool registration and session waiter functionality align with dispatcher expectations.
- Cleaned up code and improved documentation for clarity and maintainability.
# Conflicts:
#	src/astrbot_sdk/context.py
#	src/astrbot_sdk/runtime/handler_dispatcher.py
#	src/astrbot_sdk/session_waiter.py
#	src/astrbot_sdk/testing.py
refactor(sdk): reorganize internals and harden runtime regressions
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
feat: 更新认证中间件以支持 SDK 插件的自定义认证流程
test: 增加测试以验证插件重载操作的串行化
test: 添加测试以确保无效处理程序类型在插件加载时失败
9724f6230 chore: refresh vendor snapshot [skip ci]
87dd4d31f Merge pull request AstrBotDevs#103 from united-pooh:dev
816732c52 Merge branch 'dev' of https://github.com/united-pooh/astrbot-sdk into dev
740f497fb feat: 引入线程安全机制,优化插件加载和组件实例化过程
1c2c451b9 Merge pull request AstrBotDevs#102 from united-pooh/docs/72
dad29d656 docs: 翻译预期外的英文注释
0688d335f Merge pull request AstrBotDevs#101 from united-pooh/fix-96-init-gitignore-template
b7b3e6bc5 feat: generate plugin gitignore during init
REVERT: 378257db7 chore: refresh vendor snapshot [skip ci]

git-subtree-dir: astrbot-sdk
git-subtree-split: 9724f6230147d6101cc4c6493d3d6b7b8e1d4f33
56943300b chore: refresh vendor snapshot [skip ci]
215e06572 Merge pull request AstrBotDevs#104 from united-pooh:dev
ea10d593d feat: 增强插件导入机制,支持命名空间和动态导入
REVERT: 9724f6230 chore: refresh vendor snapshot [skip ci]

git-subtree-dir: astrbot-sdk
git-subtree-split: 56943300b29038e57ab859d19d900e2e967a3a8a
0a9c86345 chore: refresh vendor snapshot [skip ci]
b5d9b934b Merge pull request AstrBotDevs#105 from united-pooh:dev
c07f04e63 feat: 更新多个客户端和模块,增强类型注解和文档说明
REVERT: 56943300b chore: refresh vendor snapshot [skip ci]

git-subtree-dir: astrbot-sdk
git-subtree-split: 0a9c86345ea2192154580d0ef054b72b99892b9e
- Added validation to ensure HTTP routes and handler capabilities belong to the current plugin namespace in the SDK.
- Updated tests to reflect changes in API registration routes, ensuring they include the plugin ID as a prefix.
- Introduced new error handling for invalid route and capability registrations.
- Refactored existing tests to accommodate the new route structure and added tests for new validation logic.
- Improved the structure of SDK record creation for better readability and maintainability.
SDK 装饰器层(decorators.py)为所有装饰器增加 callable 检查和参数归一化/校验,
防止插件作者传入非法类型后在运行时才崩溃。conversation.py 修复 ConversationState
反序列化兼容。events.py 新增 has_admin_permission() 别名。

runtime/loader.py 引入 MetaPathFinder 替代纯 builtin __import__ hook,
解决插件 import alias 泄漏到 sys.modules 的问题;新增 _restore_plugin_import_hook()
用于测试 teardown。runtime/peer.py 防止 stop() 并发重入、入站消息超限拒绝(8MB),
_handle_raw_message 不再向上抛异常。runtime/transport.py 所有读循环改为帧级容错:
单帧 ValueError/UnicodeDecodeError 只跳过该帧,不中断整个连接。runtime/supervisor.py
为 worker 初始化增加 60s 超时。

core/command_compatibility.py 将 build_cross_system_conflicts 从 O(n*m) 暴力比较
优化为前缀索引查找,大幅减少命令冲突检测耗时。

core/platform/astr_message_event.py 新增语义清晰的 LLM/发送状态 API
(set_default_llm_blocked/should_call_default_llm/mark_send_operation 等),
旧 call_llm/_has_send_oper 字段保留但标记为内部实现。

core/sdk_bridge/request_runtime.py 对所有 overlay/context 字典操作加
threading.RLock,消除并发调度时的竞态条件;close_request_overlay 拆为锁内原子移除
+ 锁外 event 回写两阶段。plugin_bridge.py 新增 _snapshot_records 系列方法统一
遍历入口、Windows 平台 MCP 进程终止改用 taskkill、调度 handler 后 finally 关闭 overlay
防泄漏。dispatch_engine.py 全面使用新的 event API 替代直接写字段。

runtime_store.py 新增 mutation_lock 和 snapshot 方法。tests 覆盖 dispatch engine、
loader 隔离、transport 帧容错、bridge runtime capabilities、MCP/provider tool、
native command、vnext author experience 等场景。
新增 _internal/sdk_logger.py 作为 SDK 唯一日志出口,通过 logger.patch()
注入 plugin_tag、short_levelname、版本号等上下文字段,替换 12 个模块中
原有的 from loguru import logger 和 loader.py 里的 stdlib logging。

- decorator_lifecycle/cli/context/peer/supervisor/transport/worker/session_waiter/star: 改为从 sdk_logger 导入
- handler_dispatcher: 含方法内延迟导入也一并迁移
- loader: 移除 _LOGGER (logging.getLogger),全部改用 loguru + {} 格式串
- test_sdk_plugin_config_bridge: caplog 改为 monkeypatch 适配 loguru
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature:plugin The bug / feature is about AstrBot plugin system. size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants