适配器开发指南

wesgine 的适配器(Adapter)体系为 Cell 提供外设能力扩展——浏览器自动化、桌面操作、语音转写、Plugin 子进程等。所有适配器都是 per-Cell 的,通过 CellSpec 声明式装配。


适配器架构

Cell (数据面)
├── adapter/channel/       # IM 渠道(企业微信/飞书/钉钉/...)
├── adapter/email/         # 邮件收发
├── adapter/mcp/           # MCP Server 代理
├── adapter/cron/          # 定时任务
├── adapter/knowledge/     # 知识库数据源
├── adapter/browser/       # 内置浏览器安全阀(AlwaysOn)
├── adapter/headless-browser/ # 无头浏览器(可选)
├── adapter/playwright/    # 用户浏览器自动化(可选)
├── adapter/desktop/       # 桌面自动化 Device Agent(可选)
├── adapter/stt/           # 语音转写 Whisper(可选)
├── adapter/plugin/        # JSON-RPC 子进程 Plugin Runtime
└── adapter/runtime/       # 外设运行时共享装配

装配原则

CellSpec 外设装配

spec := wesgine.CellSpec{
    CellAdapterSpec: wesgine.CellAdapterSpec{
        Channels: []channel.ChannelConfig{...},
        Email:    []email.Config{...},
        MCPs:     []mcp.ServerConfig{...},
        Cron:     []cron.JobConfig{...},
        HITL:     &wesgine.HITLConfig{Timeout: 60 * time.Second},
        HeadlessBrowser: &wesgine.HeadlessBrowserConfig{...},
        Playwright:      &wesgine.PlaywrightConfig{...},
        Desktop:         &wesgine.DesktopConfig{...},
        STT:             &wesgine.STTConfig{...},
        Plugins:         []string{"/opt/plugins/my-plugin"},
    },
}

Headless Browser

服务端无头浏览器,使用 chromedp 实现。适用于匿名网页抓取、截图等不需要用户登录态的场景。

启用

HeadlessBrowser: &wesgine.HeadlessBrowserConfig{
    ChromePath: "/usr/bin/chromium",  // 可选,自动检测
    Timeout:    30 * time.Second,
    MaxPages:   5,
},

提供的工具

工具说明
browser_navigate导航到 URL
browser_screenshot截取页面截图
browser_extract提取页面内容
browser_click点击元素
browser_type输入文本

API

GET /cells/{id}/headless-browser/state

返回浏览器连接状态和活跃页面数。

安全阀

adapter/browser/ 提供 browser_wait_for_user 工具——这是 AlwaysOn 的安全阀,Cell boot 时无条件注册。当 Agent 遇到需要人工介入的浏览器操作(如验证码、二步验证)时触发 HITL。


Playwright 集成

用户浏览器自动化 [B-13],用于需要登录态的场景。Playwright 通过 MCP 协议与引擎通信。

架构

wesgine Cell
  └── adapter/playwright/
        └── Playwright MCP subprocess
              └── 用户浏览器(通过 Chrome DevTools Protocol)

配置

Playwright: &wesgine.PlaywrightConfig{
    Mode:       "user",         // "user"(用户浏览器) | "sandbox"
    Headless:   false,          // 用户浏览器通常 headed
    BrowserType: "chromium",
    Timeout:    60 * time.Second,
},

两种模式

模式说明适用场景
user连接用户已安装的浏览器需要登录态的操作
sandbox启动隔离的浏览器实例自动化测试

扩展 Token

Playwright 支持自动连接浏览器扩展:

PUT /cells/{id}/playwright/token
Content-Type: application/json

{"token": "auto-connect-token-xxx"}

状态查询

GET /cells/{id}/playwright/status

Desktop Agent

桌面自动化 Device Agent [B-14],通过 MCP 协议将桌面操作暴露给 Cell。

架构要点

配置

Desktop: &wesgine.DesktopConfig{
    Command:   "/usr/local/bin/device-agent",  // 二进制路径
    Transport: "stdio",                         // "stdio" | "sse"
    URL:       "",                              // SSE 模式下的 URL
    Args:      []string{"--verbose"},
},

传输协议

传输说明适用场景
stdio标准输入输出通信本地桌面应用(wesclaw/wescode)
sseHTTP SSE 通信远程桌面(teleclaw 员工 PC)

消费方实现示例

以 wesclaw 为例:

// apps/desktop/cmd/wesclaw/deviceagent.go
func deviceAgentServerConfig() mcp.ServerConfig {
    binary := findDeviceAgentBinary()
    return mcp.ServerConfig{
        Name:      "wesclaw-device",
        Command:   binary,
        Transport: "stdio",
    }
}

// Boot 时注册
cell.MCPs().Upsert(ctx, deviceAgentServerConfig())

提供的工具(由消费方定义)

desktop_click(app, target)     -- 点击 UI 元素
desktop_screenshot()           -- 截屏
desktop_clipboard_read()       -- 读取剪贴板
desktop_clipboard_write(text)  -- 写入剪贴板
desktop_open_app(bundleID)     -- 打开应用
desktop_notify(title, body)    -- 系统通知

状态查询

GET /cells/{id}/desktop/status

STT 语音转写

语音转写适配器,使用 Whisper 模型将音频转为文本。

启用

STT: &wesgine.STTConfig{
    Model:    "whisper-large-v3",
    Language: "zh",
    Device:   "cpu",  // "cpu" | "cuda"
},

工作流程

音频文件 → ffmpeg 预处理 → Whisper 转写 → 文本结果

注意事项


Plugin Runtime

JSON-RPC 子进程 Plugin 管理,通过 CellSpec.Plugins 声明。

目录结构

/opt/plugins/my-plugin/
├── manifest.yaml      # 插件声明(名称、版本、工具列表)
├── bin/
│   └── server         # 可执行文件
└── README.md

manifest.yaml

name: my-plugin
version: 1.0.0
description: 自定义工具插件
tools:
  - name: my_custom_tool
    description: 执行自定义操作
    parameters:
      type: object
      properties:
        input:
          type: string
          description: 输入数据

生命周期

Cell Boot
  → Manager 扫描 Plugin 目录
  → 启动子进程(JSON-RPC over stdio)
  → 桥接 Tools 到 Cell ToolRegistry

Cell Drain
  → Shutdown 全部子进程

配额与安全

配置

Plugins: []string{
    "/opt/plugins/my-plugin",
    "/opt/plugins/another-plugin",
},

开发新适配器

步骤

  1. 在 adapter/<name>/ 下创建实现
  2. 在 CellSpec 添加对应配置字段
  3. 在 Cell boot 流程中注册适配器
  4. 适配器通过 Cell Handle 暴露管理 API

模板

// adapter/mydevice/adapter.go
package mydevice

type Config struct {
    Enabled bool
    Address string
}

type Adapter struct {
    cfg    Config
    cell   *wesgine.Cell
    cancel context.CancelFunc
}

func New(cell *wesgine.Cell, cfg Config) *Adapter {
    return &Adapter{cfg: cfg, cell: cell}
}

func (a *Adapter) Start(ctx context.Context) error {
    ctx, a.cancel = context.WithCancel(ctx)
    // 启动适配器
    return nil
}

func (a *Adapter) Stop() error {
    a.cancel()
    return nil
}

func (a *Adapter) Tools() []tool.Tool {
    return []tool.Tool{
        // 注册工具
    }
}

关键约束


最佳实践

  1. 优先复用 wesgine 标准 adapter——避免重复实现 MCP 生命周期
  2. HeadlessBrowser 用于匿名抓取,Playwright 用于登录态——不要混淆
  3. Desktop Agent 是客户端二进制——wesgine 只提供 adapter 代码
  4. Plugin 必须有 manifest.yaml——否则跳过不加载
  5. STT 的 ffmpeg 必须预装——引擎不会自动安装依赖