Bagikan melalui


Menambahkan plugin dari spesifikasi OpenAPI

Seringkali di perusahaan, Anda sudah memiliki sekumpulan API yang melakukan pekerjaan nyata. Ini dapat digunakan oleh layanan otomatisasi lain atau aplikasi front-end yang bertenaga yang digunakan manusia untuk berinteraksi. Di Kernel Semantik, Anda dapat menambahkan API yang sama persis ini sebagai plugin sehingga agen Anda juga dapat menggunakannya.

Contoh spesifikasi OpenAPI

Ambil contoh API yang memungkinkan Anda mengubah status bola lampu. Spesifikasi OpenAPI, yang dikenal sebagai Spesifikasi Swagger, atau hanya Swagger, untuk API ini mungkin terlihat seperti ini:

{
   "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
         }
      }
   }
}

Spesifikasi ini menyediakan semua yang diperlukan oleh AI untuk memahami API dan cara berinteraksi dengannya. API mencakup dua titik akhir: satu untuk mendapatkan semua lampu dan yang lain untuk mengubah status cahaya. Ini juga menyediakan hal-hal berikut:

  • Deskripsi semantik untuk titik akhir dan parameternya
  • Jenis parameter
  • Respons yang diharapkan

Karena agen AI dapat memahami spesifikasi ini, Anda dapat menambahkannya sebagai plugin ke agen.

Semantic Kernel mendukung OpenAPI versi 2.0 dan 3.0, dan bertujuan untuk mengakomodasi spesifikasi versi 3.1 dengan menurunkannya ke versi 3.0.

Saran

Jika Anda memiliki spesifikasi OpenAPI yang ada, Anda mungkin perlu membuat perubahan untuk membuatnya lebih mudah bagi AI untuk memahaminya. Misalnya, Anda mungkin perlu memberikan panduan dalam deskripsi. Untuk tips selengkapnya tentang cara membuat spesifikasi OpenAPI Anda ramah AI, lihat Tips dan trik untuk menambahkan plugin OpenAPI.

Menambahkan plugin OpenAPI

Dengan beberapa baris kode, Anda dapat menambahkan plugin OpenAPI ke agen Anda. Cuplikan kode berikut menunjukkan cara menambahkan plugin lampu dari spesifikasi OpenAPI di atas:

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
   }
);

Dengan Semantic Kernel, Anda dapat menambahkan plugin OpenAPI dari berbagai sumber, seperti URL, file, atau streaming. Selain itu, plugin dapat dibuat sekali dan digunakan kembali di beberapa instans atau agen kernel.

// 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();

Setelah itu, Anda dapat menggunakan plugin di agen Anda seolah-olah itu adalah plugin asli.

Mengelola parameter dari plugin OpenAPI

Kernel Semantik secara otomatis mengekstrak metadata - seperti nama, deskripsi, jenis, dan skema untuk semua parameter yang ditentukan dalam dokumen OpenAPI. Metadata ini disimpan di KernelFunction.Metadata.Parameters properti untuk setiap operasi OpenAPI dan disediakan untuk LLM bersama dengan perintah untuk menghasilkan argumen yang benar untuk panggilan fungsi.

Secara default, nama parameter asli disediakan untuk LLM dan digunakan oleh Semantic Kernel untuk mencari argumen yang sesuai dalam daftar argumen yang disediakan oleh LLM. Namun, mungkin ada kasus di mana plugin OpenAPI memiliki beberapa parameter dengan nama yang sama. Menyediakan metadata parameter ini ke LLM dapat membuat kebingungan, berpotensi mencegah LLM menghasilkan argumen yang benar untuk panggilan fungsi.

Selain itu, karena fungsi kernel yang tidak memungkinkan nama parameter non-unik dibuat untuk setiap operasi OpenAPI, menambahkan plugin tersebut dapat mengakibatkan beberapa operasi menjadi tidak tersedia untuk digunakan. Secara khusus, operasi dengan nama parameter non-unik akan dilewati, dan peringatan yang sesuai akan dicatat. Bahkan jika dimungkinkan untuk menyertakan beberapa parameter dengan nama yang sama dalam fungsi kernel, ini dapat menyebabkan ambiguitas dalam proses pemilihan argumen.

Mempertimbangkan semua ini, Semantic Kernel menawarkan solusi untuk mengelola plugin dengan nama parameter yang tidak unik. Solusi ini sangat berguna ketika mengubah API itu sendiri tidak layak, baik karena itu menjadi layanan pihak ketiga atau sistem warisan.

Cuplikan kode berikut menunjukkan cara menangani nama parameter non-unik dalam plugin OpenAPI. Jika operasi change_light_state memiliki parameter tambahan dengan nama yang sama dengan parameter "id" yang ada - khususnya, untuk mewakili ID sesi selain "id" saat ini yang mewakili ID cahaya - itu dapat ditangani seperti yang ditunjukkan di bawah ini:

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);

Cuplikan kode ini menggunakan OpenApiDocumentParser kelas untuk mengurai dokumen OpenAPI dan mengakses RestApiSpecification objek model yang mewakili dokumen. Ini menetapkan nama argumen ke parameter dan mengimpor spesifikasi plugin OpenAPI yang diubah ke kernel. Kernel Semantik menyediakan nama argumen ke LLM alih-alih nama asli dan menggunakannya untuk mencari argumen yang sesuai dalam daftar yang disediakan oleh LLM.

Penting untuk dicatat bahwa nama argumen tidak digunakan sebagai pengganti nama asli saat memanggil operasi OpenAPI. Dalam contoh di atas, parameter 'id' di jalur akan digantikan oleh nilai yang dikembalikan oleh LLM untuk argumen 'lightId'. Hal yang sama berlaku untuk parameter header 'id'; nilai yang dikembalikan oleh LLM untuk argumen 'sessionId' akan digunakan sebagai nilai untuk header bernama 'id'.

Menangani muatan plugin OpenAPI

Plugin OpenAPI dapat memodifikasi status sistem menggunakan operasi POST, PUT, atau PATCH. Operasi ini sering memerlukan payload untuk disertakan dengan permintaan.

Semantic Kernel menawarkan beberapa opsi untuk mengelola penanganan payload untuk plugin OpenAPI, tergantung pada skenario spesifik dan persyaratan API Anda.

Konstruksi beban muatan dinamis

Konstruksi payload dinamis memungkinkan payload operasi OpenAPI dibuat secara dinamis berdasarkan skema payload dan argumen yang disediakan oleh LLM. Fitur ini diaktifkan secara default tetapi dapat dinonaktifkan dengan mengatur EnableDynamicPayload properti ke falseOpenApiFunctionExecutionParameters dalam objek saat menambahkan plugin OpenAPI.

Misalnya, pertimbangkan operasi change_light_state, yang memerlukan payload yang terstruktur sebagai berikut:

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

Untuk mengubah status cahaya dan mendapatkan nilai untuk properti payload, Semantic Kernel menyediakan metadata LLM untuk operasi sehingga dapat beralasan tentang hal itu:

{
    "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"}},
    ]
}

Selain memberikan metadata operasi ke LLM, Semantic Kernel akan melakukan langkah-langkah berikut:

  1. Tangani panggilan LLM ke operasi OpenAPI, buat payload berdasarkan skema dan yang ditentukan oleh nilai properti LLM.
  2. Kirim permintaan HTTP dengan payload ke API.

Keterbatasan konstruksi payload dinamis

Konstruksi payload dinamis paling efektif untuk API dengan struktur payload yang relatif sederhana. Mungkin tidak akan bekerja dengan andal atau bahkan tidak berfungsi sama sekali, untuk payload API yang menunjukkan karakteristik berikut:

  • Muatan dengan nama properti yang tidak unik, terlepas dari lokasi properti tersebut. Misalnya, dua properti bernama id, satu untuk objek pengirim dan satu lagi untuk objek penerima - json { "sender": { "id": ... }, "receiver": { "id": ... }}
  • Skema payload yang menggunakan salah satu kata kunci komposit oneOf, anyOf, allOf.
  • Skema payload dengan referensi rekursif. Misalnya, json { "parent": { "child": { "$ref": "#parent" } } }

Untuk menangani payload dengan nama properti yang tidak unik, pertimbangkan alternatif berikut:

  • Berikan nama argumen unik untuk setiap properti non-unik, menggunakan metode yang mirip dengan yang dijelaskan di bagian Menangani parameter plugin OpenAPI .
  • Gunakan namespace untuk menghindari konflik penamaan, seperti yang diuraikan di bagian berikutnya tentang Payload namespacing.
  • Nonaktifkan konstruksi payload dinamis dan izinkan LLM untuk membuat payload berdasarkan skemanya, seperti yang dijelaskan di bagian Parameter payload .

Jika skema payload menggunakan salah satu kata kunci komposit oneOf, anyOf, allOf atau referensi rekursif, pertimbangkan untuk menonaktifkan pembuatan payload dinamis dan memungkinkan LLM membuat payload berdasarkan skemanya, seperti yang dijelaskan di bagian Parameter payload.

Catatan tentang oneOf dan anyOf Kata Kunci

Kata kunci anyOf dan oneOf mengasumsikan bahwa payload dapat terdiri dari properti yang ditentukan oleh beberapa skema. Kata anyOf kunci memungkinkan payload untuk menyertakan properti yang ditentukan dalam satu atau beberapa skema, sambil oneOf membatasi payload untuk berisi properti hanya dari satu skema di antara banyak yang disediakan. Untuk informasi selengkapnya, Anda dapat merujuk ke dokumentasi Swagger di oneOf dan anyOf.

Dengan kata kunci anyOf dan oneOf, yang menawarkan alternatif untuk struktur payload, tidak mungkin untuk memprediksi pilihan alternatif mana yang akan dipilih dalam panggilan ketika menjalankan operasi yang mendefinisikan payload dengan kata kunci tersebut. Misalnya, tidak mungkin untuk menentukan terlebih dahulu apakah penelepon akan memanggil operasi dengan objek Anjing atau Kucing, atau dengan objek yang terdiri dari beberapa atau mungkin semua properti dari skema PetByAge dan PetByType yang dijelaskan dalam contoh untuk anyOf dan oneOf dalam dokumentasi Swagger. Akibatnya, karena tidak ada parameter yang diketahui sebelumnya yang bisa digunakan oleh Kernel Semantik untuk membuat fungsi plugin bagi operasi tersebut, Semantic Kernel membuat fungsi hanya dengan satu parameter payload yang memiliki skema dari operasi yang menggambarkan berbagai alternatif yang mungkin. Proses pembuatan payload diserahkan kepada pemanggil operasi: LLM atau kode panggilan yang harus memiliki semua konteks untuk mengetahui alternatif mana yang tersedia untuk memanggil fungsi tersebut.

Penamaan ruang lingkup payload

Penamaan ruang lingkup payload membantu mencegah konflik penamaan yang dapat terjadi akibat ketidakunikan nama properti dalam muatan plugin OpenAPI.

Saat namespacing diaktifkan, Semantic Kernel menyediakan LLM dengan metadata operasi OpenAPI yang mencakup nama properti yang ditambah. Nama tambahan ini dibuat dengan menambahkan nama properti induk sebagai awalan, dipisahkan oleh titik, ke nama properti anak.

Misalnya, jika operasi change_light_state telah menyertakan objek offTimer berlapis dengan properti scheduledTime:

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

Semantic Kernel akan menyediakan metadata LLM untuk operasi yang mencakup nama properti berikut:

{
    "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"}},
    ]
}

Selain menyediakan metadata operasi dengan nama properti tambahan ke LLM, Semantic Kernel melakukan langkah-langkah berikut:

  1. Tangani panggilan LLM ke operasi OpenAPI dan cari argumen yang sesuai di antara yang disediakan oleh LLM untuk semua properti dalam payload, menggunakan nama properti tambahan dan kembali ke nama properti asli jika perlu.
  2. Buat payload menggunakan nama properti asli sebagai kunci dan argumen yang diselesaikan sebagai nilai.
  3. Kirim permintaan HTTP dengan payload yang dibangun ke API.

Secara bawaan, opsi pemisahan ruang nama payload dinonaktifkan. Ini dapat diaktifkan dengan mengatur properti EnablePayloadNamespacing ke true dalam objek OpenApiFunctionExecutionParameters saat menambahkan plugin OpenAPI.

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
    });

Nota

Opsi EnablePayloadNamespace hanya berlaku ketika konstruksi payload dinamis juga diaktifkan; jika tidak, itu tidak berpengaruh.

Payload parameter

Semantic Kernel dapat bekerja dengan payload yang dibuat oleh LLM menggunakan parameter payload. Ini berguna ketika skema payload kompleks dan berisi nama properti yang tidak unik, yang membuatnya tidak layak bagi Semantic Kernel untuk membangun payload secara dinamis. Dalam kasus seperti itu, Anda akan mengandalkan kemampuan LLM untuk memahami skema dan membangun payload yang valid. Model terbaru, seperti gpt-4o efektif dalam menghasilkan payload JSON yang valid.

Untuk mengaktifkan parameter payload, atur properti ke EnableDynamicPayloadfalse di OpenApiFunctionExecutionParameters objek saat menambahkan plugin OpenAPI:

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

Ketika parameter payload diaktifkan, Semantic Kernel menyediakan LLM dengan metadata untuk operasi yang mencakup skema untuk parameter payload dan content_type, memungkinkan LLM memahami struktur payload dan membangunnya dengan sesuai:

{
    "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."
        }
    }
}

Selain menyediakan metadata operasi dengan skema untuk payload dan parameter jenis konten ke LLM, Kernel Semantik melakukan langkah-langkah berikut:

  1. Menangani panggilan LLM ke operasi OpenAPI dan menggunakan argumen yang diberikan oleh LLM untuk parameter payload dan content_type.
  2. Kirim permintaan HTTP ke API dengan payload dan jenis konten yang disediakan.

URL dasar server

Plugin Semantic Kernel OpenAPI memerlukan URL dasar, yang digunakan untuk menambahkan jalur titik akhir saat membuat permintaan API. URL dasar ini dapat ditentukan dalam dokumen OpenAPI, diperoleh secara implisit dengan memuat dokumen dari URL, atau disediakan saat menambahkan plugin ke kernel.

Url yang ditentukan dalam dokumen OpenAPI

Dokumen OpenAPI v2 menentukan URL server menggunakan bidang schemes, host, dan basePath.

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

Kernel Semantik akan membuat URL server sebagai https://example.com/v1.

Sebaliknya, dokumen OpenAPI v3 menentukan URL server menggunakan servers bidang :

{
   "openapi": "3.0.1",
   "servers": [
      {
         "url": "https://example.com/v1"
      }
   ],
   ...
}

Kernel Semantik akan menggunakan URL server pertama yang ditentukan dalam dokumen sebagai URL dasar: https://example.com/v1.

OpenAPI v3 juga memungkinkan URL server berparameter menggunakan variabel yang ditunjukkan oleh kurung kurawal:

{
   "openapi": "3.0.1",
   "servers": [
      {
         "url": "https://{environment}.example.com/v1",
         "variables": {
            "environment": {
               "default": "prod"
            }
         }
      }
   ],
   ...  
}

Dalam hal ini, Kernel Semantik akan mengganti tempat penampung variabel dengan nilai yang disediakan sebagai argumen untuk variabel atau nilai default jika tidak ada argumen yang disediakan, menghasilkan URL: https://prod.example.com/v1.

Jika dokumen OpenAPI tidak menentukan URL server, Kernel Semantik akan menggunakan URL dasar server tempat dokumen OpenAPI dimuat:

await kernel.ImportPluginFromOpenApiAsync(pluginName: "lights", uri: new Uri("https://api-host.com/swagger.json"));

URL dasar akan menjadi https://api-host.com.

Menggantikan URL Server

Dalam beberapa kasus, URL server yang ditentukan dalam dokumen OpenAPI atau server tempat dokumen dimuat mungkin tidak cocok untuk kasus penggunaan yang melibatkan plugin OpenAPI.

Kernel Semantik memungkinkan Anda mengambil alih URL server dengan menyediakan URL dasar kustom saat menambahkan plugin OpenAPI ke 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")  
    });  

Dalam contoh ini, URL dasar akan https://custom-server.com/v1, menggantikan URL server yang ditentukan dalam dokumen OpenAPI dan URL server di mana dokumen dimuat.

Otentikasi

Sebagian besar REST API memerlukan autentikasi untuk mengakses sumber daya mereka. Semantic Kernel menyediakan mekanisme yang memungkinkan Anda mengintegrasikan berbagai metode autentikasi yang diperlukan oleh plugin OpenAPI.

Mekanisme ini bergantung pada fungsi panggilan balik autentikasi, yang dipanggil sebelum setiap permintaan API. Fungsi panggilan balik ini memiliki akses ke objek HttpRequestMessage, mewakili permintaan HTTP yang akan dikirim ke API. Anda dapat menggunakan objek ini untuk menambahkan kredensial autentikasi ke permintaan. Kredensial dapat ditambahkan sebagai header, parameter kueri, atau dalam isi permintaan, tergantung pada metode autentikasi yang digunakan oleh API.

Anda perlu mendaftarkan fungsi panggilan balik ini saat menambahkan plugin OpenAPI ke kernel. Cuplikan kode berikut menunjukkan cara mendaftarkannya untuk mengautentikasi permintaan:

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
    });  

Untuk skenario autentikasi yang lebih kompleks yang memerlukan akses dinamis ke detail skema autentikasi yang didukung oleh API, Anda dapat menggunakan metadata dokumen dan operasi untuk mendapatkan informasi ini. Untuk detail selengkapnya, lihat Metadata dokumen dan operasi.

Kustomisasi pembacaan konten respons

Semantic Kernel memiliki mekanisme bawaan untuk membaca konten respons HTTP dari plugin OpenAPI dan mengonversinya ke jenis data .NET yang sesuai. Misalnya, respons gambar dapat dibaca sebagai array byte, sementara respons JSON atau XML dapat dibaca sebagai string.

Namun, mungkin ada kasus ketika mekanisme bawaan tidak mencukupi untuk kebutuhan Anda. Misalnya, ketika respons adalah objek atau gambar JSON besar yang perlu dibaca sebagai aliran untuk disediakan sebagai input ke API lain. Dalam kasus seperti itu, membaca konten respons sebagai string atau array byte lalu mengonversinya kembali ke aliran dapat menjadi tidak efisien dan dapat menyebabkan masalah performa. Untuk mengatasi hal ini, Kernel Semantik memungkinkan penyesuaian pembacaan konten respons dengan menyediakan pembaca konten kustom:

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  
    });  

Dalam contoh ini, ReadHttpResponseContentAsync metode membaca konten respons HTTP sebagai aliran ketika jenis konten adalah application/json atau ketika permintaan berisi header x-streamkustom . Metode ini mengembalikan null untuk jenis konten lainnya, yang menunjukkan bahwa pembaca konten default harus digunakan.

Metadata dokumen dan operasi

Kernel Semantik mengekstrak metadata dokumen dan operasi OpenAPI, termasuk informasi API, skema keamanan, ID operasi, deskripsi, metadata parameter, dan banyak lagi. Ini menyediakan akses ke informasi ini melalui properti KernelFunction.Metadata.AdditionalParameters. Metadata ini dapat berguna dalam skenario di mana informasi tambahan tentang API atau operasi diperlukan, seperti untuk tujuan autentikasi:

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.identityProvider.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");

Dalam contoh ini, AuthenticateRequestAsyncCallbackAsync metode membaca metadata operasi dari konteks fungsi dan mengekstrak persyaratan keamanan untuk operasi guna menentukan skema autentikasi. Kemudian mengambil kunci API, untuk skema dan cakupan, dari penyedia identitas aplikasi dan menambahkannya ke dalam header permintaan atau parameter pencarian.

Tabel berikut mencantumkan metadata yang tersedia dalam kamus KernelFunction.Metadata.AdditionalParameters.

Kunci Jenis Deskripsi
Info RestApiInfo Informasi API, termasuk judul, deskripsi, dan versi.
operasi RestApiOperation Detail operasi API, seperti id, deskripsi, jalur, metode, dll.
keamanan IList<RestApiSecurityRequirement> Persyaratan keamanan API - jenis, nama, masuk, dll.

Tips dan trik untuk menambahkan plugin OpenAPI

Karena spesifikasi OpenAPI biasanya dirancang untuk manusia, Anda mungkin perlu membuat beberapa perubahan untuk membuatnya lebih mudah dipahami oleh AI. Berikut adalah beberapa tips dan trik untuk membantu Anda melakukannya:

Rekomendasi Deskripsi
Kontrol versi spesifikasi API Anda Alih-alih menunjuk ke spesifikasi API langsung, pertimbangkan untuk memasukkan dan membuat versi dari file Swagger Anda. Ini akan memungkinkan peneliti AI Anda untuk menguji (dan mengubah) spesifikasi API yang digunakan oleh agen AI tanpa memengaruhi API langsung dan sebaliknya.
Membatasi jumlah titik akhir Cobalah untuk membatasi jumlah titik akhir di API Anda. Konsolidasikan fungsionalitas serupa ke dalam titik akhir tunggal dengan parameter opsional untuk mengurangi kompleksitas.
Menggunakan nama deskriptif untuk titik akhir dan parameter Pastikan bahwa nama endpoint dan parameter Anda deskriptif dan mudah dipahami. Ini membantu AI memahami tujuan mereka tanpa memerlukan penjelasan yang luas.
Menggunakan konvensi penamaan yang konsisten Pertahankan konvensi penamaan yang konsisten di seluruh API Anda. Ini mengurangi kebingungan dan membantu AI mempelajari dan memprediksi struktur API Anda dengan lebih mudah.
Menyederhanakan spesifikasi API Anda Seringkali, spesifikasi OpenAPI sangat rinci dan menyertakan banyak informasi yang tidak diperlukan bagi agen AI untuk membantu pengguna. Semakin sederhana API, semakin sedikit token yang perlu Anda belanjakan untuk menggambarkannya, dan semakin sedikit token yang dibutuhkan AI untuk mengirim permintaan ke dalamnya.
Hindari parameter teks Jika memungkinkan, hindari menggunakan parameter string di API Anda. Sebagai gantinya, gunakan jenis yang lebih spesifik seperti bilangan bulat, boolean, atau enum. Ini akan membantu AI memahami API dengan lebih baik.
Memberikan contoh dalam deskripsi Ketika manusia menggunakan file Swagger, mereka biasanya dapat menguji API menggunakan antarmuka pengguna Swagger, yang mencakup permintaan dan respons sampel. Karena agen AI tidak dapat melakukan ini, pertimbangkan untuk memberikan contoh dalam deskripsi parameter.
Mereferensikan titik akhir lain dalam deskripsi Seringkali, AI akan membingungkan titik akhir serupa. Untuk membantu AI membedakan antara titik akhir, pertimbangkan untuk mereferensikan titik akhir lain dalam deskripsi. Misalnya, Anda dapat mengatakan "Endpoint get_all_lights ini mirip dengan endpoint tersebut, tetapi hanya mengembalikan satu lampu."
Berikan pesan kesalahan yang bermanfaat Meskipun tidak dalam spesifikasi OpenAPI, pertimbangkan untuk memberikan pesan kesalahan yang membantu AI mengoreksi sendiri. Misalnya, jika pengguna memberikan ID yang tidak valid, pertimbangkan untuk memberikan pesan kesalahan yang menyarankan agen AI mendapatkan ID yang benar dari get_all_lights titik akhir.

Langkah berikutnya

Sekarang setelah Anda tahu cara membuat plugin, Anda sekarang dapat mempelajari cara menggunakannya dengan agen AI Anda. Tergantung pada jenis fungsi yang telah Anda tambahkan ke plugin Anda, ada berbagai pola yang harus Anda ikuti. Untuk melihat fungsi pengambilan, baca artikel menggunakan fungsi pengambilan. Untuk fungsi otomatisasi tugas, lihat artikel menggunakan fungsi otomatisasi tugas.