包内文档:pkg/framework/docs/README.md · pkg/plugins/docs/README.md · 仓库文档索引:docs/README.md
MoonHub 正在实现一个完整的插件架构。目标是让 channels、providers 和 tools 都成为可扩展的插件,保持核心精简。
| 决策项 | 选择 | 说明 |
|---|---|---|
| 加载策略 | 内置插件 + init() 注册 | 编译时加载,通过 init() 函数自动注册 |
| 热更新 | 不需要 | 启动时确定,无需运行时动态加载 |
| 二进制 | 单文件 | 保持单一二进制部署 |
pkg/framework/
├── types.go # 核心接口和元数据定义
├── channel.go # Channel 插件接口
├── provider.go # Provider 插件接口
├── tool.go # Tool 插件接口
├── registry.go # 插件注册系统
├── manager.go # 插件生命周期管理
└── builtin.go # 内置插件导入触发
type PluginType string
const (
TypeChannel PluginType = "channel"
TypeProvider PluginType = "provider"
TypeTool PluginType = "tool"
)
type Metadata struct {
ID string // "moonhub-channel-telegram"
Name string // "Telegram"
Type PluginType
Version string // "1.0.0"
Description string
Priority int // 加载顺序
}
type Plugin interface {
Metadata() Metadata
Init(ctx *RuntimeContext) error
Validate(cfg *config.Config) error
}
type RuntimeContext struct {
Config *config.Config
Bus *bus.MessageBus
Workspace string
MediaStore media.MediaStore
}type Channel interface {
Name() string
Start(ctx interface{}) error
Stop(ctx interface{}) error
Send(ctx interface{}, msg interface{}) error
IsRunning() bool
}
type ChannelPlugin interface {
Plugin
ChannelPrefix() string
CreateChannel(cfg *config.Config, bus *bus.MessageBus) (Channel, error)
IsEnabled(cfg *config.Config) bool
}type ProviderPlugin interface {
Plugin
ProviderName() string
CreateProvider(cfg *config.Config) (providers.LLMProvider, error)
CreateProviderFromModelConfig(modelCfg *config.ModelConfig) (providers.LLMProvider, error)
IsEnabled(cfg *config.Config) bool
SupportsProtocol(protocol string) bool
SupportsModel(modelName string) bool
}type ToolPlugin interface {
Plugin
CreateTools(ctx *RuntimeContext) []tools.Tool
IsCore() bool
}type Registry struct {
mu sync.RWMutex
plugins map[string]Plugin
channels map[string]ChannelPlugin
providers map[string]ProviderPlugin
tools map[string]ToolPlugin
}
func RegisterPlugin(p Plugin)
func GlobalRegistry() *Registry所有 Channel 插件位于 pkg/plugins/channels/<name>/plugin.go:
| 插件 | ID | 状态 |
|---|---|---|
| telegram | moonhub-channel-telegram | 已创建 |
| discord | moonhub-channel-discord | 已创建 |
| slack | moonhub-channel-slack | 已创建 |
| matrix | moonhub-channel-matrix | 已创建 |
| feishu | moonhub-channel-feishu | 已创建 |
| moonhub-channel-qq | 已创建 | |
| dingtalk | moonhub-channel-dingtalk | 已创建 |
| line | moonhub-channel-line | 已创建 |
| onebot | moonhub-channel-onebot | 已创建 |
| wecom | moonhub-channel-wecom | 已创建 |
| wecom_app | moonhub-channel-wecom_app | 已创建 |
| wecom_aibot | moonhub-channel-wecom_aibot | 已创建 |
| pico | moonhub-channel-pico | 已创建 |
| irc | moonhub-channel-irc | 已创建 |
| maixcam | moonhub-channel-maixcam | 已创建 |
| moonhub-channel-whatsapp | 已创建 | |
| whatsapp_native | moonhub-channel-whatsapp_native | 已创建 |
// pkg/plugins/channels/telegram/plugin.go
package telegram
import (
"github.com/RealityLink-Tech/MoonHub/pkg/framework"
"github.com/RealityLink-Tech/MoonHub/pkg/config"
)
func init() {
plugin.RegisterPlugin(&TelegramPlugin{})
}
type TelegramPlugin struct {
ctx *plugin.RuntimeContext
}
func (p *TelegramPlugin) Metadata() plugin.Metadata {
return plugin.Metadata{
ID: "moonhub-channel-telegram",
Name: "Telegram",
Type: plugin.TypeChannel,
Version: "2.0.0",
Description: "Telegram bot channel integration",
Priority: 100,
}
}
func (p *TelegramPlugin) ChannelPrefix() string {
return "telegram"
}
func (p *TelegramPlugin) IsEnabled(cfg *config.Config) bool {
return cfg.Channels.Telegram.Enabled && cfg.Channels.Telegram.Token != ""
}
func (p *TelegramPlugin) CreateChannel(cfg *config.Config, bus *bus.MessageBus) (plugin.Channel, error) {
return channelstelegram.NewTelegramChannel(cfg, bus)
}
func (p *TelegramPlugin) Init(ctx *plugin.RuntimeContext) error {
p.ctx = ctx
return nil
}
func (p *TelegramPlugin) Validate(cfg *config.Config) error {
if cfg.Channels.Telegram.Enabled && cfg.Channels.Telegram.Token == "" {
return fmt.Errorf("telegram token required when enabled")
}
return nil
}原循环: plugin -> plugins/channels/telegram -> channels -> plugin
解决方案: 将插件导入从 pkg/framework/builtin.go 移至 cmd/moonhub/internal/gateway/helpers.go。Gateway 在加载 channels 后导入插件包,确保依赖顺序正确,打破循环。
- 核心插件类型定义
- Channel 插件接口
- Provider 插件接口
- Tool 插件接口
- 插件注册系统
- 插件管理器
- 内置插件索引
- 创建 16 个 Channel 插件包装器
- 修复循环导入问题
- 更新 channel manager 使用插件系统
- 验证构建成功
- 创建 provider 插件目录结构
- 包装每个 provider (openai_compat, anthropic, anthropic_messages, antigravity, claude_cli, codex_cli, github_copilot, openai_oauth)
- 更新 provider factory 使用插件注册 (SetPluginProviderResolver)
- 创建 tool 插件目录结构
- 迁移 web (web_search, web_fetch) 和 message 到插件包
- 更新 tool 注册使用插件管理器 (InitializeToolsOnly, MergeFrom)
-
保留 built-in factory 作为 fallback
-
更新 plugin.LoadBuiltin 注释
-
构建和测试验证
-
删除遗留的 factory 模式代码 (
pkg/channels/manager.go:initChannel,pkg/channels/registry.go.getFactory) 以通过 CI 风格检查 -
文档更新:状态文档更新
pkg/framework/types.go- 核心接口pkg/framework/channel.go- Channel 接口pkg/framework/provider.go- Provider 接口pkg/framework/tool.go- Tool 接口pkg/framework/registry.go- 注册系统pkg/framework/manager.go- 管理器pkg/framework/builtin.go- 内置插件导入pkg/plugins/channels/telegram/plugin.gopkg/plugins/channels/discord/plugin.gopkg/plugins/channels/slack/plugin.gopkg/plugins/channels/matrix/plugin.gopkg/plugins/channels/feishu/plugin.gopkg/plugins/channels/qq/plugin.gopkg/plugins/channels/dingtalk/plugin.gopkg/plugins/channels/line/plugin.gopkg/plugins/channels/onebot/plugin.gopkg/plugins/channels/wecom/plugin.gopkg/plugins/channels/wecom_app/plugin.gopkg/plugins/channels/wecom_aibot/plugin.gopkg/plugins/channels/irc/plugin.gopkg/plugins/channels/maixcam/plugin.gopkg/plugins/channels/whatsapp/plugin.gopkg/plugins/channels/whatsapp_native/plugin.go
pkg/plugins/providers/openai_compat/plugin.gopkg/plugins/providers/openai_oauth/plugin.gopkg/plugins/providers/anthropic/plugin.gopkg/plugins/providers/anthropic_messages/plugin.gopkg/plugins/providers/antigravity/plugin.gopkg/plugins/providers/claude_cli/plugin.gopkg/plugins/providers/codex_cli/plugin.gopkg/plugins/providers/github_copilot/plugin.gopkg/plugins/tools/web/plugin.gopkg/plugins/tools/message/plugin.go
pkg/framework/builtin.go- 移除插件导入,LoadBuiltin 改为 no-oppkg/framework/provider.go- 新增 CreateProviderFromModelConfig, SupportsProtocolpkg/framework/manager.go- 新增 InitializeToolsOnly,initProviders 支持 ErrSkipProviderpkg/providers/factory_provider.go- 新增 SetPluginProviderResolver,CreateProviderFromConfig 优先尝试插件cmd/moonhub/internal/gateway/helpers.go- 添加 channel/provider/tool 插件导入,设置 provider resolver,创建 plugin managerpkg/channels/manager.go- 使用插件系统初始化 channelspkg/agent/loop.go- NewAgentLoopWithPluginTools,registerSharedTools 支持插件工具合并pkg/tools/registry.go- 新增 MergeFrom 方法
与 pkg/learning/docs/ 类似,实现目录旁提供分层说明,便于从代码侧阅读:
pkg/framework/docs/README.md— 插件核心类型与 Registry / Managerpkg/plugins/docs/README.md— 内置插件目录与新增插件步骤
- 构建:
make build- 应该编译无错误 - 测试:
make test- 所有现有测试应该通过 - 功能测试: 启动 gateway 并启用单个 channel
- 验证 channel 通过插件系统初始化
- 验证消息正确流转
┌─────────────────────────────────────────────────────────────┐
│ MoonHub Core │
├─────────────────────────────────────────────────────────────┤
│ cmd/moonhub │
│ │ │
│ ▼ │
│ ┌─────────┐ ┌──────────────┐ ┌────────────────┐ │
│ │ Config │────▶│ Plugin │────▶│ Plugin Manager │ │
│ └─────────┘ │ Registry │ └────────────────┘ │
│ └──────────────┘ │ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Plugins │ │
│ ├─────────────────┬─────────────────┬─────────────────┤ │
│ │ Channel Plugins │ Provider Plugins│ Tool Plugins │ │
│ ├─────────────────┼─────────────────┼─────────────────┤ │
│ │ - telegram │ - openai │ - web │ │
│ │ - discord │ - anthropic │ - filesystem │ │
│ │ - slack │ - gemini │ - cron │ │
│ │ - matrix │ - zhipu │ - memory │ │
│ │ - ... (16) │ - ... │ - ... │ │
│ └─────────────────┴─────────────────┴─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
- 创建插件基础设施 (Phase 1 完成)
- 创建 16 个 Channel 插件包装器 (Phase 2 部分完成)
- 发现循环导入问题,正在解决
- 修复循环导入:将插件导入移至 gateway/helpers.go
- 修复 16 个 channel 插件的导入、构造函数调用和配置传递
- 修复 plugin.Manager 使用 GetPlugins 替代 ListPlugins
- 修复 provider 接口使用 providers.LLMProvider
- Phase 2 完成,构建通过
- Phase 3: Provider 插件迁移完成。新增 CreateProviderFromModelConfig、SupportsProtocol 接口;创建 8 个 provider 插件;通过 SetPluginProviderResolver 注入,factory 优先尝试插件后 fallback 到 built-in
- Phase 4: Tool 插件迁移完成。创建 web、message 插件;Gateway 调用 InitializeToolsOnly;AgentLoop 通过 MergeFrom 合并插件工具
- Phase 5: 文档更新,构建验证通过
- 文档与仓库对齐:插件实现目录为
pkg/plugins/...(非pkg/frameworks/...);修正helpers.go中误写的pkg/frameworks/providers|tools导入,否则无法编译 CreateProviderFromConfig:仅在 resolver 返回ErrSkipProvider时回退 built-in;其它错误应向上返回(避免吞掉插件真实错误)