chore(i18n): refresh zh-CN translations

This commit is contained in:
openclaw-docs-i18n[bot] 2026-04-23 20:16:27 +00:00
parent 4cdfe83ae5
commit e5b62e3a42
8 changed files with 1026 additions and 1087 deletions

View File

@ -5,10 +5,10 @@ read_when:
summary: Feishu 机器人概览、功能和配置
title: Feishu
x-i18n:
generated_at: "2026-04-23T06:40:05Z"
generated_at: "2026-04-23T20:12:19Z"
model: gpt-5.4
provider: openai
source_hash: 11bf136cecb26dc939c5e78e020c0e6aa3312d9f143af0cab7568743c728cf13
source_hash: 76565e89007211ba194ea9ab47ba926f5c9b5d12782d046bc94995a075671f02
source_path: channels/feishu.md
workflow: 15
---
@ -23,7 +23,7 @@ Feishu/Lark 是一个一体化协作平台,团队可以在其中聊天、共
## 快速开始
> **需要 OpenClaw 2026.4.10 或更高版本。** 运行 `openclaw --version` 进行检查。使用 `openclaw update` 升级。
> **需要 OpenClaw 2026.4.23 或更高版本。** 运行 `openclaw --version` 进行检查。使用 `openclaw update` 升级。
<Steps>
<Step title="运行渠道设置向导">
@ -48,12 +48,12 @@ Feishu/Lark 是一个一体化协作平台,团队可以在其中聊天、共
配置 `dmPolicy` 以控制谁可以向机器人发送私信:
- `"pairing"` — 未知用户会收到一个配对码;通过 CLI 批准
- `"pairing"` — 未知用户会收到配对码;通过 CLI 批准
- `"allowlist"` — 只有列在 `allowFrom` 中的用户可以聊天(默认:仅机器人所有者)
- `"open"` — 允许所有用户
- `"disabled"` — 禁用所有私信
**批准配对请求:**
**批准一个配对请求:**
```bash
openclaw pairing list feishu
@ -64,9 +64,9 @@ openclaw pairing approve feishu <CODE>
**群组策略**`channels.feishu.groupPolicy`
| Value | 行为 |
| ------------- | ------------------------------------------ |
| `"open"` | 响应群组中的所有消息 |
| Value | Behavior |
| ------------- | -------- |
| `"open"` | 在群组中响应所有消息 |
| `"allowlist"` | 仅响应 `groupAllowFrom` 中的群组 |
| `"disabled"` | 禁用所有群组消息 |
@ -114,7 +114,7 @@ openclaw pairing approve feishu <CODE>
channels: {
feishu: {
groupPolicy: "allowlist",
// 群组 ID 形如oc_xxx
// 群组 ID 类似oc_xxx
groupAllowFrom: ["oc_xxx", "oc_yyy"],
},
},
@ -131,7 +131,7 @@ openclaw pairing approve feishu <CODE>
groupAllowFrom: ["oc_xxx"],
groups: {
oc_xxx: {
// 用户 open_id 形如ou_xxx
// 用户 open_id 类似ou_xxx
allowFrom: ["ou_user1", "ou_user2"],
},
},
@ -148,13 +148,13 @@ openclaw pairing approve feishu <CODE>
### 群组 ID`chat_id`,格式:`oc_xxx`
在 Feishu/Lark 中打开群组,点击右上角的菜单图标,然后进入 **设置**。群组 ID`chat_id`)会显示在设置页面中。
在 Feishu/Lark 中打开群组,点击右上角的菜单图标,然后进入**设置**。群组 ID`chat_id`)会显示在设置页面中。
![Get Group ID](/images/feishu-get-group-id.png)
### 用户 ID`open_id`,格式:`ou_xxx`
启动 Gateway 网关,向机器人发送一条私信,然后查日志:
启动 Gateway 网关,向机器人发送一条私信,然后查日志:
```bash
openclaw logs --follow
@ -170,8 +170,8 @@ openclaw pairing list feishu
## 常用命令
| Command | 说明 |
| --------- | --------------------------- |
| Command | Description |
| --------- | ----------- |
| `/status` | 显示机器人状态 |
| `/reset` | 重置当前会话 |
| `/model` | 显示或切换 AI 模型 |
@ -184,16 +184,16 @@ openclaw pairing list feishu
### 机器人在群聊中没有响应
1. 确保机器人已被添加到群组中
1. 确保机器人已加入群组
2. 确保你已 @mention 机器人(默认要求)
3. 验证 `groupPolicy` 不是 `"disabled"`
4. 检查日志:`openclaw logs --follow`
### 机器人没有收到消息
1. 确保机器人已在 Feishu Open Platform / Lark Developer 中发布并通过审核
1. 确保机器人已在 Feishu Open Platform / Lark Developer 中发布并获得批准
2. 确保事件订阅包含 `im.message.receive_v1`
3. 确保已选择 **persistent connection**WebSocket
3. 确保已选择**持久连接**WebSocket
4. 确保已授予所有必需的权限范围
5. 确保 Gateway 网关正在运行:`openclaw gateway status`
6. 检查日志:`openclaw logs --follow`
@ -201,7 +201,7 @@ openclaw pairing list feishu
### App Secret 泄露
1. 在 Feishu Open Platform / Lark Developer 中重置 App Secret
2. 更新你配置中的值
2. 更新你配置中的值
3. 重启 Gateway 网关:`openclaw gateway restart`
---
@ -249,20 +249,20 @@ Feishu/Lark 支持通过交互式卡片进行流式回复。启用后,机器
channels: {
feishu: {
streaming: true, // 启用流式卡片输出默认true
blockStreaming: true, // 启用块流式传输默认true
blockStreaming: true, // 启用块流式传输默认true
},
},
}
```
`streaming: false` 设为关闭后,会以一条完整消息发送完整回复。
`streaming: false` 设为关闭后,会在一条消息中发送完整回复。
### 配额优化
使用两个可选标志来减少 Feishu/Lark API 调用次数:
- `typingIndicator`(默认 `true`):设为 `false` 以跳过正在输入反应调用
- `resolveSenderNames`(默认 `true`):设为 `false` 跳过发送者资料查询
- `typingIndicator`(默认 `true`):设为 `false` 可跳过“正在输入”反应调用
- `resolveSenderNames`(默认 `true`):设为 `false` 跳过发送者资料查询
```json5
{
@ -277,7 +277,7 @@ Feishu/Lark 支持通过交互式卡片进行流式回复。启用后,机器
### ACP 会话
Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由文本命令驱动——没有原生斜杠命令菜单,因此请直接在话中使用 `/acp ...` 消息。
Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由文本命令驱动——没有原生斜杠命令菜单,因此请直接在话中使用 `/acp ...` 消息。
#### 持久 ACP 绑定
@ -335,7 +335,7 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
### 多智能体路由
使用 `bindings` 将 Feishu/Lark 私信或群组路由到不同智能体。
使用 `bindings` 将 Feishu/Lark 私信或群组路由到不同智能体。
```json5
{
@ -371,7 +371,7 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
- `match.peer.kind``"direct"`(私信)或 `"group"`(群聊)
- `match.peer.id`:用户 Open ID`ou_xxx`)或群组 ID`oc_xxx`
有关查找提示,请参见[获取群组/用户 ID](#get-groupuser-ids)。
查找技巧请参见[获取群组/用户 ID](#get-groupuser-ids)。
---
@ -379,33 +379,33 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
完整配置:[Gateway 网关配置](/zh-CN/gateway/configuration)
| Setting | 说明 | 默认值 |
| Setting | Description | Default |
| ------------------------------------------------- | ------------------------------------------ | ---------------- |
| `channels.feishu.enabled` | 启用/禁用该渠道 | `true` |
| `channels.feishu.domain` | API 域(`feishu` 或 `lark` | `feishu` |
| `channels.feishu.connectionMode` | 事件传输方式(`websocket` 或 `webhook` | `websocket` |
| `channels.feishu.defaultAccount` | 用于出站路由的默认账户 | `default` |
| `channels.feishu.verificationToken` | webhook 模式必需 | — |
| `channels.feishu.encryptKey` | webhook 模式必需 | — |
| `channels.feishu.webhookPath` | Webhook 路由路径 | `/feishu/events` |
| `channels.feishu.webhookHost` | Webhook 绑定主机 | `127.0.0.1` |
| `channels.feishu.webhookPort` | Webhook 绑定端口 | `3000` |
| `channels.feishu.accounts.<id>.appId` | App ID | — |
| `channels.feishu.accounts.<id>.appSecret` | App Secret | — |
| `channels.feishu.accounts.<id>.domain` | 按账户覆盖域设置 | `feishu` |
| `channels.feishu.dmPolicy` | 私信策略 | `allowlist` |
| `channels.feishu.allowFrom` | 私信允许列表(`open_id` 列表) | [BotOwnerId] |
| `channels.feishu.groupPolicy` | 群组策略 | `allowlist` |
| `channels.feishu.groupAllowFrom` | 群组允许列表 | — |
| `channels.feishu.requireMention` | 在群组中要求 @mention | `true` |
| `channels.feishu.groups.<chat_id>.requireMention` | 按群组覆盖 @mention 要求 | inherited |
| `channels.feishu.groups.<chat_id>.enabled` | 启用/禁用特定群组 | `true` |
| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` |
| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` |
| `channels.feishu.streaming` | 流式卡片输出 | `true` |
| `channels.feishu.blockStreaming` | 分块流式传输 | `true` |
| `channels.feishu.typingIndicator` | 发送正在输入反应 | `true` |
| `channels.feishu.resolveSenderNames` | 解析发送者显示名称 | `true` |
| `channels.feishu.enabled` | 启用/禁用该渠道 | `true` |
| `channels.feishu.domain` | API 域(`feishu` 或 `lark` | `feishu` |
| `channels.feishu.connectionMode` | 事件传输方式(`websocket` 或 `webhook` | `websocket` |
| `channels.feishu.defaultAccount` | 用于出站路由的默认账户 | `default` |
| `channels.feishu.verificationToken` | webhook 模式必需 | — |
| `channels.feishu.encryptKey` | webhook 模式必需 | — |
| `channels.feishu.webhookPath` | webhook 路由路径 | `/feishu/events` |
| `channels.feishu.webhookHost` | webhook 绑定主机 | `127.0.0.1` |
| `channels.feishu.webhookPort` | webhook 绑定端口 | `3000` |
| `channels.feishu.accounts.<id>.appId` | App ID | — |
| `channels.feishu.accounts.<id>.appSecret` | App Secret | — |
| `channels.feishu.accounts.<id>.domain` | 按账户覆盖域设置 | `feishu` |
| `channels.feishu.dmPolicy` | 私信策略 | `allowlist` |
| `channels.feishu.allowFrom` | 私信允许列表(`open_id` 列表) | [BotOwnerId] |
| `channels.feishu.groupPolicy` | 群组策略 | `allowlist` |
| `channels.feishu.groupAllowFrom` | 群组允许列表 | — |
| `channels.feishu.requireMention` | 在群组中要求 @mention | `true` |
| `channels.feishu.groups.<chat_id>.requireMention` | 按群组覆盖 @mention 要求 | 继承 |
| `channels.feishu.groups.<chat_id>.enabled` | 启用/禁用特定群组 | `true` |
| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` |
| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` |
| `channels.feishu.streaming` | 流式卡片输出 | `true` |
| `channels.feishu.blockStreaming` | 分块流式传输 | `true` |
| `channels.feishu.typingIndicator` | 发送正在输入反应 | `true` |
| `channels.feishu.resolveSenderNames` | 解析发送者显示名称 | `true` |
---
@ -419,7 +419,7 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
- ✅ 文件
- ✅ 音频
- ✅ 视频/媒体
- ✅ 贴纸
- ✅ 表情贴纸
### 发送
@ -429,11 +429,11 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
- ✅ 音频
- ✅ 视频/媒体
- ✅ 交互式卡片(包括流式更新)
- ⚠️ 富文本post 样式格式;不支持完整的 Feishu/Lark 编辑能力)
- ⚠️ 富文本post 风格格式;不支持完整的 Feishu/Lark 编辑能力)
### 话题和回复
- ✅ 内回复
- ✅ 内回复
- ✅ 话题回复
- ✅ 回复话题消息时,媒体回复会保持话题感知
@ -442,7 +442,7 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
## 相关内容
- [渠道概览](/zh-CN/channels) — 所有受支持的渠道
- [配对](/zh-CN/channels/pairing) — 私信身份验证配对流程
- [群组](/zh-CN/channels/groups) — 群聊行为提及门控
- [配对](/zh-CN/channels/pairing) — 私信身份验证配对流程
- [群组](/zh-CN/channels/groups) — 群聊行为提及门控
- [渠道路由](/zh-CN/channels/channel-routing) — 消息的会话路由
- [安全](/zh-CN/gateway/security) — 访问模型与加固措施
- [安全](/zh-CN/gateway/security) — 访问模型与加固

View File

@ -1,37 +1,37 @@
---
read_when:
- 开发 Google Chat 渠道功能
summary: Google Chat 应用支持状态、能和配置
- 正在开发 Google Chat 渠道功能
summary: Google Chat 应用支持状态、能和配置
title: Google Chat
x-i18n:
generated_at: "2026-04-05T08:14:34Z"
generated_at: "2026-04-23T20:12:13Z"
model: gpt-5.4
provider: openai
source_hash: 570894ed798dd0b9ba42806b050927216379a1228fcd2f96de565bc8a4ac7c2c
source_hash: ff546be5a3e882445f78ee739ca9f55bf4337a3e533ea1e6be0cd588bf40fb19
source_path: channels/googlechat.md
workflow: 15
---
# Google ChatChat API
状态:通过 Google Chat API webhook 支持私信和 spaces HTTP
状态:已可用于通过 Google Chat API webhook 支持私信和空间(仅限 HTTP
## 快速设置(新手)
## 快速开始(新手)
1. 创建一个 Google Cloud 项目并启用 **Google Chat API**
- 前往:[Google Chat API Credentials](https://console.cloud.google.com/apis/api/chat.googleapis.com/credentials)
- 如果 API 尚未启用,请启用它。
- 如果 API 尚未启用,请启用它。
2. 创建一个 **Service Account**
- 点击 **Create Credentials** > **Service Account**
- 随意命名(例如 `openclaw-chat`)。
- 随意命名(例如`openclaw-chat`)。
- 权限留空(点击 **Continue**)。
- 可访问的主体留空(点击 **Done**)。
3. 创建并下载 **JSON Key**
- 在服务账号列表中,点击你刚创建的那个账号
- 在 Service Account 列表中,点击你刚创建的那个账户
- 前往 **Keys** 标签页。
- 点击 **Add Key** > **Create new key**
- 选择 **JSON**点击 **Create**
4. 将下载的 JSON 文件存储在你的 gateway 主机上(例如 `~/.openclaw/googlechat-service-account.json`)。
- 选择 **JSON**,然后点击 **Create**
4. 将下载的 JSON 文件存放到你的 Gateway 网关主机上(例如:`~/.openclaw/googlechat-service-account.json`)。
5. 在 [Google Cloud Console Chat Configuration](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat) 中创建一个 Google Chat 应用:
- 填写 **Application info**
- **App name**:(例如 `OpenClaw`
@ -40,49 +40,49 @@ x-i18n:
- 启用 **Interactive features**
- 在 **Functionality** 下,勾选 **Join spaces and group conversations**
- 在 **Connection settings** 下,选择 **HTTP endpoint URL**
- 在 **Triggers** 下,选择 **Use a common HTTP endpoint URL for all triggers**,并将其设置为你的 gateway 公网 URL 后跟 `/googlechat`
- _提示运行 `openclaw status` 可找到你的 gateway 公网 URL。_
- 在 **Visibility** 下,勾选 **Make this Chat app available to specific people and groups in &lt;Your Domain&gt;**。
- 在文本框中输入你的邮箱地址(例如 `user@example.com`)。
- 在 **Triggers** 下,选择 **Use a common HTTP endpoint URL for all triggers**,并将其设置为你的 Gateway 网关公网 URL 后接 `/googlechat`
- _提示运行 `openclaw status` 以找到你的 Gateway 网关公网 URL。_
- 在 **Visibility** 下,勾选 **Make this Chat app available to specific people and groups in `<Your Domain>`**。
- 在文本框中输入你的电子邮件地址(例如 `user@example.com`)。
- 点击底部的 **Save**
6. **启用应用状态**
- 保存后,**刷新页面**。
- 找到 **App status** 部分(通常在保存后页面顶部或底部附近)。
- 找到 **App status** 部分(通常在保存后出现在页面顶部或底部附近)。
- 将状态改为 **Live - available to users**
- 再次点击 **Save**
7. 使用服务账号路径和 webhook audience 配置 OpenClaw
7. 使用 Service Account 路径和 webhook audience 配置 OpenClaw
- 环境变量:`GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json`
- 或配置:`channels.googlechat.serviceAccountFile: "/path/to/service-account.json"`。
8. 设置 webhook audience 类型和值(需与你的 Chat 应用配置匹配)。
9. 启动 gateway。Google Chat 将向你的 webhook 路径发送 POST 请求。
9. 启动 Gateway 网关。Google Chat 将向你的 webhook 路径发送 POST 请求。
## 添加到 Google Chat
在 gateway 运行且你的邮箱已加入可见性列表后
一旦 Gateway 网关正在运行,且你的邮箱已添加到可见性列表中
1. 前往 [Google Chat](https://chat.google.com/)。
2. 点击 **Direct Messages** 旁边的 **+**(加号)图标。
3. 在搜索栏中(通常用于添加联系人),输入你在 Google Cloud Console 中配置的 **App name**
- **注意**:由于这是私有应用,机器人 _不会_ 出现在 “Marketplace” 浏览列表中。你必须按名称搜索它。
4. 从结果中选择你的机器人。
5. 点击 **Add****Chat** 开始一对一话。
5. 点击 **Add****Chat** 开始一对一话。
6. 发送 “Hello” 来触发助手!
## 公网 URL webhook
## 公网 URL限 Webhook
Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只将 `/googlechat` 路径暴露到互联网**。请将 OpenClaw 仪表板和其他敏感端点保留在你的私有网络中。
Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只`/googlechat` 路径暴露到互联网**。请将 OpenClaw 仪表板和其他敏感端点保留在你的私有网络中。
### 方案 ATailscale Funnel推荐
### 选项 ATailscale Funnel推荐
对私有仪表板使用 Tailscale Serve对公开 webhook 路径使用 Funnel。这样可以保持 `/` 为私有,同时只暴露 `/googlechat`
将 Tailscale Serve 用于私有仪表板,将 Funnel 用于公网 webhook 路径。这样可以保持 `/` 为私有,同时只暴露 `/googlechat`
1. **检查你的 gateway 绑定到了哪个地址:**
1. **检查你的 Gateway 网关绑定到了哪个地址:**
```bash
ss -tlnp | grep 18789
```
记下 IP 地址(例如 `127.0.0.1`、`0.0.0.0`,或你的 Tailscale IP`100.x.x.x`)。
记下 IP 地址(例如 `127.0.0.1`、`0.0.0.0`,或你的 Tailscale IP`100.x.x.x`)。
2. **仅向 tailnet 暴露仪表板(端口 8443**
@ -94,7 +94,7 @@ Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只
tailscale serve --bg --https 8443 http://100.106.161.80:18789
```
3. **仅公开暴露 webhook 路径**
3. **仅将 webhook 路径公开暴露**
```bash
# 如果绑定到 localhost127.0.0.1 或 0.0.0.0
@ -105,7 +105,7 @@ Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只
```
4. **为该节点授权 Funnel 访问:**
如果出现提示,请访问输出中显示的授权 URL以在你的 tailnet 策略中为该节点启用 Funnel。
如果系统提示,请访问输出中显示的授权 URL便在你的 tailnet 策略中为该节点启用 Funnel。
5. **验证配置:**
@ -114,19 +114,19 @@ Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只
tailscale funnel status
```
你的公网 webhook URL 将
你的公网 webhook URL 将
`https://<node-name>.<tailnet>.ts.net/googlechat`
你的私有仪表板将保持仅 tailnet 可访问
你的私有仪表板将继续仅限 tailnet
`https://<node-name>.<tailnet>.ts.net:8443/`
在 Google Chat 应用配置中,请使用公网 URL不带 `:8443`)。
在 Google Chat 应用配置中使用公网 URL不带 `:8443`)。
> 注意:此配置在重启后仍会保留。若之后要移除,请运行 `tailscale funnel reset``tailscale serve reset`
> 注意:此配置会在重启后保留。若要稍后移除,请运行 `tailscale funnel reset``tailscale serve reset`
### 方案 B反向代理Caddy
### 选项 B反向代理Caddy
如果你使用类似 Caddy 的反向代理,只代理特定路径:
如果你使用像 Caddy 这样的反向代理,请只代理特定路径:
```caddy
your-domain.com {
@ -134,38 +134,38 @@ your-domain.com {
}
```
使用此配置后,对 `your-domain.com/` 的任何请求都会被忽略或返回 404`your-domain.com/googlechat` 会被安全地路由到 OpenClaw。
使用此配置时,任何对 `your-domain.com/`请求都会被忽略或返回 404`your-domain.com/googlechat` 会被安全地路由到 OpenClaw。
### 方案 CCloudflare Tunnel
### 选项 CCloudflare Tunnel
将你的 tunnel ingress 规则配置为仅路由 webhook 路径:
将你的 Tunnel ingress 规则配置为只路由 webhook 路径:
- **路径**`/googlechat` -> `http://localhost:18789/googlechat`
- **默认规则**HTTP 404Not Found
- **Path**`/googlechat` -> `http://localhost:18789/googlechat`
- **Default Rule**HTTP 404未找到
## 工作原理
1. Google Chat 会向 gateway 发送 webhook POST 请求。每个请求都包含 `Authorization: Bearer <token>` 请求头。
1. Google Chat 向 Gateway 网关发送 webhook POST 请求。每个请求都包含一个 `Authorization: Bearer <token>` 请求头。
- 当该请求头存在时OpenClaw 会在读取/解析完整 webhook 请求体之前先验证 bearer 身份验证。
- 对于在请求体中携带 `authorizationEventObject.systemIdToken` 的 Google Workspace Add-on 请求,也支持通过更严格的预身份验证请求体预算来处理
2. OpenClaw 会根据配置的 `audienceType``audience` 验证 token
- 对于在请求体中携带 `authorizationEventObject.systemIdToken` 的 Google Workspace Add-on 请求,系统会通过更严格的预认证请求体预算来支持
2. OpenClaw 会根据配置的 `audienceType``audience` 验证该令牌
- `audienceType: "app-url"` → audience 是你的 HTTPS webhook URL。
- `audienceType: "project-number"` → audience 是 Cloud 项目编号。
3. 消息按 space 路由:
- 私信使用会话键 `agent:<agentId>:googlechat:direct:<spaceId>`
- spaces 使用会话键名 `agent:<agentId>:googlechat:group:<spaceId>`
4. 私信访问默认采用配对。未知发送会收到一个配对码;使用以下命令批准:
3. 消息按空间路由:
- 私信使用会话键 `agent:<agentId>:googlechat:direct:<spaceId>`
- 空间使用会话键 `agent:<agentId>:googlechat:group:<spaceId>`
4. 私信访问默认采用配对。未知发送会收到一个配对码;使用以下命令批准:
- `openclaw pairing approve googlechat <code>`
5. 群组 space 默认要求 @ 提及。若提及检测需要应用的用户名称,请使用 `botUser`
5. 群组空间默认需要 @ 提及。若提及检测需要应用的用户名,可使用 `botUser`
## 目标
交付和 allowlist 使用以下标识符:
投递和 allowlist 使用以下标识符:
- 私信:`users/<userId>`(推荐)。
- 原始邮箱 `name@example.com` 是可变的,且仅在 `channels.googlechat.dangerouslyAllowNameMatching: true` 时用于直接 allowlist 匹配。
- 原始邮箱 `name@example.com` 是可变的,且仅在 `channels.googlechat.dangerouslyAllowNameMatching: true` 时用于私信 allowlist 匹配。
- 已弃用:`users/<email>` 会被视为用户 ID而不是邮箱 allowlist。
- Spaces`spaces/<spaceId>`。
- 空间`spaces/<spaceId>`。
## 配置要点
@ -175,11 +175,11 @@ your-domain.com {
googlechat: {
enabled: true,
serviceAccountFile: "/path/to/service-account.json",
// serviceAccountRef: { source: "file", provider: "filemain", id: "/channels/googlechat/serviceAccount" }
// or serviceAccountRef: { source: "file", provider: "filemain", id: "/channels/googlechat/serviceAccount" }
audienceType: "app-url",
audience: "https://gateway.example.com/googlechat",
webhookPath: "/googlechat",
botUser: "users/1234567890", // 可选;有助于提及检测
botUser: "users/1234567890", // optional; helps mention detection
dm: {
policy: "pairing",
allowFrom: ["users/1234567890"],
@ -203,16 +203,16 @@ your-domain.com {
说明:
- 服务账号凭证也可以通过 `serviceAccount` 内联传入JSON 字符串)
- 也支持 `serviceAccountRef`env/file SecretRef包括 `channels.googlechat.accounts.<id>.serviceAccountRef` 下的逐账号引用。
- Service Account 凭证也可以通过 `serviceAccount`JSON 字符串)内联传入
- 也支持 `serviceAccountRef`env/file SecretRef包括 `channels.googlechat.accounts.<id>.serviceAccountRef` 下的按账户配置引用。
- 如果未设置 `webhookPath`,默认 webhook 路径为 `/googlechat`
- `dangerouslyAllowNameMatching` 会重新启用基于可变邮箱主体的 allowlist 匹配(紧急兼容模式)。
- 启用 `actions.reactions`,可通过 `reactions` 工具和 `channels action` 使用表情回应。
- 消息操作会公开 `send` 用于发送文本,公开 `upload-file` 用于显式发送附件。`upload-file` 接受 `media` / `filePath` / `path`,并支持可选的 `message`、`filename` 和线程目标
- `dangerouslyAllowNameMatching` 会重新启用基于可变邮箱主体的 allowlist 匹配(破窗兼容模式)。
- 当启用 `actions.reactions`,可通过 `reactions` 工具和 `channels action` 使用表情回应。
- 消息操作会暴露用于文本发送的 `send`,以及用于显式发送附件的 `upload-file`。`upload-file` 接受 `media` / `filePath` / `path`,并支持可选的 `message`、`filename` 和线程定向参数
- `typingIndicator` 支持 `none`、`message`(默认)和 `reaction`reaction 需要用户 OAuth
- 附件通过 Chat API 下载并存储到媒体管道中(大小受 `mediaMaxMb` 限制)。
- 附件通过 Chat API 下载并存储到媒体管道中(大小受 `mediaMaxMb` 限制)。
Secrets 参考详情:[Secrets Management](/gateway/secrets)。
Secrets 引用详情:[Secrets Management](/zh-CN/gateway/secrets)。
## 故障排除
@ -224,15 +224,15 @@ Secrets 参考详情:[Secrets Management](/gateway/secrets)。
status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed
```
表示 webhook 处理器未注册。常见原因有
意味着 webhook 处理器尚未注册。常见原因包括
1. **渠道未配置**:你的配置中缺少 `channels.googlechat` 部分。请通过以下命令验证:
1. **渠道未配置**:你的配置中缺少 `channels.googlechat` 部分。请使用以下命令验证:
```bash
openclaw config get channels.googlechat
```
如果返回 “Config path not found”请添加配置参见[配置要点](#config-highlights))。
如果返回 “Config path not found”请添加配置参见[配置要点](#配置要点))。
2. **插件未启用**:检查插件状态:
@ -242,36 +242,36 @@ status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Al
如果显示 “disabled”请在配置中添加 `plugins.entries.googlechat.enabled: true`
3. **Gateway 网关未重启**:添加配置后,请重启 gateway
3. **Gateway 网关未重启**:添加配置后,重启 Gateway 网关
```bash
openclaw gateway restart
```
验证渠道是否正在运行:
验证渠道是否正在运行:
```bash
openclaw channels status
# 应显示:Google Chat default: enabled, configured, ...
# Should show: Google Chat default: enabled, configured, ...
```
### 其他问题
- 检查 `openclaw channels status --probe` 是否有认证错误或缺失的 audience 配置。
- 如果没有收到消息,请确认 Chat 应用的 webhook URL 和事件订阅。
- 如果提及门控阻止了回复,请将 `botUser` 设置为应用的用户资源名,并验证 `requireMention`
- 发送测试消息时使用 `openclaw logs --follow`,查看请求是否到达 gateway
- 检查 `openclaw channels status --probe`,查看是否存在认证错误或缺失的 audience 配置。
- 如果没有收到消息,请确认 Chat 应用的 webhook URL 和事件订阅配置正确
- 如果提及门控阻止了回复,请将 `botUser` 设置为应用的用户资源名,并验证 `requireMention`
- 发送测试消息时使用 `openclaw logs --follow`,查看请求是否到达 Gateway 网关
相关文档:
- [Gateway 网关配置](/gateway/configuration)
- [安全性](/gateway/security)
- [表情回应](/tools/reactions)
- [Gateway 网关配置](/zh-CN/gateway/configuration)
- [安全性](/zh-CN/gateway/security)
- [Reactions](/zh-CN/tools/reactions)
## 相关内容
- [渠道概览](/channels) — 所有支持的渠道
- [配对](/channels/pairing) — 私信身份验证和配对流程
- [群组](/channels/groups) — 群聊行为和提及门控
- [渠道路由](/channels/channel-routing) — 消息的会话路由
- [安全性](/gateway/security) — 访问模型和加固
- [渠道概览](/zh-CN/channels) — 所有支持的渠道
- [配对](/zh-CN/channels/pairing) — 私信认证和配对流程
- [群组](/zh-CN/channels/groups) — 群聊行为和提及门控
- [渠道路由](/zh-CN/channels/channel-routing) — 消息的会话路由
- [安全性](/zh-CN/gateway/security) — 访问模型和加固措施

View File

@ -1,49 +1,47 @@
---
read_when:
- 开发 Microsoft Teams 渠道功能
- 在 Microsoft Teams 渠道上开发功能
summary: Microsoft Teams 机器人支持状态、功能和配置
title: Microsoft Teams
x-i18n:
generated_at: "2026-04-23T15:10:14Z"
generated_at: "2026-04-23T20:12:13Z"
model: gpt-5.4
provider: openai
source_hash: 872318a0f4ec42efef3c13bc90b4af1b3875ade431bd314583ea2cdc038cdc7c
source_hash: 014fddd2de77a150113bf0f09bebe49ccda5847087929e01d9c0c85e4cd1219e
source_path: channels/msteams.md
workflow: 15
---
# Microsoft Teams
> “进入此处者,放弃一切希望。”
状态:支持文本和私信附件;频道/群组文件发送需要 `sharePointSiteId` + Graph 权限(参见[在群聊中发送文件](#sending-files-in-group-chats))。投票通过 Adaptive Cards 发送。消息操作会公开明确的 `upload-file`,用于优先发送文件的场景。
支持文本和私信附件;渠道和群组文件发送需要 `sharePointSiteId` + Graph 权限(参见 [在群组聊天中发送文件](#sending-files-in-group-chats))。投票通过 Adaptive Cards 发送。消息操作公开了显式的 `upload-file`,用于文件优先发送。
## 内置插件
Microsoft Teams 在当前的 OpenClaw 版本中作为内置插件提供,因此在常规打包构建中不需要单独安装。
Microsoft Teams 作为内置插件随当前的 OpenClaw 版本提供,因此在常规打包构建中
无需单独安装。
如果你使用的是较旧的构建版本,或是不包含内置 Teams 的自定义安装,请手动安装它:
如果你使用的是较旧版本,或是不包含内置 Teams 的自定义安装,
请手动安装:
```bash
openclaw plugins install @openclaw/msteams
```
本地检出版本当你从 git 仓库运行时):
本地检出(从 git 仓库运行时):
```bash
openclaw plugins install ./path/to/local/msteams-plugin
```
详情:[Plugins](/zh-CN/tools/plugin)
详情: [Plugins](/zh-CN/tools/plugin)
## 快速设置(新手
## 快速设置(初学者
1. 确保 Microsoft Teams 插件可用。
- 当前打包发布的 OpenClaw 版本已默认内置该插件
- 较旧/自定义安装可使用上面的命令手动添加。
- 当前打包的 OpenClaw 版本已默认内置它
- 较旧版本/自定义安装可使用上述命令手动添加。
2. 创建一个 **Azure Bot**App ID + 客户端密钥 + 租户 ID
3. 使用这些凭证配置 OpenClaw。
4. 通过公共 URL 或隧道暴露 `/api/messages`(默认端口为 3978
4. 通过公共 URL 或隧道公开 `/api/messages`(默认端口为 3978
5. 安装 Teams 应用包并启动 Gateway 网关。
最小配置(客户端密钥):
@ -62,15 +60,9 @@ openclaw plugins install ./path/to/local/msteams-plugin
}
```
对于生产部署,建议使用[联合身份验证](#federated-authentication-certificate--managed-identity)(证书或托管身份),而不是客户端密钥。
对于生产部署,建议考虑使用 [联合身份验证](#federated-authentication-certificate--managed-identity)(证书或托管身份)来替代客户端密钥。
注意:群聊默认被阻止(`channels.msteams.groupPolicy: "allowlist"`)。若要允许群组回复,请设置 `channels.msteams.groupAllowFrom`(或使用 `groupPolicy: "open"` 以允许任何成员,默认仍需提及)。
## 目标
- 通过 Teams 私信、群聊或频道与 OpenClaw 交流。
- 保持路由可预测:回复始终返回到消息到达时所在的渠道。
- 默认使用安全的渠道行为(除非另有配置,否则必须提及)。
注意:默认会阻止群组聊天(`channels.msteams.groupPolicy: "allowlist"`)。要允许群组回复,请设置 `channels.msteams.groupAllowFrom`(或者使用 `groupPolicy: "open"` 来允许任何成员,默认仍需提及)。
## 配置写入
@ -88,17 +80,17 @@ openclaw plugins install ./path/to/local/msteams-plugin
**私信访问**
- 默认:`channels.msteams.dmPolicy = "pairing"`。未知发送者在获前会被忽略。
- 默认 `channels.msteams.dmPolicy = "pairing"`。未知发送者在获批前会被忽略。
- `channels.msteams.allowFrom` 应使用稳定的 AAD 对象 ID。
- UPN/显示名称可能变化;默认禁用直接匹配,仅在 `channels.msteams.dangerouslyAllowNameMatching: true` 时启用。
- 当凭证权限允许时,向导可通过 Microsoft Graph 将名称解析为 ID。
- UPN/显示名称是可变的;默认禁用直接匹配,只有在设置 `channels.msteams.dangerouslyAllowNameMatching: true`才会启用。
- 当凭证允许时,向导可通过 Microsoft Graph 将名称解析为 ID。
**群组访问**
- 默认:`channels.msteams.groupPolicy = "allowlist"`(除非你添加 `groupAllowFrom`,否则会被阻止)。当未设置时,可使用 `channels.defaults.groupPolicy` 覆盖默认值。
- `channels.msteams.groupAllowFrom` 控制哪些发送者可以在群聊/频道中触发(回退到 `channels.msteams.allowFrom`)。
- 默认 `channels.msteams.groupPolicy = "allowlist"`(除非你添加 `groupAllowFrom`,否则会被阻止)。当未设置时,可使用 `channels.defaults.groupPolicy` 覆盖默认值。
- `channels.msteams.groupAllowFrom` 控制哪些发送者可以在群组聊天/渠道中触发(回退到 `channels.msteams.allowFrom`)。
- 设置 `groupPolicy: "open"` 可允许任何成员(默认仍需提及)。
- 若要**不允许任何道**,请设置 `channels.msteams.groupPolicy: "disabled"`
- 若要**不允许任何道**,请设置 `channels.msteams.groupPolicy: "disabled"`
示例:
@ -113,13 +105,14 @@ openclaw plugins install ./path/to/local/msteams-plugin
}
```
**Teams + 道允许列表**
**Teams + 道允许列表**
- 通过在 `channels.msteams.teams` 下列出 teams 和 channels限制群组/频道回复范围。
- 键应使用稳定的 team ID 和 channel 会话 ID。
- 当 `groupPolicy="allowlist"` 且存在 teams 允许列表时,仅接受列出的 teams/channels且需要提及
- 配置向导接受 `Team/Channel` 条目,并会帮你保存。
- 启动时OpenClaw 会将 team/channel 和用户允许列表中的名称解析为 ID当 Graph 权限允许时),并记录映射;未解析的 team/channel 名称会按输入原样保留,但默认会在路由中被忽略,除非启用了 `channels.msteams.dangerouslyAllowNameMatching: true`
- 通过在 `channels.msteams.teams` 下列出团队和渠道,限定群组/渠道回复范围。
- 键应使用稳定的团队 ID 和渠道会话 ID。
- 当 `groupPolicy="allowlist"` 且存在 teams 允许列表时,仅接受列出的团队/渠道(仍需提及)。
- 配置向导接受 `Team/Channel` 条目,并会帮你存储它们。
- 启动时OpenClaw 会将团队/渠道和用户允许列表中的名称解析为 ID当 Graph 权限允许时)
并记录映射;默认情况下,无法解析的团队/渠道名称会按原样保留,但会在路由中被忽略,除非启用 `channels.msteams.dangerouslyAllowNameMatching: true`
示例:
@ -142,43 +135,43 @@ openclaw plugins install ./path/to/local/msteams-plugin
## Azure Bot 设置
在配置 OpenClaw 之前,请先创建一个 Azure Bot 资源并保存其凭证。
在配置 OpenClaw 之前,请先创建 Azure Bot 资源并记录其凭证。
<Steps>
<Step title="创建 Azure Bot">
前往 [Create Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot),填写 **Basics** 选项卡:
前往 [创建 Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot)填写 **Basics** 选项卡:
| Field | Value |
| ------------------ | ------------------------------------------------- |
| **Bot handle** | 你的机器人名称,例如 `openclaw-msteams`(必须唯一) |
| **Subscription** | 你的 Azure 订阅 |
| **Resource group** | 新建或使用现有资源组 |
| **Pricing tier** | 开发/测试请选择 **Free** |
| **Type of App** | 推荐使用 **Single Tenant** |
| **Creation type** | **Create new Microsoft App ID** |
| Field | Value |
| ------------------ | -------------------------------------------------------- |
| **Bot handle** | 你的机器人名称,例如 `openclaw-msteams`(必须唯一) |
| **Subscription** | 你的 Azure 订阅 |
| **Resource group** | 新建或使用现有资源组 |
| **Pricing tier** | 开发/测试请选择 **Free** |
| **Type of App** | **Single Tenant**(推荐) |
| **Creation type** | **Create new Microsoft App ID** |
<Note>
新的多租户 bot 已在 2025-07-31 之后弃用。新建 bot 请使用 **Single Tenant**
2025-07-31 之后不再支持新的多租户机器人。新机器人请使用 **Single Tenant**
</Note>
点击 **Review + create****Create**(等待约 12 分钟)。
点击 **Review + create****Create**(等待约 1-2 分钟)。
</Step>
<Step title="获取凭证">
<Step title="记录凭证">
在 Azure Bot 资源中,进入 **Configuration**
- 复制 **Microsoft App ID**`appId`
- 点击 **Manage Password****Certificates & secrets****New client secret** → 复制其值 → `appPassword`
- 进入 **Overview****Directory (tenant) ID**`tenantId`
- 点击 **Overview****Directory (tenant) ID**`tenantId`
</Step>
<Step title="配置消息点">
<Step title="配置消息终结点">
Azure Bot → **Configuration** → 设置 **Messaging endpoint**
- 生产环境:`https://your-domain.com/api/messages`
- 本地开发:使用隧道(参见[本地开发](#local-development-tunneling)
- 生产环境: `https://your-domain.com/api/messages`
- 本地开发:使用隧道(参见 [本地开发](#local-development-tunneling)
</Step>
@ -187,22 +180,20 @@ openclaw plugins install ./path/to/local/msteams-plugin
</Step>
</Steps>
<a id="federated-authentication-certificate--managed-identity"></a>
## 联合身份验证
> 添加于 2026.3.24
对于生产部署OpenClaw 支持将**联合身份验证**作为比客户端密钥更安全的替代方案。提供两种方式:
对于生产部署OpenClaw 支持 **联合身份验证**,作为比客户端密钥更安全的替代方案。支持两种方式:
### 选项 A基于证书的身份验证
使用在你的 Entra ID 应用注册中登记的 PEM 证书。
使用在你的 Entra ID 应用注册中登记的 PEM 证书。
**设置:**
1. 生成或获取证书(带私钥的 PEM 格式)。
2. 在 Entra ID → App Registration → **Certificates & secrets****Certificates** 中上传公开证书。
1. 生成或获取一个证书(包含私钥的 PEM 格式)。
2. 在 Entra ID → 应用注册 → **Certificates & secrets****Certificates** 中上传公钥证书。
**配置:**
@ -226,24 +217,24 @@ openclaw plugins install ./path/to/local/msteams-plugin
- `MSTEAMS_AUTH_TYPE=federated`
- `MSTEAMS_CERTIFICATE_PATH=/path/to/cert.pem`
### 选项 BAzure Managed Identity
### 选项 BAzure 托管身份
使用 Azure Managed Identity 实现无密码身份验证。这非常适合部署在 Azure 基础设施上的场景AKS、App Service、Azure VM因为这些环境可提供托管身份
使用 Azure 托管身份实现无密码身份验证。这非常适合部署在 Azure 基础设施AKS、App Service、Azure VM上的场景此时托管身份可用
**工作原理:**
1. bot 所在的 pod/VM 拥有托管身份(系统分配或用户分配)。
2. 一个**联合身份凭证**将该托管身份链接到 Entra ID 应用注册。
1. 机器人 pod/VM 拥有一个托管身份(系统分配或用户分配)。
2. 一个 **federated identity credential** 将该托管身份链接到 Entra ID 应用注册。
3. 在运行时OpenClaw 使用 `@azure/identity` 从 Azure IMDS 端点(`169.254.169.254`)获取令牌。
4. 该令牌会传递给 Teams SDK用于 bot 身份验证。
4. 该令牌会传递给 Teams SDK用于机器人身份验证。
**前提条件:**
- 已启用托管身份的 Azure 基础设施AKS workload identity、App Service、VM
- 已在 Entra ID 应用注册上创建联合身份凭证
- pod/VM 可访问 IMDS`169.254.169.254:80`
- 已在 Entra ID 应用注册上创建 federated identity credential
- pod/VM 可以通过网络访问 IMDS`169.254.169.254:80`
**配置(系统分配托管身份):**
**配置(系统分配托管身份):**
```json5
{
@ -260,7 +251,7 @@ openclaw plugins install ./path/to/local/msteams-plugin
}
```
**配置(用户分配托管身份):**
**配置(用户分配托管身份):**
```json5
{
@ -282,14 +273,14 @@ openclaw plugins install ./path/to/local/msteams-plugin
- `MSTEAMS_AUTH_TYPE=federated`
- `MSTEAMS_USE_MANAGED_IDENTITY=true`
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id>`(仅用户分配场景需要
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id>`(仅适用于用户分配)
### AKS workload identity 设置
对于使用 workload identity 的 AKS 部署:
1. 在 AKS 集群上**启用 workload identity**。
2. 在 Entra ID 应用注册上**创建联合身份凭证**
1. 在你的 AKS 集群上**启用 workload identity**。
2. 在 Entra ID 应用注册上**创建 federated identity credential**
```bash
az ad app federated-credential create --id <APP_OBJECT_ID> --parameters '{
@ -319,147 +310,103 @@ openclaw plugins install ./path/to/local/msteams-plugin
azure.workload.identity/use: "true"
```
5. **确保访问 IMDS 网络地址**`169.254.169.254`)——如果你使用 NetworkPolicy请添加一条出口规则允许访问 `169.254.169.254/32` 的 80 端口流量
5. **确保网络可访问** IMDS`169.254.169.254`)—— 如果你使用 NetworkPolicy请添加一条出口规则允许访问 `169.254.169.254/32` 的 80 端口。
### 身份验证类型对比
| Method | Config | Pros | Cons |
| -------------------- | ---------------------------------------------- | -------------------- | ------------------------------ |
| **Client secret** | `appPassword` | 设置简单 | 需要轮换密钥,安全性较低 |
| **Certificate** | `authType: "federated"` + `certificatePath` | 无需通过网络共享密钥 | 证书管理有额外开销 |
| **Managed Identity** | `authType: "federated"` + `useManagedIdentity` | 无密码,无需管理密钥 | 需要 Azure 基础设施 |
| Method | Config | Pros | Cons |
| -------------------- | ---------------------------------------------- | ---------------------------------- | ------------------------------------- |
| **Client secret** | `appPassword` | 设置简单 | 需要轮换密钥,安全性较低 |
| **Certificate** | `authType: "federated"` + `certificatePath` | 无需通过网络共享密钥 | 证书管理有额外开销 |
| **Managed Identity** | `authType: "federated"` + `useManagedIdentity` | 无密码,无需管理密钥 | 需要 Azure 基础设施 |
**默认行为:** 当未设置 `authType`OpenClaw 默认使用客户端密钥身份验证。现有配置无需修改即可继续工作。
## 本地开发(隧道)
Teams 无法访问 `localhost`进行本地开发时请使用隧道:
Teams 无法访问 `localhost`。本地开发时请使用隧道:
**选项 Angrok**
```bash
ngrok http 3978
# 复制 https URL例如 https://abc123.ngrok.io
# 将消息端点设置为:https://abc123.ngrok.io/api/messages
# 将消息终结点设置为: https://abc123.ngrok.io/api/messages
```
**选项 BTailscale Funnel**
```bash
tailscale funnel 3978
# 使用你的 Tailscale funnel URL 作为消息
# 使用你的 Tailscale funnel URL 作为消息终结
```
## Teams Developer Portal替代方式
你也可以使用 [Teams Developer Portal](https://dev.teams.microsoft.com/apps),而不是手动创建 manifest ZIP
除了手动创建 manifest ZIP 之外,你也可以使用 [Teams Developer Portal](https://dev.teams.microsoft.com/apps)
1. 点击 **+ New app**
2. 填写基本信息(名称、描述、开发者信息)
3. 进入 **App features** → **Bot**
3. 前往 **App features** → **Bot**
4. 选择 **Enter a bot ID manually** 并粘贴你的 Azure Bot App ID
5. 勾选作用域:**Personal**、**Team**、**Group Chat**
5. 勾选作用域: **Personal**、**Team**、**Group Chat**
6. 点击 **Distribute** → **Download app package**
7. 在 Teams 中:**Apps** → **Manage your apps****Upload a custom app** → 选择该 ZIP
7. 在 Teams 中: **Apps****Manage your apps****Upload a custom app** → 选择该 ZIP
这通常比手动编辑 JSON manifest 更容易
这通常比手动编辑 JSON manifest 更简单
## 测试机器人
**选项 AAzure Web Chat先验证 webhook**
1. 在 Azure Portal 中,进入你的 Azure Bot 资源 → **Test in Web Chat**
2. 发送一条消息 —— 你应该能看到回复
3. 这可以在设置 Teams 之前确认你的 webhook 端点可正常工作
2. 发送一条消息 —— 你应该能看到响应
3. 这可以在设置 Teams 之前确认你的 webhook 终结点工作正常
**选项 BTeams安装应用后**
**选项 BTeams安装应用后)**
1. 安装 Teams 应用(载或组织目录)
1. 安装 Teams 应用(旁加载或组织目录)
2. 在 Teams 中找到该机器人并发送一条私信
3. 检查 Gateway 网关日志中的传入 activity
## 设置(最小纯文本配置)
<Accordion title="环境变量覆盖">
1. **确保 Microsoft Teams 插件可用**
- 当前打包发布的 OpenClaw 版本已默认内置该插件。
- 较旧/自定义安装可手动添加:
- 从 npm`openclaw plugins install @openclaw/msteams`
- 从本地检出:`openclaw plugins install ./path/to/local/msteams-plugin`
任意机器人/身份验证配置键也都可以通过环境变量设置:
2. **机器人注册**
- 创建一个 Azure Bot见上文并记录
- App ID
- 客户端密钥App password
- Tenant ID单租户
- `MSTEAMS_APP_ID`、`MSTEAMS_APP_PASSWORD`、`MSTEAMS_TENANT_ID`
- `MSTEAMS_AUTH_TYPE``"secret"` 或 `"federated"`
- `MSTEAMS_CERTIFICATE_PATH`、`MSTEAMS_CERTIFICATE_THUMBPRINT`(联合身份验证 + 证书)
- `MSTEAMS_USE_MANAGED_IDENTITY`、`MSTEAMS_MANAGED_IDENTITY_CLIENT_ID`(联合身份验证 + 托管身份;客户端 ID 仅适用于用户分配)
3. **Teams 应用 manifest**
- 包含一个 `bot` 条目,其中 `botId = <App ID>`
- 作用域:`personal`、`team`、`groupChat`。
- `supportsFiles: true`(个人作用域文件处理所必需)。
- 添加 RSC 权限(见下文)。
- 创建图标:`outline.png`32x32`color.png`192x192
- 将这三个文件一起打包为 zip`manifest.json`、`outline.png`、`color.png`。
4. **配置 OpenClaw**
```json5
{
channels: {
msteams: {
enabled: true,
appId: "<APP_ID>",
appPassword: "<APP_PASSWORD>",
tenantId: "<TENANT_ID>",
webhook: { port: 3978, path: "/api/messages" },
},
},
}
```
你也可以使用环境变量代替配置键:
- `MSTEAMS_APP_ID`
- `MSTEAMS_APP_PASSWORD`
- `MSTEAMS_TENANT_ID`
- `MSTEAMS_AUTH_TYPE`(可选:`"secret"` 或 `"federated"`
- `MSTEAMS_CERTIFICATE_PATH`(联合身份验证 + 证书)
- `MSTEAMS_CERTIFICATE_THUMBPRINT`(可选,身份验证并不要求)
- `MSTEAMS_USE_MANAGED_IDENTITY`(联合身份验证 + 托管身份)
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID`(仅用户分配的 MI 需要)
5. **机器人端点**
- 将 Azure Bot Messaging Endpoint 设置为:
- `https://<host>:3978/api/messages`(或你选择的路径/端口)。
6. **运行 Gateway 网关**
- 当内置或手动安装的插件可用,且存在带有凭证的 `msteams` 配置时Teams 渠道会自动启动。
</Accordion>
## 成员信息操作
OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,因此智能体和自动化流程可以直接通过 Microsoft Graph 解析渠道成员详情(显示名称、邮箱、角色)。
OpenClaw 为 Microsoft Teams 提供了一个由 Graph 支持的 `member-info` 操作,因此智能体和自动化流程可以直接从 Microsoft Graph 解析渠道成员详细信息(显示名称、邮箱、角色)。
要求:
- `Member.Read.Group` RSC 权限(已包含在推荐的 manifest 中)
- 对于跨 team 查询:需要带管理员同意的 `User.Read.All` Graph Application 权限
- 对于跨团队查询:具有管理员同意的 `User.Read.All` Graph 应用程序权限
该操作 `channels.msteams.actions.memberInfo` 控制(默认:当 Graph 凭证可用时启用)。
该操作由 `channels.msteams.actions.memberInfo` 控制(默认:当 Graph 凭证可用时启用)。
## 历史上下文
- `channels.msteams.historyLimit` 控制会包装进提示词中的最近频道/群组消息数量。
- 回退到 `messages.groupChat.historyLimit`。设置为 `0` 可禁用(默认 50
- 获取到的线程历史会按发送者允许列表(`allowFrom` / `groupAllowFrom`)进行过滤,因此线程上下文注入目前只包含来自允许发送者的消息。
- 引用的附件上下文(由 Teams 回复 HTML 派生的 `ReplyTo*`目前会按收到时的原样传递。
- 换句话说,允许列表控制谁可以触发智能体;而当前只有特定的补充上下文路径会被过滤。
- 私信历史可通过 `channels.msteams.dmHistoryLimit`(用户轮次)进行限制。按用户覆盖:`channels.msteams.dms["<user_id>"].historyLimit`。
- `channels.msteams.historyLimit` 控制包装进提示词中的最近渠道/群组消息数量。
- 回退到 `messages.groupChat.historyLimit`。设置为 `0` 可禁用(默认值为 50
- 获取到的线程历史会经过发送者允许列表过滤(`allowFrom` / `groupAllowFrom`),因此线程上下文预填充目前只包含来自允许发送者的消息。
- 引用的附件上下文(由 Teams 回复 HTML 派生的 `ReplyTo*`)当前会按接收到的原样传递。
- 换句话说,允许列表控制谁可以触发智能体;目前只有特定的补充上下文路径会被过滤。
- 私信历史可以通过 `channels.msteams.dmHistoryLimit`(用户轮次)限制。按用户覆盖:`channels.msteams.dms["<user_id>"].historyLimit`。
## 当前 Teams RSC 权限
这些是我们 Teams 应用 manifest 中**现有的 resourceSpecific 权限**。它们仅在安装了该应用的 team/chat 内生效。
以下是我们 Teams 应用 manifest 中**现有的 resourceSpecific 权限**。它们只在安装了该应用的团队/聊天内部生效。
**用于频道team 作用域**
**用于渠道(团队范围):**
- `ChannelMessage.Read.Group`Application- 无需 @mention 即可接收所有道消息
- `ChannelMessage.Read.Group`Application- 无需 @mention 即可接收所有渠道消息
- `ChannelMessage.Send.Group`Application
- `Member.Read.Group`Application
- `Owner.Read.Group`Application
@ -467,13 +414,13 @@ OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,
- `TeamMember.Read.Group`Application
- `TeamSettings.Read.Group`Application
**用于群聊:**
**用于群**
- `ChatMessage.Read.Chat`Application- 无需 @mention 即可接收所有群聊消息
- `ChatMessage.Read.Chat`Application- 无需 @mention 即可接收所有群消息
## Teams manifest 示例
包含所需字段的最小有效示例。请替换 ID 和 URL。
包含所需字段的最小有效示例。请替换其中的 ID 和 URL。
```json5
{
@ -525,9 +472,9 @@ OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,
- `bots[].botId` **必须**与 Azure Bot App ID 匹配。
- `webApplicationInfo.id` **必须**与 Azure Bot App ID 匹配。
- `bots[].scopes` 必须包含你计划使用的界面(`personal`、`team`、`groupChat`)。
- `bots[].supportsFiles: true` 是个人作用域文件处理所必需的。
- 如果你希望处理频道流量,`authorization.permissions.resourceSpecific` 必须包含频道读取/发送权限。
- `bots[].scopes` 必须包含你计划使用的界面范围`personal`、`team`、`groupChat`)。
- `bots[].supportsFiles: true` 是个人范围内文件处理所必需的。
- 如果你希望处理渠道流量,`authorization.permissions.resourceSpecific` 必须包含渠道读取/发送权限。
### 更新现有应用
@ -535,12 +482,12 @@ OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,
1. 使用新设置更新你的 `manifest.json`
2. **递增 `version` 字段**(例如 `1.0.0``1.1.0`
3. **重新打包为 zip**,包含 manifest 和图标`manifest.json`、`outline.png`、`color.png`
3. 使用图标**重新打包 zip**`manifest.json`、`outline.png`、`color.png`
4. 上传新的 zip
- **选项 ATeams Admin Center** Teams Admin Center → Teams apps → Manage apps → 找到你的应用 → Upload new version
- **选项 B载):** 在 Teams 中 → Apps → Manage your apps → Upload a custom app
5. **对于 team 频道:** 在每个 team 中重新安装应用,新的权限才会生效
6. **完全退出并重新启动 Teams**而不只是关闭窗口),以清除缓存的应用元数据
- **选项 ATeams 管理中心):** Teams 管理中心 → Teams 应用 → 管理应用 → 找到你的应用 → 上传新版本
- **选项 B旁加载):** 在 Teams 中 → Apps → Manage your apps → Upload a custom app
5. **对于团队渠道:** 在每个团队中重新安装该应用,使新权限生效
6. **完全退出并重新启动 Teams**不仅仅是关闭窗口),以清除缓存的应用元数据
## 功能:仅 RSC 与 Graph
@ -548,63 +495,63 @@ OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,
可用:
- 读取频道消息的**文本**内容。
- 发送频道消息的**文本**内容。
- 读取渠道消息**文本**内容。
- 发送渠道消息**文本**内容。
- 接收**个人(私信)**文件附件。
不可用:
- 频道/群组中的**图片或文件内容**payload 仅包含 HTML 占位内容)。
- 渠道/群组中的**图片或文件内容**(负载仅包含 HTML 占位内容)。
- 下载存储在 SharePoint/OneDrive 中的附件。
- 读取消息历史(超出实时 webhook 事件范围)。
- 读取消息历史(超出实时 webhook 事件之外)。
### Teams RSC 加 Microsoft Graph Application 权限
### Teams RSC 加 Microsoft Graph 应用程序权限
新增能力
新增:
- 下载托管内容(粘贴到消息中的图片)。
- 下载存储在 SharePoint/OneDrive 中的文件附件。
- 通过 Graph 读取频道/chat 消息历史。
- 通过 Graph 读取渠道/聊天消息历史。
### RSC 与 Graph API 对比
### RSC 与 Graph API
| Capability | RSC Permissions | Graph API |
| ----------------------- | ----------------- | --------------------------------- |
| **Real-time messages** | 是(通过 webhook | 否(仅轮询) |
| **Historical messages** | 否 | 是(可查询历史) |
| **Setup complexity** | 仅应用 manifest | 需要管理员同意 + 令牌流程 |
| **Works offline** | 否(必须正在运行) | 是(可随时查询) |
| Capability | RSC Permissions | Graph API |
| ----------------------- | -------------------- | ----------------------------------- |
| **实时消息** | 是(通过 webhook | 否(仅轮询) |
| **历史消息** | 否 | 是(可查询历史) |
| **设置复杂度** | 仅应用 manifest | 需要管理员同意 + 令牌流程 |
| **离线可用** | 否(必须运行中) | 是(可随时查询) |
**结论:** RSC 用于实时监听Graph API 用于历史访问。如果你希望在离线期间补回错过的消息,则需要带 `ChannelMessage.Read.All` 的 Graph API需要管理员同意
**结论:** RSC 用于实时监听Graph API 用于历史访问。如果你想在离线期间补上错过的消息,就需要启用带有 `ChannelMessage.Read.All` 的 Graph API需要管理员同意
## 启用 Graph 的媒体 + 历史记录(频道必需)
## 启用 Graph 的媒体 + 历史(渠道必需)
如果你在**频道**中需要图片/文件,或希望获取**消息历史记录**,则必须启用 Microsoft Graph 权限并授予管理员同意。
如果你需要在**渠道**中处理图片/文件,或想获取**消息历史**,则必须启用 Microsoft Graph 权限并授予管理员同意。
1. 在 Entra IDAzure AD**App Registration** 中,添加 Microsoft Graph **Application permissions**
- `ChannelMessage.Read.All`频道附件 + 历史记录
- `Chat.Read.All``ChatMessage.Read.All`(群聊)
1. 在 Entra IDAzure AD**应用注册**中,添加 Microsoft Graph **应用程序权限**
- `ChannelMessage.Read.All`渠道附件 + 历史
- `Chat.Read.All``ChatMessage.Read.All`(群
2. 为租户**授予管理员同意**。
3. 提升 Teams 应用 **manifest version**,重新上传,并且**在 Teams 中重新安装应用**
3. 提升 Teams 应用 **manifest 版本**,重新上传,并在 **Teams 中重新安装应用**
4. **完全退出并重新启动 Teams**,以清除缓存的应用元数据。
**用户提及的附加权限:** 对于当前对话中的用户,用户 @mention 开箱即用。不过,如果你想动态搜索并提及**当前对话之外**的用户,请添加 `User.Read.All`Application权限并授予管理员同意。
**用户提及的附加权限:** 对于会话中的用户,用户 @mentions 可直接使用。但是,如果你想动态搜索并提及**当前会话之外**的用户,请添加 `User.Read.All`Application权限并授予管理员同意。
## 已知限制
### Webhook 超时
Teams 通过 HTTP webhook 递消息。如果处理耗时过长(例如 LLM 响应较慢),你可能会看到:
Teams 通过 HTTP webhook 递消息。如果处理耗时过长(例如 LLM 响应较慢),你可能会看到:
- Gateway 网关超时
- Teams 重试消息(导致重复)
- Teams 重试发送消息(导致重复)
- 回复丢失
OpenClaw 通过快速返回并主动发送回复来处理这种情况,但极慢的响应仍可能引发问题。
OpenClaw 通过快速返回并主动发送回复来处理这一问题,但响应非常慢时仍可能出现问题。
### 格式化
Teams 的 markdown 能力比 Slack 或 Discord 更有限
Teams 的 markdown 比 Slack 或 Discord 更受限制
- 基本格式可用:**粗体**、_斜体_、`code`、链接
- 复杂 markdown表格、嵌套列表可能无法正确渲染
@ -612,14 +559,14 @@ Teams 的 markdown 能力比 Slack 或 Discord 更有限:
## 配置
分组设置(共享渠道模式参见 `/gateway/configuration`)。
按组分类的设置(共享渠道模式参见 `/gateway/configuration`)。
<AccordionGroup>
<Accordion title="核心与 webhook">
- `channels.msteams.enabled`
- `channels.msteams.appId`、`appPassword`、`tenantId`:机器人凭证
- `channels.msteams.webhook.port`(默认 `3978`
- `channels.msteams.webhook.path`(默认 `/api/messages`
- `channels.msteams.webhook.port`(默认 `3978`
- `channels.msteams.webhook.path`(默认 `/api/messages`
</Accordion>
<Accordion title="身份验证">
@ -629,17 +576,17 @@ Teams 的 markdown 能力比 Slack 或 Discord 更有限:
</Accordion>
<Accordion title="访问控制">
- `dmPolicy``pairing | allowlist | open | disabled`默认pairing
- `allowFrom`:私信允许列表,推荐使用 AAD 对象 ID当 Graph 访问可用时,向导会解析名称
- `dangerouslyAllowNameMatching`:用于可变 UPN/显示名称及 team/channel 名称路由的紧急放行开关
- `requireMention`:在频道/群组中要求 @mention(默认 `true`
- `dmPolicy``pairing | allowlist | open | disabled`(默认pairing
- `allowFrom`:私信允许列表,优先使用 AAD 对象 ID当 Graph 访问可用时,向导会解析名称
- `dangerouslyAllowNameMatching`:用于可变 UPN/显示名称以及团队/渠道名称路由的紧急开关
- `requireMention`:在渠道/群组中要求 @mention(默认值 `true`
</Accordion>
<Accordion title="Team 和频道覆盖">
<Accordion title="团队和渠道覆盖">
所有这些设置都会覆盖顶层默认值:
- `teams.<teamId>.replyStyle`、`.requireMention`
- `teams.<teamId>.tools`、`.toolsBySender`:按 team 设置的工具策略默认值
- `teams.<teamId>.tools`、`.toolsBySender`:按团队划分的工具策略默认值
- `teams.<teamId>.channels.<conversationId>.replyStyle`、`.requireMention`
- `teams.<teamId>.channels.<conversationId>.tools`、`.toolsBySender`
@ -649,38 +596,38 @@ Teams 的 markdown 能力比 Slack 或 Discord 更有限:
<Accordion title="投递、媒体和操作">
- `textChunkLimit`:出站文本分块大小
- `chunkMode``length`(默认)或 `newline`先按段落边界拆分,再按长度拆分
- `chunkMode``length`(默认)或 `newline`在按长度分割前先按段落边界分割
- `mediaAllowHosts`:入站附件主机允许列表(默认包含 Microsoft/Teams 域名)
- `mediaAuthAllowHosts`:重试时接收 Authorization 头的主机(默认包含 Graph + Bot Framework
- `replyStyle``thread | top-level`(参见[回复样式](#reply-style-threads-vs-posts)
- `actions.memberInfo`:切换基于 Graph 的成员信息操作(默认在 Graph 可用时开启)
- `sharePointSiteId`:群聊/频道中文件上传所必需(参见[在群聊中发送文件](#sending-files-in-group-chats)
- `mediaAuthAllowHosts`:重试时允许接收 Authorization 头的主机(默认包含 Graph + Bot Framework
- `replyStyle``thread | top-level`(参见 [回复样式](#reply-style-threads-vs-posts)
- `actions.memberInfo`:切换由 Graph 支持的成员信息操作(默认在 Graph 可用时开启)
- `sharePointSiteId`:群组聊天/渠道中文件上传所必需(参见 [在群组聊天中发送文件](#sending-files-in-group-chats)
</Accordion>
</AccordionGroup>
## 路由会话
## 路由会话
- 会话键遵循标准智能体格式(参见 [/concepts/session](/zh-CN/concepts/session)
- 私信共享主会话(`agent:<agentId>:<mainKey>`)。
- 道/群组消息使用会话 ID
- 道/群组消息使用会话 ID
- `agent:<agentId>:msteams:channel:<conversationId>`
- `agent:<agentId>:msteams:group:<conversationId>`
## 回复样式:线程 vs 发帖
## 回复样式:线程与帖子
Teams 最近在相同的底层数据模型之上引入了两种道 UI 样式:
Teams 最近在相同的底层数据模型之上引入了两种道 UI 样式:
| Style | Description | Recommended `replyStyle` |
| ------------------------ | -------------------------------------- | ------------------------ |
| **Posts**(经典) | 消息显示为卡片,下方带线程回复 | `thread`(默认) |
| **Threads**(类似 Slack | 消息线性流动,更像 Slack | `top-level` |
| Style | Description | Recommended `replyStyle` |
| ------------------------ | --------------------------------------------------------- | ------------------------ |
| **Posts**(经典) | 消息显示为卡片,下方带线程回复 | `thread`(默认) |
| **Threads**(类似 Slack | 消息线性流动,更像 Slack | `top-level` |
**问题** Teams API 不会公开频道使用的是哪种 UI 样式。如果你使用了错误的 `replyStyle`
**问题在于:** Teams API 不会暴露渠道使用的是哪种 UI 样式。如果你使用了错误的 `replyStyle`
- 在 Threads 样式的频道中使用 `thread` → 回复会以别扭的嵌套方式显示
- 在 Posts 样式的频道中使用 `top-level` → 回复会显示为单独的顶层发帖,而不是在线程内
- 在 Threads 风格的渠道中使用 `thread` → 回复会以不自然的嵌套方式显示
- 在 Posts 风格的渠道中使用 `top-level` → 回复会显示为独立的顶层帖子,而不是在线程中
**解决方案:** 根据频道的实际设置,按频道配置 `replyStyle`
**解决方案:** 根据渠道的设置,为每个渠道配置 `replyStyle`
```json5
{
@ -705,40 +652,40 @@ Teams 最近在相同的底层数据模型之上引入了两种频道 UI 样式
**当前限制:**
- **私信:** 图片和文件附件可通过 Teams bot 文件 API 正常工作。
- **频道/群组:** 附件保存在 M365 存储SharePoint/OneDrive中。webhook payload 仅包含一个 HTML 占位内容,而不包含实际文件字节。**下载频道附件需要 Graph API 权限**。
- 对于显式的优先发送文件场景,请使用 `action=upload-file`配合 `media` / `filePath` / `path`;可选的 `message` 会作为附带文本/评论,`filename` 会覆盖上传名称。
- **私信:** 图片和文件附件可通过 Teams 机器人文件 API 正常工作。
- **渠道/群组:** 附件存储在 M365 存储中SharePoint/OneDrive。webhook 负载只包含一个 HTML 占位内容,不包含实际文件字节。**下载渠道附件需要 Graph API 权限**。
- 对于显式的文件优先发送,请使用 `action=upload-file`,配合 `media` / `filePath` / `path`;可选的 `message` 会作为附带文本/评论,`filename` 会覆盖上传后的名称。
如果没有 Graph 权限,带图片的频道消息会以纯文本形式接收(机器人无法访问图片内容)。
默认情况下OpenClaw 从 Microsoft/Teams 主机名下载媒体。可通过 `channels.msteams.mediaAllowHosts` 覆盖(使用 `["*"]` 允许任意主机)。
Authorization 头只会附加到 `channels.msteams.mediaAuthAllowHosts` 中的主机(默认包含 Graph + Bot Framework 主机)。请保持该列表严格受限(避免多租户后缀)。
如果没有 Graph 权限,带图片的渠道消息会被作为纯文本接收(机器人无法访问图片内容)。
默认情况下OpenClaw 只会从 Microsoft/Teams 主机名下载媒体。可通过 `channels.msteams.mediaAllowHosts` 覆盖(使用 `["*"]` 允许任意主机)。
Authorization 头只会附加到 `channels.msteams.mediaAuthAllowHosts` 中的主机(默认包括 Graph + Bot Framework 主机)。请保持此列表严格受限(避免多租户后缀)。
## 在群聊中发送文件
## 在群中发送文件
机器人可以在私信中使用 FileConsentCard 流程发送文件(内置支持)。但是,**在群聊/频道中发送文件**需要额外设置:
机器人可以使用 FileConsentCard 流程(内置)在私信中发送文件。不过,**在群组聊天/渠道中发送文件**需要额外设置:
| Context | How files are sent | Setup needed |
| ------------------------ | ------------------------------------- | ----------------------------------------------- |
| **私信** | FileConsentCard → 用户接受 → 机器人上传 | 开箱即用 |
| **群聊/频道** | 上传到 SharePoint → 分享链接 | 需要 `sharePointSiteId` + Graph 权限 |
| **图片(任意上下文)** | Base64 编码内联 | 开箱即用 |
| Context | How files are sent | Setup needed |
| ------------------------ | -------------------------------------------- | ----------------------------------------------- |
| **私信** | FileConsentCard → 用户接受 → 机器人上传 | 开箱即用 |
| **群组聊天/渠道** | 上传到 SharePoint → 分享链接 | 需要 `sharePointSiteId` + Graph 权限 |
| **图片(任意上下文)** | Base64 编码内联 | 开箱即用 |
### 为什么群聊需要 SharePoint
### 为什么群需要 SharePoint
机器人没有个人 OneDrive drive`/me/drive` Graph API 端点不适用于应用身份)。要在群聊/频道中发送文件,机器人会上传到一个 **SharePoint site** 并创建共享链接。
机器人没有个人 OneDrive 驱动器(`/me/drive` Graph API 端点不适用于应用身份)。要在群组聊天/渠道中发送文件,机器人需要上传到一个 **SharePoint 站点** 并创建共享链接。
### 设置
1. 在 Entra IDAzure ADApp Registration 中**添加 Graph API 权限**
1. 在 Entra IDAzure AD应用注册中**添加 Graph API 权限**
- `Sites.ReadWrite.All`Application- 上传文件到 SharePoint
- `Chat.Read.All`Application- 可选,用于启用按用户的共享链接
- `Chat.Read.All`Application- 可选,启用按用户的共享链接
2. 为租户**授予管理员同意**。
3. **获取你的 SharePoint site ID**
```bash
# 通过 Graph Explorer 或使用有效令牌配合 curl
# 通过 Graph Explorer 或使用有效令牌 curl
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}"
@ -746,7 +693,7 @@ Authorization 头只会附加到 `channels.msteams.mediaAuthAllowHosts` 中的
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles"
# 响应中包含:"id": "contoso.sharepoint.com,guid1,guid2"
# 响应中包含: "id": "contoso.sharepoint.com,guid1,guid2"
```
4. **配置 OpenClaw**
@ -764,40 +711,40 @@ Authorization 头只会附加到 `channels.msteams.mediaAuthAllowHosts` 中的
### 共享行为
| Permission | Sharing behavior |
| --------------------------------------- | ---------------------------------------------- |
| `Sites.ReadWrite.All` only | 组织范围共享链接(组织内任何人都可访问) |
| `Sites.ReadWrite.All` + `Chat.Read.All` | 按用户共享链接(仅聊天成员可访问) |
| Permission | Sharing behavior |
| --------------------------------------- | --------------------------------------------------------- |
| `Sites.ReadWrite.All` only | 组织共享链接(组织内任何人都可访问) |
| `Sites.ReadWrite.All` + `Chat.Read.All` | 按用户共享链接(只有聊天成员可访问) |
按用户共享更安全,因为只有聊天参与者可以访问文件。如果缺少 `Chat.Read.All` 权限,机器人会回退到组织范围共享。
按用户共享更安全,因为只有聊天参与者可以访问文件。如果缺少 `Chat.Read.All` 权限,机器人会回退为全组织共享。
### 回退行为
| Scenario | Result |
| ------------------------------------------------- | -------------------------------------------- |
| 群聊 + 文件 + 已配置 `sharePointSiteId` | 上传到 SharePoint发送共享链接 |
| 群聊 + 文件 + 无 `sharePointSiteId` | 尝试上传到 OneDrive可能失败仅发送文本 |
| 个人聊天 + 文件 | FileConsentCard 流程(无需 SharePoint 即可工作) |
| 任意上下文 + 图片 | Base64 编码内联(无需 SharePoint 即可工作) |
| Scenario | Result |
| ------------------------------------------------- | -------------------------------------------------- |
| 群 + 文件 + 已配置 `sharePointSiteId` | 上传到 SharePoint发送共享链接 |
| 群 + 文件 + 无 `sharePointSiteId` | 尝试上传到 OneDrive可能失败仅发送文本 |
| 个人聊天 + 文件 | FileConsentCard 流程(无需 SharePoint 即可工作) |
| 任意上下文 + 图片 | Base64 编码内联(无需 SharePoint 即可工作) |
### 文件存储位置
上传的文件存储在已配置 SharePoint site 默认文档库中的 `/OpenClawShared/` 文件夹下。
上传的文件会存储在已配置 SharePoint 站点默认文档库中的 `/OpenClawShared/` 文件夹下。
## 投票Adaptive Cards
OpenClaw 通过 Adaptive Cards 发送 Teams 投票Teams 没有原生投票 API
OpenClaw 将 Teams 投票作为 Adaptive Cards 发送Teams 没有原生投票 API
- CLI`openclaw message poll --channel msteams --target conversation:<id> ...`
- CLI `openclaw message poll --channel msteams --target conversation:<id> ...`
- 投票由 Gateway 网关记录在 `~/.openclaw/msteams-polls.json` 中。
- Gateway 网关必须保持在线才能记录投票。
- 投票目前还不会自动发布结果摘要(如有需要,请检查存储文件)。
## 展示卡片
使用 `message` 工具或 CLI 向 Teams 用户或会话发送语义化展示 payload。OpenClaw 会根据通用展示契约将其渲染为 Teams Adaptive Cards。
使用 `message` 工具或 CLI 向 Teams 用户或会话发送语义化展示负载。OpenClaw 会根据通用展示契约将其渲染为 Teams Adaptive Cards。
`presentation` 参数接受语义块。提供 `presentation` 时,消息文本为可选项。
`presentation` 参数接受语义块。提供 `presentation` 时,消息文本为可选项。
**智能体工具:**
@ -821,7 +768,7 @@ openclaw message send --channel msteams \
--presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello!"}]}'
```
关于目标格式的详细信息,参见下方的[目标格式](#target-formats)。
有关目标格式的详细信息,参见下文的 [目标格式](#target-formats)。
## 目标格式
@ -831,8 +778,8 @@ MSTeams 目标使用前缀来区分用户和会话:
| ------------------- | -------------------------------- | --------------------------------------------------- |
| 用户(按 ID | `user:<aad-object-id>` | `user:40a1a0ed-4ff2-4164-a219-55518990c197` |
| 用户(按名称) | `user:<display-name>` | `user:John Smith`(需要 Graph API |
| 群组/道 | `conversation:<conversation-id>` | `conversation:19:abc123...@thread.tacv2` |
| 群组/道(原始) | `<conversation-id>` | `19:abc123...@thread.tacv2`(如果包含 `@thread` |
| 群组/道 | `conversation:<conversation-id>` | `conversation:19:abc123...@thread.tacv2` |
| 群组/道(原始) | `<conversation-id>` | `19:abc123...@thread.tacv2`(如果包含 `@thread` |
**CLI 示例:**
@ -840,10 +787,10 @@ MSTeams 目标使用前缀来区分用户和会话:
# 按 ID 向用户发送
openclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello"
# 按显示名称向用户发送(会触发 Graph API 查找
# 按显示名称向用户发送(触发 Graph API 查询
openclaw message send --channel msteams --target "user:John Smith" --message "Hello"
# 发送到群聊或频
# 发送到群组聊天或渠
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello"
# 向会话发送展示卡片
@ -874,31 +821,31 @@ openclaw message send --channel msteams --target "conversation:19:abc...@thread.
}
```
注意:如果没有 `user:` 前缀,名称默认按群组/team 解析。按显示名称指定个人时,请始终使用 `user:`
注意:如果没有 `user:` 前缀,名称默认会按群组/团队解析。按显示名称定位个人时,请始终使用 `user:`
## 主动消息
- 主动消息只有在用户已经交互**之后**才可发送,因为我们会在那时保存会话引用。
- 关于 `dmPolicy` 和允许列表限制,参见 `/gateway/configuration`
- 只有在用户发生交互**之后**,才可以发送主动消息,因为我们会在那时存储会话引用。
- 有关 `dmPolicy` 和允许列表控制,请参见 `/gateway/configuration`
## Team 和频道 ID
## 团队和渠道 ID
Teams URL 中的 `groupId` 查询参数**不是**配置中使用的 team ID。从 URL 路径中提取 ID
Teams URL 中的 `groupId` 查询参数**不是**配置中使用的 team ID。从 URL 路径中提取 ID
**Team URL**
**团队 URL**
```
https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
└────────────────────────────┘
Team ID进行 URL 解码)
Team ID进行 URL 解码)
```
**道 URL**
**道 URL**
```
https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
└─────────────────────────┘
Channel ID进行 URL 解码)
Channel ID进行 URL 解码)
```
**用于配置:**
@ -907,61 +854,73 @@ https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?gr
- Channel ID = `/channel/` 之后的路径段URL 解码后)
- **忽略** `groupId` 查询参数
## 私有
## 私有
机器人在私有道中的支持有限:
机器人在私有道中的支持有限:
| Feature | Standard Channels | Private Channels |
| ---------------------------- | ----------------- | ---------------- |
| 机器人安装 | 是 | 有限 |
| 实时消息webhook | 是 | 可能无法工作 |
| RSC 权限 | 是 | 行为可能不同 |
| @mentions | 是 | 如果机器人可访问 |
| Graph API 历史记录 | 是 | 是(有权限时) |
| Feature | Standard Channels | Private Channels |
| ---------------------------- | ----------------- | ------------------ |
| 机器人安装 | 是 | 有限 |
| 实时消息webhook | 是 | 可能无法工作 |
| RSC 权限 | 是 | 行为可能不同 |
| @mentions | 是 | 如果机器人可访问 |
| Graph API 历史 | 是 | 是(有权限时) |
**如果私有频道无法工作,可使用以下变通方案**
**如果私有渠道无法工作,可采用以下变通方法**
1. 对于机器人交互,使用标准频道
1. 使用标准渠道进行机器人交互
2. 使用私信 —— 用户始终可以直接给机器人发消息
3. 使用 Graph API 访问历史记录(需要 `ChannelMessage.Read.All`
3. 使用 Graph API 获取历史访问(需要 `ChannelMessage.Read.All`
## 故障排除
### 常见问题
- **频道中图片不显示:** 缺少 Graph 权限或管理员同意。请重新安装 Teams 应用,并完全退出后重新打开 Teams。
- **频道中没有响应:** 默认要求提及;请设置 `channels.msteams.requireMention=false`,或按 team/频道进行配置。
- **版本不匹配Teams 仍显示旧 manifest** 移除并重新添加应用,然后完全退出 Teams 以刷新。
- **webhook 返回 401 Unauthorized** 如果你在没有 Azure JWT 的情况下手动测试,这是预期行为 —— 说明端点可达,但身份验证失败。请使用 Azure Web Chat 正确测试。
- **图片未显示在渠道中:** 缺少 Graph 权限或管理员同意。重新安装 Teams 应用,并完全退出/重新打开 Teams。
- **渠道中没有响应:** 默认要求提及;设置 `channels.msteams.requireMention=false` 或按团队/渠道分别配置。
- **版本不匹配Teams 仍显示旧 manifest** 删除并重新添加该应用,然后完全退出 Teams 以刷新。
- **webhook 返回 401 Unauthorized** 在未携带 Azure JWT 的情况下手动测试时这是预期行为 —— 说明终结点可达,但身份验证失败。请使用 Azure Web Chat 正确测试。
### Manifest 上传错误
- **“Icon file cannot be empty”** manifest 引用了大小为 0 字节的图标文件。请创建有效的 PNG 图标(`outline.png` 为 32x32`color.png` 为 192x192
- **“webApplicationInfo.Id already in use”** 该应用仍安装在另一个 team/chat 中。请先找到并卸载它,或等待 510 分钟让更改传播。
- **上传时出现 “Something went wrong”** 请改用 [https://admin.teams.microsoft.com](https://admin.teams.microsoft.com) 上传,打开浏览器 DevToolsF12→ Network 选项卡,并检查响应体中的实际错误信息
- **侧载失败:** 请尝试使用 “Upload an app to your org's app catalog”而不是 “Upload a custom app” —— 这通常可以绕过侧载限制。
- **“Icon file cannot be empty”** manifest 引用的图标文件为 0 字节。请创建有效的 PNG 图标(`outline.png` 为 32x32`color.png` 为 192x192
- **“webApplicationInfo.Id already in use”** 该应用仍安装在另一个团队/聊天中。先找到并卸载,或等待 5-10 分钟让变更传播。
- **上传时出现 “Something went wrong”** 请改用 [https://admin.teams.microsoft.com](https://admin.teams.microsoft.com) 上传,打开浏览器 DevToolsF12→ Network 选项卡,并检查响应体中的实际错误。
- **旁加载失败:** 尝试使用 “Upload an app to your org's app catalog” 而不是 “Upload a custom app” —— 这通常可以绕过旁加载限制。
### RSC 权限不起作用
1. 验证 `webApplicationInfo.id` 是否与你机器人的 App ID 完全一致
2. 重新上传应用,并在 team/chat 中重新安装
2. 重新上传应用,并在团队/聊天中重新安装
3. 检查你的组织管理员是否阻止了 RSC 权限
4. 确认你使用了正确的作用域team 使用 `ChannelMessage.Read.Group`,群聊使`ChatMessage.Read.Chat`
4. 确认你使用了正确的范围:团队用 `ChannelMessage.Read.Group`,群组聊天`ChatMessage.Read.Chat`
## 参考资料
- [Create Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - Azure Bot 设置指南
- [创建 Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - Azure Bot 设置指南
- [Teams Developer Portal](https://dev.teams.microsoft.com/apps) - 创建/管理 Teams 应用
- [Teams app manifest schema](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema) - Teams 应用 manifest schema
- [Receive channel messages with RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc) - 使用 RSC 接收频道消息
- [RSC permissions reference](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent) - RSC 权限参考
- [Teams bot file handling](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4)道/群组需要 Graph
- [Proactive messaging](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages) - 主动消息
- [Teams 应用 manifest schema](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema)
- [使用 RSC 接收渠道消息](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc)
- [RSC 权限参考](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent)
- [Teams 机器人文件处理](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4)道/群组需要 Graph
- [主动消息](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages)
## 相关内容
- [Channels Overview](/zh-CN/channels) — 所有支持的渠道
- [Pairing](/zh-CN/channels/pairing) — 私信身份验证和配对流程
- [Groups](/zh-CN/channels/groups) — 群聊行为和提及限制
- [Channel Routing](/zh-CN/channels/channel-routing) — 消息的会话路由
- [Security](/zh-CN/gateway/security) — 访问模型和加固
<CardGroup cols={2}>
<Card title="渠道概览" icon="list" href="/zh-CN/channels">
所有受支持的渠道。
</Card>
<Card title="配对" icon="link" href="/zh-CN/channels/pairing">
私信身份验证与配对流程。
</Card>
<Card title="群组" icon="users" href="/zh-CN/channels/groups">
群组聊天行为与提及控制。
</Card>
<Card title="渠道路由" icon="route" href="/zh-CN/channels/channel-routing">
消息的会话路由。
</Card>
<Card title="安全" icon="shield" href="/zh-CN/gateway/security">
访问模型与加固。
</Card>
</CardGroup>

View File

@ -1,27 +1,27 @@
---
read_when:
- 你需要了解某个 CI 作业为什么运行了,者为什么没有运行
- 你需要了解某个 CI 作业为什么运行或没有运行
- 你正在调试失败的 GitHub Actions 检查
summary: CI 作业图、作用域门禁,以及本地等效命令
summary: CI 作业图、范围门控,以及本地命令对应项
title: CI 流水线
x-i18n:
generated_at: "2026-04-23T19:45:54Z"
generated_at: "2026-04-23T20:12:13Z"
model: gpt-5.4
provider: openai
source_hash: 3e250c90d9be13dc25a0b028de5d72cf821387e33c0965cac7b935579e3c6ae7
source_hash: 601bd170afc217e7e9841c3cc358b941c9706b2cec57f568c0b583888ac0b4e7
source_path: ci.md
workflow: 15
---
# CI 流水线
CI 会在每次推送到 `main` 以及每个拉取请求时运行。它使用智能作用域划分,在仅有无关区域变更时跳过高开销作业。
CI 会在每次推送到 `main` 以及每个拉取请求上运行。它使用智能范围判定,以便在只改动了无关区域时跳过昂贵作业。
QA Lab 在主智能作用域工作流之外有专门的 CI 通道。`Parity gate` 工作流会在匹配的 PR 变更以及手动触发时运行;它会构建私有 QA 运行时,并比较模拟的 GPT-5.4 和 Opus 4.6 智能体包。`QA-Lab - All Lanes` 工作流会在 `main` 上每晚运行,并支持手动触发;它会将模拟 parity gate、实时 Matrix 通道和实时 Telegram 通道作为并行作业扇出运行。实时作业使用 `qa-live-shared` 环境,而 Telegram 通道使用 Convex 租约。`OpenClaw Release Checks` 也会在发布批准前运行相同的 QA Lab 通道。
QA Lab 在主智能范围工作流之外有专用的 CI 通道。`Parity gate` 工作流会在匹配的 PR 变更和手动触发时运行;它会构建私有 QA 运行时,并比较 mock GPT-5.4 和 Opus 4.6 智能体包。`QA-Lab - All Lanes` 工作流会在 `main` 上每晚运行,也支持手动触发;它会将 mock parity gate、实时 Matrix 通道和实时 Telegram 通道作为并行作业扇出运行。实时作业使用 `qa-live-shared` environmentTelegram 通道使用 Convex leases。`OpenClaw Release Checks` 也会在发布批准前运行相同的 QA Lab 通道。
`Duplicate PRs After Merge` 工作流是一个供维护者使用的手动工作流,用于合并后的重复项清理。它默认使用 dry-run只有在 `apply=true` 时才会关闭显式列出的 PR。在修改 GitHub 之前,它会验证已合并的 PR 确实已合并,并验证每个重复 PR 要么引用了相同的 issue要么存在重叠的变更 hunk。
`Duplicate PRs After Merge` 工作流是一个供维护者在合并后进行重复 PR 清理的手动工作流。它默认是 dry-run只有在 `apply=true` 时才会关闭明确列出的 PR。在修改 GitHub 之前,它会验证已落地的 PR 确实已合并,并检查每个重复 PR 是否具有共享的引用 issue或是否存在重叠的改动 hunk。
`Test Performance Agent` 工作流是一个事件驱动的 Codex 维护通道,用于处理慢测试。它没有纯定时调度:`main` 上一次由非机器人触发且成功的 push CI 运行可以触发它,但如果当天 UTC 时间内已有另一次 workflow-run 调用已经运行或正在运行,它就会跳过。手动触发会绕过这一每日活动门禁。该通道会构建完整测试套件的分组 Vitest 性能报告,允许 Codex 仅进行小范围且不降低覆盖率的测试性能修复,然后重新运行完整测试套件报告,并拒绝任何会降低通过基线测试数量的变更。如果基线中已有失败测试Codex 只能修复明显的失败项并且在提交任何内容之前agent 运行后的完整测试套件报告必须通过
`Test Performance Agent` 工作流是一个事件驱动的 Codex 维护通道,用于处理慢测试。它没有纯定时调度:`main` 上一次成功的、非机器人触发的 push CI 运行可以触发它,但如果当天 UTC 已经有另一个 workflow-run 调用已经运行或正在运行,它就会跳过。手动触发会绕过这个每日活动门控。该通道会构建一份完整测试套件的分组 Vitest 性能报告,让 Codex 只能进行小范围且不降低覆盖率的测试性能修复,然后重新运行完整测试套件报告,并拒绝任何会降低通过基线测试数量的更改。如果基线本身存在失败测试Codex 只能修复明显的失败,并且只有在 agent 处理后的完整测试套件报告通过时,才会提交任何内容。它使用 GitHub 托管的 Ubuntu这样 Codex action 就能与 docs agent 保持相同的 drop-sudo 安全策略
```bash
gh workflow run duplicate-after-merge.yml \
@ -32,86 +32,86 @@ gh workflow run duplicate-after-merge.yml \
## 作业概览
| 作业 | 目的 | 运行时机 |
| -------------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------- |
| `preflight` | 检测是否仅为文档变更、变更的作用域、变更的扩展,并构建 CI 清单 | 所有非草稿 push 和 PR |
| `security-scm-fast` | 通过 `zizmor` 进行私钥检测和工作流审计 | 所有非草稿 push 和 PR |
| `security-dependency-audit` | 针对 npm advisories 进行无依赖的生产 lockfile 审计 | 所有非草稿 push 和 PR |
| `security-fast` | 快速安全作业的必需聚合作业 | 所有非草稿 push 和 PR |
| `build-artifacts` | 构建 `dist/`、Control UI、构建产物检查以及可复用的下游产物 | 与 Node 相关的变更 |
| `checks-fast-core` | 快速 Linux 正确性通道,例如 bundled/plugin-contract/protocol 检查 | 与 Node 相关的变更 |
| `checks-fast-contracts-channels` | 分片的渠道契约检查,并提供稳定的聚合检查结果 | 与 Node 相关的变更 |
| `checks-node-extensions` | 覆盖整个扩展套件的完整 bundled-plugin 测试分片 | 与 Node 相关的变更 |
| `checks-node-core-test` | Core Node 测试分片不包括渠道、bundled、contract 和扩展通道 | 与 Node 相关的变更 |
| `extension-fast` | 仅针对已变更 bundled plugin 的定向测试 | 带有扩展变更的拉取请求 |
| `check` | 分片后的主本地门禁等效项生产类型、lint、guard、测试类型和严格 smoke | 与 Node 相关的变更 |
| `check-additional` | 架构、边界、扩展表面 guard、包边界以及 gateway-watch 分片 | 与 Node 相关的变更 |
| `build-smoke` | 已构建 CLI 的 smoke 测试和启动内存 smoke | 与 Node 相关的变更 |
| `checks` | 用于已构建产物渠道测试的验证器,以及仅在 push 时运行的 Node 22 兼容性检查 | 与 Node 相关的变更 |
| `check-docs` | 文档格式、lint 和损坏链接检查 | 文档有变更时 |
| `skills-python` | 针对 Python 支持的 Skills 运行 Ruff + pytest | 与 Python Skills 相关的变更 |
| `checks-windows` | Windows 专用测试通道 | 与 Windows 相关的变更 |
| `macos-node` | 使用共享构建产物的 macOS TypeScript 测试通道 | 与 macOS 相关的变更 |
| `macos-swift` | macOS 应用的 Swift lint、构建和测试 | 与 macOS 相关的变更 |
| `android` | 两种 flavor 的 Android 单元测试,以及一个 debug APK 构建 | 与 Android 相关的变更 |
| `test-performance-agent` | 在可信活动之后每日运行的 Codex 慢测试优化 | 主 CI 成功后或手动触发 |
| 作业 | 目的 | 运行时机 |
| -------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------ |
| `preflight` | 检测是否仅有文档变更、已变更范围、已变更扩展,并构建 CI 清单 | 在所有非草稿 push 和 PR 上始终运行 |
| `security-scm-fast` | 通过 `zizmor` 进行私钥检测和工作流审计 | 在所有非草稿 push 和 PR 上始终运行 |
| `security-dependency-audit` | 针对 npm advisories 进行无依赖的生产 lockfile 审计 | 在所有非草稿 push 和 PR 上始终运行 |
| `security-fast` | 快速安全作业的必需聚合作业 | 在所有非草稿 push 和 PR 上始终运行 |
| `build-artifacts` | 构建 `dist/`、Control UI、构建产物检查以及可复用的下游产物 | 与 Node 相关的变更 |
| `checks-fast-core` | 快速 Linux 正确性通道,例如 bundled/plugin-contract/protocol 检查 | 与 Node 相关的变更 |
| `checks-fast-contracts-channels` | 分片的渠道契约检查,并提供稳定的聚合检查结果 | 与 Node 相关的变更 |
| `checks-node-extensions` | 针对扩展套件的完整内置插件测试分片 | 与 Node 相关的变更 |
| `checks-node-core-test` | Core Node 测试分片,不包括渠道、内置、契约和扩展通道 | 与 Node 相关的变更 |
| `extension-fast` | 仅针对已变更内置插件的聚焦测试 | 带有扩展变更的拉取请求 |
| `check` | 分片的主本地门控等价项生产类型、lint、守卫、测试类型以及严格 smoke | 与 Node 相关的变更 |
| `check-additional` | 架构、边界、扩展表面守卫、包边界以及 gateway-watch 分片 | 与 Node 相关的变更 |
| `build-smoke` | 已构建 CLI 的 smoke 测试和启动内存 smoke | 与 Node 相关的变更 |
| `checks` | 已构建产物渠道测试的校验器,以及仅在 push 上运行的 Node 22 兼容性检查 | 与 Node 相关的变更 |
| `check-docs` | 文档格式、lint 和断链检查 | 文档有变更时 |
| `skills-python` | 面向 Python 支持的 Skills 的 Ruff + pytest | 与 Python Skills 相关的变更 |
| `checks-windows` | Windows 专用测试通道 | 与 Windows 相关的变更 |
| `macos-node` | 使用共享构建产物的 macOS TypeScript 测试通道 | 与 macOS 相关的变更 |
| `macos-swift` | macOS app 的 Swift lint、构建和测试 | 与 macOS 相关的变更 |
| `android` | 两种 flavor 的 Android 单元测试,以及一次 debug APK 构建 | 与 Android 相关的变更 |
| `test-performance-agent` | 在可信活动之后每日运行的 Codex 慢测试优化 | `main` CI 成功后或手动触发 |
## 快速失败顺序
作业按顺序排列,以便低成本检查先失败,避免高成本作业继续运行
作业的排列顺序经过设计,使便宜的检查能在昂贵作业开始前先失败
1. `preflight` 决定哪些通道会存在。`docs-scope` 和 `changed-scope` 逻辑是该作业中的步骤,而不是独立作业。
2. `security-scm-fast`、`security-dependency-audit`、`security-fast`、`check`、`check-additional`、`check-docs` 和 `skills-python` 会快速失败,而无需等待更重的产物和平台矩阵作业。
3. `build-artifacts` 与快速 Linux 通道并行运行,这样下游消费者可以在共享构建准备好后立即开始。
4. 之后扇出更重的平台和运行时通道:`checks-fast-core`、`checks-fast-contracts-channels`、`checks-node-extensions`、`checks-node-core-test`、仅 PR 的 `extension-fast`、`checks`、`checks-windows`、`macos-node`、`macos-swift` 和 `android`
1. `preflight` 决定哪些通道实际存在。`docs-scope` 和 `changed-scope` 逻辑是这个作业中的步骤,而不是独立作业。
2. `security-scm-fast`、`security-dependency-audit`、`security-fast`、`check`、`check-additional`、`check-docs` 和 `skills-python` 会快速失败,而无需等待更重的构建产物和平台矩阵作业。
3. `build-artifacts` 与快速 Linux 通道并行运行,这样下游消费者就能在共享构建完成后立即开始。
4. 更重的平台和运行时通道会在此之后扇出`checks-fast-core`、`checks-fast-contracts-channels`、`checks-node-extensions`、`checks-node-core-test`、仅 PR 的 `extension-fast`、`checks`、`checks-windows`、`macos-node`、`macos-swift` 和 `android`
作用域逻辑位于 `scripts/ci-changed-scope.mjs`,并由 `src/scripts/ci-changed-scope.test.ts` 中的单元测试覆盖。
CI 工作流编辑会验证 Node CI 图以及工作流 lint但不会仅因自身变更而强制运行 Windows、Android 或 macOS 原生构建;这些平台通道仍然只对平台源码变更生效
Windows Node 检查的作用域仅限于 Windows 专用的进程/路径包装器、npm/pnpm/UI 运行器辅助工具、包管理器配置,以及执行该通道的 CI 工作流表面无关的源码、plugin、安装 smoke 和纯测试变更仍留在 Linux Node 通道中,这样它们就不会为了已由常规测试分片覆盖的内容而占用一个 16 vCPU 的 Windows worker。
单独的 `install-smoke` 工作流会通过它自己的 `preflight` 作业复用相同的作用域脚本。它根据更窄的 changed-smoke 信号计算 `run_install_smoke`,因此 Docker/安装 smoke 会针对安装、打包、与容器相关的变更、bundled extension 生产变更,以及 Docker smoke 作业所覆盖的 core plugin/channel/Gateway 网关/插件 SDK 表面运行。纯测试和纯文档编辑不会占用 Docker worker。其 QR package smoke 会强制 Docker 的 `pnpm install` 层重新运行,同时保留 BuildKit 的 pnpm store 缓存,因此它仍能覆盖安装过程,而无需每次运行都重新下载依赖。它的 gateway-network e2e 会复用该作业前面构建的运行时镜像,因此它增加了真实的 container-to-container WebSocket 覆盖,而无需新增一次 Docker 构建。本地 `test:docker:all` 会预先构建一个共享的 live-test 镜像和一个共享的 `scripts/e2e/Dockerfile` built-app 镜像,然后在 `OPENCLAW_SKIP_DOCKER_BUILD=1` 下并行运行 live/E2E smoke 通道;默认并发数为 4可通过 `OPENCLAW_DOCKER_ALL_PARALLELISM` 调整。本地聚合作业默认会在次失败后停止调度新的池化通道,并且每个通道都有一个 120 分钟的超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖。对启动或 provider 敏感的通道会在并行池之后独占运行。可复用的 live/E2E 工作流也遵循共享镜像模式:它会在 Docker 矩阵之前构建并推送一个带 SHA 标签的 GHCR Docker E2E 镜像,然后在 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行矩阵。定时的 live/E2E 工作流每天运行完整的发布路径 Docker 套件。QR 和 installer Docker 测试保各自以安装为重点的 Dockerfile。另有一个独立的 `docker-e2e-fast` 作业,会在 120 秒命令超时内运行有界的 bundled-plugin Docker 配置setup-entry 依赖修复以及合成的 bundled-loader 故障隔离。完整的 bundled 更新/渠道矩阵仍然是手动/完整套件,因为它会反复执行真实的 npm update 和 doctor repair 流程。
范围逻辑位于 `scripts/ci-changed-scope.mjs`,并由 `src/scripts/ci-changed-scope.test.ts` 中的单元测试覆盖。
CI 工作流编辑会验证 Node CI 作业图以及工作流 lint仅凭这些编辑本身不会强制运行 Windows、Android 或 macOS 原生构建;这些平台通道仍然只针对对应平台源码变更而运行
Windows Node 检查的范围限定在 Windows 专用的进程/路径包装器、npm/pnpm/UI runner 辅助、包管理器配置,以及执行该通道的 CI 工作流表面无关的源码、插件、install-smoke 和仅测试改动仍会留在 Linux Node 通道中,因此不会为已被常规测试分片覆盖的内容占用一个 16-vCPU 的 Windows worker。
独立的 `install-smoke` 工作流通过其自己的 `preflight` 作业复用同一个范围脚本。它根据更窄的 changed-smoke 信号计算 `run_install_smoke`,因此 Docker/install smoke 会在安装、打包、与容器相关的变更、内置扩展生产变更,以及 Docker smoke 作业所覆盖的核心 plugin/channel/Gateway 网关/Plugin SDK 表面发生变更时运行。仅测试和仅文档的编辑不会占用 Docker workers。它的 QR 包 smoke 会强制 Docker `pnpm install` 层重新运行,同时保留 BuildKit pnpm store cache因此仍然能够验证安装流程而不必在每次运行时重新下载依赖。它的 gateway-network e2e 会复用该作业前面构建的运行时镜像,因此它在不增加额外 Docker 构建的情况下,增加了真实的 container-to-container WebSocket 覆盖。本地 `test:docker:all` 会预先构建一个共享的 live-test 镜像和一个共享的 `scripts/e2e/Dockerfile` built-app 镜像,然后在设置 `OPENCLAW_SKIP_DOCKER_BUILD=1` 的情况下并行运行 live/E2E smoke 通道;默认并发数为 4可通过 `OPENCLAW_DOCKER_ALL_PARALLELISM` 调整。本地聚合作业默认会在第一次失败后停止调度新的池化通道,并且每个通道都有一个 120 分钟的超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖。对启动或 provider 敏感的通道会在并行池之后以独占方式运行。可复用的 live/E2E 工作流也采用共享镜像模式:在 Docker 矩阵之前先构建并推送一个带 SHA 标签的 GHCR Docker E2E 镜像,然后在矩阵中设置 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行。定时的 live/E2E 工作流每天都会运行完整的发布路径 Docker 套件。QR 和 installer Docker 测试保各自以安装为重点的 Dockerfile。另有一个独立的 `docker-e2e-fast` 作业,会在 120 秒命令超时限制下运行受限的内置插件 Docker 配置setup-entry 依赖修复,以及 synthetic bundled-loader failure isolation。完整的 bundled update/channel 矩阵仍然保持为手动/完整套件,因为它会执行重复的真实 npm update 和 doctor 修复流程。
本地 changed-lane 逻辑位于 `scripts/changed-lanes.mjs`,由 `scripts/check-changed.mjs` 执行。这个本地门禁在架构边界方面比宽泛的 CI 平台作用域更严格core 生产变更会运行 core 生产类型检查以及 core 测试core 纯测试变更只运行 core 测试类型检查/测试extension 生产变更会运行 extension 生产类型检查以及 extension 测试,而 extension 纯测试变更只运行 extension 测试类型检查/测试。公共插件 SDK 或 plugin-contract 变更会扩展为 extension 验证,因为扩展依赖这些 core 契约。仅发布元数据的版本变更会运行定向的版本/配置/root-dependency 检查。未知的根目录/配置变更会以安全优先方式落到所有通道。
本地 changed-lane 逻辑位于 `scripts/changed-lanes.mjs``scripts/check-changed.mjs` 执行。这个本地门控在架构边界方面比宽泛的 CI 平台范围更严格:核心生产改动会运行核心生产 typecheck 加核心测试,核心仅测试改动只运行核心测试 typecheck/测试,扩展生产改动会运行扩展生产 typecheck 加扩展测试,而扩展仅测试改动只运行扩展测试 typecheck/测试。公共 Plugin SDK 或 plugin-contract 改动会扩展到扩展校验,因为扩展依赖这些核心契约。仅有发布元数据的版本号变更会运行定向的版本/配置/根依赖检查。未知的根目录/配置改动会以安全优先方式落到所有通道。
在 push 上,`checks` 矩阵会增加一个仅在 push 时运行的 `compat-node22` 通道。在拉取请求上,该通道会被跳过,矩阵会保持聚焦于常规测试/渠道通道。
在 push 上,`checks` 矩阵会添加仅限 push 的 `compat-node22` 通道。在拉取请求上,这个通道会被跳过,矩阵将聚焦于常规测试/渠道通道。
最慢的 Node 测试族会被拆分或平衡,以便每个作业都保持较小规模,同时避免过度预留 runner渠道契约按权重拆成三个分片bundled plugin 测试会在六个扩展 worker 间做负载均衡,小型 core 单元通道会成对组合auto-reply 以三个平衡 worker 运行,而不是六个过小的 workeragentic Gateway 网关/plugin 配置则分布到现有的仅源码 agentic Node 作业中而不是等待已构建产物。大范围的浏览器、QA、媒体和杂项 plugin 测试使用各自专用的 Vitest 配置,而不是共享的 plugin 通用兜底配置。扩展分片作业会以单个 Vitest worker 和更大的 Node 堆大小串行运行 plugin 配置组,这样导入开销较大的 plugin 批次就不会让小型 CI runner 过载。宽泛的 agents 通道使用共享的 Vitest 文件级并行调度器,因为它的瓶颈主要在导入/调度,而不是由某个单独的慢测试文件主导。`runtime-config` 会 infra core-runtime 分片一起运行,以避免共享 runtime 分片独自拖尾。`check-additional` 会把 package-boundary 的编译/canary 工作保持在一起,并将 runtime topology architecture 与 gateway watch 覆盖拆开boundary guard 分片会在一个作业内部并发运行其小型且相互独立的 guard。Gateway watch、渠道测试以及 core support-boundary 分片会在 `dist/``dist-runtime/` 已构建完成后,于 `build-artifacts` 内部并发运行;这样既保留了它们原有的检查名称作为轻量验证作业,又避免额外占用两个 Blacksmith worker 和第二条产物消费者队列。
最慢的 Node 测试家族会被拆分或均衡,以便每个作业都保持较小规模,同时避免过度占用 runner渠道契约以三个加权分片运行内置插件测试在六个扩展 worker 之间均衡分配小型核心单元通道会成对组合auto-reply 改为三个均衡 worker 而不是六个零碎 workeragentic Gateway 网关/plugin 配置则分散到现有的仅源码 agentic Node 作业中,而不是等待构建产物。范围较广的 browser、QA、media 和杂项插件测试使用各自专用的 Vitest 配置,而不是共享的插件兜底配置。扩展分片作业会以单个 Vitest worker 和更大的 Node heap 串行运行插件配置组,这样导入开销大的插件批次就不会让小型 CI runner 过载。广泛的 agents 通道使用共享的 Vitest 文件级并行调度器,因为它的瓶颈主要在导入/调度,而不是由某个单独的慢测试文件主导。`runtime-config` 会 infra core-runtime 分片一起运行,以避免共享 runtime 分片独自拖尾。`check-additional` 会将 package-boundary compile/canary 工作保持在一起,并将 runtime topology architecture 与 gateway watch 覆盖拆开boundary guard 分片会在单个作业内并发运行其小型且相互独立的守卫检查。Gateway 网关 watch、channel 测试以及核心 support-boundary 分片会在 `dist/``dist-runtime/` 已经构建完成后,于 `build-artifacts` 中并发运行;这样既保留了它们原有的检查名称作为轻量校验作业,又避免了额外两个 Blacksmith worker 和第二个 artifact-consumer 队列。
Android CI 会同时运行 `testPlayDebugUnitTest``testThirdPartyDebugUnitTest`,然后构建 Play debug APK。第三方 flavor 没有单独的 source set 或 manifest它的单元测试通道仍会使用 SMS/call-log BuildConfig 标志编译该 flavor同时避免在每次与 Android 相关的 push 上重复行一个 debug APK 打包作业。
Android CI 会同时运行 `testPlayDebugUnitTest``testThirdPartyDebugUnitTest`,然后构建 Play debug APK。第三方 flavor 没有单独的 source set 或 manifest它的单元测试通道仍会使用 SMS/call-log BuildConfig 标志编译该 flavor同时避免在每次与 Android 相关的 push 上重复行一个 debug APK 打包作业。
`extension-fast` 仅在 PR 上运行,因为 push 运行已经会执行完整的 bundled plugin 分片。这样既能为评审提供已变更 plugin 的反馈,又不会在 `main` 上为 `checks-node-extensions` 已经覆盖的内容额外占用一个 Blacksmith worker。
`extension-fast` 仅在 PR 上运行,因为 push 运行已经会执行完整的内置插件分片。这样既能为评审提供已变更插件的反馈,又不会在 `main` 上为 `checks-node-extensions` 已经覆盖的内容额外占用一个 Blacksmith worker。
当同一 PR 或 `main` ref 上有较新的 push 到达时GitHub 可能会把被替代的作业标记为 `cancelled`。除非同一 ref 上最新的一次运行也失败,否则应将这视为 CI 噪声。聚合分片检查使用 `!cancelled() && always()`,这样它们仍会正常报告分片失败,但不会在整个工作流已被替代后继续排队
当同一个 PR 或 `main` ref 上有更新的 push 到达时GitHub 可能会将被替代的作业标记为 `cancelled`。除非同一 ref 的最新运行也失败,否则应将这视为 CI 噪音。聚合分片检查使用 `!cancelled() && always()`,因此它们仍会正常报告分片失败,但在整个工作流已经被替代后不会再排队运行
CI 并发 key 采用版本化形式(`CI-v7-*`),这样 GitHub 端旧队列组中的 zombie 就不会无限期阻塞较新的 main 运行。
CI 并发键是带版本号的(`CI-v7-*`),因此 GitHub 端旧队列组中的僵尸任务不会无限期阻塞较新的 `main` 运行。
## Runner
| Runner | 作业 |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ubuntu-24.04` | `preflight`、快速安全作业及其聚合(`security-scm-fast`、`security-dependency-audit`、`security-fast`)、快速 protocol/contract/bundled 检查、分片的渠道契约检查、除 lint 外的 `check` 分片、`check-additional` 分片及聚合、Node 测试聚合验器、文档检查、Python Skills、workflow-sanity、labeler、auto-responseinstall-smoke preflight 也使用 GitHub 托管的 Ubuntu这样 Blacksmith 矩阵可以更早进入队 |
| `blacksmith-8vcpu-ubuntu-2404` | `build-artifacts`、build-smoke、Linux Node 测试分片、bundled plugin 测试分片、`android` |
| `blacksmith-16vcpu-ubuntu-2404` | `check-lint`,它对 CPU 仍然足够敏感,以至于 8 vCPU 节省下来的成本不如它带来的损失install-smoke Docker 构建也是如此32 vCPU 的排队时间成本高于其收益 |
| `blacksmith-16vcpu-windows-2025` | `checks-windows` |
| `blacksmith-6vcpu-macos-latest` | `openclaw/openclaw`运行`macos-node`fork 会回退到 `macos-latest` |
| `blacksmith-12vcpu-macos-latest` | `openclaw/openclaw`运行`macos-swift`fork 会回退到 `macos-latest` |
| Runner | 作业 |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ubuntu-24.04` | `preflight`、快速安全作业及其聚合作业`security-scm-fast`、`security-dependency-audit`、`security-fast`)、快速 protocol/contract/bundled 检查、分片的渠道契约检查、除 lint 外的 `check` 分片、`check-additional` 分片及聚合、Node 测试聚合验器、文档检查、Python Skills、workflow-sanity、labeler、auto-responseinstall-smoke preflight 也使用 GitHub 托管的 Ubuntu这样 Blacksmith 矩阵可以更早进入队 |
| `blacksmith-8vcpu-ubuntu-2404` | `build-artifacts`、build-smoke、Linux Node 测试分片、内置插件测试分片、`android` |
| `blacksmith-16vcpu-ubuntu-2404` | `check-lint`,它仍然对 CPU 足够敏感,以至于 8 vCPU 的成本高于节省install-smoke Docker 构建,在这里 32 vCPU 的排队时间成本高于节省 |
| `blacksmith-16vcpu-windows-2025` | `checks-windows` |
| `blacksmith-6vcpu-macos-latest` | `openclaw/openclaw` 上的 `macos-node`fork 会回退到 `macos-latest` |
| `blacksmith-12vcpu-macos-latest` | `openclaw/openclaw` 上的 `macos-swift`fork 会回退到 `macos-latest` |
## 本地等效命令
## 本地对应项
```bash
pnpm changed:lanes # 查 origin/main...HEAD 的本地 changed-lane 分类器
pnpm check:changed # 智能本地门禁:按边界通道运行变更相关的类型检查/lint/测试
pnpm check # 快速本地门禁:生产 tsgo + 分片 lint + 并行快速 guard
pnpm changed:lanes # 查 origin/main...HEAD 的本地 changed-lane 分类器
pnpm check:changed # 智能本地门控:按边界通道运行已变更的 typecheck/lint/测试
pnpm check # 快速本地门控:生产 tsgo + 分片 lint + 并行快速守卫
pnpm check:test-types
pnpm check:timed # 同一套门禁,并输出各阶段耗时
pnpm check:timed # 相同门控,但带每个阶段的耗时统计
pnpm build:strict-smoke
pnpm check:architecture
pnpm test:gateway:watch-regression
pnpm test # vitest 测试
pnpm test:channels
pnpm test:contracts:channels
pnpm check:docs # 文档格式 + lint + 损坏链接检查
pnpm build # 当 CI 产物/build-smoke 通道相关时,构建 dist
node scripts/ci-run-timings.mjs <run-id> # 汇总总耗时、排队时和最慢作业
pnpm check:docs # 文档 format + lint + 断链检查
pnpm build # 当 CI artifact/build-smoke 通道相关时,构建 dist
node scripts/ci-run-timings.mjs <run-id> # 汇总总耗时、排队时和最慢作业
node scripts/ci-run-timings.mjs --recent 10 # 对比最近成功的 main CI 运行
pnpm test:perf:groups --full-suite --allow-failures --output .artifacts/test-perf/baseline-before.json
pnpm test:perf:groups:compare .artifacts/test-perf/baseline-before.json .artifacts/test-perf/after-agent.json

View File

@ -1,30 +1,30 @@
---
read_when:
- 运行或调试 Gateway 网关进程
summary: Gateway 网关服务、生命周期与运维的操作手册
title: Gateway 网关操作手册
summary: Gateway 网关服务、生命周期与运维手册
title: Gateway 网关运行手册
x-i18n:
generated_at: "2026-04-20T06:30:59Z"
generated_at: "2026-04-23T20:12:15Z"
model: gpt-5.4
provider: openai
source_hash: e1004cdd43b1db6794f3ca83da38dbdb231a1976329d9d6d851e2b02405278d8
source_hash: a024b1250c9e6badd1c72cccbd6d568ea510db829be79ced19a12f293dbe6f2a
source_path: gateway/index.md
workflow: 15
---
# Gateway 网关操作手册
# Gateway 网关运行手册
将此页面用于 Gateway 网关服务的第 1 天启动和第 2 天运维操作
将此页面用于 Gateway 网关服务的第 1 天启动和第 2 天运维。
<CardGroup cols={2}>
<Card title="深度故障排除" icon="siren" href="/zh-CN/gateway/troubleshooting">
按症状优先的诊断,提供精确的命令步骤和日志特征。
按症状优先的诊断指南,包含精确的命令步骤和日志特征。
</Card>
<Card title="配置" icon="sliders" href="/zh-CN/gateway/configuration">
面向任务的设置指南 + 完整配置参考。
</Card>
<Card title="密钥管理" icon="key-round" href="/zh-CN/gateway/secrets">
`SecretRef` 合约、运行时快照行为,以及迁移/重新加载操作。
SecretRef 合约、运行时快照行为,以及迁移/重载操作。
</Card>
<Card title="密钥计划合约" icon="shield-check" href="/zh-CN/gateway/secrets-plan-contract">
精确的 `secrets apply` 目标/路径规则,以及仅引用的 auth-profile 行为。
@ -38,9 +38,9 @@ x-i18n:
```bash
openclaw gateway --port 18789
# debug/trace 镜像输出到 stdio
# debug/trace 镜像输出到 stdio
openclaw gateway --port 18789 --verbose
# 强制终止所选端口上的监听,然后启动
# 强制终止所选端口上的监听进程,然后启动
openclaw gateway --force
```
@ -54,7 +54,7 @@ openclaw status
openclaw logs --follow
```
健康基线:`Runtime: running`、`Connectivity probe: ok`,以及与你预期匹配`Capability: ...`。当你需要读取范围的 RPC 证明,而不仅仅是可达性时,使用 `openclaw gateway status --require-rpc`
健康基线:`Runtime: running`、`Connectivity probe: ok`,以及与你预期一致`Capability: ...`。当你需要读取范围的 RPC 证明,而不仅仅是可达性时,使用 `openclaw gateway status --require-rpc`
</Step>
@ -64,35 +64,35 @@ openclaw logs --follow
openclaw channels status --probe
```
当 Gateway 网关可达时,这会按账户运行实时的渠道探测和可选审计。
如果 Gateway 网关不可达CLI 会回退到仅基于配置的渠道摘要,
当 Gateway 网关可达时,这会运行按账户划分的实时渠道探测和可选审计。
如果 Gateway 网关不可达CLI 会回退为仅配置的渠道摘要,
而不是实时探测输出。
</Step>
</Steps>
<Note>
Gateway 网关配置重载会监视当前激活的配置文件路径(从 profile/state 默认值解析,或在设置时使用 `OPENCLAW_CONFIG_PATH`)。
默认模式 `gateway.reload.mode="hybrid"`
首次成功加载后,运行中的进程会提供当前激活的内存配置快照;成功重载后会原子性地替换该快照。
Gateway 网关配置重载会监视活动配置文件路径(从 profile/state 默认值解析,或者在设置了 `OPENCLAW_CONFIG_PATH` 时使用该路径)。
默认模式 `gateway.reload.mode="hybrid"`
首次成功加载后,运行中的进程会提供活动的内存中配置快照;成功重载后会以原子方式替换该快照。
</Note>
## 运行时模型
- 一个始终运行的进程,用于路由、控制平面和渠道连接。
- 一个用于以下内容的单一复用端口
- 单个复用端口用于
- WebSocket 控制/RPC
- HTTP API、OpenAI 兼容接口`/v1/models`、`/v1/embeddings`、`/v1/chat/completions`、`/v1/responses`、`/tools/invoke`
- Control UI 和 hooks
- HTTP API,兼容 OpenAI`/v1/models`、`/v1/embeddings`、`/v1/chat/completions`、`/v1/responses`、`/tools/invoke`
- 控制 UI 和 hooks
- 默认绑定模式:`loopback`。
- 默认需要认证。共享密钥设置使用
- 默认要求身份验证。共享密钥设置使用
`gateway.auth.token` / `gateway.auth.password`(或
`OPENCLAW_GATEWAY_TOKEN` / `OPENCLAW_GATEWAY_PASSWORD`),而非 `loopback`
反向代理设置可以使用 `gateway.auth.mode: "trusted-proxy"`
`OPENCLAW_GATEWAY_TOKEN` / `OPENCLAW_GATEWAY_PASSWORD`),而非 loopback 的
反向代理设置可以使用 `gateway.auth.mode: "trusted-proxy"`
## OpenAI 兼容端点
## 兼容 OpenAI 的端点
OpenClaw 现在最有价值的兼容性接口为
OpenClaw 当前最具杠杆效应的兼容性接口是
- `GET /v1/models`
- `GET /v1/models/{id}`
@ -102,17 +102,17 @@ OpenClaw 现在最有价值的兼容性接口为:
为什么这一组很重要:
- 大多数 Open WebUI、LobeChat 和 LibreChat 集成会先探测 `/v1/models`
- 许多 RAG 和 memory 流水线依赖 `/v1/embeddings`
- 原生面向智能体的客户端越来越倾向于使用 `/v1/responses`
- 大多数 Open WebUI、LobeChat 和 LibreChat 集成会先探测 `/v1/models`
- 许多 RAG 和记忆流水线都期望 `/v1/embeddings`
- 原生支持智能体的客户端越来越倾向于使用 `/v1/responses`
规划说明:
- `/v1/models` 以智能体优先:它会返回 `openclaw`、`openclaw/default` 和 `openclaw/<agentId>`
- `openclaw/default` 是稳定别名,始终映射到已配置的默认智能体。
- 如果你想覆盖后端 provider/model请使用 `x-openclaw-model`;否则将继续由所选智能体的常规模型和 embedding 设置进行控制
- 当你想要后端提供商/模型覆盖时,使用 `x-openclaw-model`;否则,所选智能体的常规模型和嵌入设置仍会生效
所有这些都运行在 Gateway 网关端口上,并使用与 Gateway 网关其余 HTTP API 相同的可信操作员证边界。
所有这些都运行在 Gateway 网关端口上,并使用与 Gateway 网关其余 HTTP API 相同的可信操作员身份验证边界。
### 端口和绑定优先级
@ -126,15 +126,15 @@ OpenClaw 现在最有价值的兼容性接口为:
| `gateway.reload.mode` | 行为 |
| --------------------- | ------------------------------------------ |
| `off` | 不进行配置重载 |
| `hot` | 仅应用热更新安全更 |
| `restart` | 遇到需要重启的变更时重启 |
| `hybrid` (默认) | 安全时热应用,需要时重启 |
| `hot` | 仅应用可安全热更新的更 |
| `restart` | 在需要重载的更改上重启 |
| `hybrid`(默认) | 安全时热应用,需要时重启 |
## 操作员命令集
```bash
openclaw gateway status
openclaw gateway status --deep # 加系统级服务扫描
openclaw gateway status --deep # 加系统级服务扫描
openclaw gateway status --json
openclaw gateway install
openclaw gateway restart
@ -145,14 +145,14 @@ openclaw doctor
```
`gateway status --deep` 用于额外的服务发现LaunchDaemons/systemd 系统
units/schtasks而不是更深的 RPC 健康探测。
units/schtasks而不是更深的 RPC 健康探测。
## 多个 Gateway 网关(同一主机)
大多数安装应在每台机器上运行一个 Gateway 网关。单个 Gateway 网关可以承载多个
大多数安装应在每台机器上运行一个 Gateway 网关。单个 Gateway 网关可以托管多个
智能体和渠道。
只有当你明确需要隔离或一个救援 bot 时,才需要多个 Gateway 网关。
只有当你有意需要隔离或一个救援机器人时,才需要多个 Gateway 网关。
有用的检查:
@ -161,12 +161,26 @@ openclaw gateway status --deep
openclaw gateway probe
```
预期表现
预期行为
- `gateway status --deep` 可能会报告 `Other gateway-like services detected (best effort)`
并在仍存在陈旧的 launchd/systemd/schtasks 安装时打印清理提示。
- 当多个目标都有响应时,`gateway probe` 可能会警告 `multiple reachable gateways`
- 如果这是有意为之,请为每个 Gateway 网关隔离端口、配置/state 和 workspace 根目录。
- `gateway status --deep` 可以报告 `Other gateway-like services detected (best effort)`
并在仍存在过期的 launchd/systemd/schtasks 安装时打印清理提示。
- 当多个目标有响应时,`gateway probe` 可以警告 `multiple reachable gateways`
- 如果这是有意为之,请为每个 Gateway 网关分别隔离端口、配置/state 和工作区根目录。
每个实例的检查清单:
- 唯一的 `gateway.port`
- 唯一的 `OPENCLAW_CONFIG_PATH`
- 唯一的 `OPENCLAW_STATE_DIR`
- 唯一的 `agents.defaults.workspace`
示例:
```bash
OPENCLAW_CONFIG_PATH=~/.openclaw/a.json OPENCLAW_STATE_DIR=~/.openclaw-a openclaw gateway --port 19001
OPENCLAW_CONFIG_PATH=~/.openclaw/b.json OPENCLAW_STATE_DIR=~/.openclaw-b openclaw gateway --port 19002
```
详细设置:[/gateway/multiple-gateways](/zh-CN/gateway/multiple-gateways)。
@ -182,16 +196,16 @@ ssh -N -L 18789:127.0.0.1:18789 user@host
然后让客户端在本地连接到 `ws://127.0.0.1:18789`
<Warning>
SSH 隧道不会绕过 Gateway 网关认证。对于共享密钥认证,即使通过隧道,客户端
仍然必须发送 `token`/`password`。对于带身份的模式,
请求仍然必须满足该证路径。
SSH 隧道不会绕过 Gateway 网关身份验证。对于共享密钥身份验证,客户端即使
通过隧道连接,仍然必须发送 `token`/`password`。对于带身份的模式,
请求仍然必须满足该身份验证路径。
</Warning>
参见:[远程 Gateway 网关](/zh-CN/gateway/remote)、[认证](/zh-CN/gateway/authentication)、[Tailscale](/zh-CN/gateway/tailscale)。
参见:[Remote Gateway](/zh-CN/gateway/remote)、[Authentication](/zh-CN/gateway/authentication)、[Tailscale](/zh-CN/gateway/tailscale)。
## 监管和服务生命周期
## 监督与服务生命周期
为了获得接近生产环境的可靠性,请使用受监管的运行方式。
对于类生产环境的可靠性,请使用受监督的运行方式。
<Tabs>
<Tab title="macOSlaunchd">
@ -215,7 +229,7 @@ systemctl --user enable --now openclaw-gateway[-<profile>].service
openclaw gateway status
```
要在登出后保持持久运行,请启用 lingering
如需在注销后仍保持运行,请启用 lingering
```bash
sudo loginctl enable-linger <user>
@ -262,7 +276,7 @@ openclaw gateway stop
<Tab title="Linux系统服务">
多用户/始终在线主机,请使用系统 unit。
于多用户/始终运行的主机,请使用系统 unit。
```bash
sudo systemctl daemon-reload
@ -276,28 +290,7 @@ sudo systemctl enable --now openclaw-gateway[-<profile>].service
</Tab>
</Tabs>
## 一台主机上的多个 Gateway 网关
大多数设置应只运行**一个** Gateway 网关。
只有在需要严格隔离/冗余时才使用多个(例如救援 profile
每个实例的检查清单:
- 唯一的 `gateway.port`
- 唯一的 `OPENCLAW_CONFIG_PATH`
- 唯一的 `OPENCLAW_STATE_DIR`
- 唯一的 `agents.defaults.workspace`
示例:
```bash
OPENCLAW_CONFIG_PATH=~/.openclaw/a.json OPENCLAW_STATE_DIR=~/.openclaw-a openclaw gateway --port 19001
OPENCLAW_CONFIG_PATH=~/.openclaw/b.json OPENCLAW_STATE_DIR=~/.openclaw-b openclaw gateway --port 19002
```
参见:[多个 Gateway 网关](/zh-CN/gateway/multiple-gateways)。
### 开发 profile 快速路径
## 开发 profile 快速路径
```bash
openclaw --dev setup
@ -305,34 +298,34 @@ openclaw --dev gateway --allow-unconfigured
openclaw --dev status
```
默认值包括隔离的 state/config,以及基础 Gateway 网关端口 `19001`
默认值包括隔离的 state/config基础 Gateway 网关端口 `19001`
## 协议快速参考(操作员视角)
- 第一个客户端帧必须是 `connect`
- 客户端的第一帧必须是 `connect`
- Gateway 网关返回 `hello-ok` 快照(`presence`、`health`、`stateVersion`、`uptimeMs`、limits/policy
- `hello-ok.features.methods` / `events` 是保守的发现列表,而不是
每个可调用辅助路由的自动生成清单。
每个可调用 helper 路由的自动生成清单。
- 请求:`req(method, params)` → `res(ok/payload|error)`
- 常见事件包括 `connect.challenge`、`agent`、`chat`、
`session.message`、`session.tool`、`sessions.changed`、`presence`、`tick`、
`health`、`heartbeat`、pairing/approval 生命周期事件以及 `shutdown`
`health`、`heartbeat`、配对/批准生命周期事件,以及 `shutdown`
智能体运行分为两个阶段:
1. 立即接受确认(`status:"accepted"`
2. 最终完成响应(`status:"ok"|"error"`),其间会流式发送 `agent` 事件。
查看完整协议文档:[Gateway 协议](/zh-CN/gateway/protocol)。
完整协议文档参见:[Gateway Protocol](/zh-CN/gateway/protocol)。
## 运维检查
### 存活性
- 打开 WS 并发送 `connect`
- 预期收到带快照的 `hello-ok` 响应。
- 预期收到带快照的 `hello-ok` 响应。
### 就绪状态
### 就绪
```bash
openclaw gateway status
@ -340,26 +333,26 @@ openclaw channels status --probe
openclaw health
```
### 缺口恢复
### 间隙恢复
事件不会重放。出现序列缺口时,在继续之前刷新状态(`health`、`system-presence`
事件不会重放。遇到序列间隙时,请先刷新状态(`health`、`system-presence`)再继续
## 常见故障特征
## 常见失败特征
| 特征 | 可能问题 |
| 特征 | 可能问题 |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `refusing to bind gateway ... without auth` | 非 `loopback` 绑定,但没有有效的 Gateway 网关认证路径 |
| `refusing to bind gateway ... without auth` | 非 loopback 绑定但没有有效的 Gateway 网关身份验证路径 |
| `another gateway instance is already listening` / `EADDRINUSE` | 端口冲突 |
| `Gateway start blocked: set gateway.mode=local` | 配置被设为远程模式,或损坏配置中缺少本地模式标记 |
| `unauthorized` during connect | 客户端与 Gateway 网关之间的证不匹配 |
| `Gateway start blocked: set gateway.mode=local` | 配置被设置为远程模式,或受损配置中缺少 local-mode 标记 |
| `unauthorized` during connect | 客户端与 Gateway 网关之间的身份验证不匹配 |
如需完整的诊断步骤,请使用 [Gateway 故障排除](/zh-CN/gateway/troubleshooting)。
完整诊断步骤请使用 [Gateway Troubleshooting](/zh-CN/gateway/troubleshooting)。
## 安全保证
- 当 Gateway 网关不可用时Gateway 协议客户端会快速失败(不会隐式回退到直接渠道)。
- 当 Gateway 网关不可用时Gateway 网关协议客户端会快速失败(不会隐式回退到直接渠道)。
- 无效的/非 `connect` 的首帧会被拒绝并关闭连接。
- 优雅关闭会在 socket 关闭前发 `shutdown` 事件。
- 优雅关闭会在 socket 关闭前发 `shutdown` 事件。
---
@ -368,6 +361,6 @@ openclaw health
- [故障排除](/zh-CN/gateway/troubleshooting)
- [后台进程](/zh-CN/gateway/background-process)
- [配置](/zh-CN/gateway/configuration)
- [健康](/zh-CN/gateway/health)
- [健康状态](/zh-CN/gateway/health)
- [Doctor](/zh-CN/gateway/doctor)
- [证](/zh-CN/gateway/authentication)
- [身份验证](/zh-CN/gateway/authentication)

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,26 @@
---
read_when:
- 你需要从插件中调用核心辅助函数TTS、STT、图像生成、Web 搜索、子智能体)
- 你需要从插件调用核心辅助工具TTS、STT、图像生成、网络搜索、子智能体)
- 你想了解 `api.runtime` 暴露了什么
- 你正在从插件代码中访问配置、智能体或媒体辅助函数
- 你正在从插件代码中访问配置、智能体或媒体辅助工具
sidebarTitle: Runtime Helpers
summary: '`api.runtime` —— 可供插件使用的注入式运行时辅助函数'
title: 插件运行时辅助函数
summary: api.runtime —— 可供插件使用的注入式运行时辅助工具
title: 插件运行时辅助工具
x-i18n:
generated_at: "2026-04-15T16:36:52Z"
generated_at: "2026-04-23T20:12:16Z"
model: gpt-5.4
provider: openai
source_hash: c77a6e9cd48c84affa17dce684bbd0e072c8b63485e4a5d569f3793a4ea4f9c8
source_hash: 78672fff3f850854a68c9fa2494e595cec9177c49dc426c96e11f169a44fa76e
source_path: plugins/sdk-runtime.md
workflow: 15
---
# 插件运行时辅助函数
# 插件运行时辅助工具
这是对在注册期间注入到每个插件中的 `api.runtime` 对象的参考说明。使用这些辅助函数,而不是直接导入宿主内部实现。
这是对在注册期间注入到每个插件中的 `api.runtime` 对象的参考说明。请使用这些辅助工具,而不是直接导入宿主内部实现。
<Tip>
**想看使用演示?** 请参阅 [渠道插件](/zh-CN/plugins/sdk-channel-plugins) 或 [提供商插件](/zh-CN/plugins/sdk-provider-plugins) 中的分步指南,它们会在具体上下文中展示这些辅助函数的用法。
**想看操作演示?** 请参阅 [渠道插件](/zh-CN/plugins/sdk-channel-plugins) 或 [提供商插件](/zh-CN/plugins/sdk-provider-plugins) 中的分步指南,这些指南会结合上下文展示这些辅助工具的用法。
</Tip>
```typescript
@ -36,25 +36,25 @@ register(api) {
智能体身份、目录和会话管理。
```typescript
// 解析智能体的工作目录
// Resolve the agent's working directory
const agentDir = api.runtime.agent.resolveAgentDir(cfg);
// 解析智能体工作区
// Resolve agent workspace
const workspaceDir = api.runtime.agent.resolveAgentWorkspaceDir(cfg);
// 获取智能体身份
// Get agent identity
const identity = api.runtime.agent.resolveAgentIdentity(cfg);
// 获取默认思考级别
// Get default thinking level
const thinking = api.runtime.agent.resolveThinkingDefault(cfg, provider, model);
// 获取智能体超时时间
// Get agent timeout
const timeoutMs = api.runtime.agent.resolveAgentTimeoutMs(cfg);
// 确保工作区存在
// Ensure workspace exists
await api.runtime.agent.ensureAgentWorkspace(cfg);
// 运行一个嵌入式智能体轮次
// Run an embedded agent turn
const agentDir = api.runtime.agent.resolveAgentDir(cfg);
const result = await api.runtime.agent.runEmbeddedAgent({
sessionId: "my-plugin:task-1",
@ -66,11 +66,11 @@ const result = await api.runtime.agent.runEmbeddedAgent({
});
```
`runEmbeddedAgent(...)`一个中立的辅助函数,用于从插件代码启动一个普通的 OpenClaw 智能体轮次。它使用与渠道触发回复相同的 provider / model 解析逻辑和智能体 harness 选择方式
`runEmbeddedAgent(...)`用于从插件代码启动常规 OpenClaw 智能体轮次的中立辅助工具。它使用与渠道触发回复相同的提供商 / 模型解析和智能体 harness 选择逻辑
`runEmbeddedPiAgent(...)`保留作为兼容性别名
`runEmbeddedPiAgent(...)`然作为兼容性别名保留
**会话存储辅助函数** 位于 `api.runtime.agent.session` 下:
**会话存储辅助工具** 位于 `api.runtime.agent.session` 下:
```typescript
const storePath = api.runtime.agent.session.resolveStorePath(cfg);
@ -81,50 +81,49 @@ const filePath = api.runtime.agent.session.resolveSessionFilePath(cfg, sessionId
### `api.runtime.agent.defaults`
默认 model 和 provider 常量:
默认模型和提供商常量:
```typescript
const model = api.runtime.agent.defaults.model; // 例如 "anthropic/claude-sonnet-4-6"
const provider = api.runtime.agent.defaults.provider; // 例如 "anthropic"
const model = api.runtime.agent.defaults.model; // e.g. "anthropic/claude-sonnet-4-6"
const provider = api.runtime.agent.defaults.provider; // e.g. "anthropic"
```
### `api.runtime.subagent`
启动管理后台子智能体运行。
启动管理后台子智能体运行。
```typescript
// 启动一次子智能体运行
// Start a subagent run
const { runId } = await api.runtime.subagent.run({
sessionKey: "agent:main:subagent:search-helper",
message: "Expand this query into focused follow-up searches.",
provider: "openai", // 可选覆盖
model: "gpt-4.1-mini", // 可选覆盖
provider: "openai", // optional override
model: "gpt-4.1-mini", // optional override
deliver: false,
});
// 等待完成
// Wait for completion
const result = await api.runtime.subagent.waitForRun({ runId, timeoutMs: 30000 });
// 读取会话消息
// Read session messages
const { messages } = await api.runtime.subagent.getSessionMessages({
sessionKey: "agent:main:subagent:search-helper",
limit: 10,
});
// 删除一个会话
// Delete a session
await api.runtime.subagent.deleteSession({
sessionKey: "agent:main:subagent:search-helper",
});
```
<Warning>
model 覆盖(`provider` / `model`)需要操作员在配置中显式启用 `plugins.entries.<id>.subagent.allowModelOverride: true`
不受信任的插件仍然可以运行子智能体,但其覆盖请求会被拒绝。
模型覆盖(`provider` / `model`)需要操作员在配置中通过 `plugins.entries.<id>.subagent.allowModelOverride: true` 显式启用。不受信任的插件仍然可以运行子智能体,但其覆盖请求会被拒绝。
</Warning>
### `api.runtime.taskFlow`
一个 Task Flow 运行时绑定到现有的 OpenClaw 会话键或受信任的工具上下文,然后在不必每次调用都传入 owner 的情况下创建和管理 Task Flows。
将 Task Flow 运行时绑定到现有的 OpenClaw 会话键或受信任的工具上下文,然后在每次调用时无需传入所有者即可创建和管理 Task Flows。
```typescript
const taskFlow = api.runtime.taskFlow.fromToolContext(ctx);
@ -151,70 +150,70 @@ const waiting = taskFlow.setWaiting({
});
```
当你已经从自己的绑定层拿到了一个受信任的 OpenClaw 会话键时,请使用 `bindSession({ sessionKey, requesterOrigin })`。不要从原始用户输入中进行绑定。
当你已经从自己的绑定层获得受信任的 OpenClaw 会话键时,请使用 `bindSession({ sessionKey, requesterOrigin })`。不要基于原始用户输入进行绑定。
### `api.runtime.tts`
文本转语音合成。
```typescript
// 标准 TTS
// Standard TTS
const clip = await api.runtime.tts.textToSpeech({
text: "Hello from OpenClaw",
cfg: api.config,
});
// 面向电话场景优化的 TTS
// Telephony-optimized TTS
const telephonyClip = await api.runtime.tts.textToSpeechTelephony({
text: "Hello from OpenClaw",
cfg: api.config,
});
// 列出可用音色
// List available voices
const voices = await api.runtime.tts.listVoices({
provider: "elevenlabs",
cfg: api.config,
});
```
使用核心 `messages.tts` 配置和 provider 选择逻辑。返回 PCM 音频缓冲区和采样率。
使用核心 `messages.tts` 配置和提供商选择。返回 PCM 音频缓冲区 + 采样率。
### `api.runtime.mediaUnderstanding`
图像、音频和视频分析。
```typescript
// 描述一张图片
// Describe an image
const image = await api.runtime.mediaUnderstanding.describeImageFile({
filePath: "/tmp/inbound-photo.jpg",
cfg: api.config,
agentDir: "/tmp/agent",
});
// 转录音频
// Transcribe audio
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({
filePath: "/tmp/inbound-audio.ogg",
cfg: api.config,
mime: "audio/ogg", // 可选,用于无法推断 MIME 的情况
mime: "audio/ogg", // optional, for when MIME cannot be inferred
});
// 描述一个视频
// Describe a video
const video = await api.runtime.mediaUnderstanding.describeVideoFile({
filePath: "/tmp/inbound-video.mp4",
cfg: api.config,
});
// 通用文件分析
// Generic file analysis
const result = await api.runtime.mediaUnderstanding.runFile({
filePath: "/tmp/inbound-file.pdf",
cfg: api.config,
});
```
没有生成输出时(例如输入被跳过),返回 `{ text: undefined }`
未产生输出时(例如输入被跳过),会返回 `{ text: undefined }`
<Info>
`api.runtime.stt.transcribeAudioFile(...)`保留作为 `api.runtime.mediaUnderstanding.transcribeAudioFile(...)` 的兼容性别名。
`api.runtime.stt.transcribeAudioFile(...)`作为 `api.runtime.mediaUnderstanding.transcribeAudioFile(...)` 的兼容性别名保留
</Info>
### `api.runtime.imageGeneration`
@ -232,7 +231,7 @@ const providers = api.runtime.imageGeneration.listProviders({ cfg: api.config })
### `api.runtime.webSearch`
Web 搜索。
网络搜索。
```typescript
const providers = api.runtime.webSearch.listProviders({ config: api.config });
@ -254,11 +253,16 @@ const kind = api.runtime.media.mediaKindFromMime("image/jpeg"); // "image"
const isVoice = api.runtime.media.isVoiceCompatibleAudio(filePath);
const metadata = await api.runtime.media.getImageMetadata(filePath);
const resized = await api.runtime.media.resizeToJpeg(buffer, { maxWidth: 800 });
const terminalQr = await api.runtime.media.renderQrTerminal("https://openclaw.ai");
const pngQr = await api.runtime.media.renderQrPngBase64("https://openclaw.ai", {
scale: 6, // 1-12
marginModules: 4, // 0-16
});
```
### `api.runtime.config`
配置加载与写入。
加载和写入配置
```typescript
const cfg = await api.runtime.config.loadConfig();
@ -300,7 +304,7 @@ const childLogger = api.runtime.logging.getChildLogger({ plugin: "my-plugin" },
### `api.runtime.modelAuth`
model 和 provider 的凭证解析。
模型和提供商凭证解析。
```typescript
const auth = await api.runtime.modelAuth.getApiKeyForModel({ model, cfg });
@ -330,7 +334,7 @@ api.runtime.tools.registerMemoryCli(/* ... */);
### `api.runtime.channel`
渠道特定的运行时辅助函数(在加载渠道插件时可用)。
渠道专用运行时辅助工具(在加载渠道插件时可用)。
`api.runtime.channel.mentions` 是供使用运行时注入的内置渠道插件共享的入站提及策略接口:
@ -359,7 +363,7 @@ const decision = api.runtime.channel.mentions.resolveInboundMentionDecision({
});
```
可用的提及辅助函数
可用的提及辅助工具包括
- `buildMentionRegexes`
- `matchesMentionPatterns`
@ -367,7 +371,7 @@ const decision = api.runtime.channel.mentions.resolveInboundMentionDecision({
- `implicitMentionKindWhen`
- `resolveInboundMentionDecision`
`api.runtime.channel.mentions` 有意不暴露较旧的 `resolveMentionGating*` 兼容性辅助函数。请优先使用规范化的 `{ facts, policy }` 路径。
`api.runtime.channel.mentions` 有意不暴露较旧的 `resolveMentionGating*` 兼容性辅助工具。请优先使用标准化的 `{ facts, policy }` 路径。
## 存储运行时引用
@ -382,7 +386,7 @@ const store = createPluginRuntimeStore<PluginRuntime>({
errorMessage: "my-plugin runtime not initialized",
});
// 在你的入口点中
// In your entry point
export default defineChannelPluginEntry({
id: "my-plugin",
name: "My Plugin",
@ -391,17 +395,17 @@ export default defineChannelPluginEntry({
setRuntime: store.setRuntime,
});
// 在其他文件中
// In other files
export function getRuntime() {
return store.getRuntime(); // 如果尚未初始化则抛出异常
return store.getRuntime(); // throws if not initialized
}
export function tryGetRuntime() {
return store.tryGetRuntime(); // 如果尚未初始化则返回 null
return store.tryGetRuntime(); // returns null if not initialized
}
```
对于运行时存储标识,优先使用 `pluginId`。更底层的 `key` 形式适用于少见场景,即某个插件有意需要多个运行时槽位。
对于 runtime-store 标识,优先使用 `pluginId`。较底层的 `key` 形式适用于不常见的场景,即某个插件有意需要多个运行时槽位。
## 其他顶层 `api` 字段
@ -411,14 +415,14 @@ export function tryGetRuntime() {
| ------------------------ | ------------------------- | ------------------------------------------------------------------------------------------- |
| `api.id` | `string` | 插件 id |
| `api.name` | `string` | 插件显示名称 |
| `api.config` | `OpenClawConfig` | 当前配置快照(可用时为活动的内存中运行时快照) |
| `api.config` | `OpenClawConfig` | 当前配置快照(可用时为处于活动状态的内存中运行时快照) |
| `api.pluginConfig` | `Record<string, unknown>` | 来自 `plugins.entries.<id>.config` 的插件专用配置 |
| `api.logger` | `PluginLogger` | 作用域日志记录器(`debug`、`info`、`warn`、`error` |
| `api.registrationMode` | `PluginRegistrationMode` | 当前加载模式;`"setup-runtime"` 是完整入口启动 / 设置前的轻量级窗口 |
| `api.registrationMode` | `PluginRegistrationMode` | 当前加载模式;`"setup-runtime"` 是完整入口启动 / 设置前的轻量级窗口 |
| `api.resolvePath(input)` | `(string) => string` | 解析相对于插件根目录的路径 |
## 相关内容
- [SDK 概览](/zh-CN/plugins/sdk-overview) -- 子路径参考
- [插件入口点](/zh-CN/plugins/sdk-entrypoints) -- `definePluginEntry` 选项
- [插件内部机制](/zh-CN/plugins/architecture) -- 能力模型和注册表
- [SDK 概览](/zh-CN/plugins/sdk-overview) —— 子路径参考
- [插件入口点](/zh-CN/plugins/sdk-entrypoints) —— `definePluginEntry` 选项
- [插件内部机制](/zh-CN/plugins/architecture) —— 能力模型和注册表

View File

@ -1,53 +1,53 @@
---
read_when:
- 运行或修复测试
summary: 如何在本地运行测试(`vitest`),以及何时使用 `force`/`coverage` 模式
summary: 如何在本地运行测试vitest以及何时使用 force/coverage 模式
title: 测试
x-i18n:
generated_at: "2026-04-23T19:45:54Z"
generated_at: "2026-04-23T20:12:12Z"
model: gpt-5.4
provider: openai
source_hash: 078ba2d49ffc117b5069304113ab4b08d734ced76f8e0ac491e79375d6f3fde4
source_hash: b00be94a548ca75ab0f2531186a2bf7928655454352ac6a38fd8a6ffa47e8451
source_path: reference/test.md
workflow: 15
---
# 测试
- 完整测试工具包(测试套件、实时测试、Docker[测试](/zh-CN/help/testing)
- 完整测试工具包(测试套件、live、Docker[测试](/zh-CN/help/testing)
- `pnpm test:force`:终止任何仍在占用默认控制端口的残留 Gateway 网关进程,然后使用隔离的 Gateway 网关端口运行完整的 Vitest 测试套件,避免服务端测试与正在运行的实例发生冲突。当之前的 Gateway 网关运行遗留并占用了端口 `18789` 时,请使用此命令。
- `pnpm test:coverage`:通过 `vitest.unit.config.ts` 运行带 V8 覆盖率的单元测试套件。这是一个基于已加载文件的单元覆盖率门禁,不是整个仓库所有文件的覆盖率。阈值为:行数/函数/语句 70%,分支 55%。由于 `coverage.all` 为 false该门禁只衡量单元覆盖率测试套件中已加载的文件而不会将拆分测试分组中的所有源码文件都视为未覆盖。
- `pnpm test:coverage:changed`:仅对相对于 `origin/main`变更的文件运行单元覆盖率。
- `pnpm test:changed`:当 diff 只涉及可路由的源码/测试文件时,将变更的 git 路径展开为有作用域的 Vitest 分组。配置或 setup 变更仍会回退到原生根项目运行,以便在需要时对 wiring 改动进行更广泛的重跑
- `pnpm changed:lanes`:显示相对于 `origin/main` 的 diff 所触发的架构分组
- `pnpm check:changed`:对相对于 `origin/main` 的 diff 运行智能变更门禁。它会将 core 变更与 core 测试分组一起运行,将扩展变更与扩展测试分组一起运行,将仅测试变更限制为测试 typecheck/测试,针对公共插件 SDK 或 plugin-contract 的变更扩展到扩展验证,并让仅含发布元数据的版本变更保持在定向的版本/配置/根依赖检查中
- `pnpm test`通过有作用域的 Vitest 分组来路由显式的文件/目录目标。未指定目标的运行会使用固定的分片组,并展开为叶子配置以进行本地并行执行;扩展组始终会展开到每个扩展/插件的分片配置,而不是单个巨大的根项目进程。
- 完整测试和扩展分片运行会更新 `.artifacts/vitest-shard-timings.json` 中的本地时数据;后续运行会使用这些时数据来平衡慢和分片。设置 `OPENCLAW_TEST_PROJECTS_TIMINGS=0` 可忽略本地计时产物
- 部分 `plugin-sdk``commands` 测试文件现在会通过专用的轻量分组进行路由,这些分组仅保留 `test/setup.ts`,而将运行时较重的用例留在它们原有的分组中。
- 部分 `plugin-sdk``commands` 辅助源码文件也会将 `pnpm test:changed` 映射到这些轻量分组中的显式同级测试,因此小型辅助函数改动无需重跑依赖重型运行时的测试套件。
- `auto-reply` 现在也被拆分为三个专用配置(`core`、`top-level`、`reply`),这样 reply harness 就不会主导更轻量的顶层状态/token/helper 测试。
- 基础 Vitest 配置现在默认使用 `pool: "threads"``isolate: false`,并在整个仓库配置中启用共享的非隔离运行器。
- `pnpm test:channels`运行 `vitest.channels.config.ts`
- `pnpm test:extensions``pnpm test extensions`:运行所有扩展/插件分片。较重的渠道扩展和 OpenAI 会作为专用分片运行;其他扩展组保持批量执行。对单个内置插件分组使用 `pnpm test extensions/<id>`
- `pnpm test:perf:imports`:启用 Vitest 导入耗时 + 导入明细报告,同时仍对显式文件/目录目标使用有作用域的分组路由。
- `pnpm test:perf:imports:changed`:与上面相同的导入性能分析,但仅针对相对于 `origin/main`变更的文件。
- `pnpm test:perf:changed:bench -- --ref <git-ref>`对同一份已提交 git diff 的变更模式路由路径与原生根项目运行进行基准对比
- `pnpm test:perf:changed:bench -- --worktree`:无需先提交,直接对当前工作树的变更集进行基准测试。
- `pnpm test:force`:终止任何仍在占用默认控制端口的残留 Gateway 网关进程,然后使用隔离的 Gateway 网关端口运行完整的 Vitest 测试套件,避免服务端测试与正在运行的实例发生冲突。当先前的 Gateway 网关运行导致端口 `18789` 仍被占用时,请使用此命令。
- `pnpm test:coverage`:通过 `vitest.unit.config.ts` 使用 V8 覆盖率运行单元测试套件。这是针对已加载文件的单元覆盖率门禁,不是整个仓库所有文件的覆盖率。阈值为:行数 / 函数 / 语句覆盖率 70%,分支覆盖率 55%。由于 `coverage.all` 为 false此门禁只统计被单元覆盖率测试套件加载的文件而不会把拆分测试通道中的每个源文件都视为未覆盖。
- `pnpm test:coverage:changed`:仅针对自 `origin/main` 以来发生变更的文件运行单元覆盖率。
- `pnpm test:changed`:当 diff 仅涉及可路由的源文件 / 测试文件时,将变更的 Git 路径展开为有范围限制的 Vitest 测试通道。配置 / 设置变更仍会回退到原生根项目运行,以便在需要时更广泛地重新运行布线相关改动
- `pnpm changed:lanes`:显示针对 `origin/main` 的 diff 所触发的架构测试通道
- `pnpm check:changed`:对`origin/main` 的 diff 运行智能变更门禁。它会将核心代码工作与核心测试通道一起运行,将扩展工作与扩展测试通道一起运行,将仅测试改动限制为仅测试类型检查 / 测试,针对公开的插件 SDK 或插件契约变更扩展到扩展验证,并让仅发布元数据的版本号变更保持在有针对性的版本 / 配置 / 根依赖检查范围内
- `pnpm test`将显式指定的文件 / 目录目标路由到有范围限制的 Vitest 测试通道。未指定目标的运行会使用固定的分片组,并展开到叶子配置以便在本地并行执行;扩展组始终会展开为按扩展划分的分片配置,而不是一个巨大的根项目进程。
- 完整测试和扩展分片运行会更新 `.artifacts/vitest-shard-timings.json` 中的本地时数据;后续运行会使用这些时数据来平衡慢分片和快分片。设置 `OPENCLAW_TEST_PROJECTS_TIMINGS=0` 可忽略本地时序工件
- 部分 `plugin-sdk``commands` 测试文件现在会通过专用轻量测试通道路由,这些通道仅保留 `test/setup.ts`,而运行时负载较重的用例仍保留在现有测试通道中。
- 部分 `plugin-sdk``commands` 辅助源文件也会把 `pnpm test:changed` 映射到这些轻量测试通道中的显式同级测试,因此小型辅助函数改动无需重新运行重量级、依赖运行时的测试套件。
- `auto-reply` 现在也被拆分为三个专用配置(`core`、`top-level`、`reply`),这样回复测试框架就不会主导较轻量的顶层状态 / token / helper 测试。
- 基础 Vitest 配置现在默认使用 `pool: "threads"``isolate: false`,并在整个仓库配置中启用共享的非隔离运行器。
- `pnpm test:channels` 运行 `vitest.channels.config.ts`
- `pnpm test:extensions``pnpm test extensions` 运行所有扩展 / 插件分片。重量级渠道扩展和 OpenAI 会作为专用分片运行;其他扩展组保持批量运行。对某一个内置插件测试通道,请使用 `pnpm test extensions/<id>`
- `pnpm test:perf:imports`:启用 Vitest 导入耗时 + 导入明细报告,同时仍对显式文件 / 目录目标使用有范围限制的测试通道路由。
- `pnpm test:perf:imports:changed`:与上面相同的导入性能分析,但仅针对`origin/main` 以来发生变更的文件。
- `pnpm test:perf:changed:bench -- --ref <git-ref>`针对同一份已提交的 Git diff对比变更模式路由路径与原生根项目运行的基准表现
- `pnpm test:perf:changed:bench -- --worktree`:无需先提交,即可对当前工作区改动集进行基准测试。
- `pnpm test:perf:profile:main`:为 Vitest 主线程写入 CPU profile`.artifacts/vitest-main-profile`)。
- `pnpm test:perf:profile:runner`:为单元测试运行器写入 CPU + heap profile`.artifacts/vitest-runner-profile`)。
- `pnpm test:perf:groups --full-suite --allow-failures --output .artifacts/test-perf/baseline-before.json`:串行运行完整测试套件中的每个 Vitest 叶子配置,并写入分组时长数据以及每个配置的 JSON/日志产物。测试性能智能体会在尝试修复慢测试之前,将它作为基线。
- `pnpm test:perf:profile:runner`:为单元测试运行器写入 CPU + profile`.artifacts/vitest-runner-profile`)。
- `pnpm test:perf:groups --full-suite --allow-failures --output .artifacts/test-perf/baseline-before.json`:串行运行完整测试套件中的每个 Vitest 叶子配置,并写入分组耗时数据以及每个配置对应的 JSON / 日志工件。测试性能智能体会将其用作尝试修复慢测试之前的基线。
- `pnpm test:perf:groups:compare .artifacts/test-perf/baseline-before.json .artifacts/test-perf/after-agent.json`:比较性能优化改动前后的分组报告。
- Gateway 网关集成:通过 `OPENCLAW_TEST_INCLUDE_GATEWAY=1 pnpm test``pnpm test:gateway` 选择启用。
- `pnpm test:e2e`:运行 Gateway 网关端到端冒烟测试(多实例 WS/HTTP/node 配对)。在 `vitest.e2e.config.ts` 中默认使用 `threads` + `isolate: false` 自适应 worker可通过 `OPENCLAW_E2E_WORKERS=<n>` 调整,并设置 `OPENCLAW_E2E_VERBOSE=1` 获取详细日志。
- `pnpm test:live`:运行 provider 实时测试minimax/zai。需要 API key并且需要 `LIVE=1`(或 provider 专属的 `*_LIVE_TEST=1`)才能取消 skip
- `pnpm test:docker:all`:先构建共享的实时测试镜像和 Docker E2E 镜像一次,然后默认以并发数 4 使用 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行 Docker 冒烟分组。可通过 `OPENCLAW_DOCKER_ALL_PARALLELISM=<n>` 调整。除非设置了 `OPENCLAW_DOCKER_ALL_FAIL_FAST=0`,否则运行器会在首次失败后停止调度新的池化分组;每个分组默认有 120 分钟超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖。对启动或 provider 敏感的分组会在并行池之后以独占方式运行。每个分组的日志会写入 `.artifacts/docker-tests/<run-id>/`
- `pnpm test:docker:openwebui`:启动 Docker 化的 OpenClaw + Open WebUI通过 Open WebUI 登录,检查 `/api/models`,然后通过 `/api/chat/completions` 运行一次真实的代理聊天。需要可用的实时模型 key例如 `~/.profile` 中的 OpenAI会拉取外部 Open WebUI 镜像,并不像常规单元/e2e 测试套件那样要求具备 CI 稳定性。
- `pnpm test:docker:mcp-channels`:启动一个预置数据的 Gateway 网关容器和第二个客户端容器,后者会启动 `openclaw mcp serve`,然后验证路由会话发现、转录读取、附件元数据、实时事件队列行为、出站发送路由,以及通过真实 stdio bridge 传输的 Claude 风格渠道和权限通知。Claude 通知断言会直接读取原始 stdio MCP 帧,因此该冒烟测试能反映 bridge 实际发出的内容。
- `pnpm test:e2e`:运行 Gateway 网关端到端冒烟测试(多实例 WS/HTTP/节点配对)。在 `vitest.e2e.config.ts` 中默认使用 `threads` + `isolate: false` 以及自适应 worker可通过 `OPENCLAW_E2E_WORKERS=<n>` 调整,并设置 `OPENCLAW_E2E_VERBOSE=1` 以输出详细日志。
- `pnpm test:live`:运行提供商 live 测试minimax/zai。需要 API key并设置 `LIVE=1`(或提供商专用的 `*_LIVE_TEST=1`)才能取消跳过
- `pnpm test:docker:all`:先构建一次共享的 live-test 镜像和 Docker E2E 镜像,然后默认以并发度 4 运行 Docker 冒烟测试通道,并设置 `OPENCLAW_SKIP_DOCKER_BUILD=1`。可通过 `OPENCLAW_DOCKER_ALL_PARALLELISM=<n>` 调整。除非设置了 `OPENCLAW_DOCKER_ALL_FAIL_FAST=0`,否则运行器会在首次失败后停止调度新的池化测试通道;每个测试通道默认有 120 分钟超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖。对启动或提供商敏感的测试通道会在并行池之后以独占方式运行。每个测试通道的日志都会写入 `.artifacts/docker-tests/<run-id>/`
- `pnpm test:docker:openwebui`:启动 Docker 化的 OpenClaw + Open WebUI通过 Open WebUI 登录,检查 `/api/models`,然后通过 `/api/chat/completions` 运行一次真实的代理聊天。需要可用的 live 模型 key例如 `~/.profile` 中的 OpenAI会拉取外部 Open WebUI 镜像,并不像普通 unit/e2e 测试套件那样被期望具有 CI 稳定性。
- `pnpm test:docker:mcp-channels`:启动一个带种子数据的 Gateway 网关容器,以及第二个会启动 `openclaw mcp serve` 的客户端容器,然后通过真实的 stdio bridge 验证路由会话发现、转录读取、附件元数据、live 事件队列行为、出站发送路由,以及类似 Claude 的渠道与权限通知。Claude 通知断言会直接读取原始 stdio MCP 帧,因此该冒烟测试能反映 bridge 实际发出的内容。
## 本地 PR 门禁
对于本地 PR 合入/门禁检查,请运行:
在本地执行 PR 合入 / 门禁检查时,运行:
- `pnpm check:changed`
- `pnpm check`
@ -56,12 +56,12 @@ x-i18n:
- `pnpm test`
- `pnpm check:docs`
如果 `pnpm test` 在负载较高的主机上出现偶发失败,在将其视为回归之前先重跑一次,然后使用 `pnpm test <path/to/test>` 进行定位。对于内存受限的主机,可使用:
如果 `pnpm test` 在负载较高的主机上出现偶发失败,在视其为回归之前先重跑一次,然后用 `pnpm test <path/to/test>` 进行定位。对于内存受限的主机,请使用:
- `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`
- `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/tmp/openclaw-vitest-cache pnpm test:changed`
## 模型延迟基准测试(本地 keys
## 模型延迟基准测试(本地 key
脚本:[`scripts/bench-model.ts`](https://github.com/openclaw/openclaw/blob/main/scripts/bench-model.ts)
@ -103,15 +103,15 @@ x-i18n:
- `real``health`、`status`、`status --json`、`sessions`、`sessions --json`、`agents list --json`、`gateway status`、`gateway status --json`、`gateway health --json`、`config get gateway.port`
- `all`:同时包含两个预设
输出会包含每个命令的 `sampleCount`、平均值、p50、p95、最小/最大值、退出码/信号分布,以及最大 RSS 汇总。可选的 `--cpu-prof-dir` / `--heap-prof-dir` 会为每次运行写入 V8 profile这样计时与 profile 捕获会使用同一套 harness
输出会包含每个命令的 `sampleCount`、平均值、p50、p95、最小值 / 最大值、exit-code / signal 分布,以及最大 RSS 摘要。可选的 `--cpu-prof-dir` / `--heap-prof-dir` 会为每次运行写入 V8 profile使计时与 profile 捕获使用同一套测试框架
保存输出约定:
- `pnpm test:startup:bench:smoke` 会将定向冒烟产物写入 `.artifacts/cli-startup-bench-smoke.json`
- `pnpm test:startup:bench:save` 会使用 `runs=5``warmup=1` 将完整测试套件产物写入 `.artifacts/cli-startup-bench-all.json`
- `pnpm test:startup:bench:update` 会使用 `runs=5``warmup=1` 刷新已检入的基线 fixture`test/fixtures/cli-startup-bench.json`
- `pnpm test:startup:bench:smoke` 会将目标冒烟工件写入 `.artifacts/cli-startup-bench-smoke.json`
- `pnpm test:startup:bench:save` 会使用 `runs=5``warmup=1` 将完整测试套件工件写入 `.artifacts/cli-startup-bench-all.json`
- `pnpm test:startup:bench:update` 会使用 `runs=5``warmup=1` 刷新已纳入版本控制的基线 fixture`test/fixtures/cli-startup-bench.json`
检入的 fixture
纳入版本控制的 fixture
- `test/fixtures/cli-startup-bench.json`
- 使用 `pnpm test:startup:bench:update` 刷新
@ -119,7 +119,7 @@ x-i18n:
## 新手引导 E2EDocker
Docker 是可选的;只有在需要容器化新手引导冒烟测试时才需要它
Docker 是可选的;只有在进行容器化新手引导冒烟测试时才需要
在干净的 Linux 容器中运行完整冷启动流程:
@ -127,11 +127,11 @@ Docker 是可选的;只有在需要容器化新手引导冒烟测试时才需
scripts/e2e/onboard-docker.sh
```
该脚本会通过 pseudo-tty 驱动交互式向导,验证配置/工作区/会话文件,然后启动 Gateway 网关并运行 `openclaw health`
该脚本会通过 pseudo-tty 驱动交互式向导,验证 config/workspace/session 文件,然后启动 Gateway 网关并运行 `openclaw health`
## 二维码导入冒烟测试Docker
## QR 导入冒烟测试Docker
确保 `qrcode-terminal` 能在受支持的 Docker Node 运行时中加载(默认 Node 24兼容 Node 22
确保维护中的 QR 运行时 helper 能在受支持的 Docker Node 运行时下加载(默认 Node 24兼容 Node 22
```bash
pnpm test:docker:qr