當前位置: 妍妍網 > 碼農

使用 .NET 開發 AI 套用(5): 建立第一個 AI 聊天機器人套用

2024-06-18碼農

文末附全部原始碼


在前面的章節中,我們探索了構建一個基礎的問答系統。然而,在當今的人工智慧領域,更為流行的是類似於ChatGPT這樣的聊天機器人。本章將指導你如何使用.NET技術開發一個類似的聊天機器人應用程式。

前端頁面設計

Gradio.Net提供了一個內建的Chatbot聊天機器人元件,為了增強使用者體驗,我們還將添加一個文字域,使使用者能夠輸入他們的問題或命令:

var chatbot = gr.Chatbot();
var txt = gr.Textbox(placeholder:"請輸入您的問題後按回車鍵");

透過簡潔的兩行程式碼,我們就能夠輕松建立一個直觀的聊天界面。

後端邏輯實作

一旦界面部份準備就緒,我們接下來需要處理文字域的回車事件 Submit

txt.Submit(streamingFn: (input) => GenerateResponse(input, kernel), inputs: new Gradio.Net.Component[] { txt, chatbot }, outputs: new Gradio.Net.Component[] { txt, chatbot });

值得註意的是,我們采用 streamingFn 參數來處理Submit事件,這樣可以啟動元件的流式輸出模式,從而實作類似ChatGPT的連續打字效果。

接著,我們定義一個生成響應的函式:

static async IAsyncEnumerable<Output> GenerateResponse(Input input, Kernel kernel)
{
var userPrompt = Textbox.Payload(input.Data[0]);
var chatHistory = Chatbot.Payload(input.Data[1]);
chatHistory.Add(new ChatbotMessagePair(new ChatMessage { TextMessage = userPrompt }, new ChatMessage { TextMessage = "" }));
await foreach (var responseMessage in kernel.InvokePromptStreamingAsync<string>(userPrompt))
{
if (!string.IsNullOrEmpty(responseMessage))
{
chatHistory.Last().AiMessage.TextMessage += responseMessage;
yield return gr.Output(""chatHistory);
}
await Task.Delay(50);
}
}

由於我們采用了流式輸出,因此方法返回值必須是 IAsyncEnumerable<> 型別。

在這裏, userPormpt 代表使用者的輸入,而 chatHistory 則是Chatbot元件內部保存的對話歷史。由於對話通常是成對出現的,使用兩個ChatMessage來分別代表本次對話中使用者和AI的發言。由於AI尚未回復,因此其 TextMessage 欄位為空。

我們使用SK提供的 InvokePromptStreamingAsync 方法來支持流式輸出,透過簡單的 foreach 迴圈,我們可以連續獲取AI返回的文本。

最終,我們透過 yield return 語句及時輸出AI的最新回復作為對話歷史返回給Chatbot元件。

接下來,讓我們看看實際執行的效果:

結論

在本章中,我們探討了如何利用.NET技術構建一個功能齊全的聊天機器人。

透過Gradio.Net的元件,我們能夠快速搭建起聊天界面,並且透過流式輸出模式,實作了類似ChatGPT的動態互動效果。

在實作過程中,我們註意到了細節的重要性,比如如何處理文字域的回車事件,以及如何優雅地管理對話歷史。這些細節不僅影響著程式的效能,也直接關系到使用者的滿意度。

Gradio.NET(https://github.com/feiyun0112/Gradio.Net/)的目標是成為用於開發 Web 套用的 .NET 開發者的首選框架。它的設計理念是讓開發變得更加簡單,讓每個人都能夠參與到Web套用的創造中來。

添加微信 GradioDotNet ,透過加入技術討論群,開發者們可以分享經驗,解決問題,並共同推動.NET的發展。

完整原始碼:

using Gradio.Net;
using Microsoft.SemanticKernel;
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_GPT_NAME");
string apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY");
var kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(
deploymentName: deploymentName,
endpoint: endpoint,
apiKey: apiKey)
.Build();
App.Launch(await CreateBlocks(kernel));
static async Task<Blocks> CreateBlocks(Kernel kernel)
{
using (var blocks = gr.Blocks())
{
var chatbot = gr.Chatbot();
var txt = gr.Textbox(showLabel: false,
placeholder: "輸入文本並按回車鍵"
);
txt.Submit(streamingFn: (input) => GenerateResponse(input, kernel), inputs: new Gradio.Net.Component[] { txt, chatbot }, outputs: new Gradio.Net.Component[] { txt, chatbot });
return blocks;
}
}
static async IAsyncEnumerable<Output> GenerateResponse(Input input, Kernel kernel)
{
var userPrompt = Textbox.Payload(input.Data[0]);
var chatHistory = Chatbot.Payload(input.Data[1]);
chatHistory.Add(new ChatbotMessagePair(new ChatMessage { TextMessage = userPrompt }, new ChatMessage { TextMessage = "" }));
await foreach (var responseMessage in kernel.InvokePromptStreamingAsync<string>(userPrompt))
{
if (!string.IsNullOrEmpty(responseMessage))
{
chatHistory.Last().AiMessage.TextMessage += responseMessage;
yield return gr.Output(""chatHistory);
}
await Task.Delay(50);
}
}