Invoegtoepassingen toevoegen vanuit OpenAPI-specificaties
Vaak hebt u in een onderneming al een set API's die echt werken. Deze kunnen worden gebruikt door andere automatiseringsservices of krachtige front-endtoepassingen waarmee mensen communiceren. In Semantic Kernel kunt u dezelfde API's toevoegen als invoegtoepassingen, zodat uw agents ze ook kunnen gebruiken.
Een voorbeeld van een OpenAPI-specificatie
Neem bijvoorbeeld een API waarmee u de status van gloeilampen kunt wijzigen. De OpenAPI-specificatie, ook wel Swagger Specification of alleen Swagger genoemd, kan er als volgt uitzien:
{
"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
}
}
}
}
Deze specificatie biedt alles wat de AI nodig heeft om inzicht te hebben in de API en hoe u ermee kunt werken. De API bevat twee eindpunten: één om alle lichten op te halen en een andere om de status van een licht te wijzigen. Het biedt ook het volgende:
- Semantische beschrijvingen voor de eindpunten en hun parameters
- De typen van de parameters
- De verwachte antwoorden
Omdat de AI-agent deze specificatie kan begrijpen, kunt u deze als invoegtoepassing toevoegen aan de agent.
Semantic Kernel ondersteunt OpenAPI-versies 2.0 en 3.0, en het is erop gericht om tegemoet te komen aan versie 3.1-specificaties door deze te downgraden naar versie 3.0.
Tip
Als u bestaande OpenAPI-specificaties hebt, moet u mogelijk wijzigingen aanbrengen om deze eenvoudiger te maken voor een AI om ze te begrijpen. U moet bijvoorbeeld richtlijnen opgeven in de beschrijvingen. Zie Tips en adviezen voor het toevoegen van OpenAPI-invoegtoepassingenvoor meer tips over hoe u uw OpenAPI-specificaties AI-vriendelijk kunt maken.
De OpenAPI-invoegtoepassing toevoegen
Met een paar regels code kunt u de OpenAPI-invoegtoepassing toevoegen aan uw agent. In het volgende codefragment ziet u hoe u de light-invoegtoepassing toevoegt uit de Bovenstaande OpenAPI-specificatie:
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
}
);
Met Semantic Kernel kunt u OpenAPI-invoegtoepassingen uit verschillende bronnen toevoegen, zoals een URL, bestand of stream. Bovendien kunnen invoegtoepassingen eenmaal worden gemaakt en vervolgens opnieuw worden gebruikt in meerdere kernelexemplaren en agents.
// 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();
Daarna kunt u de invoegtoepassing in uw agent gebruiken alsof het een systeemeigen invoegtoepassing is.
Parameters van de OpenAPI-invoegtoepassing verwerken
Semantische kernel extraheert automatisch metagegevens, zoals naam, beschrijving, type en schema voor alle parameters die zijn gedefinieerd in OpenAPI-documenten.
Deze metagegevens worden opgeslagen in de eigenschap KernelFunction.Metadata.Parameters
voor elke OpenAPI-bewerking en worden geleverd aan de LLM, samen met de prompt om de juiste argumenten voor functieaanroepen te genereren.
De oorspronkelijke parameternaam wordt standaard opgegeven voor de LLM en wordt gebruikt door Semantische kernel om het bijbehorende argument op te zoeken in de lijst met argumenten die door de LLM worden opgegeven. Er kunnen echter gevallen zijn waarin de OpenAPI-invoegtoepassing meerdere parameters met dezelfde naam heeft. Als u deze parametermetagegevens aan de LLM opgeeft, kan er verwarring ontstaan, waardoor de LLM mogelijk niet de juiste argumenten voor functie-aanroepen genereert.
Bovendien kan het toevoegen van een dergelijke invoegtoepassing ertoe leiden dat sommige bewerkingen niet beschikbaar zijn voor gebruik omdat een kernelfunctie die niet toestaat voor niet-unieke parameternamen wordt gemaakt voor elke OpenAPI-bewerking. Bewerkingen met niet-unieke parameternamen worden overgeslagen en er wordt een bijbehorende waarschuwing vastgelegd. Zelfs als het mogelijk was om meerdere parameters met dezelfde naam in de kernelfunctie op te nemen, kan dit leiden tot dubbelzinnigheid in het argumentselectieproces.
Gezien dit alles biedt Semantic Kernel een oplossing voor het beheren van invoegtoepassingen met niet-unieke parameternamen. Deze oplossing is met name handig wanneer het wijzigen van de API zelf niet haalbaar is, ongeacht of deze een service van derden of een verouderd systeem is.
Het volgende codefragment laat zien hoe u niet-unieke parameternamen in een OpenAPI-invoegtoepassing kunt verwerken. Als de change_light_state-bewerking een extra parameter met dezelfde naam had als de bestaande parameter 'id', met name om een sessie-id weer te geven naast de huidige 'id' die de id van het licht vertegenwoordigt, kan deze worden verwerkt zoals hieronder wordt weergegeven:
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);
Dit codefragment maakt gebruik van de OpenApiDocumentParser
-klasse om het OpenAPI-document te parseren en toegang te krijgen tot het RestApiSpecification
modelobject dat het document vertegenwoordigt. Het wijst argumentnamen toe aan de parameters en importeert de getransformeerde OpenAPI-invoegtoepassingsspecificatie in de kernel. Semantische kernel geeft de argumentnamen aan de LLM in plaats van de oorspronkelijke namen en gebruikt deze om de bijbehorende argumenten op te zoeken in de lijst die door de LLM wordt opgegeven.
Het is belangrijk te weten dat de argumentnamen niet worden gebruikt in plaats van de oorspronkelijke namen bij het aanroepen van de OpenAPI-bewerking. In het bovenstaande voorbeeld wordt de parameter 'id' in het pad vervangen door een waarde die wordt geretourneerd door de LLM voor het argument 'lightId'. Hetzelfde geldt voor de headerparameter id; de waarde die door de LLM voor het argument sessionId wordt geretourneerd, wordt gebruikt als de waarde voor de header met de naam 'id'.
Het verwerken van payloads van OpenAPI-plugins
OpenAPI-invoegtoepassingen kunnen de status van het systeem wijzigen met behulp van POST-, PUT- of PATCH-bewerkingen. Voor deze bewerkingen moet vaak een payload bij het verzoek worden inbegrepen.
Semantische kernel biedt enkele opties voor het beheren van payloadafhandeling voor OpenAPI-invoegtoepassingen, afhankelijk van uw specifieke scenario en API-vereisten.
Constructie van dynamische payload
Met dynamische nettoladingconstructie kunnen de nettoladingen van OpenAPI-bewerkingen dynamisch worden gemaakt op basis van het payloadschema en de argumenten van de LLM.
Deze functie is standaard ingeschakeld, maar kan worden uitgeschakeld door de eigenschap EnableDynamicPayload
in te stellen op false
in het OpenApiFunctionExecutionParameters
-object bij het toevoegen van een OpenAPI-invoegtoepassing.
Denk bijvoorbeeld aan de change_light_state bewerking, waarvoor een payload als volgt is gestructureerd:
{
"isOn": true,
"hexColor": "#FF0000",
"brightness": 100,
"fadeDurationInMilliseconds": 500,
"scheduledTime": "2023-07-12T12:00:00Z"
}
Als u de status van het licht wilt wijzigen en waarden voor de eigenschappen van de payload wilt ophalen, biedt de Semantische Kernel de LLM met metagegevens voor de bewerking, zodat deze hierover kan nadenken.
{
"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"}},
]
}
Naast het verstrekken van metagegevens van bewerkingen aan de LLM, voert Semantische kernel de volgende stappen uit:
- Behandel de LLM-aanroep naar de OpenAPI-operatie, waarbij de payload wordt opgebouwd op basis van het schema en verstrekt door de LLM-eigendomswaarden.
- Verzend de HTTP-aanvraag met de nettolading naar de API.
Beperkingen van dynamische payloadconstructie
Dynamische nettoladingconstructie is het meest effectief voor API's met relatief eenvoudige nettoladingstructuren. Het werkt mogelijk niet betrouwbaar of helemaal niet voor API-belastingen met de volgende kenmerken:
- Nettoladingen met niet-unieke eigenschapsnamen, ongeacht de locatie van de eigenschappen. Bijvoorbeeld twee eigenschappen met de naam
id
, één voor afzenderobject en een voor ontvangerobject -json { "sender": { "id": ... }, "receiver": { "id": ... }}
- Payloadschema's die gebruikmaken van een van de samengestelde trefwoorden
oneOf
,anyOf
,allOf
. - Payloadschema's met recursieve verwijzingen. Bijvoorbeeld
json { "parent": { "child": { "$ref": "#parent" } } }
Als u nettoladingen met niet-unieke eigenschapsnamen wilt verwerken, kunt u de volgende alternatieven overwegen:
- Geef een unieke argumentnaam op voor elke niet-unieke eigenschap, met behulp van een methode die vergelijkbaar is met de methode die wordt beschreven in de parameters van de Handling OpenAPI plugin sectie.
- Gebruik naamruimten om naamconflicten te voorkomen, zoals beschreven in de volgende sectie over Payload-namenspaatsing.
- Schakel dynamische nettoladingconstructie uit en laat de LLM de nettolading maken op basis van het schema, zoals wordt uitgelegd in de sectie De parameter nettolading.
Als schema's voor nettoladingen een van de oneOf
, anyOf
, allOf
samengestelde trefwoorden of recursieve verwijzingen gebruiken, kunt u overwegen om dynamische nettoladingconstructie uit te schakelen en de LLM de nettolading te laten maken op basis van het schema, zoals wordt uitgelegd in de sectie De parameter nettolading.
Naamruimten voor laadgegevens
Payload-namespacing helpt naamconflicten te voorkomen die kunnen optreden als gevolg van niet-unieke eigenschapsnamen in OpenAPI-plug-in payloads.
Wanneer namespacing is ingeschakeld, voorziet Semantic Kernel de LLM van OpenAPI-bewerkingsmetagegevens die uitgebreide eigenschapsnamen bevatten. Deze uitgebreide namen worden gemaakt door de naam van de bovenliggende eigenschap toe te voegen als een voorvoegsel, gescheiden door een punt, aan de namen van de onderliggende eigenschappen.
Als de bewerking change_light_state bijvoorbeeld een genest offTimer
-object met een eigenschap scheduledTime
bevat:
{
"isOn": true,
"hexColor": "#FF0000",
"brightness": 100,
"fadeDurationInMilliseconds": 500,
"scheduledTime": "2023-07-12T12:00:00Z",
"offTimer": {
"scheduledTime": "2023-07-12T12:00:00Z"
}
}
Semantic Kernel zou de LLM met metagegevens hebben verstrekt van de bewerking met de volgende eigenschapsnamen:
{
"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"}},
]
}
Naast het bieden van metagegevens van bewerkingen met uitgebreide eigenschapsnamen aan de LLM, voert Semantic Kernel de volgende stappen uit:
- Verhandel de LLM-aanroep naar de OpenAPI-bewerking en zoek de bijbehorende argumenten op onder de argumenten die door de LLM zijn opgegeven voor alle eigenschappen in de nettolading, met behulp van de uitgebreide eigenschapsnamen en ga zo nodig terug naar de oorspronkelijke eigenschapsnamen.
- Maak de payload door de oorspronkelijke eigenschapsnamen als sleutels en de opgeloste argumenten als waarden te gebruiken.
- Verzend de HTTP-aanvraag met de samengestelde nettolading naar de API.
De optie 'payload namespacing' is standaard uitgeschakeld. Deze kan worden ingeschakeld door de eigenschap EnablePayloadNamespacing
in te stellen op true
in het OpenApiFunctionExecutionParameters
-object bij het toevoegen van een OpenAPI-invoegtoepassing:
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
});
Notitie
De optie EnablePayloadNamespace
is alleen van kracht wanneer de dynamische payloadconstructie ook is ingeschakeld; anders heeft het geen effect.
De payload-parameter
Semantische kernel kan werken met payloads die door de LLM zijn gemaakt met behulp van de payload-parameter. Dit is handig wanneer het payloadschema complex is en niet-unieke eigenschapsnamen bevat, waardoor het niet haalbaar is voor Semantische kernel om de nettolading dynamisch samen te stellen.
In dergelijke gevallen vertrouwt u op de vaardigheid van de LLM om het schema te begrijpen en een geldig gegevenspakket te maken. Recente modellen, zoals gpt-4o
zijn effectief bij het genereren van geldige JSON-nettoladingen.
Als u de payloadparameter wilt inschakelen, stelt u de eigenschap EnableDynamicPayload
in op false
in het OpenApiFunctionExecutionParameters
-object bij het toevoegen van een OpenAPI-invoegtoepassing:
await kernel.ImportPluginFromOpenApiAsync(
pluginName: "lights",
uri: new Uri("https://example.com/v1/swagger.json"),
executionParameters: new OpenApiFunctionExecutionParameters()
{
EnableDynamicPayload = false, // Disable dynamic payload construction
});
Wanneer de payloadparameter is ingeschakeld, biedt Semantische kernel de LLM metagegevens voor de bewerking die schema's voor de nettolading en content_type parameters bevat, zodat de LLM de nettoladingstructuur kan begrijpen en deze dienovereenkomstig kan samenstellen:
{
"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."
}
}
}
Naast het opgeven van de metagegevens van de bewerking met het schema voor payload- en inhoudstypeparameters voor de LLM, voert Semantische kernel de volgende stappen uit:
- Behandel de LLM-aanroep naar de OpenAPI-operatie en gebruik de argumenten van de LLM voor de parameters payload en content_type.
- Verzend de HTTP-aanvraag naar de API met opgegeven nettolading en inhoudstype.
Basis-URL van server
Voor Semantische Kernel OpenAPI-invoegtoepassingen is een basis-URL vereist, die wordt gebruikt voor het voorbereiden van eindpuntpaden bij het maken van API-aanvragen. Deze basis-URL kan worden opgegeven in het OpenAPI-document, impliciet verkregen door het document vanuit een URL te laden of op te geven bij het toevoegen van de invoegtoepassing aan de kernel.
Url opgegeven in OpenAPI-document
OpenAPI v2-documenten definiëren de server-URL met behulp van de velden schemes
, host
en basePath
:
{
"swagger": "2.0",
"host": "example.com",
"basePath": "/v1",
"schemes": ["https"]
...
}
Semantische kernel maakt de server-URL als https://example.com/v1
.
OpenAPI v3-documenten definiëren daarentegen de server-URL met behulp van het veld servers
:
{
"openapi": "3.0.1",
"servers": [
{
"url": "https://example.com/v1"
}
],
...
}
Semantische kernel gebruikt de eerste server-URL die in het document is opgegeven als basis-URL: https://example.com/v1
.
Met OpenAPI v3 kunt u ook geparameteriseerde server-URL's gebruiken met behulp van variabelen die worden aangegeven met accolades:
{
"openapi": "3.0.1",
"servers": [
{
"url": "https://{environment}.example.com/v1",
"variables": {
"environment": {
"default": "prod"
}
}
}
],
...
}
In dit geval vervangt Semantische kernel de tijdelijke aanduiding voor variabelen door de waarde die is opgegeven als argument voor de variabele of de standaardwaarde als er geen argument wordt opgegeven, wat resulteert in de URL: https://prod.example.com/v1
.
Als in het OpenAPI-document geen server-URL wordt opgegeven, gebruikt Semantische kernel de basis-URL van de server van waaruit het OpenAPI-document is geladen:
await kernel.ImportPluginFromOpenApiAsync(pluginName: "lights", uri: new Uri("https://api-host.com/swagger.json"));
De basis-URL wordt https://api-host.com
.
De SERVER-URL overschrijven
In sommige gevallen is de server-URL die is opgegeven in het OpenAPI-document of de server van waaruit het document is geladen, mogelijk niet geschikt voor gebruiksvoorbeelden met de OpenAPI-invoegtoepassing.
Met Semantische kernel kunt u de server-URL overschrijven door een aangepaste basis-URL op te geven bij het toevoegen van de OpenAPI-invoegtoepassing aan de kernel:
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")
});
In dit voorbeeld wordt de basis-URL https://custom-server.com/v1
, waarbij de server-URL wordt overschreven die is opgegeven in het OpenAPI-document en de server-URL waaruit het document is geladen.
Authenticatie
Voor de meeste REST API's is verificatie vereist voor toegang tot hun resources. Semantische kernel biedt een mechanisme waarmee u verschillende verificatiemethoden kunt integreren die vereist zijn voor OpenAPI-invoegtoepassingen.
Dit mechanisme is afhankelijk van een callback-functie voor verificatie, die wordt aangeroepen vóór elke API-aanvraag. Deze callback-functie heeft toegang tot het HttpRequestMessage-object, dat de HTTP-aanvraag vertegenwoordigt die naar de API wordt verzonden. U kunt dit object gebruiken om verificatiereferenties toe te voegen aan de aanvraag. De inloggegevens kunnen worden toegevoegd als headers, queryparameters of in de verzoekbody, afhankelijk van de verificatiemethode die door de API wordt gebruikt.
U moet deze callback-functie registreren bij het toevoegen van de OpenAPI-invoegtoepassing aan de kernel. In het volgende codefragment ziet u hoe u dit kunt registreren om aanvragen te verifiëren:
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
});
Voor complexere verificatiescenario's die dynamische toegang tot de details van de verificatieschema's vereisen die door een API worden ondersteund, kunt u de metagegevens van documenten en operaties gebruiken om deze informatie te verkrijgen. Zie metagegevens van documenten en bewerkingenvoor meer informatie.
Aanpassing van het lezen van de inhoud van reacties
Semantische kernel heeft een ingebouwd mechanisme voor het lezen van de inhoud van HTTP-antwoorden van OpenAPI-invoegtoepassingen en het converteren ervan naar de juiste .NET-gegevenstypen. Een afbeeldingsantwoord kan bijvoorbeeld worden gelezen als een bytematrix, terwijl een JSON- of XML-antwoord kan worden gelezen als een tekenreeks.
Er kunnen echter gevallen zijn wanneer het ingebouwde mechanisme onvoldoende is voor uw behoeften. Wanneer het antwoord bijvoorbeeld een groot JSON-object of een grote afbeelding is die moet worden gelezen als een stroom om als invoer voor een andere API te kunnen worden opgegeven. In dergelijke gevallen kan het lezen van de antwoordinhoud als een tekenreeks of bytematrix en het vervolgens converteren naar een stroom inefficiënt zijn en kan dit leiden tot prestatieproblemen. Om dit te verhelpen, maakt Semantische kernel het lezen van antwoordinhoud mogelijk door een aangepaste inhoudslezer op te geven:
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
});
In dit voorbeeld leest de ReadHttpResponseContentAsync
methode de HTTP-antwoordinhoud als een stream wanneer het inhoudstype wordt application/json
of wanneer de aanvraag een aangepaste header bevat x-stream
. De methode retourneert null
voor andere inhoudstypen, waarmee wordt aangegeven dat de standaardinhoudslezer moet worden gebruikt.
Metagegevens van documenten en bewerkingen
Semantische kernel extraheert metagegevens van OpenAPI-documenten en bewerkingen, waaronder API-informatie, beveiligingsschema's, bewerkings-id, beschrijving, parametermetagegevens en nog veel meer.
Het biedt toegang tot deze informatie via de eigenschap KernelFunction.Metadata.AdditionalParameters
. Deze metagegevens kunnen nuttig zijn in scenario's waarin aanvullende informatie over de API of bewerking is vereist, zoals voor verificatiedoeleinden:
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");
In dit voorbeeld leest de AuthenticateRequestAsyncCallbackAsync
methode de metagegevens van de bewerking uit de functiecontext en extraheert de beveiligingsvereisten voor de bewerking om het verificatieschema te bepalen. Vervolgens wordt de API-sleutel voor het schema en de scopes opgehaald van de id-provider van de app en toegevoegd aan de aanvraagheaders of queryparameters.
De volgende tabel bevat de metagegevens die beschikbaar zijn in de KernelFunction.Metadata.AdditionalParameters
woordenlijst:
Sleutel | Type | Beschrijving |
---|---|---|
info | RestApiInfo |
API-informatie, inclusief titel, beschrijving en versie. |
operatie | RestApiOperation |
Api-bewerkingsgegevens, zoals id, beschrijving, pad, methode, enzovoort. |
veiligheid | IList<RestApiSecurityRequirement > |
API-beveiligingsvereisten: type, naam, in, enzovoort. |
Tips en trucs voor het toevoegen van OpenAPI-invoegtoepassingen
Omdat OpenAPI-specificaties doorgaans zijn ontworpen voor mensen, moet u mogelijk enkele wijzigingen aanbrengen om ze gemakkelijker te begrijpen voor een AI. Hier volgen enkele tips en trucs om u te helpen dit te doen:
Aanbeveling | Beschrijving |
---|---|
versiebeheer voor uw API-specificaties | In plaats van naar een live API-specificatie te verwijzen, kunt u overwegen om uw Swagger-bestand in te checken en versiebeheer uit te voeren. Hierdoor kunnen uw AI-onderzoekers de API-specificatie testen (en wijzigen) die door de AI-agent wordt gebruikt zonder dat dit van invloed is op de live-API en vice versa. |
het aantal eindpunten beperken | Probeer het aantal eindpunten in uw API te beperken. Voeg vergelijkbare functies samen in één eindpunt met optionele parameters om de complexiteit te verminderen. |
Beschrijvende namen gebruiken voor eindpunten en parameters | Zorg ervoor dat de namen van uw eindpunten en parameters beschrijvend en verklarend zijn. Dit helpt de AI hun doel te begrijpen zonder uitgebreide uitleg nodig te hebben. |
consistente naamconventies gebruiken | Behoud consistente naamconventies in uw API. Dit vermindert verwarring en helpt de AI om de structuur van uw API gemakkelijker te leren en te voorspellen. |
vereenvoudig uw API-specificaties | OpenAPI-specificaties zijn vaak zeer gedetailleerd en bevatten veel informatie die niet nodig is voor de AI-agent om een gebruiker te helpen. Hoe eenvoudiger de API, hoe minder tokens u moet uitgeven om deze te beschrijven en hoe minder tokens de AI nodig heeft om er aanvragen naar te verzenden. |
tekenreeksparameters vermijden | Vermijd, indien mogelijk, het gebruik van tekenreeksparameters in uw API. Gebruik in plaats daarvan specifiekere typen, zoals gehele getallen, Booleaanse waarden of opsommingen. Dit helpt de AI de API beter te begrijpen. |
Voorbeelden geven in beschrijvingen | Wanneer mensen Swagger-bestanden gebruiken, kunnen ze doorgaans de API testen met behulp van de Swagger-gebruikersinterface, waaronder voorbeeldaanvragen en antwoorden. Aangezien de AI-agent dit niet kan doen, kunt u overwegen voorbeelden op te geven in de beschrijvingen van de parameters. |
Verwijzen naar andere eindpunten in beschrijvingen | Vaak verwarren AIs vergelijkbare eindpunten. Raadpleeg andere eindpunten in de beschrijvingen om de AI te helpen onderscheid te maken tussen eindpunten. U kunt bijvoorbeeld zeggen: 'Dit eindpunt is vergelijkbaar met het get_all_lights -eindpunt, maar retourneert slechts één licht'. |
Geef nuttige foutberichten | Hoewel deze niet binnen de OpenAPI-specificatie valt, kunt u overwegen foutberichten op te geven die de AI zelf corrigeren. Als een gebruiker bijvoorbeeld een ongeldige id opgeeft, kunt u een foutbericht instellen dat aangeeft dat de AI-agent de juiste id van het get_all_lights -eindpunt krijgt. |
Volgende stappen
Nu u weet hoe u een invoegtoepassing maakt, kunt u nu leren hoe u deze kunt gebruiken met uw AI-agent. Afhankelijk van het type functies dat u aan uw invoegtoepassingen hebt toegevoegd, zijn er verschillende patronen die u moet volgen. Raadpleeg het artikel over ophaalfuncties als u de