Criar plug-ins de API com TypeSpec para o Microsoft 365 Copilot

Importante

Plugins só são suportados como ações em agentes declarativos. Eles não estão habilitados no Microsoft 365 Copilot.

Os plug-ins de API são ações personalizadas para agentes declarativos que conectam uma API REST com uma especificação OpenAPI ao Microsoft 365 Copilot. Este guia demonstra como adicionar um plug-in de API a um agente declarativo usando o TypeSpec e o Microsoft 365 Agents Toolkit.

Pré-requisitos

Dica

Para obter os melhores resultados, certifique-se de que a API que você está gerando siga as diretrizes detalhadas em Como tornar um documento OpenAPI eficaz na extensão do Copilot.

Configurar o projeto de plug-in de API no VS Code

Este artigo pressupõe que você já tenha um projeto de agente declarativo que foi criado com o Microsoft 365 Agents Toolkit e o TypeSpec. Você adiciona um plug-in de API a esse projeto definindo suas operações de API REST em TypeSpec e, em seguida, provisionando o agente. As seções a seguir explicam cada operação.

  1. Se você ainda não tiver um projeto de agente, crie um seguindo Criar um agente declarativo. Quando solicitado, selecione Iniciar com TypeSpec para o Microsoft 365 Copilot.
  2. Abra seu projeto de agente no Visual Studio Code e abra o arquivo na raiz do main.tsp projeto.
  3. Defina seu plug-in de API adicionando operações, main.tsp conforme mostrado nas seções a seguir. Quando terminar, provisione o agente conforme descrito em Provisionar e teste as ações personalizadas.

Adicionar uma GET operação

Para começar, adicione uma GET operação para listar todos os itens de postagem. Abra o main.tsp arquivo e adicione um novo namespace PostsAPI no MyAgent namespace com o conteúdo a seguir.

// Omitted for brevity
namespace MyAgent {
  // Omitted for brevity
  @service
  @server("https://jsonplaceholder.typicode.com")
  @actions(#{
    nameForHuman: "Posts APIs",
    descriptionForHuman: "Manage blog post items with the JSON Placeholder API.",
    descriptionForModel: "Read, create, update and delete blog post items with the JSON Placeholder API."
  })
  namespace PostsAPI {

    /**
     * List all blog post items.
     */
    @route("/posts")
    @get op listPosts(): PostItem[];

    /**
     * Structure of a blog post item.
     */
    model PostItem {
      /**
       * The ID of the user who created the post.
       */
      userId: integer;

      /**
       * The ID of the post.
       */
      @visibility(Lifecycle.Read)
      id: integer;

      /**
       * The title of the post.
       */
      title: string;

      /**
       * The body of the post.
       */
      body: string;
    }
  }
  // Omitted for brevity
}

Este código define o PostItem modelo e a API GET /postsREST.

Adicionar uma GET operação com um parâmetro de consulta

A GET operação no exemplo anterior não aceita parâmetros. Para habilitar a filtragem por ID de usuário, atualize a GET operação com um parâmetro de consulta opcional para filtrar os resultados por ID de usuário.

Abra o main.tsp arquivo e substitua a operação existente listPosts pelo conteúdo a seguir.

/**
 * List all blog post items.
  * @param userId The ID of the user who created the post. If not provided, all posts will be returned.
  */
@route("/posts")
@get op listPosts(@query userId?: integer): PostItem[];

O @query userId? parâmetro adicionado ao listPosts atualiza a API REST para GET /posts?userId={userId}.

Adicionar um adaptive card a uma GET operação

Adicionar um Cartão Adaptável à listPosts operação altera a forma como as citações na resposta gerada são renderizadas.

Crie um novo arquivo chamado pós-card.json no diretório appPackage e adicione o conteúdo a seguir.

{
  "type": "AdaptiveCard",
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.5",
  "body": [
    {
      "type": "Container",
      "$data": "${$root}",
      "items": [
        {
          "type": "TextBlock",
          "text": "**${if(title, title, 'N/A')}**",
          "wrap": true
        },
        {
          "type": "TextBlock",
          "text": "${if(body, body, 'N/A')}",
          "wrap": true
        }
      ]
    }
  ],
  "actions": [
    {
      "type": "Action.OpenUrl",
      "title": "Read More",
      "url": "https://www.bing.com/search?q=https://jsonplaceholder.typicode.com/posts/${id}"
    }
  ]
}

Abra o main.tsp arquivo e adicione o @card decorador à listPosts operação, conforme mostrado no snippet de código a seguir.

/**
 * List all blog post items.
  * @param userId The ID of the user who created the post. If not provided, all posts will be returned.
  */
@route("/posts")
@card(#{ dataPath: "$", file: "post-card.json", properties: #{ title: "$.title" } })
@get op listPosts(@query userId?: integer): PostItem[];

Adicionar uma POST operação

Abra o main.tsp arquivo e, dentro do PostsAPI namespace, adicione o seguinte conteúdo.

/**
 * Create a new blog post item.
 * @param post The post item to create.
 */
@route("/posts")
@post op createPost(@body post: PostItem): PostItem;

Este código define a API POST /postsREST, que cria uma nova postagem no blog.

Adicionar uma PATCH operação

Abra o main.tsp arquivo e, dentro do PostsAPI namespace, adicione o seguinte conteúdo.

/**
 * Updates a blog post item.
 * @param id The ID of the post to update.
 * @param post The updated post item.
 */
@route("/posts/{id}")
@patch op updatePost(@path id: integer, @body post: PostItem): PostItem;

Este código define a API PATCH /posts/{id}REST, que atualiza uma postagem de blog existente.

Adicionar uma DELETE operação

Abra o main.tsp arquivo e, dentro do PostsAPI namespace, adicione o seguinte conteúdo.

/**
 * Deletes a blog post item.
 * @param id The ID of the post to delete.
 */
@route("/posts/{id}")
@delete op deletePost(@path id: integer): void;

Este código define a API DELETE /posts/{id}REST, que exclui uma postagem de blog existente.

Provisionar e testar as ações personalizadas

Use o painel Ciclo de Vida no Kit de Ferramentas de Agentes do Microsoft 365 para provisionar seu agente e seu plug-in de API e, em seguida, teste as ações personalizadas no Microsoft 365 Copilot.

  1. Selecione o ícone do Microsoft 365 Agents Toolkit na Barra de Atividades à esquerda.
  2. No painel Ciclo de Vida , selecione Provisionar.
  3. Aguarde a conclusão do provisionamento e abra https://m365.cloud.microsoft/ no navegador.
  4. Selecione seu agente na lista de agentes.
  5. Teste o agente com os seguintes prompts ou experimente o seu próprio.

Testar a operação GET

Solicitar: "Liste todas as postagens do blog e renderize-as como uma tabela."

Uma captura de tela de uma resposta de um agente declarativo com base em novas operações GET

Solicitar: "Liste todas as postagens do blog para o usuário com ID 1 e renderize-as como uma tabela."

Uma captura de tela de uma resposta de um agente declarativo com base em operações GET com cartões adaptáveis

Testar a operação POST

Solicitar: "Crie uma nova postagem no blog com a ID de usuário 1, título 'Nova postagem' e corpo 'Esta é uma nova postagem'."

Uma captura de tela de uma resposta de um agente declarativo com base em operações POST

Testar a operação PATCH

Solicitar: "Atualize a postagem do blog com a ID 30 e atualize o título para 'Título atualizado' e o corpo para 'Corpo atualizado'."

Uma captura de tela de uma resposta de um agente declarativo com base em operações PATCH

Testar a operação EXCLUIR

Solicitar: "Exclua a postagem do blog com a ID 50."

Uma captura de tela de uma resposta de um agente declarativo com base em operações DELETE

Exemplo de um arquivo completo main.tsp

Veja a seguir um exemplo de um arquivo completo main.tsp com as GEToperações , POST, PATCH, e DELETE adicionadas.

import "@typespec/http";
import "@typespec/openapi3";
import "@microsoft/typespec-m365-copilot";

using TypeSpec.Http;
using TypeSpec.M365.Copilot.Actions;
using TypeSpec.M365.Copilot.Agents;

@agent(
  "My Posts Agent",
  "Declarative agent focusing on blog posts management."
)

@instructions("""
  You should help users with blog posts management.
  You can read, create, update and delete blog post items.
  You can also search for blog posts by user ID.
""")

@conversationStarter(#{
  title: "List Blog Posts",
  text: "List all blog posts and render them as a table."
})

@conversationStarter(#{
  title: "Lists a user's blog posts",
  text: "List all blog posts for the user with ID 1 and render them as a table."
})

@conversationStarter(#{
  title: "Delete a blog post",
  text: "Delete the blog post with ID 50."
})

@conversationStarter(#{
  title: "Update a blog post",
  text: "Update the blog post with ID 30 and update the title to 'Updated Title' and body to 'Updated Body'."
})

@conversationStarter(#{
  title: "Create a blog post",
  text: "Create a new blog post with user ID 1, title 'New Post' and body 'This is a new post'."
})

@conversationStarter(#{
  title: "Get a blog post",
  text: "Get all the details about the blog post with ID 10."
})

namespace MyAgent {
  @service
  @server("https://jsonplaceholder.typicode.com")
  @actions(#{
    nameForHuman: "Posts APIs",
    descriptionForHuman: "Manage blog post items on JSON Placeholder APIs.",
    descriptionForModel: "Read, create, update and delete blog post items on the JSON Placeholder APIs."
  })
  namespace PostsAPI {
    /**
     * List all blog post items.
     * @param userId The ID of the user who created the post. If not provided, all posts will be returned.
     */
    @route("/posts")
    @card(#{ dataPath: "$", file: "post-card.json", properties: #{ title: "$.title" } })
    @get op listPosts(@query userId?: integer): PostItem[];

    /**
     * Get a blog post item by ID.
     */
    @route("/posts/{id}")
    @card(#{ dataPath: "$", file: "post-card.json", properties: #{ title: "$.title" } })
    @get op getPost(@path id: integer): PostItem;

    /**
     * Create a new blog post item.
     * @param post The post item to create.
     */
    @route("/posts")
    @post op createPost(@body post: PostItem): PostItem;

    /**
     * Updates a blog post item.
     * @param id The ID of the post to update.
     * @param post The updated post item.
     */
    @route("/posts/{id}")
    @patch op updatePost(@path id: integer, @body post: PostItem): PostItem;

    /**
     * Deletes a blog post item.
     * @param id The ID of the post to delete.
     */
    @route("/posts/{id}")
    @delete op deletePost(@path id: integer): void;

    model PostItem {
      /**
       * The ID of the user who created the post.
       */
      userId: integer;

      /**
       * The ID of the post.
       */
      @visibility(Lifecycle.Read)
      id: integer;

      /**
       * The title of the post.
       */
      title: string;

      /**
       * The body of the post.
       */
      body: string;
    }
  }
}