基于 API 的消息扩展

注意

基于 API 的消息扩展仅支持搜索命令。

使用 API 构建的消息扩展 (基于 API 的) 使用 Web 服务来管理用户请求和响应,并且不需要机器人注册。 基于 API 的消息扩展是一种Microsoft Teams 应用功能,可将外部 API 直接集成到 Teams 中,从而增强应用的可用性并提供无缝的用户体验。 基于 API 的消息扩展支持搜索命令,可用于从 Teams 中的外部服务提取和显示数据,通过减少在应用程序之间切换的需要来简化工作流。 基于 API 的消息扩展可帮助应用直接与第三方数据、应用和服务交互,从而增强其功能。 使用基于 API 的消息扩展,可以:

  • 检索实时信息,例如产品发布的最新新闻报道。
  • 检索基于知识的信息,例如 Figma 中的团队设计文件

观看视频,了解有关使用 Teams 工具包生成基于 API 的消息扩展的详细信息:


传统的基于机器人的消息扩展 基于 API 的消息扩展
开发人员需要生成、部署和维护服务来处理来自 Teams 客户端的调用命令。 如果可以使用 OpenAPI 规范来描述最终服务的 API,则开发人员无需使用中间层处理服务。
此服务处理传入查询,并调用开发人员的最终服务。 Teams 可以直接使用 OpenAPI 规范 来生成请求并与开发人员的最终服务进行通信。

下图显示了通过传统消息扩展和 API 消息扩展的用户查询流:

屏幕截图显示了使用传统消息扩展的用户、Teams 客户端和 Teams 机器人服务之间的用户查询流。此图还显示了 API 规范、呈现模板、API 如何相互关联。 使用传统消息扩展的用户查询流。开发人员必须维护自定义机器人处理程序服务,该服务处理来自 Teams 机器人的请求。调用查询时,处理程序服务会向开发人员的服务发送请求。


屏幕截图显示了使用 API 消息扩展的用户、Teams 客户端和 Teams 机器人服务之间的查询流。此图还显示了 API 规范、呈现模板、API 如何相互关联。 使用 API 消息扩展的用户查询流。只要在应用包的 OpenAPI 规范中明确概述了交互,就不需要开发人员维护的处理程序服务。



下面是在查询命令调用期间发生的事件的高级序列:

  1. 当用户调用查询命令时,Teams 机器人服务接收查询命令的参数。

  2. 查询命令在应用清单文件中定义。 命令定义包含对 operationId OpenAPI 规范文件内的 的引用,以及 Teams 客户端为该命令呈现的参数的详细信息。 作为参考, operationId OpenAPI 规范文件中的 对于特定的 HTTP 操作是唯一的。

  3. 然后,Teams 机器人服务使用用户提供的参数以及关联的 operationId 的 OpenAPI 规范副本,为开发人员的终结点生成 HTTP 请求。

  4. 如果身份验证是必需的,并且已在清单中配置。 它已解析为相应的令牌或密钥。 此令牌或密钥用作传出请求的一部分。 [可选]

  5. Teams 机器人服务对开发人员的服务执行 HTTP 请求。

  6. 开发人员服务应根据 OpenAPI 规范中概述的架构做出响应。 这是 JSON 格式。

  7. Teams 客户端必须向用户显示结果。 若要将上一步中的 JSON 结果转换为 UI,Teams 机器人服务使用响应呈现模板为每个结果生成自适应卡片。

  8. 自适应卡片将发送到客户端,客户端在 UI 中呈现它们。

关系图显示了在基于 API 的消息扩展中调用查询时的高级序列流。

先决条件

应用定义包包括支持此功能的各种引人注目的项目。 在开始之前,请确保对以下文件有基本的了解:

OpenAPI 说明 (OAD)

OpenAPI description documenat 是用于描述 API 的采用的行业标准。 它允许你从其实现中抽象出 API,提供人类可读和计算机可读的与语言无关的定义。 OpenAPI 说明文档概述了扩展支持的交互,使 Teams 能够生成请求并直接与服务通信,而无需中间层处理服务。

OpenAPI 说明文档包含与开发人员服务进行通信的详细信息。 确保遵循以下有关 OpenAPI 说明 (OAD) 文档的准则:

  • 支持 OpenAPI 版本 2.0 和 3.0.x。
  • JSON 和 YAML 是支持的格式。
  • 请求正文(如果存在)必须为 application/Json。
  • servers.url 属性定义 HTTPS 协议服务器 URL。
  • 仅支持 POST 和 GET HTTP 方法。
  • OpenAPI 说明文档必须具有 operationId
  • 仅允许一个不带默认值的必需参数。
  • 具有默认值的必需参数被视为可选参数。
  • 用户不得为标头或 Cookie 输入参数。
  • 操作不得具有不带默认值的必需标头或 Cookie 参数。
  • 确保 OpenAPI 说明文档中没有远程引用。
  • 不支持为请求构造数组;但是,支持 JSON 请求正文中的嵌套对象。
  • Teams 不支持 oneOf、、 anyOfallOfnot (swagger.io) 构造。

以下代码是 OpenAPI 说明文档的示例:

示例 OpenAPI 说明文档
openapi: 3.0.1
info:
title: OpenTools Plugin
description: A plugin that allows the user to find the most appropriate AI tools for their use cases, with their pricing information.
version: 'v1'
servers:
- url: https://gptplugin.opentools.ai
paths:
/tools:
 get:
   operationId: searchTools
   summary: Search for AI Tools
   parameters:
     - in: query
       name: search
       required: true
       schema:
         type: string
       description: Used to search for AI tools by their category based on the keywords. For example, ?search="tool to create music" will give tools that can create music.
   responses:
     "200":
       description: OK
       content:
         application/json:
           schema:
             $ref: '#/components/schemas/searchToolsResponse'
     "400":
       description: Search Error
       content:
         application/json:
           schema:
             $ref: '#/components/schemas/searchToolsError'
components:
schemas:
 searchToolsResponse:
   required:
     - search
   type: object
   properties:
     tools:
       type: array
       items:
         type: object
         properties:
           name:
             type: string
             description: The name of the tool.
           opentools_url:
             type: string
             description: The URL to access the tool.
           main_summary:
             type: string
             description: A summary of what the tool is.
           pricing_summary:
             type: string
             description: A summary of the pricing of the tool.
           categories:
             type: array
             items:
               type: string
             description: The categories assigned to the tool.
           platforms:
             type: array
             items:
               type: string
             description: The platforms that this tool is available on.
       description: The list of AI tools.
 searchToolsError:
   type: object
   properties:
     message:
       type: string
       description: Message of the error.

有关如何在 YAML 中编写 OpenAPI 定义的详细信息,请参阅 OpenAPI 结构。

应用部件清单

应用清单是 Teams 应用的蓝图,用于定义在 Teams 客户端中调用消息扩展的方式和位置。 它包括扩展支持的命令以及可从中访问这些命令的位置,例如撰写消息区域、命令栏和消息。 清单链接到 OpenAPI 规范和响应呈现模板,以确保正常运行。

应用清单包含查询命令定义。 确保遵循以下应用清单指南:

  • 将应用清单版本设置为 1.17
  • 设置为 composeExtensions.composeExtensionTypeapiBased
  • 将 定义为 composeExtensions.apiSpecificationFile 文件夹中 OpenAPI 说明文档的相对路径。 这会将应用清单链接到 API 规范。
  • 定义为 apiResponseRenderingTemplateFile 响应呈现模板的相对路径。 这将指定用于呈现 API 响应的模板的位置。
  • 每个命令都必须具有指向响应呈现模板的链接。 这会将每个命令连接到其相应的响应格式。
  • Commands.id应用清单中的 属性必须与 OpenAPI 说明文档中的 匹配operationId
  • 如果必需参数没有默认值,则应用清单中的命令 parameters.name 必须与 OpenAPI 说明文档中的 匹配 parameters.name
  • 如果没有必需的参数,则应用清单中的 命令 parameters.name 必须与 OpenAPI 说明文档中的可选 parameters.name 匹配。
  • 确保应用清单中每个命令的参数名称与 OpenAPI 说明文档中为操作定义的相应参数名称完全匹配。
  • 必须为每个命令定义响应呈现模板,该命令用于转换来自 API 的响应。
  • 命令和参数说明不得超过 128 个字符。

下面是一个应用清单示例,其中包含基于 API 的消息扩展的定义:

应用清单示例
 {
 "$schema": "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.schema.json",
 +  "manifestVersion": "devPreview",
 "version": "1.0.0",
 "id": "04805b4b-xxxx-xxxx-xxxx-4dbc1cac8f89",
 "packageName": "com.microsoft.teams.extension",
 "developer": {
    "name": "Teams App, Inc.",
    "websiteUrl": "https://www.example.com",
    "privacyUrl": "https://www.example.com/termofuse",
    "termsOfUseUrl": "https://www.example.com/privacy"
 },
 "icons": {
    "color": "color.png",
    "outline": "outline.png"
 },
 "name": {
    "short": "AI tools",
    "full": "AI tools"
 },
 "description": {
    "short": "AI tools",
    "full": "AI tools"
 },
 "accentColor": "#FFFFFF",
 "composeExtensions": [
    {
 +      "composeExtensionType": "apiBased",
 +      "authorization": {
 +        "authType": "apiSecretServiceAuth ",
 +        "apiSecretServiceAuthConfiguration": {
 +            "apiSecretRegistrationId": "96270b0f-7298-40cc-b333-152f84321813"
 +        }
 +      },
 +      "apiSpecificationFile": "aitools-openapi.yml",
       "commands": [
       {
          "id": "searchTools",
          "type": "query",
          "context": [
             "compose",
             "commandBox"
          ],
          "title": "search for AI tools",
          "description": "search for AI tools",
          "parameters": [
             {
             "name": "search",
             "title": "search query",
             "description": "e.g. search='tool to create music'"
             }
          ],
 +          "apiResponseRenderingTemplateFile": "response-template.json"
       }
       ]
    }
 ],
 "validDomains": []
 }

参数

名称 说明
composeExtensions.composeExtensionType Compose扩展类型。 将值更新为 apiBased
composeExtensions.authorization 基于 API 的消息扩展的授权相关信息
composeExtensions.authorization.authType 可能授权类型的枚举。 支持的值为 noneapiSecretServiceAuthmicrosoftEntra
composeExtensions.authorization.apiSecretServiceAuthConfiguration 对象捕获执行服务身份验证所需的详细信息。仅当身份验证类型为 apiSecretServiceAuth时适用。
composeExtensions.authorization.apiSecretServiceAuthConfiguration.apiSecretRegistrationId 开发人员通过开发人员门户提交 API 密钥时返回的注册 ID。
composeExtensions.apiSpecificationFile 引用应用包中的 OpenAPI 说明文件。 类型为 apiBased时包括 。
composeExtensions.commands.id 分配给搜索命令的唯一 ID。 用户请求包含此 ID。 ID 必须与 OpenAPI 说明中提供的 匹配 operationId
composeExtensions.commands.context 定义消息扩展入口点的数组。 默认值为 composecommandBox
composeExtensions.commands.parameters 定义命令的参数的静态列表。 名称必须映射到 parameters.name OpenAPI 说明中的 。 如果要引用请求正文架构中的属性,则该名称必须映射到 properties.name 或 查询参数。
composeExtensions.commands.apiResponseRenderingTemplateFile 用于格式化从开发人员 API 到自适应卡片响应的 JSON 响应的模板。 [必需]

有关详细信息,请参阅 composeExtensions

响应呈现模板

注意

Teams 支持最高版本 1.5 的自适应卡片。 使用自适应卡片设计器时,请确保将目标版本更改为 1.5。

响应呈现模板是一种预定义格式,用于指示 API 的结果在 Teams 中的显示方式。 它使用模板根据 API 的响应创建自适应卡片或其他 UI 元素,从而确保在 Teams 中提供无缝的集成用户体验。 该模板定义所呈现信息的布局和样式,其中可能包括文本、图像和交互式组件。 确保遵循以下响应呈现模板准则:

  • 在 属性中 $schema 定义架构引用 URL,以建立 响应呈现模板架构的模板结构
  • 支持的值为 responseLayoutlistgrid,用于确定响应的视觉呈现方式。 有关布局的详细信息,请参阅 响应用户请求
  • jsonPath当自适应卡的数据不是根对象时,将重新获得数组的 。 例如,如果数据嵌套在 下 productDetails,则 JSON 路径为 productDetails
  • 定义为 jsonPath API 响应中相关数据或数组的路径。 如果路径指向数组,则数组中的每个条目都与自适应卡片模板绑定,并作为单独的结果返回。 [可选]
  • 获取用于验证响应呈现模板的示例响应。 这可用作一个测试,以确保模板按预期工作。
  • 使用 Fiddler 或 Postman 等工具调用 API 并确保请求和响应有效。 此步骤对于排查和确认 API 正常运行至关重要。
  • 可以使用自适应卡片Designer将 API 响应绑定到响应呈现模板并预览自适应卡片。 在 卡片有效负载编辑器 中插入自适应卡片模板,并在示例 数据编辑器中插入示例响应条目。

以下代码是响应呈现模板的示例:

响应呈现模板示例
{
"version": "1.0",
"$schema": "developer.microsoft.com/json-schemas/teams/v1.17/MicrosoftTeams.ResponseRenderingTemplate.schema.json",
"jsonPath": "repairs",
"responseLayout": "grid",
"responseCardTemplate": {
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.4",
  "body": [
    {
      "type": "Container",
      "items": [
        {
          "type": "ColumnSet",
          "columns": [
            {
              "type": "Column",
              "width": "stretch",
              "items": [
                {
                  "type": "TextBlock",
                  "text": "Title: ${if(title, title, 'N/A')}",
                  "wrap": true
                },
                {
                  "type": "TextBlock",
                  "text": "Description: ${if(description, description, 'N/A')}",
                  "wrap": true
                },
                {
                  "type": "TextBlock",
                  "text": "Assigned To: ${if(assignedTo, assignedTo, 'N/A')}",
                  "wrap": true
                },
                {
                  "type": "Image",
                  "url": "${image}",
                  "size": "Medium",
                  "$when": "${image != null}"
                }
              ]
            },
            {
              "type": "Column",
              "width": "auto",
              "items": [
                {
                  "type": "Image",
                  "url": "${if(image, image, '')}",
                  "size": "Medium"
                }
              ]
            }
          ]
        },
        {
          "type": "FactSet",
          "facts": [
            {
              "title": "Repair ID:",
              "value": "${if(id, id, 'N/A')}"
            },
            {
              "title": "Date:",
              "value": "${if(date, date, 'N/A')}"
            }
          ]
        }
      ]
    }
  ]
  },
  "previewCardTemplate": {
  "title": "Title: ${if(title, title, 'N/A')}",
  "subtitle": "Description: ${if(description, description, 'N/A')}",
  "text": "Assigned To: ${if(assignedTo, assignedTo, 'N/A')}",
  "image": {
    "url": "${image}",
    "$when": "${image != null}"
    }
  }
 }

预览卡

响应呈现模板架构中的预览卡模板用于将 JSON 响应映射到用户在选择搜索结果时看到的预览卡。 然后,预览卡扩展为邮件撰写框中的自适应卡片。 预览卡模板是响应呈现模板的一部分,该模板还包括自适应卡片模板和元数据。

屏幕截图显示了在搜索特定单词时显示预览卡片数组的 compose 扩展的示例。在这种情况下,在“测试应用”中搜索“a”将返回五张卡片,其中显示“Title”、“Description” (截断) 和“AssignedTo”属性和值。

扩展的自适应卡片

用户选择预览版卡后自适应卡片的外观示例。自适应卡片显示 Title、完整的 Description、AssignedTo、RepairId 和 Date 值。

参数

属性 类型 说明 必需
version string 当前响应呈现模板的架构版本。
jsonPath string responseCardTemplate 和 previewCardTemplate 应应用到的结果中相关节的路径。 如果未设置,则根对象被视为相关部分。 如果相关部分是数组,则每个条目将映射到 responseCardTemplate 和 previewCardTemplate。
responseLayout responseLayoutType 指定消息扩展浮出控件中结果的布局。 支持的类型为 listgrid
responseCardTemplate adaptiveCardTemplate 用于从结果条目创建自适应卡片的模板。
previewCardTemplate previewCardTemplate 用于从结果条目创建预览卡的模板。 生成的预览卡显示在消息扩展浮出控件菜单中。

Json 路径

JSON 路径是可选的,但应用于数组,或者用作自适应卡片数据的对象不是根对象。 JSON 路径应遵循 Newtonsoft 定义的格式。 可以使用此工具。 可以使用 JSON 工具 验证 JSON 路径是否正确。 如果 JSON 路径指向数组,则该数组中的每个条目都与自适应卡片模板绑定,并返回为单独的结果。

假设你有以下用于产品列表的 JSON,并且你希望为每个条目创建卡结果。

{
   "version": "1.0",
   "title": "All Products",
   "warehouse": {
      "products": [
        ...
      ]
   }
}

可以看到,结果数组位于“products”下,嵌套在“warehouse”下,因此 JSON 路径为“warehouse.products”。

使用 https://adaptivecards.io/designer/ 将模板插入卡片有效负载编辑器来预览自适应卡片,并从数组或对象获取示例响应条目,并将其插入右侧的“相同数据”编辑器中。 确保卡正确呈现,并且符合你的喜好。 Teams 支持最高版本 1.5 的卡片,而设计器支持 1.6。

OpenAPI 架构转换

注意

我们在 HTTP 请求中发送接受语言标头,该标头发送到 OpenAPI 说明文档中定义的终结点。 接受语言基于 Teams 客户端区域设置,开发人员可以使用它返回本地化的响应。

OpenAPI 说明文档中的以下数据类型将转换为自适应卡片中的元素,如下所示:

  • stringnumberinteger类型 boolean 将转换为 TextBlock。

    • 源架构stringnumberintegerboolean

       name:
         type: string
         example: doggie
      
    • 目标架构Textblock

      {
      "type": "TextBlock",
      "text": "name: ${if(name, name, 'N/A')}",
      "wrap": true
      }
      
  • array:数组转换为自适应卡内的容器。

    • 源架构array

          type: array
                    items:
                    required:
                      - name
                    type: object
                      properties:
                      id:
                        type: integer
                      category:
                        type: object
                        properties:
                        name:
                          type: string
      
    • 目标架构Container

          {
                    "type": "Container",
                    "$data": "${$root}",
                    "items": [
                      {
                        "type": "TextBlock",
                        "text": "id: ${if(id, id, 'N/A')}",
                        "wrap": true
                      },
                      {
                        "type": "TextBlock",
                        "text": "category.name: ${if(category.name, category.name, 'N/A')}",
                        "wrap": true
                      }
                    ]
                  }
      
      
  • object:对象转换为自适应卡片中的嵌套属性。

    • 源架构object

      components:
        schemas:
          Pet:
              category:
                type: object
              properties:
                id:
                  type: integer
                name:
                  type: string
      
      
    • 目标架构:自适应卡片中的嵌套属性

      {
        "type": "TextBlock",
        "text": "category.id: ${if(category.id, category.id, 'N/A')}",
        "wrap": true
      },
      {
        "type": "TextBlock",
        "text": "category.name: ${if(category.name, category.name, 'N/A')}",
        "wrap": true
      }
      
      
  • image:如果属性是图像 URL,则它将转换为自适应卡片中的 Image 元素。

    • 源架构image

          image:
            type: string
            format: uri
            description: The URL of the image of the item to be repaired
      
      
    • 目标架构"Image"

      {
            "type": "Image",
            "url": "${image}",
            "$when": "${image != null}"
          }
      
      

后续步骤