Multi Agent でミニマムな Github Copilot もどきを作る

こんにちは。ゴールデンウィーク中に Agent の開発力を高めるべく、microsoft/autogen で遊んでいます。前回の記事では、 microsoft/autogen の概要と、簡単な対話を実行しました。今回はローカルの指定されたディレクトリのファイルを自動的に読み込んで、そのファイルは何のファイルであるのか判定する会話を作成します。

https://github.com/microsoft/autogen

**前回の記事**

https://qiita.com/faunsu/items/d4b5c3d5155d48ee5740


## 環境
* microsoft/autogen（pyautogen）
* python
* OpenAI GPT3.5-16k

## 結果イメージ

Qiita に動画を貼れない為、twitter のリンクで失礼します。

https://x.com/faunsu19000/status/1786182485786898743

## コード

ConversableAgent を2つ用意しました。片方はLLMを用いてコードを作成し続ける Agent で、もう片方は作成されたコードを実行するAgentです。コードを作成するAgentはLLMが生成したコードブロックからコードを取り出して実行するだけの為、コード実行AgentにはLLMは不要です。

```python 
from autogen import ConversableAgent
from autogen.coding import LocalCommandLineCodeExecutor

llm_config = {
    "api_type": "${環境に応じて}",
    "api_version": "2024-02-15-preview",
    "api_key": OPENAI_API_KEY,
    "base_url": OPENAI_ENDPOINT,
    "model": OPENAI_DEPLOYMENT_NAME,
}

work_dir = "~/workdir"

executor = LocalCommandLineCodeExecutor(
    timeout=10,
    work_dir=work_dir,
)


# コードの作成者と実行者は分離する。以下はコード推論 Agent。LLM を持つ。
code_writer_agent = ConversableAgent(
    "code-writer",
    system_message="\n".join(
        [
            "あなたはPython開発者です。",
            "あなたがコードを書くと自動的に外部のアプリケーション上で実行されます。",
            "ユーザーの指示に従ってあなたはコードを書きます。",
            "コードの実行結果は、あなたがコードを投稿した後に自動的に表示されます。",
            "ただし、次の条件を厳密に遵守する必要があります。",
            "ルール:",
            "1. コードブロック内でのみコードを提案します。",
            "2. あなたが作成するスクリプトは、'./' に保存されます。",
            "3. あなたが作成したスクリプトが操作する対象のアプリケーションは、 '../target_dir' にインストールされています。",
            "4. スクリプトの実行結果がエラーである場合、エラー文を元に対策を考え、修正したコードを再度作成します。"
            "5. スクリプトを実行した結果、情報を十分に得られなかった場合、現状得られた情報から修正したコードを再度作成します。",
            "6. あなたはユーザーの指示を最終目標とし、これを満たす迄何度もコードを作成・修正します。",
            "7. 目的を達成した場合、終了、とユーザーに伝えます。",
        ]
    ),
    llm_config=llm_config,
    code_execution_config=False,
    function_map=None,
    is_termination_msg=lambda msg: "終了" in msg,
    human_input_mode="ALWAYS",
)

# コードの作成者と実行者は分離する。以下はコード実行者 Agent。LLM は持たない。
code_execution_agent = ConversableAgent(
    "code-execution",
    llm_config=False,
    code_execution_config={"executor": executor},
    human_input_mode="ALWAYS",
)

user_message = "\n".join(
    [
        "'../target_dir' にはとあるアプリケーションのソースコードがあります。",
        "そのソースコードのファイルを確認して、何のアプリケーションかを推測してください。",
    ]
)

code_execution_agent.initiate_chat(code_writer_agent, message=user_message)

# work_dir をクリアする
import os
import shutil

shutil.rmtree(work_dir)
os.mkdir(work_dir)
```

## 結果

※ 注意: コードブロックをエスケープするために ``` は最後の文字を1つあけて `` ` とこの記事中では変換しています。実際の LLM の出力は ``` となっています。

```shell
% python -m pattern2
code-execution (to code-writer):

'../target_dir' にはとあるアプリケーションのソースコードがあります。
そのソースコードのファイルを確認して、何のアプリケーションかを推測してください。

--------------------------------------------------------------------------------
Provide feedback to code-execution. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: 

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...
code-writer (to code-execution):

了解しました。まずは'../target_dir'内のファイル一覧を表示して、ソースコードのファイルを確認してみましょう。

以下のコードを実行します。

`` `python
import os

target_dir = "../target_dir"

file_list = os.listdir(target_dir)

print("ファイル一覧:")
for file_name in file_list:
    print(file_name)
`` `

このコードは、'../target_dir'内に存在するファイルを一覧表示するものです。
実行結果のファイル一覧から、ソースコードのファイルを特定できるはずです。

--------------------------------------------------------------------------------
Provide feedback to code-writer. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: 

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING CODE BLOCK (inferred language is python)...
code-execution (to code-writer):

exitcode: 0 (execution succeeded)
Code output: ファイル一覧:
app.py


--------------------------------------------------------------------------------
Provide feedback to code-execution. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: 

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...
code-writer (to code-execution):

ありがとうございます。'../target_dir'内には「app.py」というファイルが存在していますね。おそらく、このアプリケーションのソースコードなのではないでしょうか。

もし「app.py」がソースコードのファイルである場合、そのアプリケーションの内容を確認するために「app.py」の中身を表示させることができます。

以下のコードを実行して、ソースコードの内容を確認してみましょう。

`` `python
with open("../target_dir/app.py", "r") as f:
    source_code = f.read()

print("ソースコード:")
print(source_code)
`` `

このコードは、「app.py」の中身を読み取り、表示するものです。
実行結果にソースコードが表示されるはずです。

--------------------------------------------------------------------------------
Provide feedback to code-writer. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: 

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING CODE BLOCK (inferred language is python)...
code-execution (to code-writer):

exitcode: 0 (execution succeeded)
Code output: ソースコード:
print("hello world")



--------------------------------------------------------------------------------
Provide feedback to code-execution. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: 

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...
code-writer (to code-execution):

ありがとうございます。ソースコードには以下のような内容が表示されましたね。

`` `
print("hello world")
`` `

このソースコードを見る限り、「hello world」という文字列を出力する非常にシンプルなアプリケーションのようです。おそらく、このアプリケーションは画面に「hello world」と表示するだけのプログラムと推測されます。

もし他にもファイルがある場合や、さらに詳細な解析が必要な場合はお知らせください。

--------------------------------------------------------------------------------
終了

```

## 簡単な解説

上記の結果から、

1. LLMが自動的にファイルの一覧を取得するスクリプトを作成し、これを実行
2. ファイル名からアプリケーションの種類を推測できなかった為、ファイルの中身を表示するスクリプトを作成し、これを実行
3. ファイルの中身からアプリケーションの種類を推測し、結果を表示

という流れで会話が進行しました。なお、途中で `NO HUMAN INPUT RECEIVED.` と表示されているのは、私が入力を行っていない為です。この会話の途中で都度都度LLMは私からの入力を待機します。これは生成されたスクリプトが例えば何か実行してはならない処理をしようとしていたときに、事前に止められないようにする為です。（例えば、 `rm -rf /` などされたら大変ですね）

## まとめ

以上が Agent を用いてファイルの種類を推測する会話の例となりました。今回はファイルの中身を確認するだけに留めましたが、アプリケーションを作成するAgentやテストするAgentを作成すればより高度なことが実現できるかもしれません。詳細は以下の論文も参照してください。

https://arxiv.org/abs/2403.08299

是非、私の過去記事も見ていってください〜