Extensão de mensagem baseada em API

Observação

  • As extensões de mensagem baseadas em API dão suporte apenas a comandos de pesquisa.
  • Não há suporte para extensões de mensagem baseadas em API no Microsoft 365 Copilot. Se você quiser criar extensões de mensagem baseadas em API compatíveis com o Microsoft 365 Copilot, confira Agentes de API do Microsoft 365 Copilot.

As extensões de mensagem criadas usando API (baseadas em API) usam um serviço Web para gerenciar solicitações e respostas do usuário e não exigem um registro de bot. As extensões de mensagem baseadas em API são um recurso do aplicativo Microsoft Teams que integra APIs externas diretamente ao Teams, aprimorando a usabilidade do aplicativo e oferecendo uma experiência de usuário perfeita. As extensões de mensagem baseadas em API oferecem suporte a comandos de pesquisa e podem ser usadas para buscar e exibir dados de serviços externos no Teams, simplificando os fluxos de trabalho ao reduzir a necessidade de alternar entre aplicativos. As extensões de mensagem baseadas em API ajudam seus aplicativos a interagir diretamente com dados, aplicativos e serviços de terceiros, aprimorando seus recursos. Com a extensão de mensagem baseada em API, você pode:

  • Recupere informações em tempo real, como a cobertura de notícias mais recente sobre o lançamento de um produto.
  • Recupere informações baseadas em conhecimento, por exemplo, os arquivos de design da equipe no Figma.

Observação

Os agentes fornecem uma experiência mais flexível, inteligente e pronta para o futuro que permite um raciocínio mais avançado, um desenvolvimento mais simples e um melhor alinhamento com o Teams em evolução e a plataforma Microsoft 365. Recomendamos que você explore e crie agentes.

Para obter mais informações, confira Criar agentes declarativos e Criar agentes no Teams.

Se você tiver uma extensão de mensagem baseada em bot existente, também poderá estendê-la como um agente .

Confira o vídeo para saber mais sobre como criar uma extensão de mensagem baseada em API usando o Microsoft 365 Agents Toolkit (anteriormente conhecido como Teams Toolkit):


Extensões de mensagem tradicionais baseadas em bot Extensões de mensagem baseadas em API
Os desenvolvedores precisam criar, implantar e manter um serviço para lidar com comandos de invocação do cliente do Teams. Se as APIs do serviço final puderem ser descritas usando a Especificação OpenAPI, os desenvolvedores poderão eliminar a necessidade do serviço de manipulação da camada intermediária.
Esse serviço processa a consulta de entrada e faz uma chamada para o serviço final do desenvolvedor. As equipes podem usar diretamente a especificação OpenAPI para criar solicitações e se comunicar com o serviço final do desenvolvedor.

As imagens a seguir mostram o fluxo de consultas de usuário por meio de extensões de mensagem tradicionais e extensões de mensagem de API:

A captura de tela mostra o fluxo de consulta do usuário entre um usuário, o cliente do Teams e o serviço de bot do Teams usando extensões de mensagem tradicionais. O diagrama também mostra como as especificações da API, os modelos de renderização e a API se relacionam entre si. Fluxo de consulta do usuário usando Extensões de Mensagem Tradicionais. O desenvolvedor deve manter um serviço de manipulador de bot personalizado, que lida com as solicitações de um bot do Teams. O serviço do manipulador envia uma solicitação ao serviço do desenvolvedor quando uma consulta é invocada.


A captura de tela mostra o fluxo de consulta entre um usuário, o cliente do Teams e o serviço de bot do Teams usando extensões de mensagem de API. O diagrama também mostra como as especificações da API, os modelos de renderização e a API se relacionam entre si. Fluxo de consulta do usuário usando extensões de mensagem de API. Não há necessidade de um serviço de manipulador mantido pelo desenvolvedor, desde que a interação esteja claramente descrita na Especificação OpenAPI no Pacote do Aplicativo.



Aqui está uma sequência de eventos de alto nível que ocorrem durante uma invocação de comando de consulta:

  1. Quando um usuário invoca um comando de consulta, os parâmetros do comando de consulta são recebidos pelo Serviço de Bot do Teams.

  2. O comando de consulta é definido dentro do arquivo de manifesto do aplicativo. A definição de comando contém uma referência ao interior do arquivo de especificação OpenAPI, operationId juntamente com os detalhes dos parâmetros que o cliente do Teams renderiza para esse comando. Para referência, o operationId interior do arquivo de especificação OpenAPI é exclusivo para uma operação HTTP específica.

  3. O Serviço de Bot do Teams usa os parâmetros fornecidos pelo usuário junto com a cópia da Especificação OpenAPI para o associado operationId para criar uma solicitação HTTP para o ponto de extremidade do desenvolvedor.

  4. Se a autenticação é necessária e está configurada no manifesto. É resolvido para o token ou chave apropriado. Esse token ou chave é usado como parte da solicitação de saída. [Opcionalmente]

  5. O serviço de bot do Teams executa a solicitação HTTP para o serviço do desenvolvedor.

  6. O serviço do desenvolvedor deve responder de acordo com o esquema descrito na Especificação OpenAPI. Está no formato JSON.

  7. O cliente do Teams deve mostrar os resultados de volta para o usuário. Para converter os resultados JSON da etapa anterior em interface do usuário, o serviço de bot do Teams usa o modelo de Renderização de resposta para criar um Cartão Adaptável para cada resultado.

  8. Os Cartões Adaptáveis são enviados para o cliente, que os renderiza na interface do usuário.

O diagrama mostra o fluxo de sequência de alto nível quando uma consulta é invocada em uma extensão de mensagem baseada em API.

Pré-requisitos

O pacote de definição de aplicativo inclui vários artefatos atraentes que dão suporte à funcionalidade desse recurso. Antes de começar, verifique se você tem uma compreensão básica dos seguintes arquivos:

Descrição do OpenAPI (OAD)

O documento de descrição do OpenAPI é um padrão do setor adotado para descrever APIs. Ele permite que você abstraia suas APIs de sua implementação, fornecendo definições independentes de linguagem que são legíveis por humanos e por máquina. O documento de descrição do OpenAPI descreve as interações que sua extensão suporta, permitindo que o Teams crie solicitações e se comunique diretamente com seu serviço sem a necessidade de um serviço de manipulação de camada intermediária.

Um documento de descrição do OpenAPI contém detalhes para se comunicar com o serviço do desenvolvedor. Certifique-se de seguir as seguintes diretrizes para o documento Descrição do OpenAPI (OAD):

  • As versões 2.0 e 3.0.x do OpenAPI são suportadas.
  • JSON e YAML são os formatos com suporte.
  • O corpo da solicitação, se presente, deve ser application/Json.
  • Defina uma URL do servidor de protocolo HTTPS para a servers.url propriedade.
  • Há suporte apenas para os métodos POST e GET HTTP.
  • O documento de descrição do OpenAPI deve ter um operationIdarquivo .
  • Somente um parâmetro necessário sem um valor padrão é permitido.
  • Um parâmetro necessário com um valor padrão é considerado opcional.
  • Os usuários não devem inserir um parâmetro para um cabeçalho ou cookie.
  • A operação não deve ter um cabeçalho obrigatório ou parâmetros de cookie sem valores padrão.
  • Certifique-se de que não haja referências remotas no documento Descrição do OpenAPI.
  • Não há suporte para a construção de matrizes para a solicitação; no entanto, há suporte para objetos aninhados em um corpo de solicitação JSON.
  • O Teams não dá suporte às oneOfconstruções , anyOf, allOf, e not (swagger.io).

O código a seguir é um exemplo de documento de descrição do OpenAPI:

Documento de descrição do OpenAPI de exemplo
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.

Para obter mais informações sobre como escrever definições de OpenAPI no YAML, consulte Estrutura do OpenAPI.

Manifesto do aplicativo

O manifesto do aplicativo é um plano para o aplicativo Teams, definindo como e onde a extensão de mensagem é invocada no cliente do Teams. Ele inclui os comandos aos quais sua extensão dá suporte e os locais dos quais eles podem ser acessados, como a área de redação de mensagem, a barra de comandos e a mensagem. O manifesto é vinculado à Especificação OpenAPI e ao Modelo de Renderização de Resposta para garantir a funcionalidade adequada.

O manifesto do aplicativo contém a definição de comando de consulta. Certifique-se de seguir as seguintes diretrizes para o manifesto do aplicativo:

  • Defina a versão do manifesto do aplicativo como 1.17.
  • Defina composeExtensions.composeExtensionType como apiBased.
  • Defina composeExtensions.apiSpecificationFile como o caminho relativo para o documento de descrição do OpenAPI dentro da pasta. Isso vincula o manifesto do aplicativo à especificação da API.
  • Defina apiResponseRenderingTemplateFile como o caminho relativo para o modelo de renderização de resposta. Isso especifica o local do modelo usado para renderizar respostas da API.
  • Cada comando deve ter um link para o modelo de renderização de resposta. Isso conecta cada comando ao seu formato de resposta correspondente.
  • A Commands.id propriedade no manifesto do aplicativo deve corresponder ao documento de descrição do operationId OpenAPI.
  • Se um parâmetro necessário não tiver um valor padrão, o comando parameters.name no manifesto do aplicativo deverá corresponder ao no documento de descrição do parameters.name OpenAPI.
  • Se não houver nenhum parâmetro necessário, o comando parameters.name no manifesto do aplicativo deverá corresponder ao opcional parameters.name no documento Descrição do OpenAPI.
  • Certifique-se de que o nome dos parâmetros de cada comando no manifesto do aplicativo corresponda exatamente ao nome correspondente do parâmetro definido para a operação no documento Descrição do OpenAPI.
  • Um modelo de renderização de resposta deve ser definido por comando, que é usado para converter respostas de uma API.
  • As descrições de comando e parâmetro não devem exceder 128 caracteres.

Veja a seguir um exemplo de manifesto do aplicativo com definições para extensões de mensagem baseadas em API:

Exemplo de manifesto de aplicativo
 {
 "$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": []
 }

Parâmetros

Nome Descrição
composeExtensions.composeExtensionType Compose tipo de extensão. Atualize o valor para apiBased.
composeExtensions.authorization Informações relacionadas à autorização para a extensão de mensagem baseada em API
composeExtensions.authorization.authType Enumeração de possíveis tipos de autorização. Os valores com suporte são none, apiSecretServiceAuthe microsoftEntra.
composeExtensions.authorization.apiSecretServiceAuthConfiguration Detalhes de captura de objeto necessários para fazer a autenticação de serviço. Aplicável somente quando o tipo de autenticação é apiSecretServiceAuth.
composeExtensions.authorization.apiSecretServiceAuthConfiguration.apiSecretRegistrationId ID de registro retornada quando o desenvolvedor envia a chave de API por meio do Portal do desenvolvedor.
composeExtensions.apiSpecificationFile Faz referência a um arquivo de Descrição do OpenAPI no pacote do aplicativo. Incluir quando o tipo é apiBased.
composeExtensions.commands.id ID exclusiva que você atribui ao comando de pesquisa. A solicitação do usuário inclui essa ID. A ID deve corresponder ao operationId disponível na descrição do OpenAPI.
composeExtensions.commands.context Matriz em que os pontos de entrada para extensão de mensagem estão definidos. Os valores padrão são compose e commandBox.
composeExtensions.commands.parameters Define uma lista estática de parâmetros para o comando. O nome deve ser mapeado para o na descrição do parameters.name OpenAPI. Se você estiver fazendo referência a uma propriedade no esquema do corpo da solicitação, o nome deverá ser mapeado para properties.name ou consultar parâmetros.
composeExtensions.commands.apiResponseRenderingTemplateFile Modelo usado para formatar a resposta JSON da API do desenvolvedor para a resposta do Cartão Adaptável. [Obrigatório]

Para obter mais informações, consulte composeExtensions.

Modelo de renderização de resposta

O modelo de renderização de respostas é um formato predefinido que determina como os resultados da sua API são exibidos no Teams. Ele usa modelos para criar Cartões Adaptáveis ou outros elementos da interface do usuário com base na resposta da API, garantindo uma experiência do usuário integrada e perfeita no Teams. O modelo define o layout e o estilo das informações apresentadas, que podem incluir texto, imagens e componentes interativos. Siga as seguintes diretrizes para o modelo de renderização de respostas:

  • Defina a URL de referência de esquema na $schema propriedade para estabelecer a estrutura do seu modelo para o esquema do modelo de renderização de resposta.
  • Os valores com suporte para responseLayout são list e grid, que determinam como a resposta é apresentada visualmente. Para obter mais informações sobre o layout, consulte responder às solicitações do usuário.
  • A jsonPath é reexigido para matrizes ou quando os dados do Cartão Adaptável não são o objeto raiz. Por exemplo, se os dados estiverem aninhados em productDetails, o caminho JSON seria productDetails.
  • Defina jsonPath como o caminho para os dados ou matrizes relevantes na resposta da API. Se o caminho apontar para uma matriz, cada entrada na matriz será associada ao modelo de Cartão Adaptável e retornará como um resultado separado. [Opcional]
  • Obtenha uma resposta de exemplo para validar o modelo de renderização de resposta. Isso serve como um teste para garantir que seu modelo funcione conforme o esperado.
  • Use ferramentas como Fiddler ou Postman para chamar a API e garantir que a solicitação e a resposta sejam válidas. Esta etapa é crucial para solucionar problemas e confirmar se sua API está funcionando corretamente.
  • Você pode usar o Adaptive Card Designer para vincular a resposta da API ao modelo de renderização de resposta e visualizar o Cartão Adaptável. Insira o modelo de Cartão Adaptável no EDITOR DE CONTEÚDO DO CARTÃO e insira a entrada de resposta de exemplo no EDITOR DE DADOS DE EXEMPLO.

O código a seguir é um exemplo de um modelo de renderização de resposta:

Exemplo de modelo de renderização de resposta
{
"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}"
    }
  }
 }

Cartão de visualização

Um modelo de card de visualização no esquema de modelo de renderização de resposta é usado para mapear respostas JSON para um card de visualização que os usuários veem quando selecionam um resultado de pesquisa. Em seguida, o card de visualização se expande para um Cartão Adaptável na caixa de redação de mensagem. O modelo de card de visualização faz parte do modelo de renderização de resposta, que também inclui um modelo de Cartão Adaptável e metadados.

A captura de tela mostra um exemplo de extensão de composição exibindo uma matriz de cartões de visualização ao pesquisar uma palavra específica. Nesse caso, pesquisar por 'a' no 'aplicativo de teste' retorna cinco cartões mostrando as propriedades e valores 'Title', 'Description' (truncated) e 'AssignedTo' em cada um.

Cartão Adaptável Expandido

Exemplo de como o Cartão Adaptável parece expandido depois que um usuário seleciona um card de visualização. O Cartão Adaptável mostra os valores Title, the complete Description, AssignedTo, RepairId e Date.

Parâmetros

Propriedade Tipo Descrição Obrigatório
version string A versão do esquema do modelo de renderização de resposta atual. Sim
jsonPath string O caminho para a seção relevante nos resultados ao qual o responseCardTemplate e o previewCardTemplate devem ser aplicados. Se não for definido, o objeto raiz será tratado como a seção relevante. Se a seção relevante for uma matriz, cada entrada será mapeada para responseCardTemplate e previewCardTemplate. Não
responseLayout responseLayoutType Especifica o layout dos resultados no submenu da extensão de mensagem. Os tipos com suporte são list e grid. Sim
responseCardTemplate adaptiveCardTemplate Um modelo para criar um Cartão Adaptável a partir de uma entrada de resultado. Sim
previewCardTemplate previewCardTemplate Um modelo para criar um card de visualização a partir de uma entrada de resultado. O card de visualização resultante é exibido no menu de submenu da extensão de mensagem. Sim

Caminho JSON

O caminho JSON é opcional, mas deve ser usado para matrizes ou em que o objeto a ser usado como dados para o Cartão Adaptável não é o objeto raiz. O caminho JSON deve seguir o formato definido pela Newtonsoft. Esta ferramenta pode ser usada. Você pode usar a ferramenta JSON para validar se um caminho JSON está correto. Se o caminho JSON apontar para uma matriz, cada entrada nessa matriz será associada ao modelo de Cartão Adaptável e retornará como resultados separados.

Exemplo Digamos que você tenha o JSON a seguir para uma lista de produtos e queira criar um resultado de card para cada entrada.

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

Como você pode ver, a matriz de resultados está em "produtos", que está aninhada em "warehouse", portanto, o caminho JSON seria "warehouse.products".

Use o Adaptive Card Designer para visualizar um Cartão Adaptável inserindo o modelo no Editor de Carga de Cartão. Pegue uma entrada de resposta de amostra de sua matriz ou para seu objeto e insira-a no Editor de Dados de Amostra. Certifique-se de que o card seja renderizado corretamente e seja do seu agrado.

Conversão de esquema OpenAPI

Observação

Enviamos um cabeçalho accept-language na solicitação HTTP que é enviada para o endpoint definido no documento de descrição do OpenAPI. O accept-language é baseado na localidade do cliente do Teams e pode ser usado pelo desenvolvedor para retornar uma resposta localizada.

Os seguintes tipos de dados no documento de descrição do OpenAPI são convertidos em elementos em um Cartão Adaptável da seguinte maneira:

  • string, number, integerboolean , tipos são convertidos em um TextBlock.

    Exemplo
    • Esquema de origem: string, number, integere boolean

       name:
         type: string
         example: doggie
      
    • Esquema de destino: Textblock

      {
      "type": "TextBlock",
      "text": "name: ${if(name, name, 'N/A')}",
      "wrap": true
      }
      
  • array: uma matriz é convertida em um contêiner dentro do Cartão Adaptável.

    Exemplo
    • Esquema de origem: array

          type: array
                    items:
                    required:
                      - name
                    type: object
                      properties:
                      id:
                        type: integer
                      category:
                        type: object
                        properties:
                        name:
                          type: string
      
    • Esquema de destino: 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: Um objeto é convertido em uma propriedade aninhada no Cartão Adaptável.

    Exemplo
    • Esquema de origem: object

      components:
        schemas:
          Pet:
              category:
                type: object
              properties:
                id:
                  type: integer
                name:
                  type: string
      
      
    • Esquema de destino: propriedade aninhada em um Cartão Adaptável

      {
        "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: se uma propriedade for uma URL de imagem, ela será convertida em um elemento Image no Cartão Adaptável.

    Exemplo
    • Esquema de origem: image

          image:
            type: string
            format: uri
            description: The URL of the image of the item to be repaired
      
      
    • Esquema de destino: "Image"

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

Próxima etapa

Confira também