OpenAPI 仕様からプラグインを追加する
多くの場合、企業には、実際の作業を実行する API のセットが既にあります。 これらは、人間が操作する他のオートメーション サービスや電源フロントエンド アプリケーションで使用できます。 セマンティック カーネルでは、これらのプラグインとまったく同じ API を追加して、エージェントで使用することもできます。
OpenAPI 仕様の例
たとえば、電球の状態を変更できる API を使用します。 この API の OpenAPI 仕様 (Swagger 仕様または Swagger のみ) は次のようになります。
{
"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
}
}
}
}
この仕様では、API とその操作方法を理解するために AI に必要なすべてのものが提供されます。 API には、2 つのエンドポイントが含まれています。1 つはすべてのライトを取得し、もう 1 つはライトの状態を変更するエンドポイントです。 また、次の機能も提供します。
- エンドポイントとそのパラメーターのセマンティックの説明
- パラメーターの型
- 予想される応答
AI エージェントはこの仕様を理解できるため、エージェントにプラグインとして追加できます。
セマンティック カーネルは OpenAPI バージョン 2.0 と 3.0 をサポートしており、バージョン 3.0 にダウングレードすることでバージョン 3.1 の仕様に対応することを目的としています。
ヒント
既存の OpenAPI 仕様がある場合は、AI で理解しやすくするために変更が必要になる場合があります。 たとえば、説明にガイダンスを提供することが必要な場合があります。 OpenAPI 仕様を AI 対応にする方法に関するその他のヒントについては、「OpenAPI プラグインを追加するためのヒントとテクニック」を参照してください。
OpenAPI プラグインの追加
数行のコードを使用して、OpenAPI プラグインをエージェントに追加できます。 次のコード スニペットは、上記の OpenAPI 仕様からライト プラグインを追加する方法を示しています。
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
}
);
セマンティック カーネルを使用すると、URL、ファイル、ストリームなど、さまざまなソースから OpenAPI プラグインを追加できます。 さらに、プラグインを 1 回作成し、複数のカーネル インスタンスまたはエージェントで再利用できます。
// 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();
その後、ネイティブ プラグインであるかのように、エージェントでプラグインを使用できます。
OpenAPI プラグイン パラメーターの処理
セマンティック カーネルは、OpenAPI ドキュメントで定義されているすべてのパラメーターの名前、説明、型、スキーマなどのメタデータを自動的に抽出します。
このメタデータは、各 OpenAPI 操作の KernelFunction.Metadata.Parameters
プロパティに格納され、関数呼び出しの正しい引数を生成するためのプロンプトと共に LLM に提供されます。
既定では、元のパラメーター名は LLM に提供され、セマンティック カーネルによって LLM によって提供される引数の一覧で対応する引数を検索するために使用されます。 ただし、OpenAPI プラグインに同じ名前の複数のパラメーターがある場合があります。 このパラメーター メタデータを LLM に指定すると混乱が生じ、LLM が関数呼び出しの正しい引数を生成できなくなる可能性があります。
さらに、OpenAPI 操作ごとに一意でないパラメーター名を許可しないカーネル関数が作成されるため、このようなプラグインを追加すると、一部の操作が使用できなくなる可能性があります。 具体的には、一意でないパラメーター名を持つ操作はスキップされ、対応する警告がログに記録されます。 カーネル関数に同じ名前の複数のパラメーターを含めることができる場合でも、引数の選択プロセスがあいまいになる可能性があります。
これらすべてを考慮すると、セマンティック カーネルには、一意でないパラメーター名を持つプラグインを管理するためのソリューションが用意されています。 このソリューションは、サードパーティのサービスかレガシ システムかに関係なく、API 自体を変更できない場合に特に便利です。
次のコード スニペットは、OpenAPI プラグインで一意でないパラメーター名を処理する方法を示しています。 change_light_state操作に、既存の "id" パラメーターと同じ名前の追加パラメーター (具体的には、ライトの ID を表す現在の "id" に加えてセッション ID を表す) がある場合は、次のように処理できます。
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);
このコード スニペットでは、OpenApiDocumentParser
クラスを使用して OpenAPI ドキュメントを解析し、ドキュメントを表す RestApiSpecification
モデル オブジェクトにアクセスします。 パラメーターに引数名を割り当て、変換された OpenAPI プラグイン仕様をカーネルにインポートします。 セマンティック カーネルは、元の名前ではなく LLM に引数名を提供し、それらを使用して LLM によって提供されるリスト内の対応する引数を検索します。
OpenAPI 操作を呼び出すときに、引数名は元の名前の代わりに使用されないことに注意してください。 上記の例では、パスの 'id' パラメーターは、'lightId' 引数の LLM によって返される値に置き換えられます。 同じことが 'id' ヘッダー パラメーターにも適用されます。'sessionId' 引数の LLM によって返される値は、'id' という名前のヘッダーの値として使用されます。
OpenAPI プラグインペイロードの処理
OpenAPI プラグインは、POST、PUT、または PATCH 操作を使用してシステムの状態を変更できます。 多くの場合、これらの操作では、要求にペイロードを含める必要があります。
セマンティック カーネルには、特定のシナリオと API の要件に応じて、OpenAPI プラグインのペイロード処理を管理するためのオプションがいくつか用意されています。
動的ペイロードの構築
動的ペイロード構築を使用すると、LLM によって提供されるペイロード スキーマと引数に基づいて、OpenAPI 操作のペイロードを動的に作成できます。
この機能は既定で有効になっていますが、OpenAPI プラグインを追加するときに、OpenApiFunctionExecutionParameters
オブジェクトで EnableDynamicPayload
プロパティを false
に設定することで無効にすることができます。
たとえば、次のように構成されたペイロードが必要なchange_light_state操作について考えてみましょう。
{
"isOn": true,
"hexColor": "#FF0000",
"brightness": 100,
"fadeDurationInMilliseconds": 500,
"scheduledTime": "2023-07-12T12:00:00Z"
}
ライトの状態を変更し、ペイロード プロパティの値を取得するために、セマンティック カーネルは、操作のメタデータを LLM に提供して、操作を推論できるようにします。
{
"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"}},
]
}
操作メタデータを LLM に提供するだけでなく、セマンティック カーネルは次の手順を実行します。
- OpenAPI 操作に対する LLM 呼び出しを処理し、スキーマに基づいてペイロードを構築し、LLM プロパティ値によって提供します。
- ペイロードを含む HTTP 要求を API に送信します。
動的ペイロード構築の制限事項
動的ペイロードの構築は、比較的単純なペイロード構造を持つ API に最も効果的です。 次の特性を示す API ペイロードでは、確実に動作しないか、まったく動作しない可能性があります。
- プロパティの場所に関係なく、一意でないプロパティ名を持つペイロード。 たとえば、
id
という名前の 2 つのプロパティ (送信側オブジェクト用と受信側オブジェクト用) -json { "sender": { "id": ... }, "receiver": { "id": ... }}
- 複合キーワード
oneOf
、anyOf
、allOf
のいずれかを使用するペイロード スキーマ。 - 再帰的参照を持つペイロード スキーマ。 例:
json { "parent": { "child": { "$ref": "#parent" } } }
一意でないプロパティ名を持つペイロードを処理するには、次の方法を検討してください。
- OpenAPI プラグイン パラメーターの処理 セクションで説明されているのと同様のメソッドを使用して、一意でないプロパティごとに一意の引数名
指定します。 - Payload namespacingの次のセクションで説明されているように、名前空間を使用して名前の競合を回避します。
- 「ペイロード パラメーターの」セクションで説明されているように、動的ペイロードの構築を無効にし、LLM がスキーマに基づいてペイロードを作成できるようにします。
ペイロード スキーマで oneOf
、anyOf
、複合キーワード、または再帰参照のいずれかを使用する場合は、「ペイロード パラメーター allOf
」セクションで説明されているように、動的ペイロード構築を無効にし、LLM がスキーマに基づいてペイロードを作成することを検討してください。
ペイロードの名前空間分割
ペイロードの名前の指定は、OpenAPI プラグインペイロードで一意でないプロパティ名が原因で発生する可能性がある名前付けの競合を防ぐのに役立ちます。
namespacing が有効になっている場合、セマンティック カーネルは拡張プロパティ名を含む OpenAPI 操作メタデータを LLM に提供します。 これらの拡張名は、親プロパティ名をプレフィックスとしてドットで区切って子プロパティ名に追加することによって作成されます。
たとえば、change_light_state操作に、scheduledTime
プロパティを持つ入れ子になった offTimer
オブジェクトが含まれていた場合は、次のようになります。
{
"isOn": true,
"hexColor": "#FF0000",
"brightness": 100,
"fadeDurationInMilliseconds": 500,
"scheduledTime": "2023-07-12T12:00:00Z",
"offTimer": {
"scheduledTime": "2023-07-12T12:00:00Z"
}
}
セマンティック カーネルは、次のプロパティ名を含む操作のメタデータを LLM に提供します。
{
"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"}},
]
}
拡張プロパティ名を持つ操作メタデータを LLM に提供するだけでなく、セマンティック カーネルは次の手順を実行します。
- OpenAPI 操作に対する LLM 呼び出しを処理し、拡張プロパティ名を使用し、必要に応じて元のプロパティ名にフォールバックして、ペイロード内のすべてのプロパティについて LLM によって提供される引数の中から対応する引数を検索します。
- 元のプロパティ名をキーとして使用し、解決された引数を値として使用してペイロードを構築します。
- 構築されたペイロードを含む HTTP 要求を API に送信します。
既定では、ペイロードの namespacing オプションは無効になっています。 これは、OpenAPI プラグインを追加するときに、EnablePayloadNamespacing
プロパティを OpenApiFunctionExecutionParameters
オブジェクトで true
に設定することで有効にすることができます。
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
});
手記
EnablePayloadNamespace
オプションは、動的ペイロードの構築も有効になっている場合にのみ有効になります。それ以外の場合、効果はありません。
ペイロード パラメーター
セマンティック カーネルは、ペイロード パラメーターを使用して LLM によって作成されたペイロードを操作できます。 これは、ペイロード スキーマが複雑で、一意でないプロパティ名が含まれている場合に便利です。これにより、セマンティック カーネルがペイロードを動的に構築することはできません。
このような場合は、スキーマを理解し、有効なペイロードを構築する LLM の機能に依存します。
gpt-4o
などの最近のモデルは、有効な JSON ペイロードの生成に有効です。
ペイロード パラメーターを有効にするには、OpenAPI プラグインを追加するときに、OpenApiFunctionExecutionParameters
オブジェクトで EnableDynamicPayload
プロパティを false
に設定します。
await kernel.ImportPluginFromOpenApiAsync(
pluginName: "lights",
uri: new Uri("https://example.com/v1/swagger.json"),
executionParameters: new OpenApiFunctionExecutionParameters()
{
EnableDynamicPayload = false, // Disable dynamic payload construction
});
ペイロード パラメーターが有効になっている場合、セマンティック カーネルは、ペイロードとcontent_typeパラメーターのスキーマを含む操作のメタデータを LLM に提供し、LLM がペイロード構造を理解し、それに応じて構築できるようにします。
{
"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."
}
}
}
操作メタデータにペイロードおよびコンテンツ タイプ パラメーターのスキーマを LLM に提供することに加えて、セマンティック カーネルは次の手順を実行します。
- OpenAPI 操作に対する LLM 呼び出しを処理し、ペイロードとcontent_typeパラメーターに対して LLM によって提供される引数を使用します。
- 提供されたペイロードとコンテンツ タイプを使用して、API に HTTP 要求を送信します。
サーバーベースの URL
セマンティック カーネル OpenAPI プラグインにはベース URL が必要です。ベース URL は、API 要求を行うときにエンドポイント パスの先頭に追加するために使用されます。 このベース URL は、OpenAPI ドキュメントで指定することも、URL からドキュメントを読み込んで暗黙的に取得することも、プラグインをカーネルに追加するときに指定することもできます。
OpenAPI ドキュメントで指定された URL
OpenAPI v2 ドキュメントでは、schemes
、host
、および basePath
フィールドを使用してサーバー URL を定義します。
{
"swagger": "2.0",
"host": "example.com",
"basePath": "/v1",
"schemes": ["https"]
...
}
セマンティック カーネルは、https://example.com/v1
としてサーバー URL を構築します。
これに対し、OpenAPI v3 ドキュメントでは、servers
フィールドを使用してサーバー URL を定義します。
{
"openapi": "3.0.1",
"servers": [
{
"url": "https://example.com/v1"
}
],
...
}
セマンティック カーネルは、ベース URL としてドキュメントで指定された最初のサーバー URL を使用します: https://example.com/v1
.
OpenAPI v3 では、中かっこで示される変数を使用して、パラメーター化されたサーバー URL も使用できます。
{
"openapi": "3.0.1",
"servers": [
{
"url": "https://{environment}.example.com/v1",
"variables": {
"environment": {
"default": "prod"
}
}
}
],
...
}
この場合、セマンティック カーネルは変数プレースホルダーを変数の引数として指定された値に置き換えます。引数が指定されていない場合は既定値に置き換えられ、URL: https://prod.example.com/v1
になります。
OpenAPI ドキュメントでサーバー URL が指定されていない場合、セマンティック カーネルは OpenAPI ドキュメントの読み込み元サーバーのベース URL を使用します。
await kernel.ImportPluginFromOpenApiAsync(pluginName: "lights", uri: new Uri("https://api-host.com/swagger.json"));
ベース URL が https://api-host.com
されます。
サーバー URL のオーバーライド
場合によっては、OpenAPI ドキュメントで指定されたサーバー URL、またはドキュメントの読み込み元のサーバーが、OpenAPI プラグインに関連するユース ケースに適していない場合があります。
セマンティック カーネルを使用すると、OpenAPI プラグインをカーネルに追加するときにカスタム ベース URL を指定することで、サーバー URL をオーバーライドできます。
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")
});
この例では、ベース URL が https://custom-server.com/v1
され、OpenAPI ドキュメントで指定されたサーバー URL と、ドキュメントの読み込み元のサーバー URL がオーバーライドされます。
認証
ほとんどの REST API では、リソースにアクセスするために認証が必要です。 セマンティック カーネルには、OpenAPI プラグインに必要なさまざまな認証方法を統合できるメカニズムが用意されています。
このメカニズムは、各 API 要求の前に呼び出される認証コールバック関数に依存します。 このコールバック関数は、API に送信される HTTP 要求を表す HttpRequestMessage オブジェクトにアクセスできます。 このオブジェクトを使用して、要求に認証資格情報を追加できます。 資格情報は、API で使用される認証方法に応じて、ヘッダー、クエリ パラメーター、または要求本文に追加できます。
OpenAPI プラグインをカーネルに追加するときに、このコールバック関数を登録する必要があります。 次のコード スニペットは、要求を認証するために登録する方法を示しています。
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
});
API でサポートされている認証スキーマの詳細への動的アクセスを必要とするより複雑な認証シナリオでは、ドキュメントと操作のメタデータを使用してこの情報を取得できます。 詳細については、「ドキュメントと操作のメタデータの
応答コンテンツの読み取りカスタマイズ
セマンティック カーネルには、OpenAPI プラグインから HTTP 応答の内容を読み取り、適切な .NET データ型に変換するためのメカニズムが組み込まれています。 たとえば、画像の応答はバイト配列として読み取ることができますが、JSON または XML 応答は文字列として読み取ることができます。
ただし、組み込みのメカニズムがニーズに対して不十分な場合があります。 たとえば、応答が、別の API への入力として提供するためにストリームとして読み取る必要がある大きな JSON オブジェクトまたはイメージである場合です。 このような場合、応答コンテンツを文字列またはバイト配列として読み取り、ストリームに変換すると非効率的になり、パフォーマンスの問題が発生する可能性があります。 これに対処するために、セマンティック カーネルでは、カスタム コンテンツ リーダーを提供することで、応答コンテンツの読み取りのカスタマイズが可能になります。
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
});
この例では、ReadHttpResponseContentAsync
メソッドは、コンテンツ タイプが application/json
されたとき、または要求にカスタム ヘッダー x-stream
が含まれている場合に、HTTP 応答コンテンツをストリームとして読み取ります。 このメソッドは、既定のコンテンツ リーダーを使用する必要があることを示す、他のコンテンツ タイプの null
を返します。
ドキュメントと操作のメタデータ
セマンティック カーネルは、API 情報、セキュリティ スキーマ、操作 ID、説明、パラメーター メタデータなど、OpenAPI ドキュメントと操作メタデータを抽出します。
KernelFunction.Metadata.AdditionalParameters
プロパティを使用して、この情報へのアクセスを提供します。 このメタデータは、認証目的など、API または操作に関する追加情報が必要なシナリオで役立ちます。
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");
この例では、AuthenticateRequestAsyncCallbackAsync
メソッドは関数コンテキストから操作メタデータを読み取り、操作のセキュリティ要件を抽出して認証スキームを決定します。 その後、アプリ ID プロバイダーからスキームとスコープの API キーを取得し、要求ヘッダーまたはクエリ パラメーターに追加します。
次の表に、KernelFunction.Metadata.AdditionalParameters
ディクショナリで使用できるメタデータを示します。
鍵 | 種類 | 説明 |
---|---|---|
情報 | RestApiInfo |
タイトル、説明、バージョンなどの API 情報。 |
操作 | RestApiOperation |
ID、説明、パス、メソッドなどの API 操作の詳細。 |
安全 | IList<RestApiSecurityRequirement > |
API のセキュリティ要件 - 型、名前、入力など |
OpenAPI プラグインを追加するためのヒントとテクニック
OpenAPI 仕様は通常、人間向けに設計されているため、AI が理解しやすくするためにいくつかの変更が必要になる場合があります。 これを行う際に役立つヒントとテクニックを次に示します。
勧告 | 説明 |
---|---|
バージョンによって API 仕様が制御 | ライブ API 仕様を指す代わりに、Swagger ファイルのチェックインとバージョン管理を検討してください。 これにより、AI 研究者は、ライブ API に影響を与えずに、AI エージェントによって使用される API 仕様をテスト (および変更) できるようになります。 |
エンドポイントの数を制限する | API 内のエンドポイントの数を制限してみてください。 複雑さを軽減するために、省略可能なパラメーターを使用して、同様の機能を単一のエンドポイントに統合します。 |
エンドポイントとパラメーターにわかりやすい名前を使用 | エンドポイントとパラメーターの名前がわかりやすいわかりやすいものになっていることを確認します。 これは、AI が広範な説明を必要とせずに目的を理解するのに役立ちます。 |
一貫性のある名前付け規則を使用する | API 全体で一貫した名前付け規則を維持します。 これにより、混乱が軽減され、AI が API の構造をより簡単に学習して予測するのに役立ちます。 |
API 仕様を簡略化する | 多くの場合、OpenAPI 仕様は非常に詳細であり、AI エージェントがユーザーを支援するために必要ではない多くの情報を含みます。 API が単純な場合、API の記述に費やすトークンの数が少ないほど、AI が要求を送信するために必要なトークンも少なくなります。 |
文字列パラメーターの を避ける | 可能な場合は、API で文字列パラメーターを使用しないでください。 代わりに、整数、ブール値、列挙型などのより具体的な型を使用します。 これは、AI が API をよりよく理解するのに役立ちます。 |
説明に例を提供する | 人間が Swagger ファイルを使用する場合、通常、サンプルの要求と応答を含む Swagger UI を使用して API をテストできます。 AI エージェントではこれを行えないので、パラメーターの説明に例を提供することを検討してください。 |
説明 の他のエンドポイントを参照する | 多くの場合、AI は同様のエンドポイントを混乱させます。 AI がエンドポイントを区別できるように、説明の他のエンドポイントを参照することを検討してください。 たとえば、"このエンドポイントは get_all_lights エンドポイントに似ていますが、単一のライトのみを返します" と言います。 |
役立つエラーメッセージを | OpenAPI 仕様には含まれていませんが、AI の自己修正に役立つエラー メッセージを提供することを検討してください。 たとえば、ユーザーが無効な ID を指定した場合は、AI エージェントが get_all_lights エンドポイントから正しい ID を取得することを提案するエラー メッセージを提供することを検討してください。 |
次の手順
プラグインを作成する方法がわかったら、AI エージェントでプラグインを使用する方法を学習できるようになりました。 プラグインに追加した関数の種類に応じて、従う必要があるパターンが異なります。 取得関数については、取得関数を使用した 記事を参照してください。 タスク自動化関数については、タスク自動化関数を使用する に関する記事 参照してください。
取得関数 の使用について説明します