跳转到内容

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。

Terminal window
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": "..." }

  1. 在 Slack 应用中打开 Event Subscriptions,把 webhookUrl 作为请求 URL。添加机器人事件 app_mention 和 message.im。
  2. 用同一个 webhookUrl 打开 Interactivity,这样机器人的按钮才能用。
  3. 给机器人 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_...。你在这个路径上的客户端先让这个人登录,然后调用:

  1. GET /v1/channel-links/:code,显示这个关联码属于谁。
  2. 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。所以:

  • author 的权限决定 Agent 可以做什么,
  • requests 发给 author,
  • Agent 知道它在和 author 一起工作,并会了解这个人。

一个人需要这个 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 现在无法访问它的存储。