Semantic Kernel: OpenAPIのプラグイン

SKのプラグインは、AI機能をさらに強化します。プラグインは、AIがローカル機能やデータとインタラクションすることを可能にし、AIとアプリケーションの結合において優れた接着剤の役割を果たします。

プラグイン自体とその機能をどのように分離することができるでしょうか。APIを呼び出すことが一つの良い方法です。たとえば、以下は注文を問い合わせる機能で、ミニAPIプロジェクトです。

```csharp
using System.Text.Json.Serialization;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/orders", () =>
{
    app.Logger.LogInformation("查询orders");
    
    var orders = Enumerable.Range(1, 5).Select(index =>
        new Order(Guid.NewGuid().ToString(), $"Product {index}", index, index * 10))
        .ToArray();
    
    return orders;
})
.WithName("orders").WithDescription("获取订单列表");
app.Run();

class Order
{
    public Order(string id, string product, int quantity, decimal price)
    {
        Id = id;
        Product = product;
        Quantity = quantity;
        Price = price;
    }
    [JsonPropertyName("编号")]
    public string Id { get; set; }
    [JsonPropertyName("产品名称")]
    public string Product { get; set; }
    [JsonPropertyName("订单数量")]
    public int Quantity { get; set; }
    [JsonPropertyName("订单金额")]
    public decimal Price { get; set; }
}
```

このAPIのOpenAPIのフレームワークは次のとおりです：http://localhost:5000/openapi/v1.json

```bash
{
  "openapi": "3.0.1",
  "info": {
    "title": "OpenAPIDemoForAI | v1",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "http://localhost:5000"
    }
  ],
  "paths": {
    "/orders": {
      "get": {
        "tags": [
          "OpenAPIDemoForAI"
        ],
        "description": "获取订单列表",
        "operationId": "orders",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Order"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Order": {
        "required": [
          "编号",
          "产品名称",
          "订单数量",
          "订单金额"
        ],
        "type": "object",
        "properties": {
          "编号": {
            "type": "string"
          },
          "产品名称": {
            "type": "string"
          },
          "订单数量": {
            "type": "integer",
            "format": "int32"
          },
          "订单金额": {
            "type": "number",
            "format": "double"
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "OpenAPIDemoForAI"
    }
  ]
}
```

今、インターフェースを呼び出して注文を問い合わせてみましょう：http://localhost:5000/orders

```css
[
  {
    "编号": "c82bd2f1-25d1-4a17-bb40-7cfe9fd35f71",
    "产品名称": "Product 1",
    "订单数量": 1,
    "订单金额": 10
  },
  {
    "编号": "bc6f1eef-4ea9-426c-a446-1cff12d963ac",
    "产品名称": "Product 2",
    "订单数量": 2,
    "订单金额": 20
  },
  {
    "编号": "b7b27a33-db1f-4c7c-99c6-064d330d8893",
    "产品名称": "Product 3",
    "订单数量": 3,
    "订单金额": 30
  },
  {
    "编号": "c58f6e36-4363-441d-b54f-50c99970d07a",
    "产品名称": "Product 4",
    "订单数量": 4,
    "订单金额": 40
  },
  {
    "编号": "2789ac41-0d90-4da8-8041-b2e3cd3f7859",
    "产品名称": "Product 5",
    "订单数量": 5,
    "订单金额": 50
  }
]
```

SKはOpenAPI（ここではOpenAIではありません）仕様に準拠したAPIをSKプラグインとしてインポートすることをサポートしています。OpenApiKernelPluginFactoryを使用してこれを完了します。具体的な実装は以下の通りです：

```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Plugins.OpenApi;
using System.IO;
using System.Text.Json;
#pragma warning disable

var apikey = File.ReadAllText("c:/gpt/key.txt");
using HttpClient httpClient = new();

var kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAIChatCompletion("gpt-4o", apiKey: apikey);
var kernel = kernelBuilder.Build();

var pluginArr = new List<PluginSetting>
{
    new PluginSetting{PluginName="OrderService",UriString="http://localhost:5000/openapi/v1.json"}
};
foreach (var pluginItem in pluginArr)
{
    var plugin = await OpenApiKernelPluginFactory.CreateFromOpenApiAsync(pluginName: pluginItem.PluginName,
        uri: new Uri(pluginItem.UriString),
        executionParameters: new OpenApiFunctionExecutionParameters(httpClient)
        {
            IgnoreNonCompliantErrors = true,
            EnableDynamicPayload = true,
        });
    kernel.Plugins.Add(plugin);
}
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
var openAIPromptExecutionSettings = new OpenAIPromptExecutionSettings()
{
    ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};
ChatHistory history = [];
while (true)
{
    Console.WriteLine("回车开始");
    Console.ReadLine();
    Console.WriteLine("用户 > 查询一下订单，然后总汇订单的总金额 ");
    history.AddUserMessage("总汇一下订单的总金额");
    
    var result = await chatCompletionService.GetChatMessageContentAsync(
        history,
        executionSettings: openAIPromptExecutionSettings,
        kernel: kernel);
    
    Console.WriteLine("助理 > " + result);
    history.AddMessage(result.Role, result.Content!);
}

public class PluginSetting
{
    public string PluginName { get; set; }
    public string UriString { get; set; }
}
```

![画像](https://mmbiz.qpic.cn/sz_mmbiz_png/4CAA63nvHRA1SQcL0JX4C3qgxrSWC11DbJicxgLx2icss9DPDx63jYs7lNWZicMue73OPluxU3f9c85DWDVib5HSpg/640?wx_fmt=png&from=appmsg)

(Translated by GPT)