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에는 두 개의 엔드포인트, 즉 모든 조명을 가져오는 엔드포인트와 조명의 상태를 변경하는 엔드포인트가 포함됩니다. 또한 다음을 제공합니다.
- 엔드포인트 및 해당 매개 변수에 대한 의미 체계 설명
- 매개 변수의 형식
- 예상 응답
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 플러그 인을 추가할 수 있습니다. 또한 플러그 인을 한 번 만들고 여러 커널 인스턴스 또는 에이전트에서 다시 사용할 수 있습니다.
// 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 플러그 인을 추가할 때 EnableDynamicPayload
속성을 OpenApiFunctionExecutionParameters
개체에서 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에 작업 메타데이터를 제공하는 것 외에도 의미 체계 커널은 다음 단계를 수행합니다.
- 스키마를 기반으로 하고 LLM 속성 값에서 제공하는 페이로드를 생성하여 OpenAPI 작업에 대한 LLM 호출을 처리합니다.
- 페이로드가 있는 HTTP 요청을 API로 보냅니다.
동적 페이로드 생성의 제한 사항
동적 페이로드 생성은 비교적 간단한 페이로드 구조를 가진 API에 가장 효과적입니다. 다음 특성을 나타내는 API 페이로드의 경우, 안정적으로 작동하지 않거나 아예 작동하지 않을 수 있습니다.
- 속성의 위치에 관계없이 고유하지 않은 속성 이름을 가진 페이로드입니다. 예를 들어
id
라는 두 개의 속성이 있을 때, 하나는 발신자 개체에 대한 것이고 다른 하나는 수신자 개체에 대한 것입니다 -json { "sender": { "id": ... }, "receiver": { "id": ... }}
- 복합 키워드
oneOf
,anyOf
,allOf
를 사용하는 페이로드 스키마입니다. - 재귀 참조가 포함된 페이로드 스키마. 예:
json { "parent": { "child": { "$ref": "#parent" } } }
고유하지 않은 속성 이름으로 페이로드를 처리하려면 다음 대안을 고려하세요.
- Handling OpenAPI 플러그 인 매개 변수 섹션에 설명된 것과 유사한 메서드를 사용하여 고유하지 않은 각 속성에 고유한 인수 이름을 제공합니다.
- 이름 간격을 지정하는
다음 섹션에 설명된 대로 네임스페이스를 사용하여 이름 충돌을 방지합니다. - 동적 페이로드 생성을 사용하지 않도록 설정하고 페이로드 매개 변수 섹션에 설명된 대로 LLM이 해당 스키마에 따라 페이로드를 만들 수 있도록 허용합니다.
페이로드 스키마가 oneOf
, anyOf
, allOf
의 복합 키워드 또는 재귀 참조를 사용하는 경우, 동적 페이로드 생성을 비활성화하고 페이로드 매개 변수 섹션에 설명된 대로 LLM이 해당 스키마에 기반하여 페이로드를 생성하도록 허용하는 것이 좋습니다.
페이로드 네임스페이싱
페이로드 네임스페이싱은 OpenAPI 플러그인 페이로드에서 고유하지 않은 속성 이름으로 인해 발생할 수 있는 명명 충돌을 방지하는 데 도움이 됩니다.
네임스페이싱이 사용 가능하게 되면, 시맨틱 커널은 확장된 속성 이름을 포함하는 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로 보냅니다.
기본적으로 페이로드 네임스페이스 옵션은 비활성화되어 있습니다. OpenAPI 플러그인을 추가할 때, OpenApiFunctionExecutionParameters
객체에서 EnablePayloadNamespacing
속성을 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
});
페이로드 매개 변수를 사용하도록 설정하면 의미 체계 커널은 LLM에 페이로드 및 content_type 매개 변수에 대한 스키마를 포함하는 작업에 대한 메타데이터를 제공하여 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 호출을 처리하고 LLM에서 제공하는 인수를 페이로드 및 content_type 매개 변수에 사용합니다.
- 제공된 페이로드 및 콘텐츠 형식을 사용하여 HTTP 요청을 API에 보냅니다.
서버 기본 URL
의미 체계 커널 OpenAPI 플러그 인에는 API 요청을 수행할 때 엔드포인트 경로를 앞에 추가하는 데 사용되는 기본 URL이 필요합니다. 이 기본 URL은 OpenAPI 문서에서 지정하거나, URL에서 문서를 로드하여 암시적으로 가져오거나, 커널에 플러그 인을 추가할 때 제공할 수 있습니다.
OpenAPI 문서에 지정된 URL
OpenAPI v2 문서는 schemes
, host
및 basePath
필드를 사용하여 서버 URL을 정의합니다.
{
"swagger": "2.0",
"host": "example.com",
"basePath": "/v1",
"schemes": ["https"]
...
}
Semantic Kernel은 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가 간단할수록 이를 설명하는 데 사용해야 하는 토큰이 줄어들고 AI가 요청을 보내는 데 필요한 토큰이 줄어듭니다. |
문자열 매개변수를 피하십시오 | 가능하면 API에서 문자열 매개 변수를 사용하지 마세요. 대신 정수, 부울 또는 열거형과 같은 보다 구체적인 형식을 사용합니다. 이렇게 하면 AI가 API를 더 잘 이해할 수 있습니다. |
설명에 예제 제공 | 사용자가 Swagger 파일을 사용하는 경우 일반적으로 샘플 요청 및 응답을 포함하는 Swagger UI를 사용하여 API를 테스트할 수 있습니다. AI 에이전트는 이 작업을 수행할 수 없으므로 매개 변수 설명에 예제를 제공하는 것이 좋습니다. |
다른 엔드포인트를 설명에 참조 | 종종 AIS는 유사한 엔드포인트를 혼동합니다. AI가 엔드포인트를 구분하는 데 도움이 되도록 설명에서 다른 엔드포인트를 참조하는 것이 좋습니다. 예를 들어 "이 엔드포인트는 get_all_lights 엔드포인트와 비슷하지만 단일 조명만 반환합니다."라고 말할 수 있습니다. |
유용한 오류 메시지 제공 | OpenAPI 사양 내에 있지는 않지만 AI가 자체 수정하는 데 도움이 되는 오류 메시지를 제공하는 것이 좋습니다. 예를 들어 사용자가 잘못된 ID를 제공하는 경우 AI 에이전트가 get_all_lights 엔드포인트에서 올바른 ID를 가져오도록 제안하는 오류 메시지를 제공하는 것이 좋습니다. |
다음 단계
이제 플러그 인을 만드는 방법을 알게 되었으므로 이제 AI 에이전트와 함께 사용하는 방법을 알아볼 수 있습니다. 플러그인에 추가한 함수 유형에 따라 다른 패턴을 따라야 합니다. 검색 함수에 대해서는 검색 함수 문서를 참조하여
검색 함수 사용하는 방법에 대해 알아봅니다.