Erstellen von API-Plug-Ins mit TypeSpec für Microsoft 365 Copilot

Wichtig

MCP- und API-Plug-Ins werden als Aktionen innerhalb deklarativer Agents unterstützt. Sie sind nicht als eigenständige Erfahrungen in Microsoft 365 Copilot aktiviert.

API-Plug-Ins sind benutzerdefinierte Aktionen für deklarative Agents, die eine REST-API mit einer OpenAPI-Spezifikation mit Microsoft 365 Copilot verbinden. Dieser Leitfaden veranschaulicht, wie Sie einem deklarativen Agent mithilfe von TypeSpec und dem Microsoft 365 Agents Toolkit ein API-Plug-In hinzufügen.

Voraussetzungen

Tipp

Um die besten Ergebnisse zu erzielen, stellen Sie sicher, dass die API, die Sie generieren, den Richtlinien folgt, die unter So machen Sie ein OpenAPI-Dokument bei der Erweiterung von Copilot effektiv beschrieben.

Richten Sie das API-Plug-In-Projekt in VS Code ein

In diesem Artikel wird davon ausgegangen, dass Sie bereits über ein deklaratives Agent-Projekt verfügen, das mit dem Microsoft 365 Agents Toolkit und TypeSpec erstellt wurde. Sie fügen diesem Projekt ein API-Plug-In hinzu, indem Sie die REST-API-Vorgänge in TypeSpec definieren und dann den Agent bereitstellen. In den folgenden Abschnitten werden die einzelnen Vorgänge schrittweise durchlaufen.

  1. Wenn Sie noch nicht über ein Agentprojekt verfügen, erstellen Sie eines, indem Sie wie folgt vorgehen: Erstellen eines deklarativen Agents. Wenn Sie dazu aufgefordert werden, wählen Sie Mit TypeSpec für Microsoft 365 Copilot starten aus.
  2. Öffnen Sie Ihr Agent-Projekt in Visual Studio Code und dann die main.tsp Datei im Projektstamm.
  3. Definieren Sie Ihr API-Plug-In, indem Sie Vorgänge hinzufügen, main.tsp wie in den folgenden Abschnitten gezeigt. Wenn Sie fertig sind, stellen Sie den Agent wie unter Bereitstellen und Testen der benutzerdefinierten Aktionen beschrieben bereit.

Hinzufügen eines GET Vorgangs

Fügen Sie zunächst einen GET Vorgang hinzu, um alle Beitragselemente aufzulisten. Öffne die main.tsp Datei, und füge im MyAgent Namespace einen neuen Namespace PostsAPI mit dem folgenden Inhalt hinzu.

// 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
}

Dieser Code definiert das PostItem Modell und die REST-API GET /posts.

Hinzufügen eines GET Vorgangs mit einem Abfrageparameter

Der GET Vorgang im vorherigen Beispiel akzeptiert keine Parameter. Um das Filtern nach Benutzer-ID zu aktivieren, aktualisieren Sie den GET Vorgang mit einem optionalen Abfrageparameter, um die Ergebnisse nach Benutzer-ID zu filtern.

Öffnen Sie die main.tsp Datei, und ersetzen Sie den vorhandenen listPosts Vorgang durch den folgenden Inhalt.

/**
 * 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[];

Der @query userId? Parameter, der hinzugefügt wurde, listPosts aktualisiert die REST-API auf GET /posts?userId={userId}.

Hinzufügen einer adaptiven Karte zu einem GET Vorgang

Das Hinzufügen einer adaptiven Karte zum listPosts Vorgang ändert die Art und Weise, wie die Zitate in der generierten Antwort gerendert werden.

Erstellen Sie eine neue Datei mit dem Namen post-Karte.json im appPackage-Verzeichnis und fügen Sie den folgenden Inhalt hinzu.

{
  "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}"
    }
  ]
}

Öffnen Sie die main.tsp Datei, und fügen Sie den @cardlistPosts Dekorator zum Vorgang hinzu, wie im folgenden Codeausschnitt gezeigt.

/**
 * 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[];

Hinzufügen eines POST Vorgangs

Öffnen Sie die main.tsp Datei, und fügen Sie im PostsAPI Namespace den folgenden Inhalt hinzu.

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

Dieser Code definiert die REST-API POST /posts, die einen neuen Blogbeitrag erstellt.

Hinzufügen eines PATCH Vorgangs

Öffnen Sie die main.tsp Datei, und fügen Sie im PostsAPI Namespace den folgenden Inhalt hinzu.

/**
 * 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;

Dieser Code definiert die REST-API PATCH /posts/{id}, die einen vorhandenen Blogbeitrag aktualisiert.

Hinzufügen eines DELETE Vorgangs

Öffnen Sie die main.tsp Datei, und fügen Sie im PostsAPI Namespace den folgenden Inhalt hinzu.

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

Dieser Code definiert die REST-API DELETE /posts/{id}, die einen vorhandenen Blogbeitrag löscht.

Bereitstellen und Testen der benutzerdefinierten Aktionen

Verwenden Sie den Lebenszyklusbereich im Microsoft 365 Agents Toolkit, um Ihren Agent und sein API-Plug-In bereitzustellen, und testen Sie dann die benutzerdefinierten Aktionen in Microsoft 365 Copilot.

  1. Wählen Sie in der linken Aktivitätsleiste das Symbol "Microsoft 365 Agents Toolkit " aus.
  2. Wählen Sie im Bereich "Lebenszyklus " die Option "Bereitstellen" aus.
  3. Warten Sie, bis die Bereitstellung abgeschlossen ist, und öffnen Sie https://m365.cloud.microsoft/ sie dann in Ihrem Browser.
  4. Wählen Sie Ihren Agent aus der Liste der Support-Agenten aus.
  5. Testen Sie den Agent mit den folgenden Eingabeaufforderungen, oder probieren Sie Ihre eigenen aus.

Testen des GET-Vorgangs

Eingabeaufforderung: "Liste alle Blogbeiträge auf und rendere sie als Tabelle."

Screenshot einer Antwort von einem deklarativen Agent basierend auf neuen GET-Vorgängen

Eingabeaufforderung: "Liste alle Blogbeiträge für den Benutzer mit der ID 1 auf und rendere sie als Tabelle."

Screenshot einer Antwort von einem deklarativen Agent basierend auf GET-Vorgängen mit adaptiven Karten

Testen des POST-Vorgangs

Eingabeaufforderung: "Erstelle einen neuen Blogbeitrag mit der Benutzer-ID 1, dem Titel 'Neuer Beitrag' und dem Text 'Dies ist ein neuer Beitrag'."

Screenshot einer Antwort von einem deklarativen Agent basierend auf POST-Vorgängen

Testen des PATCH-Vorgangs

Eingabeaufforderung: "Aktualisieren Sie den Blogbeitrag mit der ID 30 und aktualisieren Sie den Titel auf 'Aktualisierter Titel' und den Text auf 'Aktualisierter Text'."

Screenshot einer Antwort von einem deklarativen Agenten basierend auf PATCH-Operationen

Testen des DELETE-Vorgangs

Eingabeaufforderung: "Blogbeitrag mit ID 50 löschen."

Screenshot einer Antwort von einem deklarativen Agent basierend auf DELETE-Vorgängen

Beispiel für eine vollständige main.tsp Datei

Im Folgenden finden Sie ein Beispiel für eine vollständige main.tsp Datei mit hinzugefügten GETOperationen ", POSTPATCH", und DELETE ".

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;
    }
  }
}