CCCMKホールディングス TECH Labの Tech Blog

TECH Labスタッフによる格闘記録やマーケティング界隈についての記事など

Multi-Agent Conversation Framework "AutoGen"を使ってみました。

Multi-Agent Conversation Framework "AutoGen"を使ってみました。

こんにちは、CCCMKホールディングス TECH LABの三浦です。

日曜日は天気が良くて、近所をのんびり散歩してみました。春は外を歩くのが気持ちがいいです。散歩していると見慣れた近所にもまだまだ知らなかった発見があることに気が付きます。こんなところから富士山が見えるんだ、とか、スカイツリーが見えるんだ、とか、少し得した気持ちになりました。

さて、LLM(大規模言語モデル)を使ってタスクを解決するためのテクニックは様々なものが提案されていますが、最近知ったテクニックがとても面白いアプローチを取っていました。それは対話が可能なエージェントを複数稼働させて相互に会話をさせ、1つのタスクを解決するMulti-Agent Conversationというアプローチです。

Multi-Agent Conversationの詳細は論文で説明されており、さらにMulti-Agent Conversationを実装するためのフレームワーク"AutoGen"をMicrosoftが公開しています。

今回はAutoGenで実装されているMulti-Agent Conversationというテクニックがどのような内容かをまとめ、AutoGenのセットアップから実行までをまとめます。

Multi-Agent Conversation

最初にAutoGenのMulti-Agent Conversationについて、論文の内容からまとめます。参照した論文はこちらです。

  • Title: AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation
  • Authors: Qingyun Wu, Gagan Bansal, Jieyu Zhang, Yiran Wu, Beibin Li, Erkang Zhu, Li Jiang, Xiaoyun Zhang, Shaokun Zhang, Jiale Liu, Ahmed Hassan Awadallah, Ryen W White, Doug Burger, Chi Wang
  • Submit: Submitted on 16 Aug 2023 (v1), last revised 3 Oct 2023
  • arXivURL: https://arxiv.org/abs/2308.08155

Multi-Agent Conversationは複数の個別の役割を持ったAgentを作り、相互に対話をさせながら協力して1つのタスクに取り組ませます。

あるAgentは別のAgentからのメッセージを受け取り、応答を作成し、応答を送信します。応答を作成する時はGPT-4などのLLMを使用したり、関数やAPIなどのToolを呼び出したり、あるいは人間に入力を促したりします。

なぜMulti-Agent Conversationがタスクを解くために有効なのか。それはAgentのエンジンとなるLLMの性質にあります。対話に特化したLLMはユーザーの入力やフィードバックに対し、それを加味した回答を作ることが出来ます。また、適切なプロンプトを与えることでソフトウェアエンジニアのような専門的な役割を意識した回答を生成することが出来るようになります。そしてLLMは複雑なタスクを1つのプロンプトで指示されると上手く回答出来ないことが多いですが、複雑なタスクを単純な複数のタスクに分割して順に指示すると上手く回答出来る傾向があります。

このような特徴を鑑みると、複数の役割を持ったAgentに複雑なタスクを分割した単純なタスクを与え、その結果を会話形式で同期しながらタスクに取り組むMulti-Agent Conversationというテクニックが理にかなっているものであることが分かります。

AutoGen

AutoGenはMulti-Agent Conversationを実装するFrameworkです。Pythonのライブラリとして公開されています。

microsoft.github.io

AutoGenに関する情報は上記ページで見ることが出来るのですが、論文に目を通しておいた方が理解が深まるように感じます。ここでは論文の内容を参考に、AutoGenについてまとめます。

AutoGenのAgentはConversableAgentというクラスのサブクラスとして実装されます。ConversableAgentsend, receive, generate_replyというメソッドを持っており、特にgenerate_replyを実装することでそのAgentの行動を定めることが出来ます。

AutoGenには2つの実装済みのConversableAgentのサブクラスAssistantAgentUserProxyAgentがあります。AssistantAgentはAIアシスタントの役割を持ち、LLMに基づいて行動をします。一方UserProxyAgentは"ユーザーの代理"の役割を持ちます。普段ChatGPTなどのAIアシスタントと対話しながら作業をする時、ChatGPTが出してくれた提案やコードをユーザーが実行し、その結果をAIアシスタントに教えて次の提案やコードの改善案を求めると思います。UserProxyAgentは"提案やコードをユーザーの代理で実行し、その結果をAIアシスタントに伝える"役割を担うAgentです。

複数Agent間の会話を成立させるため、AutoGenのAgentには"agent auto-reply"と呼ばれる機能が備わっています。これは一度他のAgentから会話を受け取ると、会話の終了条件を満たさない限り自動的にgenerate_replyメソッドを呼び出す機能です。開発者が独自に作成した関数をgenerate_replyの中で呼ぶことが可能で、これによってAgentの応答をカスタマイズすることが出来ます。

また会話の制御は自然言語とPythonによるプログラミングの両方で行うことが可能です。たとえばAgentのSystemMessageで"全部のタスクが完了したら、'TERMINATE'と返すこと。"のように自然言語でAgentの終了時の行動を定めることが出来ます。そしてPythonで書かれたgenerate_replyメソッドの中でメッセージの内容を解析し、条件分岐をさせることが可能です。論文に掲載されている次の図がイメージが湧きやすいと思いました。

AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation, Figure 2

AutoGenを使ってみる

セットアップ

ここからはAutoGenを使うまでに行った手順についてまとめます。

AutoGen自体はPythonのライブラリ"pyautogen"として利用可能で、pipコマンドで簡単にインストールが出来ます。しかしAutoGenではAgentが別のAgentが生成したコードを自動的に実行することがあり、予期せぬコードが実行され影響が出てしまう可能性もあるため、Dockerで独立した環境を作って試すようにしました。

Dockerfileは以下の様に作成しました。AutoGenの検証環境としてjupyter notebookを使いたくて合わせてpipでインストールしました。また、Agentに機械学習用のデータを生成させたりグラフ描画をさせることが出来るかを確認するためscikit-learn, pandas, matplotlibもインストールしました。

FROM python:3.12
WORKDIR /workdir
RUN pip install pyautogen jupyter scikit-learn pandas matplotlib
EXPOSE 8888
COPY OAI_CONFIG_LIST /workdir
CMD /bin/bash

OAI_CONFIG_LISTはAutoGenで使用するAzureOpenAIモデルの接続情報を記したファイルです。内容は以下の様になっています。

[
    {
        "model": "gpt-4",
        "api_key": "**********",
        "base_url": "**********",
        "api_type": "azure",
        "api_version": "2024-02-15-preview"
    },
    {
        "model": "gpt-35-turbo-16k",
        "api_key": "**********",
        "base_url": "**********",
        "api_type": "azure",
        "api_version": "2024-02-15-preview"
    }
]

Dockerのイメージをビルドしコンテナを立ち上げ、jupyter notebookを起動します。

使用するLLMの設定

jupyter notebookを起動し、notebookを作り、最初に以下のコードを実行します。

import autogen

# LLM settings

config_list = autogen.config_list_from_json(
    "OAI_CONFIG_LIST",
    file_location="../",
    filter_dict={
         "model": ["gpt-4"],
    }
)

llm_config = {
    "config_list": config_list,
    "timeout": 120,
}

autogen.config_list_from_jsonで事前に作成しているOAI_CONFIG_LISTを読み込んでいます。 file_locationでOAI_CONFIG_LISTのパスを指定しています。

Agentの設定

ここからは2つのAgentを作り、与えられたタスクをPythonのコードを実行して解決するMulti-Agent Conversationを実行します。次のコードでAgentを作成しています。

# Agent settings

## AssistantAgent
assistant = autogen.AssistantAgent(
    system_message="あなたはPythonのコードを書くことが出来ます。タスクが完了したらTERMINATEと応答してください。",
    name="assistant",
    llm_config=llm_config,
    code_execution_config=False,
    human_input_mode="NEVER",
)

## UserProxyAgent
user_proxy = autogen.UserProxyAgent(
    name="user_proxy",
    is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
    code_execution_config={
        "work_dir": "coding",
        "use_docker": False
    },
    max_consecutive_auto_reply=2,
    human_input_mode="NEVER",
    llm_config=False,
)

assistantAssistantAgentで、LLM(ここではGPT-4)でタスクを解くためのPythonのコードを生成します。タスクが終わったことを伝えるために、"TERMINATE"を応答として出力するようにします。これらの求める動作はsystem_messageで指定しています。

user_proxyは最初にユーザーの入力を受け取ると以降はユーザーの代理としてassistantが生成したPythonのコードを実行し、結果をassistantに伝えます。code_execution_configはコードを実行する時の各種設定を行うパラメータで、"work_dir"でコードを実行して生成されるファイルの格納場所を指定したり、実行環境としてDocker Containerを別途用意するのかを"use_docker"で指定します。

実行する

これで準備が出来たので、簡単なタスクを与えてみます。

# Execute

user_proxy.initiate_chat(
    assistant,
    message="1+1を計算して。",
)

すると次のような内容が出力されます。

Agent達がタスクをこなしている様子。

Assistant同士のやり取りが出力され、コードを実行して答えの2が得られたことが分かります。

もう少し難しいタスク

もう少し難しいタスクを与えてみます。機械学習用のサンプルデータを作ってもらう、というタスクです。

user_proxy.initiate_chat(
    assistant,
    message="2クラス分類用のモデル学習用のサンプルデータを500件くらいで作ってファイルに出力して。",
)

次の様な内容が出力され、UserProxyAgentを作る時に指定した"coding"ディレクトリにCSVファイルが出力されていることが確認出来ました。

Agent達が機械学習用のデータを作成している様子。

initiate_chatを呼び出すと会話が初期化されるのですが、sendを使うと会話を続けることが出来ます。今作成したCSVファイルのクラスラベルごとの件数を集計させてみます。

user_proxy.send(
    recipient=assistant,
    message="このファイルのクラスごとのデータ件数を集計して表示して。",
)

pandasのimport漏れで一度コードの実行に失敗しているようです。それに対し、assistantが修正案を提示し、結果正しく実行完了となりました。

最後にグラフを画像ファイルに出力します。

user_proxy.send(
    recipient=assistant,
    message="このデータセットの目的変数のヒストグラムをPNG形式で出力して。",
)

処理が完了した後、確かにヒストグラムが出力されていることが確認出来ました。

Agentが生成したヒストグラム

すごいですね!

まとめ

ということで、今回はMulti-Agent Conversationとそれを実装するFramework "AutoGen"についてまとめてみました。

Multi-Agent Conversationは使用するAgentの構成パターンを変えることで様々なタスクに対応が出来るようです。例えばプロダクトマネージャ役のAgent, プログラマー役のAgent, プロジェクトマネージャ役のAgentを構成し、まるでソフトウェア開発チームのような体制でタスクに取り組ませることも可能です。色々なパターンを試してみたいと思いました。