Sdílet prostřednictvím


Přidat plug-iny ze specifikací OpenAPI

V podniku už máte sadu rozhraní API, která provádějí skutečnou práci. Ty mohou využívat jiné automatizační služby nebo výkonné front-endové aplikace, se kterými lidé pracují. V sémantickém jádru můžete přidat stejná rozhraní API jako moduly plug-in, aby je mohli používat i vaši agenti.

Příklad specifikace OpenAPI

Vezměte například rozhraní API, které umožňuje změnit stav žárovky. Specifikace OpenAPI, označovaná jako specifikace Swaggeru nebo jen Swagger, pro toto rozhraní API může vypadat takto:

{
   "openapi": "3.0.1",
   "info": {
      "title": "Light API",
      "version": "v1"
   },
   "paths": {
      "/Light": {
         "get": {
            "summary": "Retrieves all lights in the system.",
            "operationId": "get_all_lights",
            "responses": {
               "200": {
                  "description": "Returns a list of lights with their current state",
                  "application/json": {
                     "schema": {
                        "type": "array",
                        "items": {
                              "$ref": "#/components/schemas/LightStateModel"
                        }
                     }
                  }
               }
            }
         }
      },
      "/Light/{id}": {
         "post": {
               "summary": "Changes the state of a light.",
               "operationId": "change_light_state",
               "parameters": [
                  {
                     "name": "id",
                     "in": "path",
                     "description": "The ID of the light to change.",
                     "required": true,
                     "style": "simple",
                     "schema": {
                           "type": "string"
                     }
                  }
               ],
               "requestBody": {
                  "description": "The new state of the light and change parameters.",
                  "content": {
                     "application/json": {
                           "schema": {
                              "$ref": "#/components/schemas/ChangeStateRequest"
                           }
                     }
                  }
               },
               "responses": {
                  "200": {
                     "description": "Returns the updated light state",
                     "content": {
                           "application/json": {
                              "schema": {
                                 "$ref": "#/components/schemas/LightStateModel"
                              }
                           }
                     }
                  },
                  "404": {
                     "description": "If the light is not found"
                  }
               }
         }
      }
   },
   "components": {
      "schemas": {
         "ChangeStateRequest": {
               "type": "object",
               "properties": {
                  "isOn": {
                     "type": "boolean",
                     "description": "Specifies whether the light is turned on or off.",
                     "nullable": true
                  },
                  "hexColor": {
                     "type": "string",
                     "description": "The hex color code for the light.",
                     "nullable": true
                  },
                  "brightness": {
                     "type": "integer",
                     "description": "The brightness level of the light.",
                     "format": "int32",
                     "nullable": true
                  },
                  "fadeDurationInMilliseconds": {
                     "type": "integer",
                     "description": "Duration for the light to fade to the new state, in milliseconds.",
                     "format": "int32",
                     "nullable": true
                  },
                  "scheduledTime": {
                     "type": "string",
                     "description": "Use ScheduledTime to synchronize lights. It's recommended that you asynchronously create tasks for each light that's scheduled to avoid blocking the main thread.",
                     "format": "date-time",
                     "nullable": true
                  }
               },
               "additionalProperties": false,
               "description": "Represents a request to change the state of the light."
         },
         "LightStateModel": {
               "type": "object",
               "properties": {
                  "id": {
                     "type": "string",
                     "nullable": true
                  },
                  "name": {
                     "type": "string",
                     "nullable": true
                  },
                  "on": {
                     "type": "boolean",
                     "nullable": true
                  },
                  "brightness": {
                     "type": "integer",
                     "format": "int32",
                     "nullable": true
                  },
                  "hexColor": {
                     "type": "string",
                     "nullable": true
                  }
               },
               "additionalProperties": false
         }
      }
   }
}

Tato specifikace poskytuje vše, co AI potřebuje k pochopení rozhraní API a způsobu interakce s ním. API obsahuje dva koncové body: jeden pro získání všech světel a druhý pro změnu stavu světla. Poskytuje také následující:

  • Sémantické popisy koncových bodů a jejich parametrů
  • Typy parametrů
  • Očekávané odpovědi

Vzhledem k tomu, že agent AI dokáže tuto specifikaci pochopit, můžete ho přidat jako modul plug-in do agenta.

Sémantické jádro podporuje OpenAPI verze 2.0 a 3.0 a jeho cílem je přizpůsobit specifikace verze 3.1 tak, že je downgraduje na verzi 3.0.

Spropitné

Pokud máte existující specifikace OpenAPI, možná budete muset udělat změny, aby jim AI usnadnila pochopení. Možná budete muset poskytnout pokyny v popisech. Další tipy o tom, jak vytvořit specifikace OpenAPI přívětivé pro AI, najdete v tématu Tipy a triky pro přidání modulů plug-in OpenAPI.

Přidání modulu plug-in OpenAPI

Pomocí několika řádků kódu můžete do svého agenta přidat modul plug-in OpenAPI. Následující fragment kódu ukazuje, jak přidat modul plug-in Light ze specifikace OpenAPI výše:

await kernel.ImportPluginFromOpenApiAsync(
   pluginName: "lights",
   uri: new Uri("https://example.com/v1/swagger.json"),
   executionParameters: new OpenApiFunctionExecutionParameters()
   {
      // Determines whether payload parameter names are augmented with namespaces.
      // Namespaces prevent naming conflicts by adding the parent parameter name
      // as a prefix, separated by dots
      EnablePayloadNamespacing = true
   }
);

Sémantické jádro umožňuje přidávat moduly plug-in OpenAPI z různých zdrojů, jako je adresa URL, soubor nebo stream. Moduly plug-in je navíc možné vytvářet jednou a opakovaně používat napříč několika instancemi jádra nebo agenty.

// Create the OpenAPI plugin from a local file somewhere at the root of the application
KernelPlugin plugin = await OpenApiKernelPluginFactory.CreateFromOpenApiAsync(
    pluginName: "lights",
    filePath: "path/to/lights.json"
);

// Add the plugin to the kernel
Kernel kernel = new Kernel();
kernel.Plugins.Add(plugin);
await kernel.add_plugin_from_openapi(
   plugin_name="lights",
   openapi_document_path="https://example.com/v1/swagger.json",
   execution_settings=OpenAPIFunctionExecutionParameters(
         # Determines whether payload parameter names are augmented with namespaces.
         # Namespaces prevent naming conflicts by adding the parent parameter name
         # as a prefix, separated by dots
         enable_payload_namespacing=True,
   ),
)
String yaml = EmbeddedResourceLoader.readFile("petstore.yaml", ExamplePetstoreImporter.class);

KernelPlugin plugin = SemanticKernelOpenAPIImporter
   .builder()
   .withPluginName("petstore")
   .withSchema(yaml)
   .withServer("http://localhost:8090/api/v3")
   .build();

Kernel kernel = ExampleOpenAPIParent.kernelBuilder()
   .withPlugin(plugin)
   .build();

Potom můžete použít plugin ve vašem agentu, jako by to byl nativní plugin.

Zpracování parametrů modulu plug-in OpenAPI

Sémantické jádro automaticky extrahuje metadata – například název, popis, typ a schéma pro všechny parametry definované v dokumentech OpenAPI. Toto metadata je uloženo ve vlastnosti KernelFunction.Metadata.Parameters pro každou operaci OpenAPI a je k dispozici LLM spolu s výzvou pro generování správných argumentů pro volání funkce.

Ve výchozím nastavení se do LLM zadává původní název parametru a používá ho sémantické jádro k vyhledání odpovídajícího argumentu v seznamu argumentů zadaných LLM. Mohou však existovat případy, kdy modul plug-in OpenAPI má více parametrů se stejným názvem. Poskytnutí těchto metadat parametrů LLM může způsobit nejasnost, což by mohlo bránit LLM v generování správných argumentů pro volání funkce.

Vzhledem k tomu, že se pro každou operaci OpenAPI vytvoří funkce jádra, která neumožňuje názvy parametrů, které nejsou jedinečné, může přidání takového modulu plug-in vést k tomu, že některé operace nebudou k dispozici pro použití. Konkrétně se operace s názvy parametrů, které nejsou jedinečné, přeskočí a zaprotokoluje se odpovídající upozornění. I když bylo možné do funkce jádra zahrnout více parametrů se stejným názvem, může to vést k nejednoznačnosti v procesu výběru argumentu.

S ohledem na všechny tyto skutečnosti nabízí sémantické jádro řešení pro správu pluginů s neunikátními názvy parametrů. Toto řešení je zvlášť užitečné při změně samotného rozhraní API není možné, ať už je to služba třetí strany nebo starší systém.

Následující fragment kódu ukazuje, jak zpracovat ne jedinečné názvy parametrů v modulu plug-in OpenAPI. Pokud operace change_light_state měla další parametr se stejným názvem jako existující parametr "id" – konkrétně k reprezentaci ID relace kromě aktuálního ID, které představuje ID světla – bylo by možné ho zpracovat, jak je znázorněno níže:

OpenApiDocumentParser parser = new();

using FileStream stream = File.OpenRead("path/to/lights.json");

// Parse the OpenAPI document
RestApiSpecification specification = await parser.ParseAsync(stream);

// Get the change_light_state operation
RestApiOperation operation = specification.Operations.Single(o => o.Id == "change_light_state");

// Set the 'lightId' argument name to the 'id' path parameter that represents the ID of the light
RestApiParameter idPathParameter = operation.Parameters.Single(p => p.Location == RestApiParameterLocation.Path && p.Name == "id");
idPathParameter.ArgumentName = "lightId";

// Set the 'sessionId' argument name to the 'id' header parameter that represents the session ID
RestApiParameter idHeaderParameter = operation.Parameters.Single(p => p.Location == RestApiParameterLocation.Header && p.Name == "id");
idHeaderParameter.ArgumentName = "sessionId";

// Import the transformed OpenAPI plugin specification
kernel.ImportPluginFromOpenApi(pluginName: "lights", specification: specification);

Tento fragment kódu využívá třídu OpenApiDocumentParser k analýze dokumentu OpenAPI a přístupu k objektu modelu RestApiSpecification, který představuje dokument. Přiřadí názvy argumentů parametrům a naimportuje transformovanou specifikaci modulu plug-in OpenAPI do jádra. Sémantické jádro poskytuje názvy argumentů LLM místo původních názvů a používá je k vyhledání odpovídajících argumentů v seznamu zadaném LLM.

Je důležité si uvědomit, že názvy argumentů se nepoužívají místo původních názvů při volání operace OpenAPI. V předchozím příkladu se parametr ID v cestě nahradí hodnotou vrácenou LLM pro argument lightId. Totéž platí pro parametr hlavičky 'id'; hodnota vrácená LLM pro argument sessionId se použije jako hodnota hlavičky s názvem ID.

Zpracování obsahu pluginů OpenAPI

Moduly plug-in OpenAPI můžou změnit stav systému pomocí operací POST, PUT nebo PATCH. Tyto operace často vyžadují, aby byla datová část součástí požadavku.

Sémantické jádro nabízí několik možností pro správu zpracování datových částí pro moduly plug-in OpenAPI v závislosti na konkrétním scénáři a požadavcích rozhraní API.

Konstrukce dynamického datového bloku

Konstrukce dynamické datové části umožňuje dynamicky vytvářet datové části operací OpenAPI na základě schématu datové části a argumentů poskytovaných LLM. Tato funkce je ve výchozím nastavení povolená, ale lze ji zakázat nastavením vlastnosti EnableDynamicPayload na false v objektu OpenApiFunctionExecutionParameters při přidávání modulu plug-in OpenAPI.

Představte si například operaci change_light_state, která vyžaduje datovou část strukturovanou následujícím způsobem:

{
   "isOn": true,
   "hexColor": "#FF0000",
   "brightness": 100,
   "fadeDurationInMilliseconds": 500,
   "scheduledTime": "2023-07-12T12:00:00Z"
}

Pokud chcete změnit stav světla a získat hodnoty vlastností datové části, poskytuje sémantické jádro LLM metadata pro operaci, aby o tom mohl přemýšlet.

{
    "name":"lights-change-light-state",
    "description": "Changes the state of a light.",
    "parameters":[
        { "name": "id", "schema": {"type":"string", "description": "The ID of the light to change.", "format":"uuid"}},
        { "name": "isOn", "schema": { "type": "boolean", "description": "Specifies whether the light is turned on or off."}},
        { "name": "hexColor", "schema": { "type": "string", "description": "Specifies whether the light is turned on or off."}},
        { "name": "brightness", "schema": { "type":"string", "description":"The brightness level of the light.", "enum":["Low","Medium","High"]}},
        { "name": "fadeDurationInMilliseconds", "schema": { "type":"integer", "description":"Duration for the light to fade to the new state, in milliseconds.", "format":"int32"}},
        { "name": "scheduledTime", "schema": {"type":"string", "description":"The time at which the change should occur.", "format":"date-time"}},
    ]
}

Kromě poskytování metadat operací LLM provede sémantické jádro následující kroky:

  1. Zpracujte volání LLM k operaci OpenAPI tak, že sestavíte datovou část na základě schématu a hodnot poskytovaných vlastnostmi LLM.
  2. Odešle požadavek HTTP s datovou částí do rozhraní API.

Omezení dynamické konstrukce datové části

Konstrukce dynamické datové části je nejúčinnější pro rozhraní API s relativně jednoduchými strukturami datové části. Nemusí vůbec spolehlivě fungovat nebo fungovat, pokud datové části rozhraní API vykazují následující charakteristiky:

  • Datové části s nejedinečnými názvy vlastností bez ohledu na jejich umístění. Například dvě vlastnosti s názvem id, jedna pro objekt odesílatele a druhá pro objekt příjemce – json { "sender": { "id": ... }, "receiver": { "id": ... }}
  • Schémata datové části, která používají některá z složených klíčových slov oneOf, anyOf, allOf.
  • Schémata datové části s rekurzivními odkazy Například json { "parent": { "child": { "$ref": "#parent" } } }

Pokud chcete zpracovávat datové části s ne jedinečnými názvy vlastností, zvažte následující alternativy:

Pokud schémata datových částí používají některou z oneOf, anyOf, allOf složených klíčových slov nebo rekurzivní odkazy, zvažte zakázání dynamické datové části a povolení LLM vytvořit datovou část na základě jejího schématu, jak je vysvětleno v Parametr datové části oddílu.

Mezery mezi názvy datových částí

Mezery mezi názvy datových částí pomáhají zabránit konfliktům pojmenování, ke kterým může dojít kvůli ne jedinečným názvům vlastností v datových částech modulu plug-in OpenAPI.

Pokud je povolené řádkování názvů, poskytuje sémantické jádro LLM metadata operací OpenAPI, která zahrnují rozšířené názvy vlastností. Tyto rozšířené názvy se vytvářejí přidáním názvu nadřazené vlastnosti jako předpony k názvům podřízených vlastností, přičemž je dělí tečka.

Pokud například operace change_light_state zahrnovala vnořený objekt offTimer s vlastností scheduledTime:

{
  "isOn": true,
  "hexColor": "#FF0000",
  "brightness": 100,
  "fadeDurationInMilliseconds": 500,
  "scheduledTime": "2023-07-12T12:00:00Z",
  "offTimer": {
      "scheduledTime": "2023-07-12T12:00:00Z"
  }
}

Sémantické jádro by poskytlo LLM metadata pro operaci, která obsahuje následující názvy vlastností:

{
    "name":"lights-change-light-state",
    "description": "Changes the state of a light.",
    "parameters":[
        { "name": "id", "schema": {"type":"string", "description": "The ID of the light to change.", "format":"uuid"}},
        { "name": "isOn", "schema": { "type": "boolean", "description": "Specifies whether the light is turned on or off."}},
        { "name": "hexColor", "schema": { "type": "string", "description": "Specifies whether the light is turned on or off."}},
        { "name": "brightness", "schema": { "type":"string", "description":"The brightness level of the light.", "enum":["Low","Medium","High"]}},
        { "name": "fadeDurationInMilliseconds", "schema": { "type":"integer", "description":"Duration for the light to fade to the new state, in milliseconds.", "format":"int32"}},
        { "name": "scheduledTime", "schema": {"type":"string", "description":"The time at which the change should occur.", "format":"date-time"}},
        { "name": "offTimer.scheduledTime", "schema": {"type":"string", "description":"The time at which the device will be turned off.", "format":"date-time"}},
    ]
}

Kromě poskytování metadat operací s rozšířenými názvy vlastností LLM provádí sémantické jádro následující kroky:

  1. Zpracujte volání LLM operace OpenAPI a vyhledejte odpovídající argumenty mezi argumenty, které poskytuje LLM pro všechny vlastnosti v datové části, pomocí názvů rozšířených vlastností a v případě potřeby se vrátit k původním názvům vlastností.
  2. Vytvořte datovou část pomocí původních názvů vlastností jako klíčů a vyřešených argumentů jako hodnot.
  3. Odešle požadavek HTTP s sestavenou datovou částí do rozhraní API.

Ve výchozím nastavení je možnost pro řádkování názvů datové části zakázaná. Tuto vlastnost lze povolit nastavením vlastnosti EnablePayloadNamespacing na true v objektu OpenApiFunctionExecutionParameters při přidávání modulu plug-in OpenAPI:

await kernel.ImportPluginFromOpenApiAsync(
    pluginName: "lights",
    uri: new Uri("https://example.com/v1/swagger.json"),
    executionParameters: new OpenApiFunctionExecutionParameters()
    {
        EnableDynamicPayload = true, // Enable dynamic payload construction. This is enabled by default.
        EnablePayloadNamespacing = true // Enable payload namespacing
    });

Poznámka

Volba EnablePayloadNamespace se projeví pouze tehdy, je-li zároveň povolena dynamická konstrukce datové části; jinak nemá žádný účinek.

Parametr datové části

Sémantické Jádro může pracovat s payloady vytvořenými LLM pomocí parametru payloadu. To je užitečné, když je schéma datové části složité a obsahuje neunikátní názvy vlastností, což znemožňuje sémantickému jádru dynamicky sestavit datovou část. V takových případech se budete spoléhat na schopnost LLM porozumět schématu a vytvořit platný datový soubor. Nedávné modely, například gpt-4o, jsou efektivní při generování platných datových částí JSON.

Pokud chcete povolit parametr datové části, nastavte vlastnost EnableDynamicPayload na false v objektu OpenApiFunctionExecutionParameters při připojení OpenAPI pluginu:

await kernel.ImportPluginFromOpenApiAsync(
    pluginName: "lights",
    uri: new Uri("https://example.com/v1/swagger.json"),
    executionParameters: new OpenApiFunctionExecutionParameters()
    {
        EnableDynamicPayload = false, // Disable dynamic payload construction
    });

Pokud je povolený parametr datové části, poskytuje sémantické jádro LLM metadata pro operaci, která obsahuje schémata pro datovou část a content_type parametry, což umožňuje LLM porozumět struktuře datové části a odpovídajícím způsobem ji sestavit:

{
    "name": "payload",
    "schema":
    {
        "type": "object",
        "properties": {
            "isOn": {
                "type": "boolean",
                "description": "Specifies whether the light is turned on or off."
            },
            "hexColor": {
                "type": "string",
                "description": "The hex color code for the light.",
            },
            "brightness": {
                "enum": ["Low", "Medium", "High"],
                "type": "string",
                "description": "The brightness level of the light."
            },
            "fadeDurationInMilliseconds": {
                "type": "integer",
                "description": "Duration for the light to fade to the new state, in milliseconds.",
                "format": "int32"
            },
            "scheduledTime": {
                "type": "string",
                "description": "The time at which the change should occur.",
                "format": "date-time"
            }
        },
        "additionalProperties": false,
        "description": "Represents a request to change the state of the light."
    },
    {
        "name": "content_type",
        "schema":
        {
            "type": "string",
            "description": "Content type of REST API request body."
        }
    }
}

Kromě poskytování metadat operací se schématem pro datový obsah a parametry typu obsahu do LLM provádí Sémantické jádro následující kroky:

  1. Zpracujte volání LLM směrem k operaci OpenAPI a použijte argumenty poskytnuté LLM pro parametry "payload" a "content_type."
  2. Odešle požadavek HTTP do rozhraní API s poskytnutou datovou částí a typem obsahu.

Základní adresa URL serveru

Plug-iny Semantic Kernel OpenAPI vyžadují základní adresu URL, která se používá k předávání cest ke koncovým bodům při vytváření požadavků na API. Tuto základní adresu URL lze zadat v dokumentu OpenAPI, získat implicitně načtením dokumentu z adresy URL nebo poskytnutím při přidávání modulu plug-in do jádra.

Adresa URL zadaná v dokumentu OpenAPI

Dokumenty OpenAPI v2 definují adresu URL serveru pomocí polí schemes, hosta basePath:

{
   "swagger": "2.0",
   "host": "example.com",
   "basePath": "/v1",
   "schemes": ["https"]
   ...
}

Sémantické jádro vytvoří adresu URL serveru jako https://example.com/v1.

Naproti tomu dokumenty OpenAPI v3 definují adresu URL serveru pomocí pole servers:

{
   "openapi": "3.0.1",
   "servers": [
      {
         "url": "https://example.com/v1"
      }
   ],
   ...
}

Sémantické jádro použije první adresu URL serveru zadanou v dokumentu jako základní adresu URL: https://example.com/v1.

OpenAPI v3 také umožňuje parametrizované adresy URL serveru pomocí proměnných označených složenými závorkami:

{
   "openapi": "3.0.1",
   "servers": [
      {
         "url": "https://{environment}.example.com/v1",
         "variables": {
            "environment": {
               "default": "prod"
            }
         }
      }
   ],
   ...  
}

V tomto případě Semantic Kernel nahradí zástupný symbol proměnné hodnotou zadanou jako argument pro proměnnou nebo výchozí hodnotu, pokud není zadaný žádný argument, což vede k adrese URL: https://prod.example.com/v1.

Pokud dokument OpenAPI určuje žádnou adresu URL serveru, použije sémantické jádro základní adresu URL serveru, ze kterého byl dokument OpenAPI načten:

await kernel.ImportPluginFromOpenApiAsync(pluginName: "lights", uri: new Uri("https://api-host.com/swagger.json"));

Základní adresa URL bude https://api-host.com.

Přepisování adresy URL serveru

V některých případech nemusí být adresa URL serveru zadaná v dokumentu OpenAPI nebo na serveru, ze kterého byl dokument načten, vhodný pro případy použití modulu plug-in OpenAPI.

Sémantické jádro umožňuje přepsat adresu URL serveru poskytnutím vlastní základní adresy URL při přidávání modulu plug-in OpenAPI do jádra:

await kernel.ImportPluginFromOpenApiAsync(  
    pluginName: "lights",  
    uri: new Uri("https://example.com/v1/swagger.json"),  
    executionParameters: new OpenApiFunctionExecutionParameters()  
    {  
        ServerUrlOverride = new Uri("https://custom-server.com/v1")  
    });  

V tomto příkladu bude základní adresa URL https://custom-server.com/v1, což přepíše jak adresu URL serveru zadanou v dokumentu OpenAPI, tak adresu URL serveru, ze kterého byl dokument načten.

Autentizace

Většina rozhraní REST API vyžaduje pro přístup k prostředkům ověřování. Sémantické jádro poskytuje mechanismus, který umožňuje integrovat různé metody ověřování vyžadované moduly plug-in OpenAPI.

Tento mechanismus spoléhá na funkci zpětného volání ověřování, která se vyvolá před každým požadavkem rozhraní API. Tato funkce zpětného volání má přístup k objektu HttpRequestMessage představující požadavek HTTP, který se odešle do rozhraní API. Tento objekt můžete použít k přidání ověřovacích přihlašovacích údajů k požadavku. Přihlašovací údaje je možné přidat jako hlavičky, parametry dotazu nebo v textu požadavku v závislosti na metodě ověřování používané rozhraním API.

Tuto funkci zpětného volání je potřeba zaregistrovat při přidávání modulu plug-in OpenAPI do jádra. Následující fragment kódu ukazuje, jak ho zaregistrovat k ověřování požadavků:

static Task AuthenticateRequestAsyncCallback(HttpRequestMessage request, CancellationToken cancellationToken = default)
{
    // Best Practices:  
    // * Store sensitive information securely, using environment variables or secure configuration management systems.  
    // * Avoid hardcoding sensitive information directly in your source code.  
    // * Regularly rotate tokens and API keys, and revoke any that are no longer in use.  
    // * Use HTTPS to encrypt the transmission of any sensitive information to prevent interception.  

    // Example of Bearer Token Authentication  
    // string token = "your_access_token";  
    // request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);  

    // Example of API Key Authentication  
    // string apiKey = "your_api_key";  
    // request.Headers.Add("X-API-Key", apiKey);    

    return Task.CompletedTask;  
}

await kernel.ImportPluginFromOpenApiAsync(  
    pluginName: "lights",  
    uri: new Uri("https://example.com/v1/swagger.json"),  
    executionParameters: new OpenApiFunctionExecutionParameters()  
    {  
        AuthCallback = AuthenticateRequestAsyncCallback
    });  

V případě složitějších scénářů ověřování, které vyžadují dynamický přístup k podrobnostem schémat ověřování podporovaných rozhraním API, můžete k získání těchto informací použít metadata dokumentů a operací. Další podrobnosti najdete v tématu metadata dokumentů a operací.

Přizpůsobení čtení obsahu odpovědi

Sémantické jádro má integrovaný mechanismus pro čtení obsahu odpovědí HTTP z modulů plug-in OpenAPI a jejich převod na příslušné datové typy .NET. Odpověď obrázku může být například přečtena jako pole bajtů, zatímco odpověď JSON nebo XML je možné číst jako řetězec.

Může však nastat případ, kdy integrovaný mechanismus není pro vaše potřeby dostatečný. Pokud je například odpovědí velký objekt JSON nebo obrázek, který je potřeba číst jako datový proud, aby se dal jako vstup do jiného rozhraní API. V takových případech může být čtení obsahu odpovědi jako řetězce nebo bajtového pole a následné převedení zpět na datový proud neefektivní a může vést k problémům s výkonem. Aby se to vyřešilo, sémantické jádro umožňuje přizpůsobení čtení obsahu odpovědi poskytnutím vlastní čtečky obsahu:

private static async Task<object?> ReadHttpResponseContentAsync(HttpResponseContentReaderContext context, CancellationToken cancellationToken)  
{  
    // Read JSON content as a stream instead of as a string, which is the default behavior.  
    if (context.Response.Content.Headers.ContentType?.MediaType == "application/json")  
    {  
        return await context.Response.Content.ReadAsStreamAsync(cancellationToken);  
    }  

    // HTTP request and response properties can be used to determine how to read the content.  
    if (context.Request.Headers.Contains("x-stream"))  
    {  
        return await context.Response.Content.ReadAsStreamAsync(cancellationToken);  
    }  

    // Return null to indicate that any other HTTP content not handled above should be read by the default reader.  
    return null;  
}  

await kernel.ImportPluginFromOpenApiAsync(  
    pluginName: "lights",  
    uri: new Uri("https://example.com/v1/swagger.json"),  
    executionParameters: new OpenApiFunctionExecutionParameters()  
    {  
        HttpResponseContentReader = ReadHttpResponseContentAsync  
    });  

V tomto příkladu ReadHttpResponseContentAsync metoda přečte obsah odpovědi HTTP jako datový proud, pokud je typ obsahu application/json nebo když požadavek obsahuje vlastní hlavičku x-stream. Metoda vrátí null pro všechny ostatní typy obsahu, což znamená, že se má použít výchozí čtenář obsahu.

Metadata dokumentů a operací

Sémantické jádro extrahuje metadata operací a dokumentů OpenAPI, včetně informací o rozhraní API, schémat zabezpečení, ID operace, popisu, metadat parametrů a mnoha dalších. Poskytuje přístup k tomuto informacím prostřednictvím vlastnosti KernelFunction.Metadata.AdditionalParameters. Tato metadata můžou být užitečná ve scénářích, kdy se vyžadují další informace o rozhraní API nebo operaci, například pro účely ověřování:

static async Task AuthenticateRequestAsyncCallbackAsync(HttpRequestMessage request, CancellationToken cancellationToken = default)
{
    // Get the function context
    if (request.Options.TryGetValue(OpenApiKernelFunctionContext.KernelFunctionContextKey, out OpenApiKernelFunctionContext? functionContext))
    {
        // Get the operation metadata
        if (functionContext!.Function!.Metadata.AdditionalProperties["operation"] is RestApiOperation operation)
        {
            // Handle API key-based authentication
            IEnumerable<KeyValuePair<RestApiSecurityScheme, IList<string>>> apiKeySchemes = operation.SecurityRequirements.Select(requirement => requirement.FirstOrDefault(schema => schema.Key.SecuritySchemeType == "apiKey"));
            if (apiKeySchemes.Any())
            {
                (RestApiSecurityScheme scheme, IList<string> scopes) = apiKeySchemes.First();

                // Get the API key for the scheme and scopes from your app identity provider
                var apiKey = await this.identityPropvider.GetApiKeyAsync(scheme, scopes);

                // Add the API key to the request headers
                if (scheme.In == RestApiParameterLocation.Header)
                {
                    request.Headers.Add(scheme.Name, apiKey);
                }
                else if (scheme.In == RestApiParameterLocation.Query)
                {
                    request.RequestUri = new Uri($"{request.RequestUri}?{scheme.Name}={apiKey}");
                }
                else
                {
                    throw new NotSupportedException($"API key location '{scheme.In}' is not supported.");
                }
            }

            // Handle other authentication types like Basic, Bearer, OAuth2, etc. For more information, see https://swagger.io/docs/specification/v3_0/authentication/
        }
    }
}

// Import the transformed OpenAPI plugin specification
var plugin = kernel.ImportPluginFromOpenApi(
    pluginName: "lights",
    uri: new Uri("https://example.com/v1/swagger.json"),
    new OpenApiFunctionExecutionParameters()
    {
        AuthCallback = AuthenticateRequestAsyncCallbackAsync
    });

await kernel.InvokePromptAsync("Test");

V tomto příkladu AuthenticateRequestAsyncCallbackAsync metoda načte metadata operace z kontextu funkce a extrahuje požadavky na zabezpečení operace k určení schématu ověřování. Poté načte klíč API pro schéma a oprávnění od poskytovatele identity aplikace a přidá ho do hlaviček požadavku nebo parametrů dotazu.

Následující tabulka uvádí metadata dostupná ve slovníku KernelFunction.Metadata.AdditionalParameters:

Klíč Typ Popis
informace RestApiInfo Informace o rozhraní API, včetně názvu, popisu a verze
operace RestApiOperation Podrobnosti o operaci rozhraní API, jako je ID, popis, cesta, metoda atd.
bezpečnost IList<RestApiSecurityRequirement> Požadavky na zabezpečení rozhraní API – typ, název, in atd.

Tipy a triky pro přidávání modulů plug-in OpenAPI

Vzhledem k tomu, že specifikace OpenAPI jsou obvykle navrženy pro lidi, možná budete muset udělat některé změny, aby je AI snadněji porozuměla. Tady je několik tipů a triků, které vám pomůžou s tím:

Doporučení Popis
Řízení verzí vašich API specifikací Místo odkazování na specifikaci živého rozhraní API zvažte zaznamenání a verzování vašeho souboru Swagger. To umožní výzkumníkům umělé inteligence testovat (a měnit) specifikaci rozhraní API používanou agentem AI, aniž by to mělo vliv na živé rozhraní API a naopak.
Omezit počet koncových bodů Zkuste omezit počet koncových bodů ve vašem rozhraní API. Sloučit podobné funkce do jednotlivých koncových bodů s volitelnými parametry, aby se snížila složitost.
Použití popisných názvů pro koncové body a parametry Ujistěte se, že názvy koncových bodů a parametrů jsou popisné a vysvětlující. To pomáhá umělé inteligenci pochopit jejich účel, aniž by potřebovala rozsáhlé vysvětlení.
Používat konzistentní konvence vytváření názvů Udržujte konzistentní konvence vytváření názvů v celém rozhraní API. To snižuje nejasnosti a pomáhá umělé inteligenci učit se a předpovídat strukturu vašeho rozhraní API snadněji.
Zjednodušení specifikací rozhraní API Specifikace OpenAPI jsou často velmi podrobné a obsahují mnoho informací, které nejsou nezbytné pro agenta AI, aby pomohl uživateli. Jednodušší rozhraní API znamená, že potřebujete utratit méně tokenů na jeho popis a AI potřebuje méně tokenů k odesílání požadavků na něj.
Vyhněte se parametrům řetězce Pokud je to možné, vyhněte se použití parametrů řetězce ve vašem rozhraní API. Místo toho použijte konkrétnější typy, jako jsou celá čísla, logická hodnota nebo výčty. To pomůže umělé inteligenci lépe porozumět rozhraní API.
Příklady v popisech Když lidé používají soubory Swagger, obvykle můžou rozhraní API otestovat pomocí uživatelského rozhraní Swagger, které zahrnuje ukázkové požadavky a odpovědi. Vzhledem k tomu, že agent AI to nedokáže, zvažte poskytnutí příkladů v popisech parametrů.
Odkazovat na další koncové body v popisech AI často zaměňují podobné koncové body. Pokud chcete umělé inteligenci rozlišovat mezi koncovými body, zvažte odkazování na jiné koncové body v popisech. Můžete například říct, že "Tento koncový bod je podobný koncovému bodu get_all_lights, ale vrátí jenom jedno světlo".
Poskytněte užitečné chybové zprávy I když není ve specifikaci OpenAPI, zvažte poskytnutí chybových zpráv, které umělé inteligenci pomáhají správně opravit. Pokud například uživatel zadá neplatné ID, zvažte zadání chybové zprávy, která navrhuje, aby agent umělé inteligence získal správné ID z koncového bodu get_all_lights.

Další kroky

Teď, když víte, jak vytvořit modul plug-in, se teď můžete naučit, jak je používat s vaším agentem AI. V závislosti na typu funkcí, které jste přidali do modulů plug-in, byste měli postupovat podle různých vzorů. Podívejte se na článek o funkcích načítání s využitím funkcí načítání. Informace o funkcích automatizace úloh najdete v článku o použití funkcí automatizace úloh.