为 Moly AI 实现 OpenClaw 支持

Moly AI(GitHub)是一个使用 Rust 编写的 LLM 客户端,底层技术基于 Makepad。目前 OpenClaw 大火,并且支持大量主流的聊天工具接入。既然如此,自然也可以接入 Moly AI。现在已经给原始的仓库提交了 PR。这篇文章就用来描述:”如何为 Moly AI 实现 OpenClaw 支持”。

这篇文章将分为三个部分:

  1. 如何配置 OpenClaw
  2. 如何在 Moly AI 中配置使用 OpenClaw
  3. 为 Moly AI 支持 OpenClaw 的技术原理

如何配置 OpenClaw

OpenClaw 已经有非常详细的安装文档了:https://docs.openclaw.ai/start/wizard ,所有的细节均可以参考 OpenClaw 的官方文档。但我还是会带着大家过一遍,你需要下面三个前置条件:

  1. 需要会阅读基本文档
  2. 需要准备好一个 DeepSeek 账号
  3. 需要通畅的网络

下面是安装步骤:

  1. 命令行安装
1
curl -fsSL https://openclaw.ai/install.sh | bash
  1. 注意安装好之后会有一个风险提示,请选择同意

  2. 然后会自然进入配置模式,选择第一个 QuickStart

  3. 接下来进入到模型配置,因为我们要用 DeepSeek,所以选择 Skip All

  4. 然后选择 Provider 供应商,因为 DeepSeek 不在里面,随便选(跳过,后续再改)

  5. 聊天软件配置,都是 Telegram、Discord 之类的,我们继续跳过

  6. Skill 包,和本次目标无关,继续跳过

  7. Hooks 配置,可以选一个 mem,对于本次使用不重要

  8. 然后就部署完成了。注意,我们的模型是随便选的,无法使用

  9. 接下来验证:openclaw --version

然后进入最重要的配置阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 设置 DeepSeek 作为模型 Provider(注意替换为你自己的 API Key)
openclaw config set 'models.providers.deepseek' --json '{
"baseUrl": "https://api.deepseek.com/v1",
"apiKey": "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"api": "openai-completions",
"models": [
{ "id": "deepseek-chat", "name": "DeepSeek Chat" },
{ "id": "deepseek-reasoner", "name": "DeepSeek Reasoner" }
]
}'

# 设置模型模式为 merge
openclaw config set models.mode merge

# 设置默认模型
openclaw models set deepseek/deepseek-chat

# 启动 OpenClaw Gateway
openclaw gateway --port 18789 --verbose

# 打开 Dashboard
openclaw dashboard

上面是最重要的配置命令:

  1. 使用 openclaw config set 命令设置模型 Provider、Key 等。注意你要换成自己的 DeepSeek API Key
  2. 使用 openclaw config set models.mode merge 设置 models.mode 为 merge
  3. 使用 openclaw models set deepseek/deepseek-chat 设置模型。注意其实第一个命令已经设置过了,但是这里还是需要再次设置。没有为什么,截止 2026/2/4 需要这样
  4. 然后使用 openclaw gateway --port 18789 --verbose 启动 OpenClaw Gateway
  5. 接下来使用 openclaw dashboard 会弹出网页。当然你要是第一次弹出是什么内容都没有的,Chat 为空

Dashboard 后面会出现的 Token 是很重要的。

可以尝试和 OpenClaw 聊天,查看是否配置正常。

如何配置 Moly AI

因为 Moly AI 还没有正式合并代码,请使用我的 PR:https://github.com/moly-ai/moly-ai/pull/630

如果不知道如何使用这个 PR,请用刚才配置好的 OpenClaw 来帮助你把代码拉到本地。

Moly AI 本身是需要使用 Rust 的,要想体验这个功能需要提前安装 Rust 工具链。你依然可以要求 OpenClaw 来帮助你安装 Rust 工具链。

接下来讲如何使用,以下操作的前提在于你已经安装好 Rust 工具链,并且下载好了这个 PR 的代码。

  1. 直接 cargo run

  2. 依次点击 Moly Server → Go to Providers

  3. 然后依次点击 OpenClaw,填入 OpenClaw Host。它是一个 WebSocket 地址,这是 OpenClaw 的 WebSocket 端点,端口号为上一步设定的端口 18789(如果你设定了其他端口,记得调整)

  4. 设定 API Key。严格来说它并不是 API Key,而是 OpenClaw 给出的 Token。记得填写”如何配置 OpenClaw”章节最后 OpenClaw Dashboard 给出的 Token

  5. 依次打开 4、5 处的开关代表启用

  6. 实际上 AI 是配置在 OpenClaw 中的

  7. 注意看上面图中的 1 处,上面显示 ON,代表已经启用 OpenClaw

  8. 接下来开启聊天

OpenClaw Gateway 集成技术原理

概述

以下介绍 Moly 应用与 OpenClaw Gateway 的集成实现,通过 WebSocket 协议将本地 AI 助手平台接入跨平台 Rust GUI 客户端。

什么是 OpenClaw?

OpenClaw 是一个本地 AI 助手平台,提供:

  • 多渠道消息支持(WhatsApp、Telegram、Slack、Discord 等)
  • 浏览器控制和自动化能力
  • Canvas 工作区用于可视化输出
  • 可扩展的技能系统
  • 定时任务和 Webhooks
  • 节点操作(摄像头、屏幕捕获等)

架构设计

整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
┌─────────────────────────────────────────────────────────┐
│ Moly 应用层 │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Chat Screen (UI Layer) │ │
│ └──────────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼─────────────────────────────┐ │
│ │ RouterClient (消息路由) │ │
│ └──────────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼─────────────────────────────┐ │
│ │ MapClient (Bot ID 映射) │ │
│ └──────────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼─────────────────────────────┐ │
│ │ OpenClawClient (BotClient trait) │ │
│ │ • WebSocket 连接管理 │ │
│ │ • 协议握手 │ │
│ │ • 流式响应处理 │ │
│ └──────────────────────┬─────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────┘
│ WebSocket (ws://127.0.0.1:18789)

┌─────────────────────────▼───────────────────────────────┐
│ OpenClaw Gateway (本地服务) │
│ • Agent 处理 │
│ • 工具执行 │
│ • 多渠道协调 │
└──────────────────────────────────────────────────────────┘

与 Moly/MolyKit/AITK 的集成

层级 职责
AITK 核心协议层,定义 BotClient trait、通用类型(Bot、Message、MessageContent)、跨平台异步工具
MolyKit 可复用 UI 组件,聊天 Widget、消息渲染、模型选择器
Moly App 主应用,实现特定 Provider 客户端,管理配置和路由

协议实现

OpenClaw 协议流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Client                          Gateway
│ │
├─── connect request ───────────>│
│ (auth token, protocol v3) │
│ │
│<──── connect.challenge ────────┤ (可选,远程模式)
│ │
├─── connect request ───────────>│ (重新发送带认证)
│ │
│<──── hello-ok response ────────┤
│ │
├─── agent request ─────────────>│
│ (message + history) │
│ │
│<──── agent.text.delta ─────────┤ (流式响应)
│<──── agent.text.delta ─────────┤
│<──── agent.text.delta ─────────┤
│ ... │
│ │
│<──── chat (state: done) ───────┤ (完成信号)
│ │

消息类型

类型 方向 说明
req Client → Gateway 请求消息(connect、agent)
res Gateway → Client 响应消息(hello-ok、agent 完成)
event Gateway → Client 事件消息(流式文本、状态变更)

请求结构

1
2
3
4
5
6
struct Request<T> {
r#type: &'static str, // "req"
id: String, // UUID v4
method: String, // "connect" | "agent"
params: T, // 方法特定参数
}

Connect 参数:

1
2
3
4
5
6
7
8
struct ConnectParams {
min_protocol: u32, // 3
max_protocol: u32, // 3
role: &'static str, // "operator"
scopes: Vec<&'static str>, // ["operator.read", "operator.write"]
client: ClientInfo,
auth: Option<AuthInfo>, // 可选认证 token
}

Agent 参数:

1
2
3
4
5
struct AgentParams {
message: String, // 用户消息 + 历史
idempotency_key: String, // UUID v4 防重复
agent_id: &'static str, // "main"
}

核心实现

BotClient Trait 实现

OpenClawClient 实现了 AITK 定义的 BotClient trait:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
impl BotClient for OpenClawClient {
// 返回可用的 Bot 列表
fn bots(&mut self) -> BoxPlatformSendFuture<'static, ClientResult<Vec<Bot>>>;

// 发送消息并返回流式响应
fn send(
&mut self,
bot_id: &BotId,
messages: &[Message],
tools: &[Tool],
) -> BoxPlatformSendStream<'static, ClientResult<MessageContent>>;

// 克隆客户端
fn clone_box(&self) -> Box<dyn BotClient>;
}

流式响应处理

使用 async_stream 宏实现异步流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let stream = stream! {
// 1. 建立 WebSocket 连接
let (ws_stream, _) = connect_async(&inner.url).await?;
let (mut write, read) = ws_stream.split();

// 2. 发送 connect 请求
write.send(WsMessage::Text(connect_json)).await?;

// 3. 处理消息循环
loop {
match read.next().await {
// 处理事件和响应
// yield 流式内容给 UI
}
}
};

事件处理状态机

1
2
3
4
5
6
7
8
enum ProcessResult {
Continue, // 继续等待
Yield(MessageContent), // 输出内容到 UI
Error(ClientError), // 错误终止
SendConnect, // 发送连接请求
SendAgent, // 发送 agent 请求
Done, // 完成
}

文本去重算法

OpenClaw 可能发送重叠的文本片段,需要智能合并:

1
2
3
4
5
6
7
8
9
10
11
fn merge_text_content(content: &mut String, incoming: &str) {
// 1. 空内容直接追加
// 2. incoming 包含 content → 替换
// 3. content 包含 incoming → 忽略
// 4. 查找重叠部分 → 只追加新内容
}

fn find_overlap_bytes(content: &str, incoming: &str) -> usize {
// 基于字符边界的重叠检测
// 支持 UTF-8 多字节字符
}

跨平台支持

平台差异处理

平台 WebSocket 库 异步运行时
Desktop (macOS/Windows/Linux) tokio-tungstenite Tokio
Web (WASM) 不支持 wasm-bindgen
1
2
3
4
5
6
7
8
9
#[cfg(not(target_arch = "wasm32"))]
fn send(...) -> BoxPlatformSendStream<...> {
// 完整 WebSocket 实现
}

#[cfg(target_arch = "wasm32")]
fn send(...) -> BoxPlatformSendStream<...> {
// 返回友好提示,引导用户使用桌面版或 Web UI
}

历史消息限制

为防止请求过大,限制历史消息数量:

1
2
3
4
5
6
const MAX_HISTORY_MESSAGES: usize = 32;

fn build_history_message(messages: &[Message]) -> String {
let start = messages.len().saturating_sub(MAX_HISTORY_MESSAGES);
// 格式化为 "role: content" 形式
}

错误处理

错误类型

1
2
3
4
5
6
enum ClientErrorKind {
Network, // 网络连接错误
Format, // 序列化/反序列化错误
Response, // Gateway 返回错误
Unknown, // 未知错误
}

超时处理

1
2
3
4
5
6
7
8
9
10
11
const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(10);

// 使用 futures::select! 实现超时
futures::select! {
_ = handshake_deadline => {
if !connected {
yield ClientError::new(ClientErrorKind::Network, "Timed out...");
}
}
msg = read.next() => { /* 处理消息 */ }
}

文件变更清单

文件 变更类型 说明
src/data/openclaw_client.rs 新增 核心客户端实现 (613 行)
src/data/mod.rs 修改 添加模块导出
src/data/providers.rs 修改 添加 OpenClaw 枚举变体
src/data/supported_providers.json 修改 添加 Provider 配置
src/data/bot_fetcher.rs 修改 添加客户端工厂逻辑
src/chat/chat_screen.rs 修改 添加 UI 路由
Cargo.toml 修改 添加 tokio-tungstenite 依赖

依赖项

1
2
3
4
5
[dependencies]
tokio-tungstenite = "0.26" # WebSocket 客户端
uuid = { version = "1", features = ["v4"] } # 请求 ID 生成
async-stream = "0.3" # 异步流宏
futures = "0.3" # 异步原语

总结

OpenClaw 集成的核心设计原则:

  1. Trait 抽象:通过实现 BotClient trait 无缝接入现有架构
  2. 流式处理:使用 async stream 实现实时响应展示
  3. 跨平台兼容:通过条件编译处理平台差异
  4. 健壮性:完善的错误处理、超时机制、文本去重
  5. 可配置:通过 JSON 配置文件管理 Provider 信息