# Microsoft Teams

连接 Microsoft Teams 机器人，让聊天和频道可以启动并继续 Beeuni 会话。

Canonical page: https://beeuni.ai/docs/zh/connect/teams

Microsoft Teams 是一种渠道：在 Teams 聊天或频道中 `@`提及机器人即可启动一个[会话](/docs/zh/work/sessions)，智能体会在原处回复并实时显示进度。它遵循与 [Slack](/docs/zh/connect/slack) 相同的会话、身份和凭据规则；本页介绍 Teams 特有的内容。

每个项目都可以连接 Teams。无需启用功能标志。

<a id="connect"></a>

## 连接

有两种方式，均可在项目的 **Connectors → Channels** 页面操作：

- **一键连接（托管）。** Teams **租户管理员**点击 **Add to Teams**，登录并授予同意。Beeuni 会自动将应用发布到组织的 Teams 应用目录——无需构建或粘贴任何内容。之后，该行会显示 **Add to Teams**（指向 Teams 客户端的深层链接），任何人都可以将机器人添加到聊天或频道。Beeuni 发布新版应用时，请参阅[更新应用](#update-the-app)。
- **使用自己的机器人。** 点击 **Use your own Microsoft Teams app**，进入三步向导：注册 Azure Bot，使用其应用 ID、客户端密钥和租户 ID 将其连接到 Beeuni，然后下载生成的应用包并上传到 Teams。若要使用自己注册的机器人，请选择此方式。

  操作顺序很重要。Beeuni 会根据**你的**应用 ID 构建清单，因此只有完成第 2 步后才能生成应用包——向导会先显示要粘贴到 Azure 的消息传递终结点，然后才会要求你提供其他信息。

CLI 与控制面板的操作相同：

```bash
beeuni channels connect --platform teams      # prints the admin-consent URL
beeuni channels status --platform teams        # tenant, catalog, publish state, app version
beeuni channels disconnect --platform teams
```

<a id="update-the-app"></a>

## 更新应用

Beeuni 会逐步为 Teams 应用添加权限。应用版本 1.1.0 添加了
`ChannelMessage.Read.Group`，智能体需要此权限才能读取频道帖子串。
版本 1.3.0 添加了 `ChatMessage.Read.Chat`，因此无需提及机器人，群聊中的回复也能继续会话。如果团队仍在使用 1.0.0，则所有帖子串读取都会被拒绝，而且智能体收到的拒绝信息会说明由谁修复。

需要两个人按以下顺序更新应用：

1. **Teams 管理员发布更新。** 如果组织应用目录中的版本较旧，Channels 页面会显示 **Update the Beeuni app in Teams**，Teams 行上的按钮则显示 **Publish app update**。管理员点击该按钮并使用 Microsoft 登录。`beeuni channels status --platform teams`会显示两个版本，`beeuni channels connect --platform teams`会显示登录链接。
2. **团队所有者在 Teams 提供更新的每个团队中更新应用。** 在 Teams 中打开团队，然后选择 **⋯ → Manage team → Apps**，在 Beeuni 应用上选择 **Update** 并确认。除非更新新增了权限或消息操作，否则 Teams 会自动安装更新；新增权限或消息操作需要团队所有者确认。

Beeuni 会记录每次发布时的应用版本。Beeuni 开始记录版本之前发布的安装会一直显示此通知，直到下次发布。

<a id="chats-channels-and-mentions"></a>

## 聊天、频道和提及

- **个人聊天：**每条消息都会发送给机器人。
- **团队频道 / 群聊：**新帖子必须 `@`提及机器人。此后，在机器人已关联的对话中回复即可继续会话，**无需**再次提及机器人，就像 Slack 帖子串一样。这需要应用拥有 `ChannelMessage.Read.Group`（频道）和 `ChatMessage.Read.Chat`（群聊）权限；租户管理员会在添加应用时授予这些权限。如果添加应用时尚未请求这些权限，请由团队所有者更新应用：参阅[更新应用](#update-the-app)。

每个聊天或频道对话都是独立会话。第一条消息会创建会话；之后的消息会继续该会话，即使沙盒已停止并恢复也一样。如果一个租户连接了多个项目，第一条尚未路由的消息会显示**项目选择器**，并记住所选项目。在频道中，只有提及机器人的消息才会显示选择器；如果命令涉及项目（例如 `/models`），则会先询问项目。

在频道中，每个新帖子都会启动独立会话。个人聊天或群聊在整个生命周期内都属于同一段对话，因此会一直使用同一个会话，直到发送 `/new`。在其后附上消息（`/new summarize today's deploys`），即可用该消息启动新会话。之前的会话及其完整历史记录仍保留在 Beeuni 中，确认信息也会链接到该会话。

<a id="conversation-names"></a>

## 对话名称

Teams 会发送类似 `19:…@thread.tacv2;messageid=…` 的 ID。Channels 页面和
`beeuni channels bindings` 会显示人们在 Teams 中看到的名称：

| 对话 | 名称 | 名称下方 |
|---|---|---|
| 频道帖子 | `Team › Channel` | 会话标题，用于区分同一频道中的不同帖子 |
| 群聊 | 聊天标题 | 群聊 |
| 个人聊天 | 对方姓名 | 个人聊天 |

频道消息包含团队 ID，但不包含团队名称，因此 Beeuni 会从 Teams 读取团队和频道名称并保存。团队 General 频道中的帖子会在 Channels 页面下次列出它时显示名称。其他频道中的帖子则会在收到下一条消息时显示名称。在此之前，名称显示为 **Channel thread**。

<a id="who-may-join-a-session"></a>

## 谁可以加入会话

每段对话都有加入策略，可在 Teams 中通过 `/policy` 设置，也可在 Channels 页面设置；Teams 行上的 **N channels** 会列出所有对话：

- **open**（默认）——任何关联项目成员都可以加入在此启动的会话。
- **approval**——会话所有者会收到针对每位新成员的 Approve / Deny 卡片。
- **owner**——只有启动会话的人可以加入。

在 **approval** 和 **owner** 策略下，整个项目在 Beeuni 中也无法查看该会话：只有会话所有者、获准加入的人员以及负责监督会话的账户管理员可以查看。无论采用哪种策略，对审核或批准卡片的操作都会恢复智能体运行。

<a id="identity-linking"></a>

## 身份关联

发送者只需通过 `/login` 关联一次 Beeuni 账户；之后智能体会以该发送者的身份运行，并使用其个人凭据、机密和已连接的应用。未关联账户的发送者，或无权访问该项目的发送者，会收到关联账户或申请访问权限的卡片，而不会启动会话。

登录链接只会出现在只有你能看到的位置：你与机器人的一对一聊天中，或者在频道或群聊中标记为 **Only you can see this message** 的回复里。链接会将发放对象关联到打开链接的人，因此绝不会出现在其他人能看到的位置。如果 Teams 拒绝发送这条私密回复，机器人会将链接发送到你与机器人的私人聊天中，并在对话中说明情况。只有当你已将应用添加到自己的 Teams 中时，Teams 才允许这样做；否则，卡片会要求你打开与机器人的私人聊天，并在那里发送 `/login`。如果你在尚未关联账户时于频道中发送了消息，并在 10 分钟内完成关联，该消息就会执行。你在浏览器中完成关联后，机器人会在与你的私人聊天中确认你关联的 Beeuni 账户。

登录链接只能使用一次，并会在 10 分钟后过期。已连接到一个 Beeuni 账户的 Teams 账户无法再连接到另一个账户：请先发送 `/logout`。如果组织要求进行双重身份验证，Beeuni 会在关联账户前要求你提供第二重验证信息；聊天会使用你通过该验证关联的账户。如果创建链接时未进行双重身份验证，打开链接后会要求你重新关联。

没有访问权限的人提出申请后，Beeuni 会向每位账户所有者和管理员发送请求。已连接 Teams 的人还会在与机器人的私人聊天中收到请求，并看到 **在 Beeuni 中审核** 按钮。

<a id="private-replies"></a>

## 私人回复

在频道或群聊中，机器人对某个人说的话只有该人可见，并会标记为 **只有你能看到此消息**，就像 Slack 的临时消息一样：

- 对你所发命令的回复；
- 用于连接账户或申请访问权限的卡片；
- 说明消息为何未运行的内容：加入策略，或你无权使用的代理；
- 在 **审批** 下，所有者的批准/拒绝卡片，以及向申请人发送的审批决定。

只有当你的消息可以运行时，所有人都会看到 **正在处理…** 卡片。Teams 会在 24 小时后删除私人回复。如果 Teams 拒绝发送私人回复，机器人就会改为在整个对话中发布卡片。登录链接是例外：此时链接会发送到你与机器人的私人聊天中，绝不会发送到对话中。

<a id="roles-and-permissions"></a>

## 角色和权限

聊天本身不带有 Beeuni 凭据，因此每项操作都以执行者所连接的 Beeuni 账户身份运行，并遵循该账户的项目角色。账户所有者和管理员均视为项目经理。

| 操作 | 谁有权执行 |
| --- | --- |
| 发送由代理回复的消息 | 已连接且有权访问该项目的账户，并且符合对话的加入策略 |
| 停止运行 | 发送了正在回复的消息的人，或任何已获准加入该会话且仍可使用该项目的人 |
| 在群聊中 `/new` | 已连接的项目成员；在 `approval` 或 `owner` 下，已获准加入当前会话的人 |
| 更改项目、代理、模型或策略 | 项目经理；在与机器人的私人聊天中，任何项目成员均可 |
| 批准或拒绝审核 | 项目经理 |
| 批准或拒绝连接器调用 | 项目经理，或启动会话的人 |
| 批准某人加入 `approval` 会话 | 会话所有者 |
| 申请访问权限 | 任何已连接的人；每位账户所有者和管理员都会收到一次通知 |

被排除在某个代理使用范围之外的人，无法像在 Beeuni 中一样从 Teams 启动该代理的会话。

<a id="commands"></a>

## 命令

在与机器人的聊天中输入这些命令（在频道中，请先 `@` 提及机器人）：

| 命令 | 功能 |
| --- | --- |
| `/help` | 列出命令 |
| `/login`、`/logout`、`/whoami` | 连接、断开连接或查看你的 Beeuni 账户 |
| `/status` | 查看此处生效的项目、代理和模型，以及正在运行的任务 |
| `/stop` | 停止此处正在运行的任务 |
| `/new [message]` | 在此聊天中启动新会话（私人聊天和群聊） |
| `/models`、`/model <ref>` | 为此对话选择模型 — 请参阅 [模型和密钥](#models-and-keys) |
| `/agents`、`/agent <name>` | 为此对话选择代理 |
| `/projects`、`/use <name>` | 列出已连接的项目，或将当前对话切换到其他项目（项目自己的机器人除外） |
| `/policy [open\|approval\|owner]` | 查看谁可以加入在此启动的会话 |
| `/unbind` | 断开此对话与其项目的连接；下一条消息会重新选择项目 |
| `/home` | 查看组织的项目以及可以尝试的内容 |

对任何消息，选择 **⋯ → 在 Beeuni 中打开**，即可打开该对话所属的会话。为自己添加应用后，与机器人的私人聊天会显示与 `/home` 相同的概览。

<a id="models-and-keys"></a>

## 模型和密钥

`/models` 会列出你在此处可以运行的所有模型，和网页模型选择器一样：Beeuni 模型，以及通过提供商 API 密钥或 ChatGPT 订阅使用的模型。每个模型都会说明其付费方式。列表较长时会显示可搜索的下拉菜单。点击模型，或输入 `/model <id>`；`/model default` 可恢复为项目默认模型。

可用密钥取决于聊天类型：

- **与机器人的私人聊天** — 你自己的密钥和 ChatGPT 订阅，以及与项目共享的密钥。会话仅你可见。
- **群聊或频道** — 仅限与整个项目共享的密钥。聊天内容是共享的；若在此使用个人密钥，所有人都可以使用一个人的订阅。因此，请改在私人聊天中选择此类模型。

通过密钥运行的模型会使用你有权使用的该提供商的所有密钥，并在遇到速率限制时轮换密钥。此选择从下一条消息开始生效；聊天会保留当前会话。更改正在运行的会话所用模型需要已连接账户；在 `owner` 或 `approval` 策略下，只有会话所有者可以更改。若私人聊天中的会话是在此类会话转为私密之前启动的，该会话仍与项目共享：`/model` 会对此作出说明，`/new` 则会启动私人会话。请参阅 [提供商密钥和成员访问权限](/docs/zh/project/models#provider-keys-and-member-access)。

<a id="while-the-agent-works"></a>

## 代理工作期间

代理会在一张卡片中回复：工作时显示步骤，完成后显示回复。长回复会在行末截断，标明内容已截断，并附上 Beeuni 中完整会话的链接。后续消息会等待当前任务完成，然后以单独的卡片显示。安静的步骤不会结束任务；只有运行时报告完成才会结束。

<a id="stopping-a-run"></a>

## 停止运行

代理工作期间，其动态卡片上会显示 **停止** 按钮。卡片滚出可见范围后，`/stop` 也可执行相同操作。两者都会在运行时终止任务，而不只是关闭卡片 — 如果任务一直处于打开状态，对话中的下一条消息就会被该任务接收，而不会得到回复。

发送了正在回复的消息的人可以停止任务，任何已获准加入该会话且仍可使用该项目的人也可以。频道中的旁观者不可以；获准加入该聊天较早会话的人也不可以。

<a id="files"></a>

## 文件

代理使用 `teams send --file` 发送文件。在私人聊天、群聊和频道中，图片会以内嵌形式显示。超大图片在私人聊天中会改为显示 OneDrive 授权卡片，在频道中则会改用团队文件；群聊无法接收超大文件传输。其他文件在私人聊天中会显示授权卡片，分享给团队时则会以 SharePoint 链接的形式发送。代理可以下载和处理收到的文件及粘贴的图片，包括嵌入频道消息正文中的图片。

- 只有当代理根据 [读取频道](#reading-a-channel) 中的规则可以读取该话题或聊天时，粘贴到频道或群聊中的图片才会被下载。否则，`teams download` 会回复 `403`。
- 下载上限为 50 MB，超过时会回复 `413`。
- `teams history` 和 `teams thread` 会列出帖子的内嵌图片（`images`，每项都是 `teams download` 的 URL）以及从 SharePoint 分享的文件名称（`files`）。代理无法下载 SharePoint 文件。

粘贴屏幕截图并提问时，即使该对话所用模型仅支持文本，这一轮也会改用支持读取图片的模型运行 — 不会更改会话的模型选择。若会话所用模型已无法运行（例如密钥已撤销，或聊天无法使用 ChatGPT 订阅），该消息也会以同样方式改用其他模型，而不是直接失败。

<a id="questions-and-forms"></a>

## 问题和表单

智能体使用交互控件提问，而非纯文字。一个问题且最多六个选项时，每个选项对应一个按钮。多个问题、可多选的问题或选项列表较长时，会显示一个表单，并附带填写自定义答案的输入框。日期、时间、数字和开/关选项会使用相应的输入控件。你的回答会作为下一条消息发送给智能体，并标明它回答的是哪个问题。你也可以随时直接输入答案。

<a id="approvals"></a>

## 审批

智能体需要人在继续之前作出决定时——例如执行有风险的步骤，或批准它想要进行的更改——它会提交一项审核。如果会话是从此对话启动的，决定请求会以卡片的形式显示在这里：包含标题、摘要、风险级别，以及 **批准**、**请求更改** 和 **拒绝** 按钮，还有一个指向 Beeuni 的链接。

卡片带有一个可选的反馈框。你输入的内容会与决定一起保存，并一并传给智能体，因此选择“请求更改”后，不必再多来回一次才能说明你的意思。此反馈框有意设为可选——直接批准不应该还需要输入内容。

对话中的所有人都能看到此卡片，但只有关联了 Beeuni 账户且有权访问该项目的人才能处理它。

<a id="posting-on-its-own"></a>

## 主动发消息

除了在收到提问的对话中回复，智能体还可以向该项目中机器人已加入的任何聊天或频道发消息——例如夜间摘要、警报或交接信息。它无法联系从未将它添加进去的对话。

<a id="when-a-session-will-not-start"></a>

## 会话无法启动时

Beeuni 会说明问题所在，而不是让你重试，因为大多数启动失败重试也无法解决：沙盒模板已不存在、账户未关联、你无权访问该项目，或工作区额度不足。每条提示都会指出解决方法及相应命令。

如果对话所设定的智能体已被删除或重命名，系统会显示一个选择器：选择一个可用的智能体，然后重新发送消息。列表中只会显示你有权使用的智能体。

<a id="reading-a-channel"></a>

## 读取频道内容

在频道中，机器人只会收到提及它的消息，因此它的会话中没有它被要求查看的讨论内容。需要这些上下文时，智能体会通过应用加入团队时请求的 `ChannelMessage.Read.Group` 权限，读取提及它的帖子所在的对话串，或频道中的近期帖子。若团队在请求该权限之前就已添加应用，则必须更新应用：参见[更新应用](#update-the-app)。在个人聊天中，每条消息都会进入会话，因此没有更早的内容需要读取。

连接到同一 Microsoft 365 租户的每个项目都使用同一个应用，因此 Beeuni 会确保读取范围仅限于项目自身的对话。在频道中，每条新帖子都属于一个独立的对话：

| 读取内容 | 允许条件 |
| --- | --- |
| 对话串（`teams thread`） | 该对话串属于此项目，或者不属于任何项目且频道可读取 |
| 频道的近期帖子（`teams history`） | 此项目在该频道中有对话。发起其他项目对话的帖子不会包含在内 |
| 没有任何项目进行对话的频道 | 没有其他项目连接到该租户 |

读取被拒绝时，`403` 会以 `conversation_not_in_project` 回复，并附上指出频道或对话串的消息。若租户中有多个项目，而你要读取第一种频道，请在其中提及机器人并选择此项目。

`get_channel` 和 `list_channels` 可读取标准频道。只有当智能体根据上述规则有权读取时，才会显示专用或共享频道。`get_team`、`list_members`、`get_user` 和 `list_teams` 会读取租户目录，不受限制。

<a id="limits"></a>

## 限制

Teams 机器人无法添加消息反应或搜索消息——这些 Slack 功能在 Teams 中没有对应功能。智能体只能读取其所在团队中的频道，并且必须遵循[读取频道内容](#reading-a-channel)中的规则。
