共用方式為


從 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
         }
      }
   }
}

此規格提供 AI 所需的一切,以瞭解 API 以及如何與其互動。 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
   }
);

使用語意核心,您可以從各種來源新增 OpenAPI 外掛程式,例如 URL、檔案或數據流。 此外,可以建立外掛程式一次,並在多個核心實例或代理程式之間重複使用。

// 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,並由 Semantic Kernel 用來在 LLM 提供的自變數清單中查閱對應的自變數。 不過,在某些情況下,OpenAPI 外掛程式有多個具有相同名稱的參數。 將此參數元數據提供給 LLM 可能會造成混淆,而可能導致 LLM 無法為函數調用產生正確的自變數。

此外,由於針對每個 OpenAPI 作業建立不允許非唯一參數名稱的核心函式,因此新增這類外掛程式可能會導致某些作業無法使用。 具體而言,將會略過具有非唯一參數名稱的作業,並記錄對應的警告。 即使可以在核心函式中包含多個具有相同名稱的參數,這可能會導致自變數選取程式中模棱兩可。

考慮到這一切,Semantic Kernel 提供了一個解決方案,可用來管理具有非唯一參數名稱的外掛程式。 當變更 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外掛程式規格匯入核心。 Semantic Kernel 會提供 LLM 的自變數名稱,而不是原始名稱,並使用它們來查閱 LLM 所提供的清單中對應的自變數。

請務必注意,自變數名稱不會在呼叫 OpenAPI 作業時取代原始名稱。 在上述範例中,路徑中的「id」參數會被 LLM 所傳回的「lightId」參數的值取代。 這同樣適用於 『id』 標頭參數;LLM 針對 'sessionId' 自變數所傳回的值,將會作為名為 'id' 標頭的值。

處理 OpenAPI 外掛程式承載

OpenAPI 外掛程式可以使用 POST、PUT 或 PATCH 作業來修改系統的狀態。 這些操作通常需要在請求中包含負載。

語意核心提供一些選項來管理 OpenAPI 外掛程式的承載處理,視您的特定案例和 API 需求而定。

動態承載建構

動態承載建構允許根據 LLM 所提供的承載架構和自變數,動態建立 OpenAPI 作業的承載。 此功能預設為啟用,但在新增 OpenAPI 外掛程式時,可以在 OpenApiFunctionExecutionParameters 物件中,將 EnableDynamicPayload 屬性設定為 false 來將其停用。

例如,請考慮change_light_state操作,其所需承載結構如下所示:

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

若要變更光線的狀態,並取得承載屬性的值,Semantic Kernel 會為作業提供 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 之外,Semantic Kernel 還會執行下列步驟:

  1. 根據架構和 LLM 屬性值,處理對 OpenAPI 作業的 LLM 請求並建構數據承載。
  2. 使用有效負載將 HTTP 要求傳送至 API。

動態承載建構的限制

動態承載建構最適合具有相對簡單承載結構的 API。 對於呈現下列特性的 API 有效負載,它可能無法可靠運作,甚至完全無法運作。

  • 不論屬性的位置為何,具有非唯一屬性名稱的承載。 例如,名為 id的兩個屬性,一個用於傳送者物件,另一個用於接收者物件 - json { "sender": { "id": ... }, "receiver": { "id": ... }}
  • 使用任何復合關鍵字的承載架構 oneOfanyOfallOf
  • 具有遞歸參考的負載結構。 例如,json { "parent": { "child": { "$ref": "#parent" } } }

若要處理具有非唯一屬性名稱的承載,請考慮下列替代方案:

  • 針對每個非唯一屬性提供唯一的自變數名稱,其使用與 處理 OpenAPI 外掛程式參數 一節中所述的方法類似。
  • 使用命名空間來避免命名衝突,如下一節 Payload namespacing中所述。
  • 停用動態承載建構,並允許 LLM 根據其架構建立承載,如 承載參數 一節所述。

如果承載架構使用任何 oneOfanyOfallOf 復合關鍵詞或遞歸參考,請考慮停用動態承載建構,並允許 LLM 根據其架構建立承載,如 承載參數 一節所述。

負載命名空間

Payload namepacing 有助於防止因 OpenAPI 外掛程式承載中非唯一屬性名稱而發生的命名衝突。

啟用 namepacing 時,Semantic Kernel 會提供 LLM 與包含增強屬性名稱的 OpenAPI 作業元數據。 這些擴增名稱是透過將父屬性名稱作為前置詞,以點分隔附加到子屬性名稱上來建立的。

例如,如果 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 提供具有增強屬性名稱的作業元數據之外,Semantic Kernel 還會執行下列步驟:

  1. 處理 OpenAPI 操作的 LLM 呼叫,查找 LLM 為承載中所有屬性提供的對應參數,使用增強後的屬性名稱,並在必要時回退到原始屬性名稱。
  2. 使用原始屬性名稱作為索引鍵和解析的自變數做為值來建構承載。
  3. 使用建構的承載將 HTTP 要求傳送至 API。

根據預設,會停用承載命名空間選項。 新增 OpenAPI 外掛程式時,可以將 EnablePayloadNamespacing 屬性設定為 true,以在 OpenApiFunctionExecutionParameters 物件中啟用:

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 外掛程式時,將 EnableDynamicPayload 屬性設定為 OpenApiFunctionExecutionParameters 物件中的 false

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

啟用承載參數時,Semantic Kernel 會提供包含承載架構和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 之外,Semantic Kernel 執行下列步驟:

  1. 處理 OpenAPI 作業的 LLM 呼叫,並使用 LLM 針對承載和content_type參數所提供的自變數。
  2. 使用提供的承載和內容類型,將 HTTP 要求傳送至 API。

伺服器基底 URL

語意核心 OpenAPI 外掛程式需要基底 URL,此 URL 可用來在提出 API 要求時加上端點路徑。 您可以在 OpenAPI 檔中指定這個基底 URL,透過從 URL 載入檔以隱含方式取得,或在將外掛程式新增至核心時提供。

OpenAPI 檔中指定的 URL

OpenAPI v2 檔案會使用 schemeshostbasePath 欄位來定義伺服器 URL:

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

語意核心會將伺服器 URL 建構為 https://example.com/v1

相反地,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"
            }
         }
      }
   ],
   ...  
}

在這種情況下,Semantic Kernel 會將變數佔位符替換為引數提供的值或預設值(如果未提供引數),從而生成 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 或載入檔案的伺服器 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 要求之前叫用。 此回呼函式可以存取 HttpRequestMessage 物件,代表將傳送至 API 的 HTTP 要求。 您可以使用這個物件,將驗證認證新增至要求。 可以根據 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 回應可以讀取為字串。

不過,在某些情況下,內建機制不足以滿足您的需求。 例如,當回應是需要讀取為數據流的大型 JSON 物件或映射時,才能作為另一個 API 的輸入提供。 在這種情況下,將響應內容讀取為字串或位元組數位,然後將它轉換回數據流可能會沒有效率,而且可能會導致效能問題。 為了解決此問題,Semantic Kernel 提供自定義內容讀取器,從而允許自定義化的回應內容讀取。

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,表示應該使用預設的內容讀取器。

檔案和操作元數據

語意核心會擷取 OpenAPI 檔和作業元數據,包括 API 資訊、安全性架構、作業標識碼、描述、參數元數據等等。 它會透過 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 方法會從函式內容讀取作業元數據,並擷取作業的安全性需求,以判斷驗證配置。 然後,它會從應用程式識別提供者擷取配置和範圍的 API 金鑰,並將它新增至要求標頭或查詢參數。

下表列出 KernelFunction.Metadata.AdditionalParameters 字典中可用的元資料:

鑰匙 類型 描述
資訊 RestApiInfo API 資訊,包括標題、描述和版本。
操作 RestApiOperation API 作業詳細數據,例如標識碼、描述、路徑、方法等。
安全 IList<RestApiSecurityRequirement> API 安全性需求 - 類型、名稱、in 等等。

新增 OpenAPI 外掛程式的秘訣和訣竅

由於 OpenAPI 規格通常是針對人類所設計,因此您可能需要進行一些變更,讓 AI 更容易瞭解。 以下是協助您執行此動作的一些秘訣和訣竅:

建議 描述
對 API 規格進行版本控制 請考慮提交和版本管理您的 Swagger 檔案,而不是指向實時 API 規格。 這將允許您的 AI 研究人員測試並修改 AI 代理程式所使用的 API 規格,而不會影響實際運行的 API,反過來也不會影響實驗環境下的 API。
限制端點數目 嘗試限制 API 中的端點數目。 將類似的功能合併到具有選擇性參數的單一端點,以減少複雜性。
針對端點和參數使用描述性名稱 請確定端點和參數的名稱具有描述性和自我說明性。 這有助於 AI 瞭解其用途,而不需要大量的說明。
使用一致的命名慣例 在整個 API 中維護一致的命名慣例。 這可減少混淆,並協助 AI 更輕鬆地學習和預測 API 的結構。
簡化 API 規格 OpenAPI 規格通常非常詳細,並包含許多 AI 代理程式不需要的信息來協助使用者。 API 越簡單,您需要花費較少的令牌來描述 API,而 AI 需要傳送要求給它的令牌就越少。
避免字串參數 可能的話,請避免在 API 中使用字串參數。 請改用更特定的類型,例如整數、布爾值或列舉。 這有助於 AI 進一步瞭解 API。
提供描述中的範例 當人類使用 Swagger 檔案時,他們通常可以使用 Swagger UI 來測試 API,其中包括範例要求和回應。 由於 AI 代理程式無法執行此動作,請考慮在參數的描述中提供範例。
在描述中參考其他端點 通常,AIS 會混淆類似的端點。 為了協助 AI 區分端點,請考慮參考描述中的其他端點。 例如,您可以說「此端點類似於 get_all_lights 端點,但只會傳回單一光線」。
提供實用的錯誤訊息 雖然不在 OpenAPI 規格內,但請考慮提供可協助 AI 自我更正的錯誤訊息。 例如,如果使用者提供無效的標識碼,請考慮提供錯誤訊息,建議 AI 代理程式從 get_all_lights 端點取得正確的識別符。

後續步驟

既然您已瞭解如何建立外掛程式,您現在可以瞭解如何將其與 AI 代理程式搭配使用。 根據您新增至外掛程式的函式類型而定,您應該遵循不同的模式。 如需擷取函式的相關資訊,請參閱使用擷取函式的文章 。 如需任務自動化功能,請參閱 使用任務自動化功能 文章。