Microsoft AutoGenのマルチエージェント会話パターン

# はじめに
ここ最近AIについて学習を始め、OllamaやDifyを触ってみたり、pythonを学習したりと、いろいろと手を出してきました。
少しですか知識が得られてきたかなぁと実感してきたところです。
しかし、ツールを使って、AIとチャットできた！ではつまらないので、AIにどんなことをさせると仕事に役立つんだろうと、その**使い方**に最近は関心が出てきました。

以前Microsoft主催のAIに関するセミナーがあって出席したのですが、AIはどんどん進化していますが、それを「**どのように活用するかがこれからの課題**で、みなさんに期待しているところです」と話されていました。


# RAGの活用やAIとの対話
ChatのようにAIと自分が会話をして答えを見出していくことはよくある手法です。
またアプリケーションにするとしても、特定のプロンプトをアプリケーションに書いて、それに従って提供されたドキュメントや質問に回答するというのもよくあります。
でもその精度を上げたり回答に対してさらに質問するには、人間(自分)が考える必要があります。
そのあたりをもっとAIに任せられないか？と考えました。
そこで思いついくのはAIを複数のエージェントにして、AI同士で会話や議論をさせて推敲されたものを回答してもらうということです。
これを実現しているフレームワークがMicrosoftの**AutoGen**です。

# AutoGenとは
https://github.com/microsoft/autogen

README.mdによれば
>AutoGen は、AI エージェントを構築し、複数のエージェント間の連携を促進してタスクを解決するためのオープンソース プログラミング フレームワークです。AutoGen は、PyTorch がディープラーニングで行っているように、エージェント AI の開発と研究を効率化することを目的としています。AutoGen は、相互に対話できるエージェント、さまざまな大規模言語モデル (LLM) の使用とツール使用サポート、自律型および人間参加型ワークフロー、マルチエージェント会話パターンなどの機能を提供します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/ea3813fa-3e9d-d955-d213-99a78e495d12.png)

AIによる複数のエージェントが存在して、互いに会話をしていく仕組みです。
提案されているパターンとしては以下のようなものが紹介されています。

## Two-Agent Chat and Chat Result
以下に出てくるコードは、autogenのドキュメントを元に引用しています

https://microsoft.github.io/autogen/docs/tutorial/conversation-patterns

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/41897d39-79e4-bc75-cfce-e8bd9b408334.png)
2つのエージェントに会話をさせるパターンですね。
チュートリアルでは**ConversableAgent**を使用して2人のエージェントを定義します。
エージェントには他に**AssistantAgent**や**UserProxyAgent**があります。
1. ConversableAgent
AutoGenの中心的な機能で、他のエージェントと会話する機能を有します。設定によって人間が回答を行うこともできます
1. AssistantAgent
AIアシスタントとしてデフォルトではLLMを使用して会話を行います
1. UserProxyAgent
ユーザー代理エージェント。人間の回答を求めたり、**コードの実行**も行うことができます

話を戻してTwo-Agent Chatです。
- **name** エージェントの名前
- **system_message** 各エージェントのロールを設定することができます
- **llm_config** LLMの設定項目。"config_list"となっていて様々なLLMをエージェントごとに割り当てることもできるようになっています。

ということで、以下はチュートリアルでの例です
```python
import os

from autogen import ConversableAgent

student_agent = ConversableAgent(
    name="Student_Agent",
    system_message="You are a student willing to learn.",
    llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
)
teacher_agent = ConversableAgent(
    name="Teacher_Agent",
    system_message="You are a math teacher.",
    llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
)
```
そしてどちらかのエージェントから会話を開始(**initiate_chat**)させます。
- **max_turns** 何回会話を繰り返すかを設定できます。
- **summary_method="reflection_with_llm"** 要約を行わせます。
- **chat_result.cost** 複数のチャットを繰り返しますのでトークンが気になりますが、どのくらいのトークンとなったかを確認することができます。
```py

chat_result = student_agent.initiate_chat(
    teacher_agent,
    message="What is triangle inequality?",
    summary_method="reflection_with_llm",
    max_turns=2,
)

print(chat_result.cost)
```

ちなみにですが、自分はWindowsの[WSL環境にOllamaサーバーを構築している](https://qiita.com/mkuwan/items/ff84679ed8308ae78979)ので、そちらを使えるようにllm_configのところを変更しています。これでローカル環境のみでautogenを試すことができます。
たとえばollama_config.jsonというファイルで以下のような定義をしていたとします。
```json
[
    {
        "model": "llama3.1",
        "base_url": "http://localhost:11434/v1",
        "api_key": "ollama",
    },
    {
        "model": "codellama",
        "base_url": "http://localhost:11434/v1",
        "api_key": "ollama",
    }
]
```
これをconfig_listでllama3.1を使用するには以下のような書き方をします。
```python
config_list=autogen.config_list_from_json(
    env_or_file="ollama_config.json",
    filter_dict={
        "model":["llama3.1"]
    }
)
# 以上のように設定したconfig_listを使用して、llm_configを作成します。
llm_config={
    "config_list": config_list,
}
# このllm_configをAgentの設定値として与えます。
student_agent = ConversableAgent(
    name="Student_Agent",
    system_message="You are a student willing to learn.",
    llm_config=llm_config, # <- この右辺が作成した設定です
)
```

## Sequential Chats
会話を順番につなげていきますが、会話ごとに結果を次の会話に引き継いでいきます。
たとえば、チームで会議をして会議録を作る。次の会議ではその会議録に基づいてさらに議論を続けるというイメージに近いといえます。
このチュートリアルの例では、最初にあげたTwo-Agent Chatを複数回行う形となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/3cc92c3d-70c1-d853-a3a6-94a467176426.png)
この図をよく見ると、AgentAがすべてに出てきます。つまり、AgentA(Recipient)が話を振って会話をし、次の人とまた会話をしていくというイメージです。
チュートリアルでは、計算を足し算エージェント、引き算エージェント、掛け算エージェント、割り算エージェントにして、結果として出た値を次のエージェントに引き継いで、続きの計算をさせています。
```python
chat_results = number_agent.initiate_chats(
    [
        {
            "recipient": adder_agent,
            "message": "14",
            "max_turns": 2,
            "summary_method": "last_msg",
        },
        {
            "recipient": multiplier_agent,
            "message": "These are my numbers",
            "max_turns": 2,
            "summary_method": "last_msg",
        },
        {
            "recipient": subtracter_agent,
            "message": "These are my numbers",
            "max_turns": 2,
            "summary_method": "last_msg",
        },
        {
            "recipient": divider_agent,
            "message": "These are my numbers",
            "max_turns": 2,
            "summary_method": "last_msg",
        },
    ]
)
```
ただし、このあたりは、AIのモデルによってうまくできたりできなかったりしました。自分が試したときには、チュートリアルと同じコードでモデルだけ変更したところ、おかしな会話になって哲学的な話をしていたり、いつのまにやら文章でもない不思議な文字だけになったりとしました。プロンプトやモデル、活用方法などまだまだ検証する必要がありそうです。

## Group Chats
グループチャットという言葉通り、グループメンバーをAIに担ってもらいチャットをします。
この図ではさらにグループチャットを複数作成していますが、一つのグループだけでも可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/43d97aa1-ff26-1b3a-02ac-58f7ecaf5896.png)
これまでのAgentAを中心とした会話ではなく、グループチャットマネージャーというロールをもったAIが全体を取り仕切ります。

手順としては
1. グループチャットのメンバーを作成(AssistantAgentやConversableAgentなど)
1. グループチャットを作成
1. グループチャットマネージャーを作成
1. メンバーの誰かから会話を開始
という流れです。
グループチャットマネージャーは会話の中で、次に誰に話をしてもらうかといった指示を行います。会話を振られたエージェントはグループチャットマネージャーに対して返答をするという感じで会話が進んでいきます。
Sequencial Chatと組み合わせて、グループチャットをつなげたりもできます。
このあたりになると組み合わせをどのようにすると回答の精度が上がるかは試行錯誤という感じになりそうです。
あまりAIにだけ繰り返し行わせていると、話が訳の分からないところまで行ってしまうことがあるので、UserProxyAgentなどを利用して、人間のフィードバックも含めていくのがよいように思います。

## Nested Chats
チュートリアルの図を見るといきなり複雑になったように見えますが、フローをじっくり見ていくとそれほど大変なことをしているわけではありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/956246/0bc5bca9-6ec7-95e8-452c-60bedb978377.png)
これは大きく二つに分けられていて、左側の部分はAgentA、右側はSequential ChatsがNested Chatsに含まれる図となっています。
AgentAがAIエージェントと何かしらの会話をします。その会話が終了したら、次に別のAIエージェントと会話をしていくという感じです。現実世界で言えば、最初にリーダーがチームメンバーとの打ち合わせをして、次にリーダーはテクニカルサポートチームと会話をする、そして最後はマネージャーと会話をするみたいな感じですね。現場での話し合い・有識者チームとの話し合い・報告とレビューのための上司との話し合いです。
このように打ち合わせや会議といった、いわばパッケージ化された会話グループを作っていくものとされています。
グループチャットと何が違うのかというと、グループチャットは各グループでの会話がそのまま回答として出てきますが、Nested Chatの場合はグループ内が抽象化されて会話が外に出ず、外に出てくる回答はAgentAがまとめて、次のチャットを続けていくというものになります。


## ざっくりと説明
中身の仕組みとかを考えると頭が痛くなりそうですが、誤解を恐れず簡潔に実装を伝えると
1. TwoWay Chat
initiate_chatでConversalAgentやAssistantAgentに対して会話を開始する
1. Group Chat
initiate_chatでGroupChatManagerに対して会話を開始する
1. Sequential Chat
initiate_chatで会話を開始するが、リストでまとめられたAgentに対して順番に話をしていく
1. Nested chat
Sequential Chatを含んだregister_nested_chatsを作成してからinitiate_chatで会話を始める

といった感じです。initiate_chat以外の開始方法もありますが、ざっくりと説明するとこうなるかと思います。
あとは、これらをレゴブロックのように組み合わせて大きなワークフローを作成してくださいね、とチュートリアルでは説明されています。

検証作業が大変かなと思い、サクッとアプリを作成とはいかないかもしれませんが、さまざまなユースケースに合わせた組み合わせを検討できるので、今後の活用方法が楽しみなフレームワークだと感じています。