跳转至

消息行为

How replies are routed and threaded, and how attachments and files move in and out of Teams.

路由与会话

  • 会话键遵循标准 agent 格式(参见 /concepts/session):
  • 直接消息默认共享主会话(agent:<agentId>:main)。
  • 频道/群组消息使用会话 ID:
    • agent:<agentId>:msteams:channel:<conversationId>
    • agent:<agentId>:msteams:group:<conversationId>

频道元数据

消息工具的 channel-list 操作需要 teamId。channel-info 操作需要 teamId 和 channelId。请使用 Microsoft Teams 的团队 ID,即使同时配置了 Slack 也是如此;共享的 teamId 字段接受各提供方的 ID 格式。这些操作保留已配置的 Teams 访问规则。

Graph 操作

在 Teams 操作处理器启动后,置顶、取消置顶、添加或移除表情回应、更改参与者以及重命名会话会在每次 Graph 请求前重新检查其调用者。在处理器准备 token、目标授权或参与者查找期间取消,会停止其下一个请求。取消不会撤销 Microsoft 已接受的变更。现有的账户、目标和所有者/管理员规则仍然适用;置顶和取消置顶仍仅限于聊天。

回复样式:线程与帖子

Teams 在同一底层数据模型上有两种频道 UI 样式:

样式 描述 推荐 replyStyle
Posts(经典) 消息以卡片形式显示,下方为线程回复 thread(默认)
Threads(类似 Slack) 消息线性流动,更接近 Slack top-level

问题: Teams API 不会公开频道使用哪种 UI 样式。如果使用错误的 replyStyle:

  • 在线程样式频道中使用 thread → 回复会以尴尬的方式嵌套显示。
  • 在帖子样式频道中使用 top-level → 回复会显示为独立的顶级帖子,而不是线程内回复。

解决方案: 根据频道的设置方式,按频道配置 replyStyle:

{
  channels: {
    msteams: {
      replyStyle: "thread",
      teams: {
        "19:abc...@thread.tacv2": {
          channels: {
            "19:xyz...@thread.tacv2": {
              replyStyle: "top-level",
            },
          },
        },
      },
    },
  },
}

解析优先级

当机器人向频道发送回复时,replyStyle 会从最具体的覆盖项解析到默认值。第一个非 undefined 值生效:

  1. 按频道 - channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle
  2. 按团队 - channels.msteams.teams.<teamId>.replyStyle
  3. 全局 - channels.msteams.replyStyle
  4. 隐式默认 - 由 requireMention 派生:
  5. requireMention: true → thread
  6. requireMention: false → top-level

如果全局设置 requireMention: false 且没有显式 replyStyle,帖子样式频道中的提及会显示为顶级帖子,即使入站消息是线程回复。为避免意外,请在全局、团队或频道级别固定 replyStyle: "thread"。

对于发送到已存储频道会话的主动发送(排队的工具调用回复、长时间运行的 agent),应用相同的团队/频道解析;无论 replyStyle 如何,群组聊天和个人(DM)会话的主动发送始终解析为 top-level。

线程上下文保留

当 replyStyle: "thread" 生效,且机器人从频道线程内部被 @提及 时,OpenClaw 会将原始线程根重新附加到出站会话引用(19:...@thread.tacv2;messageid=<root>),使回复落在同一线程内。这既适用于实时(回合内)发送,也适用于 Bot Framework 回合上下文过期后进行的主动发送(例如长时间运行的 agent、通过 mcp__openclaw__message 排队的工具调用回复)。

线程根取自会话引用上存储的 threadId。早于 threadId 的旧存储引用会回退到 activityId(最后为会话播种的入站活动),因此现有部署无需重新播种即可继续工作。

当 replyStyle: "top-level" 生效时,频道线程入站消息会被有意作为新的顶级帖子回复;不会附加线程后缀。对于线程样式频道,这是正确的;如果你期望线程回复却看到顶级帖子,说明该频道的 replyStyle 设置不正确。

投递取消与重试

文本、图片、文件和演示卡片的任务与队列投递会在每次 Teams 请求前检查其当前投递权限,包括在获取 token 和等待速率限制之后。取消会停止尚未开始的请求。Teams 已接受的消息和文件卡片保留其投递回执;部分完成的文本与媒体发送保留其已接受的部分。

必需的 SharePoint 上传、成员查找、共享链接和重定向在继续前会检查相同的权限。已上传的 SharePoint 文件是为 Teams 文件卡片做准备;仅上传本身不会确认已投递到聊天。

出站提及

在出站文本中使用 @[Name](id),将 id 替换为 Teams 用户/机器人 ID 或 Microsoft Entra 对象 ID。将显示名称中的方括号转义为 \[ 和 \]:Alice \[Ops\] 在原生提及中变为 Alice [Ops]。转义的反斜杠使用 \\。相同的格式适用于消息编辑和文件说明。

附件与图片

当前限制:

  • DM: 图片和文件附件可通过 Teams 机器人文件 API 工作。
  • 频道/群组: 附件存储在 M365 存储(SharePoint/OneDrive)中。webhook 负载仅包含 HTML 存根,而不是实际文件字节。下载频道附件需要 Graph API 权限。
  • 对于显式的文件优先发送,请使用 action=upload-file 并配合 media / filePath / path;可选的 message 会成为伴随文本/注释,filename(或 title)会覆盖上传名称。

没有 Graph 权限时,包含图片的频道消息会以纯文本形式到达(图片内容对机器人不可访问)。 默认情况下,OpenClaw 仅从 Microsoft/Teams 主机名下载媒体。使用 channels.msteams.mediaAllowHosts 覆盖(使用 ["*"] 允许任意主机)。 授权头仅附加到 channels.msteams.mediaAuthAllowHosts 中的主机(默认为 Graph + Bot Framework 主机)。请保持此列表严格(避免多租户后缀)。

在群聊中发送文件

机器人可以使用内置的 FileConsentCard 流程在私聊中发送文件。在群聊/频道中发送文件需要额外设置:

上下文 文件发送方式 所需设置
私聊 FileConsentCard → 用户接受 → 机器人上传 开箱即用
群聊/频道 上传到 SharePoint → 原生文件卡片 需要 sharePointSiteId + Graph 权限
图片(任意上下文) Base64 编码内联 开箱即用

为什么群聊需要 SharePoint

机器人使用应用程序身份,而 Microsoft Graph 的 /me 资源需要已登录用户。要在群聊/频道中发送文件,机器人会上传到 SharePoint 站点并创建共享链接。

设置

  1. 在 Entra ID(Azure AD)→ 应用注册中添加 Graph API 权限:
  2. Sites.ReadWrite.All(应用程序)- 上传文件到 SharePoint。
  3. ChatMember.Read.All(应用程序)- 用于群聊文件发送的最小权限租户范围权限。Chat.Read.All 也可用,并且在启用群聊历史记录时已覆盖此权限。作为按聊天替代方案,使用 ChatMember.Read.Chat 资源特定同意权限。
  4. 为租户授予管理员同意。
  5. 获取你的 SharePoint 站点 ID:
# Via Graph Explorer or curl with a valid token:
curl -H "Authorization: Bearer $TOKEN" \
  "https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}"

# Example: for a site at "contoso.sharepoint.com/sites/BotFiles"
curl -H "Authorization: Bearer $TOKEN" \
  "https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles"

# Response includes: "id": "contoso.sharepoint.com,guid1,guid2"
  1. 配置 OpenClaw:
{
  channels: {
    msteams: {
      // ... other config ...
      sharePointSiteId: "contoso.sharepoint.com,guid1,guid2",
    },
  },
}

共享行为

上下文和权限 共享行为
频道 + Sites.ReadWrite.All 组织范围共享链接(组织内任何人可访问)
群聊 + Sites.ReadWrite.All + 受支持的聊天成员读取授权 按用户共享链接(仅聊天成员可访问)
群聊没有受支持的聊天成员读取授权 发送失败(安全失败)

按用户共享更安全,因为只有聊天参与者可以访问文件。OpenClaw 要求群聊成功查找成员;超时、传输失败、空结果和 Graph API 拒绝会导致发送失败,而不是将访问范围扩大到整个组织。

回退行为

场景 结果
群聊 + 文件 + 已配置 SharePoint 和成员权限 上传到 SharePoint,发送原生文件卡片
群聊 + 文件 + 缺少 SharePoint 或成员权限 以可操作的配置错误失败
频道 + 文件 + 已配置 sharePointSiteId 上传到 SharePoint,发送原生文件卡片
个人聊天 + 文件 FileConsentCard 流程(无需 SharePoint 也可用)
任意上下文 + 图片 Base64 编码内联(无需 SharePoint 也可用)

文件存储位置

上传的文件存储在已配置 SharePoint 站点的默认文档库中的 /OpenClawShared/ 文件夹中。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw