之前介紹了Foundry Local這個AI套件,不過串接AI API這件事,選擇很多,也有Ollama這個選擇在,但不論Foundry Local或是Ollama,它們都有對應的串接套件,這讓人覺得麻煩,如果能用微軟提供IChatClient這個統一的介面來串,對於應用端來說,他就不需煩惱底層是啥,或是說很方便做抽換。

Ollama的就不介紹,這邊示範的是Foundry Local使用IChatClient來對接:

var modelAlias = "phi-4";
var config = new Configuration
{
    AppName = "demo-Foundry",
    LogLevel = Microsoft.AI.Foundry.Local.LogLevel.Information,
    Web = new Configuration.WebService
    {
        Urls = "http://localhost:8888"
    },
};
                
...

var manager = FoundryLocalManager.Instance;
_catalog = await manager.GetCatalogAsync(ct);
IModel? model = await _catalog.GetModelAsync(modelAlias);
await model.DownloadAsync(progress =>
{
    Console.Write($"\rDownloading model: {progress:F2}%");
    if (progress >= 100f)
    {
        Console.WriteLine();
    }
});

await model.LoadAsync();
await manager.StartWebServiceAsync(ct);

上面是一些簡略過的宣告,詳細的程式碼可參考之前的文章。

接下來是串接方式:

IChatClient chatClient = new OpenAI.Chat.ChatClient(
    modelAlias, new ApiKeyCredential("1234"), //key隨便填,就是不能為空
    new OpenAI.OpenAIClientOptions
    {
        Endpoint = new Uri("http://localhost:8888/v1") //Ollama的位址
    }
    ).AsIChatClient();

接下來就是呼叫方式

string exitCommand = "exit";
string userInput = "請你自我介紹";       
List<ChatMessage> messages = new()
{
    new ChatMessage(ChatRole.Assistant, "你現在是個3C達人,回答請用繁體中文"),
    new ChatMessage(ChatRole.User, userInput)
};
while (userInput != exitCommand)
{
    StringBuilder responseText = new StringBuilder(); 
    var streaming = chatClient.GetStreamingResponseAsync(messages);
    await foreach (var chunk in streaming)
    {
        if (!string.IsNullOrEmpty(chunk.Text))
        {
            Console.Write(chunk.Text);
            responseText.Append(chunk.Text);
        }
    }
    Console.WriteLine();
    messages.Add(new ChatMessage(ChatRole.Assistant, responseText.ToString()));

    Console.WriteLine();
    Console.Write("請輸入下一個問題,或輸入 'exit' 結束對話:");
    userInput = Console.ReadLine() ?? "";
    if (userInput == exitCommand)
    {
        Console.WriteLine("對話結束");
    }
    messages.Add(new ChatMessage(ChatRole.User, userInput));
}

// remember to unload the model
await model.UnloadAsync();
}
catch (Exception ex)
{
    Console.WriteLine($"錯誤訊息: {ex.Message}");
    Console.WriteLine($"詳細錯誤: {ex.StackTrace}");
}

大致上是這樣,目前AI套件更新很快,這篇大概很快就過時了吧(思)

參考資料