ChatGPT Plugin 插件開發:基於 ASP.NET Core Minimal API

前言

這是一篇ChatGPT插件開發教程,描述如何使用 ASP.NET Core Minimal API 開發 ChatGPT 插件,以最簡單的 Todo List 指導示例作爲入門教程。

這個Todo List插件主要功能是以自然語言的方式向ChatGPT發起指令,ChatGPT將根據合適的時機選擇調用此插件。例如:我明天下午3點有一個會議,請幫我記錄。此時 ChatGPT將會根據插件的元數據功能描述,然後選擇調用插件,將明天下午3點有一個會議通過API記錄到待辦列表中。

環境準備

首先你需要有一個開通了 Plugins 模塊的賬號,然後你才能進行調試使用。如果你沒有可以在這裏申請加入等待列表。說明一下,我是Plus用戶,我在提交了申請列表大概過了2-3周左右收到的開通郵件。

在提交申請的時候,最好選擇 "I am a developer and want to build a plugin",然後填寫較爲充分的理由,這樣更容易通過一些。

開通完成後,你可以在界面上看到列表中出現 Model 中可以選擇 Plugins 選項。

概念說明

整體上,構建 ChatGPT 插件需要3個步驟,

  1. 構建服務端 API
  2. 啓用 Swagger OpenApi 接口描述
  3. 創建一個插件清單文件,描述插件元數據信息

完成之後,你可以在界面上打開 Plugin Store,然後選擇 Develop your own Plugin,填入本地 Api 地址即可。

使用 ASP.NET Core Minimal 開發服務端 API

爲了簡單起見,我們的接口不進行授權(No Auth),主要分爲幾個部分:

  1. 編寫ai-plugin.json元數據文件
  2. 啓用跨域
  3. 啓用Swagger,並詳細描述接口參數
  4. 編寫接口代碼

編寫 ai-plugin.json元數據文件

每個插件都需要一個 ai-plugin.json 文件,該文件需要託管在API的域中。例如,一家名爲 example.com 的公司將通過 https://example.com 域訪問插件JSON文件,因爲這是他們的API託管的地方。
當通過ChatGPT UI安裝插件時,ChatGPT會查找位於 /.well-known/ai-plugin.json 的文件,以便和插件進行連接。如果找不到文件,則無法安裝插件。對於本地開發,可以使用HTTP,要指向遠程服務器,則需要HTTPS。

新建一個 ai-plugin.json 清單文件,填入以下內容:

{
  "schema_version": "v1",
  "name_for_human": "TODO Plugin (no auth)",
  "name_for_model": "todo",
  "description_for_human": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
  "description_for_model": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
  "auth": {
    "type": "none"
  },
  "api": {
    "type": "openapi",
    "url": "http://localhost:5000/openapi.yaml",
    "is_user_authenticated": false
  },
  "logo_url": "http://localhost:5000/logo.png",
  "contact_email": "[email protected]",
  "legal_info_url": "http://example.com/legal"
}

內容很簡單,需要說明的有2處。

  1. api:url 這個是指向 swagger 的 openapi描述文件,需要在服務端暴露出來。
  2. description_for_model 這個是當用戶的指令可能有插件的潛在請求查詢時,模型會查看該描述,您可能測試多個提示和描述,以查看哪些最有效。

description_for_model 屬性讓你可以自由地指導模型如何使用你的插件。總的來說,ChatGPT背後的語言模型非常能夠理解自然語言並遵循指令。因此,這是一個很好的地方,可以放置關於插件功能以及模型應該如何正確使用它的一般說明。使用自然語言,最好使用簡潔、描述性和客觀的語氣。您可以查看一些示例,以瞭解這應該是什麼樣子。我們建議以“Plugin for ...”,然後枚舉API提供的所有功能。

啓用跨域

由於是在網頁前端調用的本地localhost接口,所以需要接口啓用跨域以支持 chat.openai.com 的訪問。

在 ASP.NET Core啓用跨域很簡單。

builder.Services.AddCors(x => x.AddDefaultPolicy(policyBuilder =>
    policyBuilder.WithOrigins("https://chat.openai.com").AllowAnyHeader().AllowAnyMethod()));
    
// 省略部分代碼

app.UseCors();

啓用Swagger,並詳細描述接口參數

ChatGPT需要使用OpenAi V3版本,所以需要確保你引用了最新的 Swashbuckle.AspNetCore NuGet包。

builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("openapi", new OpenApiInfo
    {
        Description = "A plugin that allows the user to create and manage a TODO list using ChatGPT. If you do not know the user's username, ask them first before making queries to the plugin. Otherwise, use the username \"global\".",
        Version = "v1",
        Title = "TODO Plugin"
    });
    c.AddServer(new OpenApiServer() { Url = "http://localhost:5000" });

    var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});

//省略部分代碼

if (app.Environment.IsDevelopment())
{
    app.UseSwagger(x => x.RouteTemplate = "{documentName}.yaml");
    app.UseSwaggerUI(x =>
    {
        x.RoutePrefix = "";
        x.SwaggerEndpoint("/openapi.yaml", "TODO Plugin");
    });
}

我們配置 RoutePrefix=""以使主頁即爲swagger默認地址,配置 x.SwaggerEndpoint("/openapi.yaml", "TODO Plugin") 爲 OpenAPI文件的訪問地址,該地址和 ai-plgion.json中的地址要對應。

API 接口代碼

我們使用 Minimal Api 來構建,代碼中需要使用 OpenApi規範對參數進行詳細描述,這樣ChatGPT才能識別的更加準確。


var todos = new Dictionary<string, List<string>>();

app.MapPost("/todos/{username}", (string username, [FromBody] AddTodoRequest request) =>
{
    var todo = request.Todo;
    if (!todos.ContainsKey(username))
    {
        todos[username] = new List<string>();
    }
    todos[username].Add(todo);
    return todo;
})
.Produces<string>()
.WithOpenApi(operation =>
{
    operation.OperationId = "addTodo";
    operation.Summary = "Add a todo to the list";
    var parameter = operation.Parameters[0];
    parameter.Description = "The name of the user.";
    return operation;
});


app.MapGet("/todos/{username}", (string username) =>
    Results.Json(todos.TryGetValue(username, out var todo) ? todo : Array.Empty<string>())
)
.Produces<List<string>>()
.WithOpenApi(operation =>
{
    operation.OperationId = "getTodos";
    operation.Summary = "Get the list of todos";

    var parameter = operation.Parameters[0];
    parameter.Description = "The name of the user.";

    operation.Responses["200"].Description = "The list of todos";
    return operation;
});


app.MapDelete("/todos/{username}", (string username, [FromBody] DeleteTodoRequest request) =>
{
    var todoIdx = request.TodoIdx;
    if (todos.ContainsKey(username) && 0 <= todoIdx && todoIdx < todos[username].Count)
    {
        todos[username].RemoveAt(todoIdx);
    }
})
.Produces<List<string>>()
.WithOpenApi(operation =>
{
    operation.OperationId = "getTodos";
    operation.Summary = "Delete a todo from the list";
    operation.Parameters[0].Description = "The name of the user.";
    return operation;
});

app.MapGet("/logo.png", () => Results.File("logo.png", contentType: "image/png"))
    .ExcludeFromDescription();

app.MapGet("/.well-known/ai-plugin.json", () => Results.File("ai-plugin.json", contentType: "text/json"))
    .ExcludeFromDescription();

app.Run();

/// <summary>
/// AddTodoRequest Dto
/// </summary>
/// <param name="Todo">The todo to add to the list.</param>
internal record AddTodoRequest(string Todo);

/// <summary>
/// DeleteTodoRequest Dto
/// </summary>
/// <param name="TodoIdx">The index of the todo to delete.</param>
internal record DeleteTodoRequest(int TodoIdx);

測試插件

總結

以上,就是簡單的使用 ASP.NET Core minimal api 開發的一個 Todo List插件,功能比較簡單,基本上看下代碼就懂了。

完整的代碼我已經上傳到了Github,大家可自行查看。

https://github.com/yang-xiaodong/chatgpt-aspnetcore-plugin

如果你覺得本篇文章對您有幫助的話,感謝您的【推薦】。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章