平台的功能域,每一个都是插件
uuos 用同一套 Product Plugin 机制承载自己:核心能力注册为ai.uuos.core,SaaS 商业化与 IM 消息渠道是第一批官方插件。声明式 manifest、强校验注册、幂等对账——你的下一个功能模块,走同一条路进来。
多租户
套餐、订阅、配额、计量与账单以插件接入:运行前配额拦截,用量自动入账。
多消息渠道
10 个 IM 平台统一接入:平台消息触发工作流运行,结果回到原会话。
自定义插件
与官方插件同一套机制:实现 Plugin 接口、声明 Descriptor、注册即上线。
两层扩展体系,各管一层
「接入一个完整子产品」和「给运行时加一种能力」是两类问题,uuos 不用一个机制硬扛两件事。
产品插件 · Product Plugin
整个功能域一次接入:一级导航、页面、API 路由、权限与初始化,全部由一份 manifest 声明。saas 与im-channel就是这样进来的——插件本体是独立仓库、独立发布的产品。
运行时窄 SPI
LLM 提供商、工具(自定义 HTTP / MCP)、知识库连接器、发布渠道、触发器——每类能力一个窄接口,独立注册表。工作流里用到的能力扩展走这里,不与产品插件混用。
两种运行模式
| 模式 | 面向 | 后端形态 | 前端形态 | 状态 |
|---|---|---|---|---|
| embedded | 官方 / 源码级可信集成 | 同进程挂载,编译期 Go 适配器 | React 页面包直接加载 | 可用 |
| remote | 第三方插件 | 独立服务,M2M 短期令牌 + OpenAPI / 事件 | iframe + 来源校验握手 | 设计中 |
两种模式共用同一份 Descriptor 与同一套注册/对账机制——写一次插件,形态可迁移。
平台侧的四道保障
注册期强校验
插件 ID、路由(method + path)、菜单/页面/API 资源三重唯一性校验,冲突在启动时报错,而不是在生产环境路由打架。
幂等对账与自修复
启动时核对 manifest 与插件目录的 checksum,漂移自动修复;初始化状态逐项落库,随时可查可重放。
失败隔离
单个插件初始化失败只隔离自己:指数退避重试(5s 起、5 分钟封顶),平台其余部分照常服务。
能力驱动的动态菜单
GET /v1/navigation 按登录者的 capability 投影菜单——没有权限的插件入口不会下发到前端,而不是渲染后再隐藏。
发现与运维 API
| GET | /v1/product-plugins | 已注册插件清单与 manifest |
| GET | /v1/navigation | 按 capability 过滤的动态一级导航 |
| GET | /v1/product-plugins/initialization | 各插件初始化状态报告 |
| POST | /v1/product-plugins/reconcile | 显式触发对账(平台管理员) |
插件目录持久化于控制面(g_product_plugins 等表),初始化逐步落库、可审计。
关于机制的常见问题
为什么不用 Go 原生 plugin 动态加载?
动态库无法审计、版本兼容脆弱、崩溃边界不可控。uuos 选择两种受控形态:编译期集成(embedded)与独立服务(remote)——都以声明式 manifest 为准,可校验、可对账、可回滚。
插件出问题会拖垮平台吗?
不会。注册期强校验把冲突挡在启动阶段;运行期初始化失败被隔离并指数退避;manifest 与目录漂移由对账机制自动修复。核心执行引擎不依赖任何插件。
插件的数据存在哪里?
每个插件拥有独立的表命名空间(如 g_saas_*、t_im_channel_*),迁移脚本由插件自己维护;跨产品只存 opaque 外部 ID,不建外键——数据所有权边界清晰,卸载不留悬挂引用。