Plugin 运行时

wesgine 的 JSON-RPC 子进程 Plugin 运行时,通过 CellSpec 声明式装配外部可执行文件为 Cell 工具。


Plugin 概念

Plugin Runtime(adapter/plugin/)是一套完整的 JSON-RPC 子进程生命周期管理系统:

组件职责
protocol.goJSON-RPC 协议定义
runtime.go子进程运行时管理
bridge.go工具桥接(Plugin ↔ Cell ToolRegistry)
manager.goPlugin 集合管理

Plugin 通过子进程方式运行,与引擎进程隔离,通过 JSON-RPC 通信。


CellSpec.Plugins 声明

每个 Plugin 项是一个容器目录,子目录含 manifest.yaml + 可执行文件:

spec := wesgine.CellSpec{
    Plugins: []string{
        "/opt/plugins/data-analysis",
        "/opt/plugins/code-interpreter",
    },
}

目录结构:

/opt/plugins/data-analysis/
├── manifest.yaml        # 声明:名称、版本、工具列表
└── bin/
    └── data-analysis    # 可执行文件(JSON-RPC server)

manifest.yaml 示例

name: data-analysis
version: "1.0.0"
description: "数据分析工具集"
command: "./bin/data-analysis"
tools:
  - name: analyze_csv
    description: "分析 CSV 数据文件"
    parameters:
      type: object
      properties:
        file_path:
          type: string
          description: "CSV 文件路径"
        query:
          type: string
          description: "分析查询"

Plugin 生命周期

Boot 阶段

Cell.Start()
  → Manager.Boot()
    → 扫描 CellSpec.Plugins 目录
    → 解析每个子目录的 manifest.yaml
    → 启动子进程(JSON-RPC server)
    → 桥接 Tools 到 Cell ToolRegistry

运行时

Model 调用 Plugin 工具
  → Cell ToolRegistry 查找
  → Bridge 转发 JSON-RPC 请求
  → 子进程执行
  → Bridge 转发 JSON-RPC 响应
  → 返回给 Model

Drain 阶段

Cell.Stop()
  → Manager.Drain()
    → 向每个子进程发送 Shutdown RPC
    → 等待优雅退出
    → 超时后强制终止

Plugin 配额

CellQuotas.MaxPlugins 的行为与其他外设配额不同:

特性说明
判定时机Boot 期截断
运行期写门没有(不是写点拒绝)
超限行为截断目录列表,多余的 Plugin 不启动
// Boot 时
plugins := scanPluginDirs(spec.Plugins)
if len(plugins) > quotas.MaxPlugins {
    plugins = plugins[:quotas.MaxPlugins]
    // 日志警告截断
}

这与 Cron/Email/MCP/Channel 的"写点拒绝"模式不同——Plugin 没有运行期增删 API。


PluginPolicy — 签名验证

Plugin 可通过 ed25519 签名验证确保安全:

spec := wesgine.CellSpec{
    PluginPolicy: &wesgine.PluginPolicy{
        RequireSignature: true,
        TrustedKeys: []string{
            "ed25519:abc123...",
        },
    },
}
策略行为
RequireSignature: false接受所有 Plugin
RequireSignature: true只接受签名匹配 TrustedKeys 的 Plugin

Plugin 与 Tool 关系

Plugin 暴露的工具自动注册到 Cell ToolRegistry:

Cell ToolRegistry
├── 引擎内置工具 (read, write, exec, grep, ...)
├── SDK 注册工具 (cell.Tools().Register)
├── MCP 代理工具 (cell.MCPs().Upsert)
├── Plugin 工具 (adapter/plugin/)  ← Plugin 工具在这里
├── Adapter 派生工具 (kb/email/cron/...)
└── Per-run 临时工具 (RunParams.Tools)

Cell 之间物理不可见——Cell A 的 Plugin 工具不会出现在 Cell B 的工具列表中。


JSON-RPC 协议

Plugin 子进程实现标准 JSON-RPC 2.0 协议:

请求

{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "analyze_csv",
        "arguments": {
            "file_path": "/data/report.csv",
            "query": "计算每月销售总额"
        }
    }
}

响应

{
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "content": [
            {
                "type": "text",
                "text": "分析结果:1月销售额 100万..."
            }
        ]
    }
}

生命周期方法

方法说明
initialize初始化 Plugin
tools/list列出工具
tools/call调用工具
shutdown优雅关闭

Plugin vs MCP

Plugin 与 MCP 都是扩展 Cell 工具集的方式,但定位不同:

特性PluginMCP
进程管理引擎管理子进程生命周期外部独立进程
传输JSON-RPC over stdiostdio / SSE / HTTP
配置CellSpec.Pluginscell.MCPs().Upsert
运行时增删❌ Boot 期固定✅ 运行时动态
签名验证✅ PluginPolicy❌
适用场景紧密集成的扩展松耦合的外部服务

开发 Plugin

1. 创建目录

mkdir -p my-plugin/bin

2. 编写 manifest.yaml

name: my-plugin
version: "1.0.0"
description: "我的自定义插件"
command: "./bin/my-plugin"
tools:
  - name: my_tool
    description: "工具描述"
    parameters:
      type: object
      properties:
        input:
          type: string

3. 实现 JSON-RPC Server

使用任何语言实现 stdin/stdout JSON-RPC 2.0 服务。

4. 配置到 CellSpec

spec.Plugins = []string{"/path/to/my-plugin"}

最佳实践

  1. Plugin 用于紧密集成:需要引擎管理生命周期的场景
  2. MCP 用于松耦合:外部服务或运行时动态增删
  3. 注意 MaxPlugins:超限的 Plugin 会被截断不启动
  4. 签名验证:生产环境建议启用 PluginPolicy
  5. 优雅关闭:实现 shutdown 方法,释放资源