Channels
Channel 是你的组织在一个聊天平台上的一个机器人,比如一个 Slack 应用或一个 Telegram 机器人。人们在私聊和群聊中与这个 Channel 的 Agent 交谈。OpenBot 仍然是无界面的:Channel 是平台和 API 之间的连接,不是用户界面。
| 类型 | 平台 |
|---|---|
slack |
启用了 Events API 和交互功能的 Slack 应用。 |
lark |
启用了事件订阅和卡片回调的 Lark(飞书)应用。 |
telegram |
一个 Telegram 机器人。 |
custom |
任何其他使用 OpenBot Channel 协议 的系统。 |
每个平台都把它的事件发到一个 webhook(OpenBot 接收回调的地址)。OpenBot 会检查每个事件的签名,立即回答,然后再做工作。
添加 Channel
由管理员添加 Channel。这需要 channels.write。
curl -H "Authorization: Bearer $OPENBOT_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Team Slack","agent":"agt_...","linkPath":"/link","credentials":{"kind":"slack","botToken":"xoxb-...","signingSecret":"..."}}' \ https://api.example.com/v1/organization/channels| 字段 | 作用 |
|---|---|
name |
一个简短的名字。 |
agent |
负责回答的 Agent。它必须是组织共享的长期 Agent。 |
credentials |
平台的机密。见下文。它们可以写入,但永远不会返回。返回结果只显示 set 和一个 hint。 |
linkPath |
可选。你的公开 origin 上的一个路径,你的客户端在这里让人关联账户。 |
enabled |
可选。false 会停用这个 Channel。它的 webhook 之后会返回 401。 |
返回 201 和这个 Channel。把它的 webhookUrl 交给平台。调用者成为这个 Channel 的 actor。Channel 的聊天是 actor 的 Sessions。actor 必须能把工作交给这个 Agent。
GET、PATCH 和 DELETE /v1/organization/channels/:channelId 读取、修改和删除一个 Channel。修改需要 expectedVersion,也就是你读到的 version。旧的 version 会返回 409 和 ChannelConflict。修改会让调用者成为 actor。不填 credentials 就会保留原来的机密。Channel 的类型不能修改。
Slack
credentials:{ "kind": "slack", "botToken": "xoxb-...", "signingSecret": "..." }
- 在 Slack 应用中打开 Event Subscriptions,把
webhookUrl作为请求 URL。添加机器人事件app_mention和message.im。 - 用同一个
webhookUrl打开 Interactivity,这样机器人的按钮才能用。 - 给机器人
chat:write、app_mentions:read和im:history这些权限范围(scope)。
OpenBot 会检查每个请求的 X-Slack-Signature。超过 5 分钟的请求会被拒绝。
Lark
credentials:{ "kind": "lark", "domain": "feishu", "appId": "cli_...", "appSecret": "...", "verificationToken": "...", "encryptKey": "..." }
在中国使用飞书时,domain 是 feishu;在其他地方使用 Lark 时,它是 lark。在 Lark 应用中设置一个 Encrypt Key,并把 webhookUrl 作为事件和卡片回调的请求 URL。添加事件 im.message.receive_v1。OpenBot 会检查每个事件的签名和验证令牌(verification token)。
Telegram
credentials:{ "kind": "telegram", "botToken": "123456:..." }
OpenBot 会替你调用 setWebhook,并带上它自己的一个密钥。Telegram 每次发来更新时都会带回这个密钥。在群组中,机器人只会收到提到它或回复它的消息,除非你关闭它的隐私模式。
关联你的账户
机器人只为组织的成员工作。当一个还没有关联的人给机器人写消息时,什么都不会运行。机器人会改为给他发一个关联码(link code)。它把关联码发到只有这个人能看到的地方:私聊中,或者在平台支持时,作为群里的私密消息。关联码 15 分钟内有效。
设置了 linkPath 时,机器人会发一个链接,比如 https://api.example.com/link?code=ocl_...。你在这个路径上的客户端先让这个人登录,然后调用:
GET /v1/channel-links/:code,显示这个关联码属于谁。POST /v1/channel-links/:code/accept,把账户关联到调用者。一个关联码只能用一次。
关联需要一个 POST,所以没有人能只靠给别人发一个链接就关联账户。没有 linkPath 时,机器人只发送关联码,这个人用同样的调用来接受它。
GET /v1/user/channel-identities 列出你关联的账户。DELETE /v1/user/channel-identities/:channelId/:externalUserId 解除一个关联。管理员用 GET /v1/organization/channels/:channelId/identities 列出一个 Channel 的人,并对 .../identities/:externalUserId 使用 DELETE 解除一个人的关联。
聊天和 Sessions
每个聊天都是一个 kind: "channel" 的长期 Session:每个私聊一个 Session,每个群聊一个 Session。以 actor 的身份用 GET /v1/sessions?kind=channel 列出它们。
- 在私聊中,每条消息都会交给 Agent。
- 在群聊中,只有提到机器人的消息才会交给 Agent。每条消息都以写它的人的名字开头,这样 Agent 知道是谁在说话。
- Agent 还在工作时收到的消息会在队列中等待。它作为单独的 Turn 运行。
- Turn 运行时,机器人会显示它的文本,并随着文本变长不断修改这条消息。很长的回答会分成几部分发出。
- 如果平台把同一个事件发了第二次,OpenBot 会丢弃它。
每个 Turn 为它的发言人工作
聊天中的 Turn 为写下这条消息的人工作,而不是为 actor 工作。OpenBot 把这个人叫作 Turn 的 author(作者)。Turn 的 author 字段是这个人的用户 ID。所以:
一个人需要这个 Channel 上的 channels.use 才能和它的 Agent 交谈。不再处于活跃状态的成员不能使用它。
聊天中的批准
当一个 request 在等待某个人时,机器人会发送一张带三个按钮的卡片:Allow once(允许一次)、Allow for this Session(在这个 Session 中允许)和 Deny(拒绝)。在计划模式中等待批准的计划也会收到同样的卡片。如果这个人在这个 Channel 上关联了账户,卡片会发到他的私聊。否则,卡片会发到这项工作所在的聊天。
只有 request 在等待的那个人才能按按钮。当 request 发生变化时,卡片会显示这个决定。
Agent 也可以用工具 openbot_channel_send 向同一个 Channel 的另一个聊天发消息。每次发消息都是能力 channels.send,所以由决策者链来决定。
自定义 Channel
自定义 Channel 可以连接任何其他聊天系统。用 credentials:{ "kind": "custom", "endpoint": "https://chat.example.com/openbot" } 创建它。然后用 PUT /v1/organization/channels/:channelId/secret 生成它的签名密钥。OpenBot 只显示一次这个密钥。新的密钥会让旧的密钥失效。
两个方向的签名方式相同。每个请求都有两个请求头:
X-OpenBot-Timestamp:以秒为单位的时间。X-OpenBot-Signature:sha256=,后面接用密钥对timestamp + "." + body计算出的十六进制 HMAC-SHA256。
你的系统把事件发到 webhookUrl:
{ "type": "message", "id": "evt_1", "conversation": { "id": "room-1", "kind": "group", "title": "Ops" }, "sender": { "id": "u_42", "name": "Ada" }, "text": "What broke last night?", "mentioned": true}id 在这个 Channel 中必须唯一。按下按钮的事件是 { "type": "action", "id", "conversation", "sender", "value" }。
OpenBot 把消息发到 endpoint。一个 send 包含 markdown、可选的 buttons,以及 conversation、person 或两者都有。两者都有时,只有这个人应该在这个聊天中看到它。只有 person 时,它会发到私聊。用 { "ref": "<message id>" } 回答;如果 OpenBot 没有发送 conversation,就加上 conversation。一个 edit 包含 conversation、ref 和新的 markdown。OpenBot 只调用公开地址。
权限
| 权限组 | 允许做什么 |
|---|---|
channels.read |
读取组织的 Channels,以及与它们关联的人。 |
channels.write |
创建、修改和删除 Channels,设置它们的密钥,并解除人的关联。 |
channels.use |
与 Channel 的 Agent 交谈,并让你的 Agents 向它发消息。 |
channel_identities.read |
读取你关联的账户。 |
channel_identities.write |
关联和解除关联你的账户。 |
Channels 位于 openbot.org.<org>.channel 之下。管理员管理它们。每个成员都可以使用它们,并关联自己的账户。见 权限。
错误
| 错误 | 状态 | 何时出现 |
|---|---|---|
ChannelInvalid |
400 | Agent 不是共享的长期 Agent、类型被修改了,或者平台没有接受这个 webhook。 |
ChannelNotFound |
404 | 没有这个 Channel。 |
ChannelLinkInvalid |
404 | 关联码未知、已经用过或已经过期。请再向机器人要一次。 |
ChannelIdentityNotFound |
404 | 没有这个关联的账户。 |
ChannelConflict |
409 | 你读取之后,这个 Channel 已经改变。 |
ChannelHookDenied |
401 | 签名错误或太旧,或者这个 Channel 已停用。 |
ChannelsUnavailable |
503 | OpenBot 现在无法访问它的存储。 |