Azure OpenAI Service 13 - Azure OpenAI Assistants API 介紹和基本實做

OpenAI 在 2023 年 11 月推出了 GPTs,讓使用者可以客制化自己的聊天助理,透過自定義的提示詞來設定 ChatGPT 的用途和功能並且可以串接自己或是第三方的 API 來讓自定義的 GPTs 可以完成更多模型無法直接完成的功能,而在推出 GPTs 的時候也推出了 Assistants API 讓開發者也可以客制畫出自己的聊天助理,透過這個 API 也可以讓我們更快速的來建立聊天助理,而不需要另外處理過往的聊天記錄,在過去要客制化自己的聊天助理,需要另外準備 DB 或是其它方式來儲存過往的聊天歷史對話,現在透過 Assistants API 就可以記錄在 OpenAI 上,透過 API 就可以存取對話的歷史聊天記錄,在 2024 年 2 月微軟也把這個 API 新增到 Azure 上了,後面就來介紹這個 API 並且實做。

說明

運行流程

首先用一張 OpenAI 官方的圖來說明一些後面會提到的物件。

物件說明
Assistant 助理使用 OpenAI 模型和可呼叫工具的 AI 
Thread 聊天串助理和使用者的對話內容。儲存聊天訊息並且可以自動處理成適合模型的前後文內容。
Message 訊息每個訊息皆為助理或使用者建立的。訊息內容可以包含文字、圖像和文件,會以列表的形式儲存在聊天串上。
Run 執行助理在聊天串的呼叫。助理透過呼叫模型和工具,利用其設定和聊天串的訊息來執行任務。作為每一次執行的一部分,助理將訊息附加到聊天串上。
Run Step 執行步驟一個助理在每一次執行中所採取的詳細步驟清單。助理可以在其執行期間呼叫工具或建立訊息。檢查執行步驟讓您能夠深入了解助理是如何得到其最終結果的。

整個 Assistant API 運行的步驟說明如下:

  1. 建立助理
    首先要先建立一個助理,建立之後會產生一組獨立的 Id,未來可以利用此 Id 來取得設定好的助理來修改相關設定,建立助理的時候可以設定使用的 GPT 模型以及使用的工具,目前支援的工具包含了程式碼解譯器 (Code Interpreter)、知識檢索 (Knowledge Retrieval)和函示呼叫 (Function calling),其中知識檢索在 Azure 上面還不支援,這些工具的說明和使用會在後面詳細說明。助理是不會被自動刪除,且有 API 可以列出所有建立過的助理。
  2. 建立聊天串和附加訊息
    再來建立一個聊天串,可以放入聊天的訊息前後文讓呼叫的模型達到更好的效果,建立的時候也會產生一組獨立的 Id,強烈建議要把這組 Id 儲存起來,因為目前 API 無法取得過往的聊天串,如果沒記錄就會消失在大海中,未來想重複使用或刪除就會無法使用,而閒置的聊天串會在 60 天 後被刪除 (這只在討論區看到,並沒有找告官方文件說明)。
  3. 呼叫助理並傳入聊天工作階段來執行 (Run) 結果
    透過前面建立的助理和聊天串 Id 來產生一次的執行,執行結果的回覆或產生的檔案會被加入到聊天串中,後續就繼續把使用者的回覆新增到聊天串之後進行下一次的執行。
聊天串和助理並沒有絕對的關連,可以把聊天串給不同的助理去產生結果。

透過 Azure OpenAI Studio 來建立和使用助理

後面我們會用 Azure 上面的 Azure OpenAI Assistants API 來實做,但是 OpenAI 跟 Azure OpenAI 大部分都是相容的,所以程式碼基本上只需要調整 Client 就可以通用了。

Azure OpenAI Studio 上點選助理來查看看管理已建立的助理,如果目前沒有部署任何 GPT 模型的話會出現底下畫面,可以點選建立新部署來建立 GPT 模型。

如果我們有建立好模型的話就可以來建立我們第一個助理。最簡單的助理設定就是名字和使用的 Gpt 模型,函數和程式碼解譯器就是前面提到的助理工具,目前只支援程式碼解譯器 (Code Interpreter)和函示呼叫 (Function calling),至於檔案的部分則是可以上傳檔案給助理使用,而這檔案是可以多助理共用的,這邊就不多做介紹,後面我們會用程式來實做,透過 Azure OpenAI Studio 遊樂場來測試助理是無法真正發揮他的用處的,像是函示呼叫也不會真的可以運作,這邊僅適合建立跟管理助理和做簡單的聊天測試,要真正發揮助理還是得靠程式實做才行,如果要列出所有已建立的助理可以點選 Open 就可以列出跟切換建立好的助理,這邊就不多太多做說明,還是後面透過程式實做來說明。

基本範例

接下來我們會使用 Azure.AI.OpenAI 來操作 Assistants API,可以透過 NuGet 來使用這個套件。

dotnet add package Azure.AI.OpenAI --prerelease

接下來建立一個 Console 程式。

static async Task Main(string[] args)
{
    var azureResourceUrl = "https://{your account}.openai.azure.com/";
    var azureApiKey = "{Your Api Key}";
    var deploymentName = "{Your Model Name}";
    // Assistants is a beta API and subject to change; acknowledge its experimental status by suppressing the matching warning.
    #pragma warning disable OPENAI001
    AssistantClient client = new AzureOpenAIClient(new Uri(azureResourceUrl), new AzureKeyCredential(azureApiKey)).GetAssistantClient();

    // 1. 建立助理
    Assistant assistant = await client.CreateAssistantAsync(
        model: deploymentName,
        new AssistantCreationOptions()
        {
            Name = "DEMO 助理",
            Instructions = "你是 Azure 專家,會回覆關於 Azure 的問題。"
        });

    // 2. 建立聊天串
    AssistantThread thread = await client.CreateThreadAsync();
    var messageResponse = await client.CreateMessageAsync(thread,
        [
            "如何建立一台 VM?"
        ]
    );

    // 3. 運行助理回覆問題
    var runResponse = await client.CreateRunAsync(thread, assistant);
    ThreadRun run = runResponse.Value;

    do
    {
        await Task.Delay(TimeSpan.FromMilliseconds(500));
        runResponse = await client.GetRunAsync(thread.Id, runResponse.Value.Id);
    }
    while (runResponse.Value.Status == RunStatus.Queued || runResponse.Value.Status == RunStatus.InProgress);

    // 4. 顯示出助理運行完後的聊天串
    var afterRunMessagesResponse = client.GetMessagesAsync(thread);

    await foreach (ThreadMessage threadMessage in afterRunMessagesResponse)
    {
        Console.WriteLine($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
        foreach (MessageContent contentItem in threadMessage.Content)
        {
            if (!string.IsNullOrEmpty(contentItem.Text))
            {
                Console.Write(contentItem.Text);
            }
            else if (!string.IsNullOrEmpty(contentItem.ImageFileId))
            {
                Console.Write($"<image from ID: {contentItem.ImageFileId}");
            }
            Console.WriteLine();
        }
    }
}

我們也可以把 3 和 4 合併執行,可以改成底下程式碼。

RunCreationOptions runOptions = new()
{
	//AdditionalInstructions = "Please address the user as Jane Doe. The user has a premium account."
};

await foreach (StreamingUpdate streamingUpdate in client.CreateRunStreamingAsync(thread, assistant, runOptions))
{
	if (streamingUpdate.UpdateKind == StreamingUpdateReason.RunCreated)
	{
		Console.WriteLine($"--- Run started! ---");
	}
	else if (streamingUpdate is MessageContentUpdate contentUpdate)
	{
		Console.Write(contentUpdate.Text);
		if (contentUpdate.ImageFileId is not null)
		{
			Console.WriteLine($"[Image content file ID: {contentUpdate.ImageFileId}");
		}
	}
}

執行程式之後可以得到這樣的結果:

以上就是一個簡單的呼叫範例,如同前面說明按照順序執行就可以跑出結果了,後續就可以把聊天串 Id 記錄下來重複利用就可以完成聊天助理了,完全不需要再自己處理過往的聊天訊息。

費用

Assistants API 的費用會分成兩個部分,一個是呼叫模型,另一個是呼叫工具,而模型就取決於助理設定的 GPT 模型來計費,工具的部分目前僅有程式碼解譯器會產生額外的費用,他會根據工作階段來計費,每個工作階段預設為一小時,目前 Azure OpenAI 和 OpenAI 的價錢是一樣的。

可透過 API 取得每一的執行的使用量資料,但目前 Azure OpenAI 上的 Assistants API 還不支援, OpenAI 的 Assistants API 回傳的結果會包含使用的 Token 數。

結論

之前我們要透過 OpenAI 來實做聊天機器人會需要花很多時間處理和儲存過往的聊天訊息,把它帶入每一次的模型呼叫,現在我們透過 Assistants API 就完全不需要處理這些,API 都會在後面幫我們處理掉,而且還可以把聊天串給不同的助理來回覆,可以讓結果達到最佳的校過,而本文僅簡單實做 Assistants API,後面我們會更進一步說明 API 的內容和使用。 

參考資料