2024年10月23日 星期三

Azure OpenAI AOAI 2.0 : 1 第一次使用 Azure.AI.OpenAI 2.0.0 開發教學

Azure OpenAI AOAI 2.0 : 1 第一次使用 Azure.AI.OpenAI 2.0.0 開發教學

在2024/10/01 微軟宣布Azure.AI.OpenAI 2.0.0正式發布,這是一個重大的里程碑,因為這是一個全新的版本,並且有許多新功能和改進。在此之前,所使用的Azure OpenAI client library for .NET 套件都是Beta 版本,而在此正式版堆出之後,將會有一些使用方式的改變與增加許多功能,所以,將會針對這些Azure OpenAI client library for .NET 2.0 版本進行一系列的教學。

在這裡先要來了解AOAI 採用的是大語言模型技術,使用者可以傳入文字,透過大語言模型的處理,就會產生出一段回應文字,這樣的技術可以應用在許多場景,例如:對話式應用程式、自動回覆、自動翻譯、自動摘要、自動產生程式碼等等。

這個傳入的文字可以稱之為Prompt 「提示詞」、「提示訊息」或「引導詞」,而產生的回應文字可以稱之為Completion 「完成字」、「完成訊息」或「完成句」。

在這個教學中,將會介紹如何使用Azure.AI.OpenAI 2.0.0 來開發一個提問與回答的應用程序,這個應用程序可以通過Prompt 提問,然後通過Completion 來回答,這個應用程序可以應用在許多場景,例如:自動回复、自動翻譯、自動摘要、自動產生代碼等等。

例如,若使用者輸入了一個Prompt “Say 'this is a test.'”,這個Prompt 將會被傳入到Azure OpenAI 服務中,然後Azure OpenAI 服務將會產生一段Completion 回應文字[ This is a test. ]。下圖將會展示這個應用程式的運作方式。

建立測試專案

請依照底下的操作,建立起這篇文章需要用到的練習項目

  • 打開Visual Studio 2022 IDE 應用程式
  • 從[Visual Studio 2022] 對話窗中,點選右下方的[建立新的項目] 按鈕
  • 在[建立新項目] 對話窗右半部
    • 切換[所有語言(L)] 下拉選單控制項為[C#]
    • 切換[所有項目類型(T)] 下拉選單控制項為[控制台]
  • 在中間的項目模板清單中,找到並且點擊[控制台應用程式] 項目模板選項

    項目,用於建立可在Windows、Linux 及macOS 於.NET 執行的命令列應用程序

  • 點擊右下角的[下一步] 按鈕
  • 在[設定新的項目] 對話窗
  • 找到[項目名稱] 字段,輸入csAzureAIOpenAIQuickStart作為項目名稱
  • 在剛剛輸入的[項目名稱] 欄位下方,確認沒有勾選[將解決方案與項目至於相同目錄中] 這個檢查盒控制項
  • 點擊右下角的[下一步] 按鈕
  • 現在將會看到[其他資訊] 對話窗
  • 在[架構] 欄位中,請選擇最新的開發框架,這裡選擇的[架構] 是 :.NET 8.0 (長期支援)
  • 在這個練習中,需要去勾選[不要使用最上層陳述式(T)] 這個檢查盒控制項

    這裡的這個操作,可以由讀者自行決定是否要勾選這個檢查盒控制項

  • 請點擊右下角的[建立] 按鈕

稍微等候一下,這個背景工作服務項目將會建立完成

安裝要用的NuGet 開發套件

因為開發此專案時會使用這些NuGet 套件,請依照底下說明,將需要用到的NuGet 套件安裝。

安裝Azure.AI.OpenAI 套件

請依照底下說明操作步驟,將這個套件安裝到專案內

  • 鼠標右擊[方案總管] 視窗內的[專案節點] 下方的[依賴] 節點
  • 從彈出功能表清單中,點選[管理NuGet 套件] 這個功能選項清單
  • 此時,將會看到[NuGet: csAzureAIOpenAIQuickStart] 視窗
  • 切換此視窗的標籤頁次到名稱為[瀏覽] 這個標籤頁次
  • 在左上方找到一個搜尋文字輸入盒,在此輸入Azure.AI.OpenAI
  • 在視窗右方,將會看到該套件詳細說明的內容,其中,右上方有的[安裝] 按鈕

    請確認有取消Pre-release 這個選項,與選擇2.0 正式版

  • 點擊這個[安裝] 按鈕,將這個套件安裝到專案內

修改Program.cs 類別內容

在這篇文章中,將會把會使用到的新類別與代碼,都寫入[Program.cs] 這個檔案中,請依照底下的操作,修改[Program.cs] 這個檔案的內容

  • 在專案中找到並開啟[Program.cs] 檔案
  • 將底下的代碼取代掉Program.cs檔案中內容
using Azure.AI.OpenAI;
using Azure;
using OpenAI.Chat;
using System.Net;

namespace csAzureAIOpenAIQuickStart;

internal class Program
{
    static void Main(string[] args)
    {
        // 讀取環境變數 AOAILabKey 的 API Key
        string apiKey = System.Environment.GetEnvironmentVariable("AOAILabKey");
        AzureOpenAIClient azureClient = new(
            new Uri("https://gpt4tw.openai.azure.com/"),
            new System.ClientModel.ApiKeyCredential(apiKey));
        ChatClient chatClient = azureClient.GetChatClient("gpt-4");

        string userPrompt = "Say 'this is a test.'";
        Console.WriteLine($"{DateTime.Now}  [User]: {userPrompt}");
        ChatCompletion completion = chatClient.CompleteChat("Say 'this is a test.'");

        foreach (var message in completion.Content)
        {
            Console.WriteLine($"{DateTime.Now} {message.Text}");
        }
        Console.WriteLine($"Role : {completion.Role}");
        Console.WriteLine($"InputTokenCount : {completion.Usage.InputTokenCount}");
        Console.WriteLine($"OutputTokenCount : {completion.Usage.OutputTokenCount}");
        Console.WriteLine($"ReasoningTokenCount : {completion.Usage.OutputTokenDetails?.ReasoningTokenCount}");
        Console.WriteLine($"TotalTokenCount : {completion.Usage.TotalTokenCount}");
    }
}

當要使用Azure OpenAI library for .NET 來呼叫ChatGPT 相關應用API 的時候,首先需要建立一個[AzureOpenAIClient] 類別的物件,這個物件是用來建立一個Azure OpenAI client library for .NET 的物件,這個物件是用來呼叫Azure OpenAI 的API 服務。

在此需要宣告與傳入兩個參數,第一個參數是Azure OpenAI 的API 服務的網址,第二個參數是Azure OpenAI 的API 服務的密鑰,這個密鑰是用來驗證使用者的身份,這個金鑰是在Azure OpenAI 的網站上申請的。有了這兩個信息,便可以取得與使用Azure 所提供的OpenAI 服務,透過其云大語言模型技術,來進行文字的生成。

因此,在這裡使用了AzureOpenAIClient azureClient = new(new Uri("https://gpt4tw.openai.azure.com/"), new System.ClientModel.ApiKeyCredential(apiKey));敘述來完成上述需求,不過,對於Azure OpenAI 用到的密鑰信息,是具有敏感性的,因為,任何人取得了服務端點與該密鑰,就可以使用你訂閱的服務,所產生的費用,是由你負擔,因此,在這裡不會將該密鑰放在項目原始碼內,而是通過環境變量的方式來取得。這樣的好處是,可以避免密鑰資訊洩露,而且,可以在不同的環境中,使用不同的密鑰資訊。

此時,開啟指令提示字元視窗,輸入[set] 指令,便可以看到所有環境參數的清單

從上面螢幕截圖,可以看到已經有設定一個環境變數AOAILabKey,這個環境變量是用來存放Azure OpenAI 金鑰資訊的。

使用底下指令將建立OpenAI Key 永久性的環境變數,這代表這個環境變量將會一直存在,不會因為該系統重新開機之後,該環境變量就不存在了,直到你刪除它為止

setx AOAILabKey "剪貼簿內的 OpenAI Key 值" /M

如何刪除此環境變數

REG delete "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /V AOAILabKey /f

若金鑰與服務端點都設定完成,且AzureOpenAIClient azureClient 可以正常運作,接下來就要產生ChatClient chatClient 物件,這裡使用了ChatClient chatClient = azureClient.GetChatClient("gpt-4");敘述來取得ChatClient 類別的物件,這個物件是用來呼叫ChatGPT 相關應用API 服務的。

想要知道有那些模型可以使用,可以透過Azure 網頁查詢

進入Azure 網頁,搜尋或找到[Azure OpenAI] 服務

點擊進入後,參考下圖,在網頁中間下方,可以看到[Explore and deploy] 選項

點擊進入後,可以看到[Azure OpenAI Studio] 頁面,在最左方找到[部署] 鏈接,就可以看到所有的模型清單,這裡使用的是gpt-4模型

現在要來建立一個ChatClient chatClient 物件,這個物件是用來呼叫ChatGPT 相關應用API 服務的,這裡使用了ChatClient chatClient = azureClient.GetChatClient("gpt-4");敘述來取得ChatClient 類別的物件,這個物件是用來呼叫ChatGPT 相關應用API 服務的。 azureClient.GetChatClient("gpt-4");

AzureOpenAIClient Class Methods中,有列出所有可用取得特定服務方法

  • GetAudioClient(String)

    取得一個新的OpenAI.Audio.AudioClient實例,該實例配置為與Azure OpenAI 服務一起使用音頻操作。

  • GetChatClient(String)

    取得一個新的OpenAI.Chat.ChatClient實例,該實例配置為與Azure OpenAI 服務一起使用聊天完成操作。

  • GetEmbeddingClient(String)

    取得一個新的OpenAI.Embeddings.EmbeddingClient實例,該實例配置為與Azure OpenAI 服務一起使用嵌入操作。

  • GetImageClient(String) 取得一個新的OpenAI.Images.ImageClient實例,該實例配置為與Azure OpenAI 服務一起使用映像操作。

透過這個方法GetChatClient 所取得的物件,可以進行對指定大語言模型進行對話式應用程式的操作,這個對像是用來呼叫ChatGPT 相關的應用API 服務。

想要對大語言模型提出問題,可以使用類似這樣用法chatClient.CompleteChat("Say 'this is a test.'");其中,使用CompleteChat 方法傳入進去的字符串,就是Prompt,該方法將會回傳[ChatCompletion],透過這個物件,就可以得到大語言模型生成的文字內容。

在[ChatCompletion] 物件內有個類型為[ChatMessageContent] 的屬性[Content] ,這個屬性是用來存放ChatGPT 產生的文字內容,透過這個屬性,就可以取得ChatGPT 產生的文字內容。這裡使用底下方法將生成文字內容印在螢幕上。

foreach (var message in completion.Content)
{
    Console.WriteLine($"{DateTime.Now} {message.Text}");
}

該物件也可以取得ChatGPT 產生的文字內容的其他信息,例如:Role、InputTokenCount、OutputTokenCount、ReasoningTokenCount、TotalTokenCount 等等,這些資訊可以透過ChatCompletion 物件的屬性來取得。有了這些信息,便可以計算出這次呼叫GPT 運算的成本,這樣就可以控制Azure OpenAI 服務的使用成本。

Console.WriteLine($"Role : {completion.Role}");
Console.WriteLine($"InputTokenCount : {completion.Usage.InputTokenCount}");
Console.WriteLine($"OutputTokenCount : {completion.Usage.OutputTokenCount}");
Console.WriteLine($"ReasoningTokenCount : {completion.Usage.OutputTokenDetails?.ReasoningTokenCount}");
Console.WriteLine($"TotalTokenCount : {completion.Usage.TotalTokenCount}");

執行測試專案

  • 按下F5開始執行專案
  • 將會看到輸出結果

2024/10/23 上午 10:14:24  [User]: Say 'this is a test.'
2024/10/23 上午 10:14:26 This is a test.
Role : Assistant
InputTokenCount : 14
OutputTokenCount : 5
ReasoningTokenCount : 

TotalTokenCount : 19 




2024年10月21日 星期一

.NET 8 Blazor 005 - 自動轉譯會決定如何在執行階段轉譯元件

.NET 8 Blazor 005 - 自動轉譯會決定如何在執行階段轉譯元件

在前四篇文章中 .NET 8 Blazor 001 - 了解 SSR Static Server Render 運作模式 / .NET 8 Blazor 002 - 了解 互動式伺服器端轉譯 Interactive server-side rendering (interactive SSR) 運作模式 / .NET 8 Blazor 003 - 了解 互動式 WebAssembly 用戶端端轉譯 Client-side rendering (CSR) 運作模式 / .NET 8 Blazor 004 - 觀察 Global 與 Per Page/Component 之間的差異 中,說明了四種 Blazor 專案的轉譯設計方法,現在要來說明最後一種用法自動轉譯會決定如何在執行階段轉譯元件

建立 測試專案

請依照底下的操作,建立起這篇文章需要用到的練習專案

  • 打開 Visual Studio 2022 IDE 應用程式
  • 從 [Visual Studio 2022] 對話窗中,點選右下方的 [建立新的專案] 按鈕
  • 在 [建立新專案] 對話窗右半部
    • 切換 [所有語言 (L)] 下拉選單控制項為 [C#]
    • 切換 [所有專案類型 (T)] 下拉選單控制項為 [Web]
  • 在中間的專案範本清單中,找到並且點選 [Blazor Web App] 專案範本選項

    A project template for creating a Blazor Web app that support both server-side rending and client interactivity. This template can be used for web apps with rich dynamic user interfaces (UIs)

  • 點選右下角的 [下一步] 按鈕
  • 在 [設定新的專案] 對話窗
  • 找到 [專案名稱] 欄位,輸入 csBlazorAuto 作為專案名稱
  • 在剛剛輸入的 [專案名稱] 欄位下方,確認沒有勾選 [將解決方案與專案至於相同目錄中] 這個檢查盒控制項
  • 點選右下角的 [下一步] 按鈕
  • 現在將會看到 [其他資訊] 對話窗
  • 在 [架構] 欄位中,請選擇最新的開發框架,這裡選擇的 [架構] 是 : .NET 8.0 (長期支援)
  • 在 [驗證類型] 欄位中,請選擇 [無]
  • 勾選 [針對 HTTPS 進行設定] 檢查盒欄位
  • 在 [Interactive render mode] 欄位中,請選擇 [Auto (Server and WebAssembly)]
  • 在 [Interactivity location] 欄位中,請選擇 [Per page/component]
  • 勾選 [Include sample pages] 檢查盒欄位
  • 勾選 [Do not use top-level statements] 檢查盒欄位

    這裡的這個操作,可以由讀者自行決定是否要勾選這個檢查盒控制項

  • 不要勾選 [在 .NET Aspire 協調流程中登入] 檢查盒欄位 
  • 請點選右下角的 [建立] 按鈕

稍微等候一下,這個 Blazor Web App 專案將會建立完成

Program.cs - Blazor 專案程式進入點

在這個 Blazor 專案中,Program.cs 檔案的內容如下,從這裡可以看到,這個專案是使用了自動轉譯的方式,這個專案會自動決定如何在執行階段轉譯元件。其中有用到了 builder.Services.AddInteractiveServerComponents()builder.Services.AddInteractiveWebAssemblyComponents()方法,這表示了,這個專案將會可以使用到 Interactive SSR 與 CSR 兩種渲染方法。

在 app.MapRazorComponents<App>() 方法中,加入了 .AddInteractiveServerRenderMode() 與 .AddInteractiveWebAssemblyRenderMode() 方法,這表示了,這個專案將會可以使用到 Interactive SSR 與 CSR 兩種渲染方法。

using csBlazorAuto.Client.Pages;
using csBlazorAuto.Components;

namespace csBlazorAuto
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddRazorComponents()
                .AddInteractiveServerComponents()
                .AddInteractiveWebAssemblyComponents();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseWebAssemblyDebugging();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();

            app.UseStaticFiles();
            app.UseAntiforgery();

            app.MapRazorComponents<App>()
                .AddInteractiveServerRenderMode()
                .AddInteractiveWebAssemblyRenderMode()
                .AddAdditionalAssemblies(typeof(Client._Imports).Assembly);

            app.Run();
        }
    }
}

觀察計數器元件的程式碼

在此 [csBlazorCsr.Client] 專案內 [Pages] 資料夾內找到,並且找到並且打開 [Counter.razor] 這個檔案,就會看到底下內容

@page "/counter"
@rendermode InteractiveAuto

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

在這個元件中,有使用到 @rendermode InteractiveAuto 這個指示詞,這表示了,這個元件將會採用自動轉譯的方式,這個專案會自動決定如何在執行階段轉譯元件。

觀察運作模式

現在再次執行這個專案,接著,開啟無痕視窗

按下 F12 按鍵,進入到開發人員模式,切換到 [Network] 標籤頁次,接著在網址列輸入 https://localhost:7297/,就會看到底下的畫面

接著,切換到 [Counter] 頁面,就會看到底下的畫面

此時,可以看到當第一次顯示這個網頁將會採用 Interactive SSR 的方式,當點擊 [Click me] 按鈕時,可以從 WebSocket 頁籤中看到,這個專案會自動轉換成 Interactive CSR 的方式,這表示了,這個專案會自動決定如何在執行階段轉譯元件。

也就是說,[Counter] 頁籤會立即顯示在螢幕上,並且可以立即使用,這就是自動轉譯的運作模式。

同個時間,因為是第一次開起這個系統,此時會用背景方式進行載入 WebAssembly 檔案

因此,若對這個頁面重新整理,可以觀察到,之後的頁面將會採用 Interactive CSR 的方式,這表示了,這個專案會自動決定如何在執行階段轉譯元件。