Semantic Kernel: 新しいAgent代理

　前回のSemantic KernelにおけるAgentに関する記事では、現在では利用しているパッケージが古くなっているため、今回はそれを更新します。

　元の記事は以下の通りです：

> Semantic Kernel:Agent代理  
> 桂素伟，公众号：桂迹 [Semantic Kernel:Agent代理](https://mp.weixin.qq.com/s/qIz-CuoukULlDqAVr2bMJQ?token=2009718296&lang=zh_CN)

　以前のプロジェクトで使用していたNugetパッケージは以下の通りで、バージョンは1.18.2に留まっており、2024年9月4日現在のものです。

![オリジナルパッケージ](https://mmbiz.qpic.cn/sz_mmbiz_png/4CAA63nvHRDIHN1mA4ZyxLKorW5MIXfl8SGxamT3PLiarLLFE4CxTsG72HSRRztwwrQvuxyMgqJtAR7II4gu6yA/640?wx_fmt=png&from=appmsg)

　最新のAgentパッケージに変更されたものは以下の通りです：

![最新版パッケージ](https://mmbiz.qpic.cn/sz_mmbiz_png/4CAA63nvHRDIHN1mA4ZyxLKorW5MIXflqTvOdRbD4ZU28devCLsTYrUM2dmYiaMTfeZkh9S6wDWtkrhNLJfDZpg/640?wx_fmt=png&from=appmsg)

　以下に示すのは、最新のパッケージを用いて行った変更で、コード内のコメントが各タイプとメソッドの機能を説明しています。ここでは多くを語りません。現在の問題は、各エージェントを作成する際に、**Nameには中国語を使用できない**ことです。そうでなければ、http 400エラーが発生します。この問題は今後改善されると信じています。

```powershell
using Microsoft.SemanticKernel.Agents.Chat;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel;
using System.ClientModel;
using System.Collections.ObjectModel;
using System.Reflection.Emit;
using System;
#pragma warning disable

var modelID = "gpt-4o";
var openAIKey = File.ReadAllText("c://gpt/key.txt");
var kernel = Kernel.CreateBuilder()
           .AddOpenAIChatCompletion(modelID, openAIKey).Build();

//翻訳家のエージェントを作成
var agentTranslator = await CreateTranslatorAsync(kernel);
//審査員のエージェントを作成
var agentAuditor = await CreateAuditorAsync(kernel);
//代理チャットグループを作成し、まず翻訳家が翻訳し、その後審査員が審査
var chat = new AgentGroupChat(agentTranslator, agentAuditor)
{
    ExecutionSettings = new()
    {
        // 代理メッセージに"採用する"というフレーズがある場合終了するTerminationStrategyのサブクラスを使用。
        TerminationStrategy = new AdoptTerminationStrategy("採用する")
        {
            Agents = [agentAuditor],
            MaximumIterations = 6,
        }
    }
};

//チャットメッセージを追加
var chatMessage = new ChatMessageContent(AuthorRole.User, File.ReadAllText("content.txt"));
chat.AddChatMessage(chatMessage);
Console.WriteLine(chatMessage);
var lastAgent = string.Empty;
Console.WriteLine();
// ここでは非同期ストリームで代理のメッセージを処理します。
await foreach (var response in chat.InvokeStreamingAsync())
{
    if (string.IsNullOrEmpty(response.Content))
    {
        continue;
    }
    // 役割とエージェントの名前を入力
    if (!lastAgent.Equals(response.AuthorName, StringComparison.Ordinal))
    {
        Console.WriteLine($"\n# {response.Role} - {response.AuthorName ?? "＊"}:");
        lastAgent = response.AuthorName ?? string.Empty;
    }
    Console.Write(response.Content);
}

//ChatMessageContent[] history = await chat.GetChatMessagesAsync().Reverse().ToArrayAsync();
//全コンテンツ
//for (int index = 0; index < history.Length; index++)
//{
//    Console.WriteLine(history[index]);
//}
Console.WriteLine($"\n[完了しました: {chat.IsComplete}]");

async Task<ChatCompletionAgent> CreateAuditorAsync(Kernel kernel)
{
    var agentTranslator = new ChatCompletionAgent()
    {
        Instructions = """
                あなたは中日文翻訳の翻訳審査員であり、翻訳と審査に豊富な経験を持ち、翻訳の品質に高い要求を持ち、常に厳しい基準を設け、正確な翻訳を目指します。
                目的は、与えられた翻訳が要求を満たしているかどうか、採用するかどうかを決定することです。
                翻訳内容が受け入れられると判断された場合、"採用する"と言ってください。
                """,
        Name = "Auditor",
        Kernel = kernel,
    };
    return agentTranslator;
}

async Task<OpenAIAssistantAgent> CreateTranslatorAsync(Kernel kernel)
{
    var agentAuditor = await OpenAIAssistantAgent.CreateAsync(
            clientProvider: OpenAIClientProvider.ForOpenAI(new ApiKeyCredential(openAIKey)),
            definition: new OpenAIAssistantDefinition(modelID)
            {
                Instructions = """
                あなたは中国語から日本語への翻訳者です。
                ユーザーからの入力を、目の前のターゲットに集中し、正確で高品質な翻訳に変換します。
                翻訳内容を充実させる際には、審査員からの提案を考慮してください。
                """,
                Name = "JapaneseTranslator",
            },
            kernel: kernel);
    return agentAuditor;
}

class AdoptTerminationStrategy(string adoptCommand) : TerminationStrategy
{
    protected override Task<bool> ShouldAgentTerminateAsync(Agent agent, IReadOnlyList<ChatMessageContent> history, CancellationToken cancellationToken)
        => Task.FromResult(history[history.Count - 1].Content?.Contains(adoptCommand, StringComparison.OrdinalIgnoreCase) ?? false);
}
```

**実行結果：**

![実行結果](https://mmbiz.qpic.cn/sz_mmbiz_png/4CAA63nvHRDIHN1mA4ZyxLKorW5MIXflbDFe66cpSTSb85qXwbOYUEbvrr5pEwYJuVibeVWRKYdJOlKBaRjvy2w/640?wx_fmt=png&from=appmsg)

(Translated by GPT)