Lägga till plugin-program från OpenAPI-specifikationer
Ofta i ett företag har du redan en uppsättning API:er som utför verkligt arbete. Dessa kan användas av andra automationstjänster eller power frontend-program som människor interagerar med. I Semantic Kernel kan du lägga till exakt samma API:er som plugin-program så att dina agenter också kan använda dem.
Ett exempel på OpenAPI-specifikation
Ta till exempel ett API som gör att du kan ändra tillståndet för glödlampor. OpenAPI-specifikationen, som kallas Swagger-specifikation, eller bara Swagger, för det här API:et kan se ut så här:
{
"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
}
}
}
}
Den här specifikationen innehåller allt som ai:n behöver för att förstå API:et och hur du interagerar med det. API:et innehåller två slutpunkter: en för att hämta alla lampor och en annan för att ändra tillståndet för ett ljus. Den innehåller också följande:
- Semantiska beskrivningar för slutpunkterna och deras parametrar
- Parametrarnas typer
- Förväntade svar
Eftersom AI-agenten kan förstå den här specifikationen kan du lägga till den som ett plugin-program i agenten.
Semantisk kernel stöder OpenAPI-versionerna 2.0 och 3.0 och syftar till att anpassa version 3.1-specifikationerna genom att nedgradera den till version 3.0.
Tips
Om du har befintliga OpenAPI-specifikationer kan du behöva göra ändringar för att göra dem enklare för en AI att förstå dem. Du kan till exempel behöva ge vägledning i beskrivningarna. Mer tips om hur du gör dina OpenAPI-specifikationer AI-vänliga finns i Tips och tricks för att lägga till OpenAPI-plugin-program.
Lägga till OpenAPI-plugin-programmet
Med några rader kod kan du lägga till OpenAPI-plugin-programmet i din agent. Följande kodfragment visar hur du lägger till plugin-programmet light från OpenAPI-specifikationen ovan:
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
}
);
Med semantisk kernel kan du lägga till OpenAPI-plugin-program från olika källor, till exempel en URL, fil eller dataström. Dessutom kan plugin-program skapas en gång och återanvändas över flera kernelinstanser eller agenter.
// 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();
Efteråt kan du använda plugin-programmet i din agent som om det vore ett inbyggt plugin-program.
Hantera parametrar för OpenAPI-plugin
Semantisk kernel extraherar automatiskt metadata – till exempel namn, beskrivning, typ och schema för alla parametrar som definierats i OpenAPI-dokument.
Dessa metadata lagras i egenskapen KernelFunction.Metadata.Parameters
för varje OpenAPI-åtgärd och tillhandahålls till LLM tillsammans med uppmaningen att generera rätt argument för funktionsanrop.
Som standard tillhandahålls det ursprungliga parameternamnet till LLM och används av semantisk kernel för att leta upp motsvarande argument i listan över argument som tillhandahålls av LLM. Det kan dock finnas fall där OpenAPI-plugin-programmet har flera parametrar med samma namn. Att tillhandahålla den här parametermetadatan till LLM kan skapa förvirring, vilket kan hindra LLM från att generera rätt argument för funktionsanrop.
Eftersom en kernelfunktion som inte tillåter icke-unika parameternamn skapas för varje OpenAPI-åtgärd kan tillägg av ett sådant plugin-program dessutom leda till att vissa åtgärder blir otillgängliga för användning. Specifikt kommer operationer med icke-unika parameternamn att hoppas över, och en motsvarande varning kommer att loggas. Även om det var möjligt att inkludera flera parametrar med samma namn i kernelfunktionen kan detta leda till tvetydighet i argumentvalsprocessen.
Med tanke på allt detta erbjuder Semantic Kernel en lösning för att hantera plugin-program med icke-unika parameternamn. Den här lösningen är särskilt användbar när det inte går att ändra själva API:et, oavsett om det beror på att det är en tjänst från tredje part eller ett äldre system.
Följande kodfragment visar hur du hanterar icke-unika parameternamn i ett OpenAPI-plugin-program. Om den change_light_state åtgärden hade en ytterligare parameter med samma namn som den befintliga "id"-parametern – mer specifikt för att representera ett sessions-ID utöver det aktuella "ID" som representerar ljusets ID – kan den hanteras enligt nedan:
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);
Det här kodfragmentet använder klassen OpenApiDocumentParser
för att parsa OpenAPI-dokumentet och komma åt RestApiSpecification
modellobjektet som representerar dokumentet. Den tilldelar argumentnamn till parametrarna och importerar den omvandlade OpenAPI-plugin-specifikationen till kerneln. Semantisk kernel tillhandahåller argumentnamnen till LLM i stället för de ursprungliga namnen och använder dem för att leta upp motsvarande argument i listan som tillhandahålls av LLM.
Observera att argumentnamnen inte används i stället för de ursprungliga namnen när du anropar OpenAPI-åtgärden. I exemplet ovan ersätts parametern "id" i sökvägen med ett värde som returneras av LLM för argumentet "lightId". Samma sak gäller för rubrikparametern id. värdet som returneras av LLM för argumentet "sessionId" används som värde för rubriken med namnet "id".
Hantera OpenAPI-plugin-programnyttolast
OpenAPI-plugin-program kan ändra systemets tillstånd med post-, PUT- eller PATCH-åtgärder. Dessa åtgärder kräver ofta att en nyttolast ingår i begäran.
Semantisk kernel erbjuder några alternativ för att hantera nyttolasthantering för OpenAPI-plugin-program, beroende på ditt specifika scenario och API-krav.
Dynamisk konstruktion av nyttolast
Dynamisk nyttolastkonstruktion gör att nyttolasten för OpenAPI-åtgärder kan skapas dynamiskt baserat på nyttolastschemat och argumenten som tillhandahålls av LLM.
Den här funktionen är aktiverad som standard men kan inaktiveras genom att ange egenskapen EnableDynamicPayload
till false
i objektet OpenApiFunctionExecutionParameters
när du lägger till ett OpenAPI-plugin-program.
Tänk till exempel på åtgärden för att ändra ljusets tillstånd, som kräver en nyttolast strukturerad på följande sätt:
{
"isOn": true,
"hexColor": "#FF0000",
"brightness": 100,
"fadeDurationInMilliseconds": 500,
"scheduledTime": "2023-07-12T12:00:00Z"
}
För att ändra ljusets tillstånd och hämta värden för nyttolastegenskaperna tillhandahåller Semantic Kernel LLM med metadata för åtgärden så att den kan resonera om det:
{
"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"}},
]
}
Förutom att tillhandahålla åtgärdsmetadata till LLM utför Semantic Kernel följande steg:
- Hantera LLM-anropet till OpenAPI-åtgärden genom att konstruera nyttolasten baserat på schemat och de värden som LLM-egenskaperna tillhandahåller.
- Skicka HTTP-begäran med nyttolasten till API:et.
Begränsningar för dynamisk nyttolastkonstruktion
Dynamisk nyttolastkonstruktion är mest effektivt för API:er med relativt enkla nyttolaststrukturer. Det kanske inte fungerar pålitligt eller alls för API-payloads som uppvisar följande egenskaper:
- Laster med icke-unika egenskapsnamn oavsett var egenskaperna finns. Till exempel två egenskaper med namnet
id
, en för avsändarens objekt och en annan för mottagarobjekt –json { "sender": { "id": ... }, "receiver": { "id": ... }}
- Nyttolastscheman som använder något av de sammansatta nyckelorden
oneOf
,anyOf
,allOf
. - Nyttolastscheman med rekursiva referenser. T.ex.
json { "parent": { "child": { "$ref": "#parent" } } }
Om du vill hantera nyttolaster med icke-unika egenskapsnamn bör du överväga följande alternativ:
- Ange ett unikt argumentnamn för varje icke-unik egenskap med hjälp av en metod som liknar den som beskrivs i avsnittet Hantering av OpenAPI-plugin-parametrar.
- Använd namnområden för att undvika namngivningskonflikter, enligt beskrivningen i nästa avsnitt om Nyttolastnamnområden.
- Inaktivera dynamisk nyttolastkonstruktion och låt LLM skapa nyttolasten baserat på schemat, enligt beskrivningen i avsnittet Nyttolastparametern.
Om nyttolastscheman använder något av oneOf
, anyOf
, allOf
sammansatta nyckelord eller rekursiva referenser kan du överväga att inaktivera dynamisk nyttolastkonstruktion och låta LLM skapa nyttolasten baserat på schemat, enligt beskrivningen i avsnittet Nyttolastparametern.
Nyttolastnamnavstånd
Namnrymden för nyttolaster hjälper till att förhindra namnkonflikter som kan uppstå på grund av icke-unika egenskapsnamn i OpenAPI-pluginens nyttolaster.
När namnbestämning är aktiverad tillhandahåller Semantic Kernel det LLM med OpenAPI-åtgärdsmetadata som innehåller utökade egenskapsnamn. Dessa utökade namn skapas genom att det överordnade egenskapsnamnet läggs till som ett prefix, avgränsat med en punkt, till de underordnade egenskapsnamnen.
Om till exempel change_light_state-åtgärden hade inkluderat ett kapslat offTimer
objekt med en scheduledTime
egenskap:
{
"isOn": true,
"hexColor": "#FF0000",
"brightness": 100,
"fadeDurationInMilliseconds": 500,
"scheduledTime": "2023-07-12T12:00:00Z",
"offTimer": {
"scheduledTime": "2023-07-12T12:00:00Z"
}
}
Semantisk kernel skulle ha gett LLM metadata för åtgärden som innehåller följande egenskapsnamn:
{
"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"}},
]
}
Förutom att tillhandahålla åtgärdsmetadata med förhöjda egenskapsnamn till LLM utför Semantic Kernel följande steg:
- Hantera LLM-anropet till OpenAPI-åtgärden och leta upp motsvarande argument bland dem som tillhandahålls av LLM för alla egenskaper i nyttolasten, med hjälp av förhöjda egenskapsnamn och återgå till de ursprungliga egenskapsnamnen om det behövs.
- Konstruera nyttolasten med de ursprungliga egenskapsnamnen som nycklar och de lösta argumenten som värden.
- Skicka HTTP-begäran med den konstruerade nyttolasten till API:et.
Som standard är alternativet namnavstånd för nyttolast inaktiverat. Det kan aktiveras genom att ange egenskapen EnablePayloadNamespacing
till true
i objektet OpenApiFunctionExecutionParameters
när du lägger till ett OpenAPI-plugin-program:
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
});
Notera
Alternativet EnablePayloadNamespace
träder endast i kraft när dynamisk nyttolastkonstruktion också är aktiverat. annars har det ingen effekt.
Nyttolastparametern
Semantisk kernel kan fungera med nyttolaster som skapats av LLM med hjälp av nyttolastparametern. Detta är användbart när nyttolastschemat är komplext och innehåller icke-unika egenskapsnamn, vilket gör det omöjligt för semantisk kernel att dynamiskt konstruera nyttolasten.
I sådana fall förlitar du dig på LLM:s förmåga att förstå schemat och skapa en giltig nyttolast. De senaste modellerna, till exempel gpt-4o
är effektiva för att generera giltiga JSON-nyttolaster.
Om du vill aktivera nyttolastparametern anger du egenskapen EnableDynamicPayload
till false
i OpenApiFunctionExecutionParameters
-objektet när du lägger till ett OpenAPI-plugin-program:
await kernel.ImportPluginFromOpenApiAsync(
pluginName: "lights",
uri: new Uri("https://example.com/v1/swagger.json"),
executionParameters: new OpenApiFunctionExecutionParameters()
{
EnableDynamicPayload = false, // Disable dynamic payload construction
});
När nyttolastparametern är aktiverad tillhandahåller Semantic Kernel LLM med metadata för åtgärden som innehåller scheman för nyttolasten och content_type parametrar, vilket gör att LLM kan förstå nyttolaststrukturen och konstruera den i enlighet med detta:
{
"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."
}
}
}
Förutom att tillhandahålla åtgärdsmetadata med schemat för parametrar för nyttolast och innehållstyp till LLM utför Semantic Kernel följande steg:
- Hantera LLM-anropet till OpenAPI-åtgärden och använder argumenten som tillhandahålls av LLM för nyttolasten och content_type-parametrarna.
- Skicka HTTP-begäran till API:et med angivet nyttolast och innehållstyp.
Serverbas-URL
Semantiska Kernel OpenAPI-plugin-program kräver en bas-URL som används för att förbereda slutpunktssökvägar när API-begäranden görs. Den här bas-URL:en kan anges i OpenAPI-dokumentet, hämtas implicit genom att dokumentet läses in från en URL eller tillhandahålls när plugin-programmet läggs till i kerneln.
Url som anges i OpenAPI-dokumentet
OpenAPI v2-dokument definierar server-URL:en med hjälp av fälten schemes
, host
och basePath
:
{
"swagger": "2.0",
"host": "example.com",
"basePath": "/v1",
"schemes": ["https"]
...
}
Semantisk kernel skapar server-URL:en som https://example.com/v1
.
OpenAPI v3-dokument definierar däremot server-URL:en med hjälp av fältet servers
:
{
"openapi": "3.0.1",
"servers": [
{
"url": "https://example.com/v1"
}
],
...
}
Semantisk kernel använder den första server-URL:en som anges i dokumentet som bas-URL: https://example.com/v1
.
OpenAPI v3 tillåter också parameteriserade server-URL:er med variabler som anges av klammerparenteser:
{
"openapi": "3.0.1",
"servers": [
{
"url": "https://{environment}.example.com/v1",
"variables": {
"environment": {
"default": "prod"
}
}
}
],
...
}
I det här fallet ersätter Semantic Kernel variabelplatshållaren med antingen det värde som anges som ett argument för variabeln eller standardvärdet om inget argument anges, vilket resulterar i URL:en: https://prod.example.com/v1
.
Om OpenAPI-dokumentet inte anger någon server-URL använder Semantic Kernel bas-URL:en för servern som OpenAPI-dokumentet lästes in från:
await kernel.ImportPluginFromOpenApiAsync(pluginName: "lights", uri: new Uri("https://api-host.com/swagger.json"));
Bas-URL:en blir https://api-host.com
.
Åsidosätta server-URL:en
I vissa fall är server-URL:en som anges i OpenAPI-dokumentet eller servern som dokumentet lästes in från kanske inte lämplig för användningsfall som rör OpenAPI-plugin-programmet.
Med semantisk kernel kan du åsidosätta server-URL:en genom att ange en anpassad bas-URL när du lägger till OpenAPI-plugin-programmet i kerneln:
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")
});
I det här exemplet kommer bas-URL:en att https://custom-server.com/v1
och åsidosätta server-URL:en som anges i OpenAPI-dokumentet och server-URL:en som dokumentet lästes in från.
Autentisering
De flesta REST-API:er kräver autentisering för att få åtkomst till sina resurser. Semantisk kernel tillhandahåller en mekanism som gör att du kan integrera en mängd olika autentiseringsmetoder som krävs av OpenAPI-plugin-program.
Den här mekanismen förlitar sig på en återanropsfunktion för autentisering som anropas före varje API-begäran. Den här återanropsfunktionen har åtkomst till HttpRequestMessage-objektet som representerar DEN HTTP-begäran som ska skickas till API:et. Du kan använda det här objektet för att lägga till autentiseringsuppgifter i begäran. Autentiseringsuppgifterna kan läggas till som rubriker, frågeparametrar eller i begärandetexten, beroende på vilken autentiseringsmetod som används av API:et.
Du måste registrera den här återanropsfunktionen när du lägger till OpenAPI-plugin-programmet i kerneln. Följande kodfragment visar hur du registrerar det för att autentisera begäranden:
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
});
För mer komplexa autentiseringsscenarier som kräver dynamisk åtkomst till information om de autentiseringsscheman som stöds av ett API kan du använda dokument- och åtgärdsmetadata för att hämta den här informationen. Mer information finns i dokument- och åtgärdsmetadata.
Anpassning av läsning av svarsinnehåll
Semantisk kernel har en inbyggd mekanism för att läsa innehållet i HTTP-svar från OpenAPI-plugin-program och konvertera dem till lämpliga .NET-datatyper. Ett bildsvar kan till exempel läsas som en bytematris, medan ett JSON- eller XML-svar kan läsas som en sträng.
Det kan dock finnas fall då den inbyggda mekanismen inte är tillräcklig för dina behov. Till exempel när svaret är ett stort JSON-objekt eller en bild som måste läsas som en ström för att kunna anges som indata till ett annat API. I sådana fall kan det vara ineffektivt att läsa svarsinnehållet som en sträng eller bytematris och sedan konvertera tillbaka det till en dataström och leda till prestandaproblem. För att åtgärda detta tillåter Semantic Kernel anpassning av svarsinnehållsläsning genom att tillhandahålla en anpassad innehållsläsare:
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
});
I det här exemplet läser ReadHttpResponseContentAsync
-metoden HTTP-svarsinnehållet som en ström när innehållstypen är application/json
eller när begäran innehåller en anpassad rubrik x-stream
. Metoden returnerar null
för andra innehållstyper, vilket anger att standardinnehållsläsaren ska användas.
Dokument- och åtgärdsmetadata
Semantisk kernel extraherar OpenAPI-dokument och åtgärdsmetadata, inklusive API-information, säkerhetsscheman, åtgärds-ID, beskrivning, parametermetadata och många fler.
Den ger åtkomst till den här informationen via egenskapen KernelFunction.Metadata.AdditionalParameters
. Dessa metadata kan vara användbara i scenarier där ytterligare information om API:et eller åtgärden krävs, till exempel i autentiseringssyfte:
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");
I det här exemplet läser AuthenticateRequestAsyncCallbackAsync
-metoden åtgärdsmetadata från funktionskontexten och extraherar säkerhetskrav för åtgärden för att fastställa autentiseringsschemat. Den hämtar sedan API-nyckeln, för schemat och omfången, från appidentitetsprovidern och lägger till den i begäranderubrikerna eller frågeparametrarna.
I följande tabell visas de metadata som är tillgängliga i ordlistan KernelFunction.Metadata.AdditionalParameters
:
Nyckel | Typ | Beskrivning |
---|---|---|
info | RestApiInfo |
API-information, inklusive rubrik, beskrivning och version. |
operation | RestApiOperation |
API-åtgärdsinformation, till exempel ID, beskrivning, sökväg, metod osv. |
säkerhet | IList<RestApiSecurityRequirement > |
API-säkerhetskrav – typ, namn, i osv. |
Tips för att lägga till OpenAPI-plugin-program
Eftersom OpenAPI-specifikationer vanligtvis är utformade för människor kan du behöva göra vissa ändringar för att göra dem enklare för en AI att förstå. Här följer några tips som hjälper dig att göra det:
Rekommendation | Beskrivning |
---|---|
Version styr dina API-specifikationer | I stället för att peka på en live-API-specifikation bör du överväga att checka in och versionshantera Swagger-filen. Detta gör att dina AI-forskare kan testa (och ändra) API-specifikationen som används av AI-agenten utan att påverka live-API:et och vice versa. |
Begränsa antalet slutpunkter | Försök att begränsa antalet slutpunkter i api:et. Konsolidera liknande funktioner i enskilda slutpunkter med valfria parametrar för att minska komplexiteten. |
Använd beskrivande namn för slutpunkter och parametrar | Se till att namnen på dina slutpunkter och parametrar är beskrivande och självförklarande. Detta hjälper AI:n att förstå sitt syfte utan att behöva utförliga förklaringar. |
Använd konsekventa namngivningskonventioner | Upprätthålla konsekventa namngivningskonventioner i hela API:et. Detta minskar förvirringen och hjälper AI:n att lära sig och förutsäga strukturen för ditt API enklare. |
Förenkla api-specifikationerna | Ofta är OpenAPI-specifikationerna mycket detaljerade och innehåller mycket information som inte är nödvändig för att AI-agenten ska hjälpa en användare. Ju enklare API:et är, desto färre token behöver du spendera för att beskriva det, och ju färre token ai:n behöver för att skicka begäranden till det. |
Undvik strängparametrar | Undvik att använda strängparametrar i API:et när det är möjligt. Använd i stället mer specifika typer som heltal, booleska värden eller uppräkningar. Detta hjälper AI:n att förstå API:et bättre. |
Ange exempel i beskrivningar | När människor använder Swagger-filer kan de vanligtvis testa API:et med hjälp av Swagger-användargränssnittet, som innehåller exempelbegäranden och svar. Eftersom AI-agenten inte kan göra detta kan du överväga att tillhandahålla exempel i beskrivningarna av parametrarna. |
Referera till andra slutpunkter i beskrivningar | Ofta förväxlar AI liknande slutpunkter. Om du vill hjälpa AI:n att skilja mellan slutpunkter kan du överväga att referera till andra slutpunkter i beskrivningarna. Du kan till exempel säga "Den här slutpunkten liknar den get_all_lights slutpunkten, men den returnerar bara ett enda ljus." |
Ge användbara felmeddelanden | Även om det inte finns i OpenAPI-specifikationen kan du överväga att tillhandahålla felmeddelanden som hjälper AI:n att självkorrigering. Om en användare till exempel tillhandahåller ett ogiltigt ID kan du överväga att ange ett felmeddelande som tyder på att AI-agenten får rätt ID från get_all_lights slutpunkten. |
Nästa steg
Nu när du vet hur du skapar ett plugin-program kan du nu lära dig hur du använder dem med din AI-agent. Beroende på vilken typ av funktioner du har lagt till i dina plugin-program finns det olika mönster som du bör följa. Information om hämtningsfunktioner finns i artikeln med hjälp av hämtningsfunktioner. Information om funktioner för uppgiftsautomatisering finns i artikeln med hjälp av funktioner för uppgiftsautomatisering.