AIエージェント達の仮想ソフトウェア開発会社【ChatDev】を日本語対応のローカルLLMで動かしてみた【LMStudio】

## はじめに

AI技術が急速に進化する中で、AGIっぽいAIが注目を集めています。その中心となるのが、AIエージェント達が協力して動作する仮想ソフトウェア開発会社「ChatDev」です。この記事では、ChatDevを日本語環境で設定し、ローカルのLLM（Large Language Model）で動かす方法を紹介します。

## デモ動画

デモ動画では、ChatDevが実際に動作している様子を映像でご覧いただけます。AIエージェントたちがどのように協力し合いながらタスクを遂行するのか、その過程を詳細に追うことができます。


<iframe width="560" height="315" src="https://www.youtube.com/embed/KNfigtqA7xg?si=3r4wrk42YmFgGeL7" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>



## ChatDevとは

下記、元の論文です。

https://arxiv.org/abs/2312.17025


最近の大規模言語モデル（LLM）の進歩により、特にLLM駆動の自律エージェントを通じて、様々な分野に大きな変化がもたらされています。これらのエージェントは、タスクを分割し、精度を向上させることで、人間の関与を最小限に抑えることが可能になっています。しかし、これらのエージェントは多様なタスクに対して孤立して取り組むことが多く、過去の経験を活用することなく、繰り返しの間違いや非効率な試行を行ってしまうことがあります。

この問題に対処するため、本論文では「体験共同学習（Experiential Co-Learning）」という新しいフレームワークを紹介します。これは、指導者と助手の役割を持つエージェントが、過去の歴史的軌跡から短絡的な経験を収集し、これらの過去の経験を相互の推論に活用するものです。このパラダイムは、以前の経験で豊かになったことにより、エージェントが未見のタスクにより効果的に対処することを可能にします。

このアプローチにより、エージェントは単に現在のタスクに集中するのではなく、過去の経験から学んだ知識を活用して新しい問題に対応できるようになります。これにより、エージェント間の協力が向上し、未知のタスクに対してより迅速かつ効率的に取り組むことができるようになるのです。


## ChatDev＋ローカルLLM構成図

LM StudioをOpenAIのサーバーの代わりに通信する構成です。

![](https://hamaruki.com/wp-content/uploads/2023/12/chatdev-localLLM-1024x576.png)

## Dockerのセットアップ

Windows上でDockerを用いてGUI操作が可能な環境を構築します。これにより、ChatDevの実行環境を効率的に設定できます。

https://hamaruki.com/a-simple-guide-to-running-gui-apps-using-docker-on-wsl/


## ChatDev のセットアップ

### Quick Start

ChatDevの基本的なセットアップは、以下のリンクにあるガイドに従って行います。
[日本語版ChatDev Wiki](https://hamaruki.com/japanese-version-of-the-wiki-of-ai-agents-virtual-software-development-company-chatdev/) 

https://hamaruki.com/japanese-version-of-the-wiki-of-ai-agents-virtual-software-development-company-chatdev/

### 日本語版 RoleConfig.json の作成

各AIエージェントの役割を定義するRoleConfig.jsonファイルの日本語版作成方法については、こちらのガイドを参照してください。
[RoleConfig.json 日本語版ガイド](https://hamaruki.com/virtual-software-development-company-for-ai-agents-chatdev-i-created-a-japanese-version-of-roleconfig-json/) 

https://hamaruki.com/virtual-software-development-company-for-ai-agents-chatdev-i-created-a-japanese-version-of-roleconfig-json/



### 日本語版 PhaseConfig.json の作成

開発フェーズを定義するPhaseConfig.jsonファイルの日本語版作成に関する情報は、以下のリンクにあります。
[PhaseConfig.json 日本語版ガイド](https://hamaruki.com/virtual-software-development-company-for-ai-agents-chatdev/) 

https://hamaruki.com/virtual-software-development-company-for-ai-agents-chatdev/

## LM Studio のセットアップ

モデルのダウンロードからパラメータの調整、GPUの設定まで、LM Studioのセットアップを行います。

### LM Studio のダウンロード

下記からダウンロードします。

https://lmstudio.ai/

### モデルのダウンロード

今回は、「DeepSeek Coder」のGGUFである「TheBloke/deepseek-coder-6.7B-instruct-GGUF」を使用します。

https://huggingface.co/TheBloke/deepseek-coder-6.7B-instruct-GGUF

検索マークからモデルのidを入力してGoボタンを押すと候補が出てくるのでQ4以上のM以上をダウンロードします。
この辺りは自分のマシンのスペックと好みです。

![file](https://hamaruki.com/wp-content/uploads/2023/12/image-1703854836056.png)

RTX3060の私はこれを選択しました。

![file](https://hamaruki.com/wp-content/uploads/2023/12/image-1703854923475.png)

#### DeepSeek Coder とは

https://deepseekcoder.github.io/

DeepSeek Coderは、英語と中国語の両方で87%のコードと13%の自然言語を用いてゼロからトレーニングされた一連のコード言語モデルから成り立っています。各モデルは、2兆トークンで事前トレーニングされており、1B（10億）から33B（330億）バージョンまで、さまざまなサイズのコードモデルを提供しています。各モデルは、16Kのウィンドウサイズと追加の空欄埋めタスクを用いてリポジトリレベルのコードコーパス上で事前トレーニングされ、これにより基礎モデル（DeepSeek-Coder-Base）が形成されます。さらに、このベースモデルを2B（20億）トークンの指示データで微調整することで、指示チューニングモデルであるDeepSeek-Coder-Instructが作成されます。

- 2兆トークン以上にわたり、80以上のプログラミング言語で事前トレーニングされています。
- 異なる要件に対応するための様々なモデルサイズ（1.3B、5.7B、6.7B、33B）。
- 16Kのウィンドウサイズをサポートし、プロジェクトレベルのコード補完やインフィリングに対応。
- オープンコードモデルの中で最先端の性能を発揮。
- 研究および商業利用のためにオープンソースかつ無料で利用可能。

DeepSeek Coderは、プログラミングの自動化とコード生成の分野において、高い精度と多様なプログラミング言語に対応する能力を持っているモデルになります。

#### GGUFについて

最新のファイルフォーマット「GGUF」は、llama.cppで使用される旧来の.binファイルを進化させたものです。従来のフォーマットであるGGML、GGMF、GGJTの後継として設計されており、モデルのロードに必要な情報を全て含んでいます。このため、曖昧さを排除し、モデルに関する理解を深めることができます。

**GGUFの魅力**

1. **安定性の向上** : 以前のフォーマットでは、モデルに関する追加情報を加えることができないという問題がありました。GGUFはこの問題を解決し、変更時に生じる問題を最小限に抑えます。 
2. **多様なモデルへの対応** : llamaモデルだけでなく、falcon、rwkv、bloomなど、多様なモデルに対応しています。 
3. **操作性の向上** : 従来必要だったrope-freq-base、rope-freq-scale、gqa、rms-norm-epsなどの複雑な設定作業が不要になり、より手軽にモデルを使用できます。 
4. **自動化されたプロンプト設定** : プロンプトの形式設定が自動化され、よりスムーズなモデル操作が可能になります。

GGUFは、柔軟性と効率性を高めた新しいファイルフォーマットです。これにより、さまざまなモデルの操作と共有が以前に比べて格段に簡単になり、ユーザーはより便利にモデルを扱うことができるようになります。



### モデルの確認

モデルを有効にして試しに会話してみます。ちゃんと応答が返ってきたら問題ありません。
ここで、変な回答が帰ってくる場合、どこかの設定がミスってる可能性があります。

![file](https://hamaruki.com/wp-content/uploads/2023/12/image-1703855041884.png)

### サーバーの起動

サーバーのアイコンからサーバー画面を開いて、GPU オフロードにチェックを入れます。
これでGPUが使えます。CPUで動作させる場合は不要です。`-1`にすると全部の層がGPUに乗ります。GPUが足りない場合はここの数字を10とかにしてみてください。`Context length`は使用するトークン数の1.5倍くらいにするとエラーが出ずに回ります。


![file](https://hamaruki.com/wp-content/uploads/2023/12/image-1703855216315.png)


設定が終わったら「Start server」で起動させます。下記のような画面になったら問題ありません。

![file](https://hamaruki.com/wp-content/uploads/2023/12/image-1703855406496.png)


## ChatDev の起動

### 環境変数の設定

環境変数の`.env`ファイルを作成します。

```env


# Set your OpenAI API key here
OPENAI_API_KEY=sk-DUMMY
# BASE_URL="http://api:8080/v1"
BASE_URL="http://host.docker.internal:1234/v1"

# Set your display IP address here
DISPLAY=:0
PULSE_SERVER=/mnt/wslg/PulseServer
WAYLAND_DISPLAY=wayland-0
XDG_RUNTIME_DIR=/run/user/1000/


```

### docker-compose の起動

必ずWSLで起動しましょう。コマンドプロンプトではGUIが共有されません。

```bash

maki@maki-lab3060V3:/mnt/e/Prj/ChatDev$ docker-compose up --build
[+] Building 57.1s (12/12) FINISHED                                                          docker:default
 => [chatdev internal] load build definition from Dockerfile                                           0.1s
 => => transferring dockerfile: 673B                                                                   0.0s
 => [chatdev internal] load .dockerignore                                                              0.1s
 => => transferring context: 2B                                                                        0.0s
 => [chatdev internal] load metadata for docker.io/library/python:3.9-slim                             3.0s
 => [chatdev auth] library/python:pull token for registry-1.docker.io                                  0.0s
 => [chatdev 1/6] FROM docker.io/library/python:3.9-slim@sha256:96be08c44307e781fd9ce8e05b49c969b4cb9  0.0s
 => [chatdev internal] load build context                                                              2.3s
 => => transferring context: 136.36MB                                                                  2.2s
 => CACHED [chatdev 2/6] WORKDIR /app                                                                  0.0s
 => [chatdev 3/6] COPY . /app                                                                          4.7s
 => [chatdev 4/6] RUN apt-get update && apt-get install -y python3-tk x11-apps curl iputils-ping net  27.7s
 => [chatdev 5/6] RUN pip install --no-cache-dir -r requirements.txt                                  16.4s
 => [chatdev 6/6] RUN apt update -y &&    apt-get install x11-apps -y;                                 1.9s
 => [chatdev] exporting to image                                                                       0.7s
 => => exporting layers                                                                                0.6s
 => => writing image sha256:c25fc6d635e5209aa42b31b4810ffa7f4df3cc5e4eab37aaba5f6b070929b38c           0.0s
 => => naming to docker.io/library/chatdev-chatdev                                                     0.0s
[+] Running 1/1
 ✔ Container chatdev-chatdev-1  Recreated                                                              0.2s
Attaching to chatdev-chatdev-1
```

### 疎通確認

Docker環境からLM Studioにプロンプトを送信して、疎通の確認を行います。これにより、システム間の正常な通信を保証します。

```bash

root@166bfe2b168f:/app# python clientOpenAI.py
As an AI, I don't need to introduce myself as the "I" doesn't exist in my programming. However, if you would like, here is a standard introduction: Hello and welcome to our conversation. My name is [AI's Name]. How can I assist you today?

root@166bfe2b168f:/app#
```

#### clientOpenAI.py

 
ここでは必要なライブラリをインポートしています。`os` は環境変数の読み込み、`openai` はOpenAIのAPIとのインタラクション、`pickle` はPythonオブジェクトの永続化および復元のために使用されます。

```python

# 環境変数の設定
OPENAI_API_KEY = os.environ['OPENAI_API_KEY']
BASE_URL = os.environ.get('BASE_URL', "http://host.docker.internal:1234/v1")

openai.api_base = BASE_URL
openai.api_key = "dummy"
```

 
ここでは環境変数からOpenAI APIの設定を読み込んでいます。APIキーとベースURLが設定されています。`"dummy"` はおそらくテスト目的のためのプレースホルダーです。

```python

def load_args_and_run():
    with open('args.pickle', 'rb') as f:
        data = pickle.load(f)
        args = data['args']
        kwargs = data['kwargs']
        model_config_dict = data['model_config_dict']

    model_config_dict["max_tokens"] = 8000
    model_name = "C:\\path\\to\\model.gguf"
    response = openai.ChatCompletion.create(*args, **kwargs, model=model_name, **model_config_dict)
    print(response.choices[0].message.content)
```

 
この関数は、`args.pickle` から引数を読み込んでOpenAI APIを呼び出すためのものです。`model_config_dict` はモデルの設定を含み、`max_tokens` を8000に設定しています。`model_name` は使用するモデルのパスです。最後に、APIを呼び出して結果を出力しています。

```python

# 関数を実行
# load_args_and_run()
```


ここでは上記の関数を実行するコードですが、コメントアウトされているため実際には実行されません。
デバッグ用のコードです。

```python

# ChatCompletion の実行
completion = openai.ChatCompletion.create(
    model="C:\\path\\to\\model.gguf",
    messages=[
        {"role": "system", "content": "Always answer in rhymes."},
        {"role": "user", "content": "Introduce yourself."}
    ]
)

print(completion.choices[0].essage.content)
```

 
最後に、`openai.ChatCompletion.create` を直接呼び出して、特定のモデルで会話を生成しています。この部分は、システムとユーザーのロールを持つメッセージを使用してチャット会話をシミュレートしています。そして、生成された応答を出力しています。




## ChatDev にタスクを与えてみる

実際にChatDevにタスクを与え、AIエージェントがどのように協力してタスクを遂行するかを確認します。

```bash

root@166bfe2b168f:/app# python3 run.py --task "Create a simple clock with Python" --name "DEEPclockJP3" --config "DefaultJP2"  --model "nekomata-14b-instruction-Q4_K_M"






Warning: Your OpenAI version is outdated.
 Please update as specified in requirement.txt.
 The old API interface is deprecated and will no longer be supported.
**[Preprocessing]**

**ChatDev Starts** (20231229102519)

**Timestamp**: 20231229102519

**config_path**: /app/CompanyConfig/DefaultJP2/ChatChainConfig.json

**config_phase_path**: /app/CompanyConfig/DefaultJP2/PhaseConfig.json

**config_role_path**: /app/CompanyConfig/DefaultJP2/RoleConfig.json

**task_prompt**: Create a simple clock with Python

**project_name**: DEEPclockJP3

**Log File**: /app/WareHouse/DEEPclockJP3_DefaultOrganization_20231229102519.log

**ChatDevConfig**:
ChatEnvConfig.clear_structure: True
ChatEnvConfig.git_management: False
ChatEnvConfig.gui_design: True
ChatEnvConfig.incremental_develop: False
ChatEnvConfig.background_prompt: ChatDev は、最高経営責任者、最高人事責任者、最高製品責任者、最高技術責任者 など、日本の複数のインテリジェ ントなエージェントを擁する日本のソフトウェア会社であり、マルチエージェント組織構造と'デジタルを変える'という使命を持っています。プログラミ ングを通じて世界へ。日本語の書類を使ってコミュニケーションをとり、日本語で会話をします。
```



## ChatDevの生成結果

### Software Info

```bash


Software Info: 

💰**cost**=$0.000000

🔨**version_updates**=8.0

📃**num_code_files**=3

🏞**num_png_files**=0

📚**num_doc_files**=8

📃**code_lines**=37

📋**env_lines**=7

📒**manual_lines**=25

🗣**num_utterances**=72

🤔**num_self_reflections**=3

❓**num_prompt_tokens**=276190

❗**num_completion_tokens**=14440

🌟**num_total_tokens**=290630

🕑**duration**=2343.00s

ChatDev Starts (20231229102519)

ChatDev Ends (20231229110422)

```


### main

```python

from clock import Clock
from display import Display
import time
def main():
     # 時計を作成
    clock = Clock()
     # GUI表示を作成
    display = Display(clock)
    while True:
         # 現在の時刻を取得し、GUIに反映
        current_time = clock.get_current_time()
        display.update_display(current_time)
         # 1秒待機
        time.sleep(1)
```

下記のように若干修正したら無事に動きました！！

![file](https://hamaruki.com/wp-content/uploads/2023/12/image-1703856612127.png)

---


## リポジトリ

https://github.com/Sunwood-ai-labs/ChatDev