【AIファクトリー】超ごり押し！AIがAIを作るシステムを作る（その1）

# はじめに
こんにちは！

ノベルワークスでアルバイトをしているザワッチ([@zawatti](https://x.com/zawattizawawa))です。

意味不明なタイトルですが、AIがAIを作るシステムを作ったので、紹介します。

# 背景
なにはともあれ、OpenAI社が出しているswarmというマルチエージェントフレームワークが使いやすくて、拡張性が高くて、使いやすすぎるということを知ってほしいです！

https://github.com/openai/swarm?tab=readme-ov-file#utils

swarmを通じて、

- エージェントフレームワーク最近いろいろ出てきたけど、どこまでできるか
- AIエージェントの制御感
- AIエージェントのアウトプットのクオリティ
- AIにAIを作らせられるのか

以上を調査していきます。

# AIエージェントについて
AIエージェントの定義は、いろいろあり、人それぞれの解釈でいいと思いますが、私は、
「与えられた目的を達成するために、関数（ツール）を駆使して、Plan-Do-Check-Action（PDCA)サイクルを行う主体としてのAI」と捉えています。

ワークフローのように、ある入力をもとに決められた順序で決められた関数を処理していくのではなく、AIエージェントはある入力をもとに、どの順序で関数を実行していくか、そしてどのような最終アウトプットを出すのかも自律的に決定していきます。

この点、AIエージェントは人間の脳内で行っている処理の流れを模倣しており、AIエージェントの完成系は、人間の脳みそと代替可能な代物であると考えています。

# システム概要
まず、エージェント設計を行います。

エージェントの粒度の考え方は、人間の役職に照らし合わせると、想像しやすいです。

今回は、AIを作るということで、AIチームにいそうな人達を仮で作ってみて、エージェントに落とし込みます。

- モデルを設計する人
- データセットを作る人
- モデルを学習させる人
- 学習されたモデルを評価する人
- AIチームのマネージャー

次に、各エージェントの手足を設計します。

例えば、モデルの設計者であれば、与えられた要件を満たすモデルの設計やアルゴリズムの選定、ハイパーパラメータの選定などがあります。

モデルを学習させる人であれば、設計されたモデル構造に基づいたデータセットの作成が挙げられます。

このような人間が行うアクションを関数としてとらえることで、エージェントが行う処理をコンピューターの世界に落とし込みます。

これらのエージェントが実行したい環境をも作らせるようにし、各エージェント間の共通の環境として使用できます。

以下が、システム概要図になります。

![AIファクトリーの全体像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478853/5e5c7b99-b445-44b9-a408-be36c2ede5ae.png "AIファクトリーの全体像")

マネージャーがどのエージェントを呼び出し、実行させるかを決め、選ばれたエージェントの実行結果をもとに、次のエージェントを選択し、ユーザーが求めるAIを構築するよう努めます。

ユーザが求める出力をモデルファイルの出力とします。

AIエージェントの実行プラットフォームは、このシステムを実行する環境になります。

実行履歴は、チャット履歴のように出力結果をエージェント間で共有します。

また、各エージェントのツールは、関数単位で定義でき、かつ拡張性が非常に高いです。

以下は、モデルを訓練する人をswarmを使用して、エージェントとして定義しているコードです。
```python
from swarm import Agent
from agents.Trainer.utils import execute_command, create_requirements_file, create_virtual_environment, read_model_file, create_train_file, update_train_file, update_requirements_file
from agents.Evaluator.main import transfer_evaluator
    
train_ai = Agent(
    name= "Model Trainer",
    model= "gpt-4o",
    instructions="""
    あなたは、構築されたモデルと作成されたデータセットをもとに、モデルを学習させる人です、
    1. model.pyファイルを読み込み、train.pyという学習ファイル名で、作成すること。
    2. 学習ファイルに基づいて、学習を行い、モデルファイルを出力すること。
    3. モデルの評価者に依頼すること。
    """,
    functions=[create_train_file, create_virtual_environment, execute_command, create_requirements_file, transfer_evaluator, read_model_file, update_requirements_file, update_train_file]
)

def transfer_trainer():
    return train_ai
```

Agentというクラスに、モデルを訓練する人の振る舞いやどのようなfunctions(ツール)が使えるかを定義します。

functionsのベースは、ファイルIOがほとんどなので、file_name、content(コード内容)を生成して、それぞれの関数の引数にして呼び出す形になり、かなりシンプルです。

```python
def create_train_file(file_name: str, content: str):
    try:
        with open(f'./{file_name}', 'w', encoding='utf-8') as file:
            file.write(content)
    except IOError as e:
        return Result(
            value="Failed",
            context_variables={
            "error_message": e
            }
        )
        
    return Result(
        value="Done",
        context_variables={
            "train_file_path": file_name
        }
    )
```

※要望があれば、全コード公開します。

# デモ

https://x.com/zawattizawawa/status/1889710199075746046


## エージェントによって生成されたファイル群
以下の写真は、デモ中に各エージェントによって生成されたファイル群を表示しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478853/7bb87929-d466-4427-be65-24431a3d9d90.png)

エージェントごとにどんなファイルが生成されたか詳細に見ていきます。

### モデルを設計する人
- model.py
```python
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline

class EmailCategoryClassifier():
    def __init__(self):
        # Create a model with TF-IDF vectorizer and a Naive Bayes classifier
        self.model = make_pipeline(TfidfVectorizer(), MultinomialNB())

    def train(self, X_train, y_train):
        """
        Train the model with training data
        :param X_train: List of training examples (emails)
        :param y_train: List of labels corresponding to the training examples
        """
        self.model.fit(X_train, y_train)

    def predict(self, X):
        """
        Predict the categories of input examples
        :param X: List of examples (emails) to categorize
        :return: List of predicted categories
        """
        return self.model.predict(X)

    def score(self, X_test, y_test):
        """
        Compute the accuracy of the model
        :param X_test: List of test examples (emails)
        :param y_test: List of true labels for the test examples
        :return: Accuracy of the model on the test data
        """
        return self.model.score(X_test, y_test)
```



### データセットを作る人

- emails_train.csv
```csvs
email,category
"Subject: Meeting reminder","Work"
"Subject: Special promotion available now!","Spam"
"Subject: Your recent purchase details","Transaction"
"Subject: Team lunch this Friday","Work"
"Subject: Don't miss our summer offers!","Spam"
"Subject: Invoice #12345","Transaction"
"Subject: New policy updates","Work"
"Subject: Get your free gift voucher!","Spam"
"Subject: Amazon Order Receipt","Transaction"
```
- emails_test.csv
```csvs
email,category
"Subject: Urgent action required","Work"
"Subject: You've won a prize!","Spam"
"Subject: Order Confirmation","Transaction"
"Subject: Meeting scheduled for Monday","Work"
"Subject: Congratulations! You've been selected!","Spam"
"Subject: Payment Confirmation","Transaction"
"Subject: Workflow changes notification","Work"
"Subject: Limited time sale on all items!","Spam"
"Subject: Delivery Notification","Transaction"
```

### モデルを学習させる人
- train.py

```python
import pandas as pd
from sklearn.model_selection import train_test_split
from model import EmailCategoryClassifier
import joblib

# Load the dataset
train_data = pd.read_csv('emails_train.csv')
test_data = pd.read_csv('emails_test.csv')

# Prepare training and test data
X_train = train_data['email']
y_train = train_data['category']
X_test = test_data['email']
y_test = test_data['category']

# Initialize and train the classifier
classifier = EmailCategoryClassifier()
classifier.train(X_train, y_train)

# Evaluate the model
accuracy = classifier.score(X_test, y_test)
print(f'Model accuracy: {accuracy:.2f}')

# Save the trained model
joblib.dump(classifier, 'email_category_classifier.pkl')
```

- requirements.txt
```text 
pandas
scikit-learn
joblib
```

- email_category_classifier.pkl
学習され、生成されたモデルファイル
- myvenv
requierments.txtに記載しているライブラリがインストールされた仮想環境

### モデルを評価する人
残念ながら、デモ中にはうまく評価してくれませんでした(´;ω;｀)

- evaluate_model.py
```python
import pandas as pd
from sklearn.metrics import accuracy_score, classification_report
import joblib

# Load the trained model
classifier = joblib.load('email_category_classifier.pkl')

# Load the test data
emails_test = pd.read_csv('emails_test.csv')
X_test = emails_test['email']
y_true = emails_test['category']

# Make predictions
y_pred = classifier.predict(X_test)

# Evaluate the model
accuracy = accuracy_score(y_true, y_pred)
report = classification_report(y_true, y_pred)

# Print the evaluation metrics
with open('evaluation_results.txt', 'w') as f:
    f.write(f'Model Accuracy: {accuracy:.2f}\n')
    f.write('Classification Report:\n')
    f.write(report)

print(f'Model Accuracy: {accuracy:.2f}')
print('Classification Report:')
print(report)
```

# 生成されたAIモデルを動かす
モデルを動かす方法や手順を教えてくれるテックライター的なエージェントを設けていなかったので、train.pyをもとにモデルの推論コード(inference.py)を作成します。

```python
import joblib

classifier = joblib.load('email_category_classifier.pkl')

new_emails = [
    "Subject: Meeting reminder",
    "Subject: Don't miss our Big winter offers",
    "Subject: Limited Time Offer: Get Your Meds at 90% Discount!",
    "Subject: Order Completion Notification",
    "Subject: Reservations for today's (February 10, 2025) boarding"
]

predicted_categories = classifier.predict(new_emails)

for email, category in zip(new_emails, predicted_categories):
    print("メール:", email)
    print("予測されたカテゴリ:", category)
```

- <strong>Subject: Meeting reminder ： Work</strong>
→ 訓練データに使用されている（正解して当たり前）

- <strong>Subject: Don't miss our Big winter offers：Spam</strong>
→ 訓練データに似たようなものがあり、summerをBig winterに変えた（正解してくれると、ちょっと嬉しい）

- <strong>Subject: Limited Time Offer: Get Your Meds at 90% Discount!：Spam</strong>
→ スパムにありそうな例で作成したもの（正解してくれると、嬉しい）

- <strong>Subject: Reservations for today's (February 10, 2025) boarding：Transaction</strong>
→ 私の新幹線の予約通知メールを英訳したもの（正解してくれると、めっちゃうれしい）

上記のコードで動かしてみると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478853/ad4e05f7-19b3-4a65-aca7-91359c1429be.png)


ちゃんと学習されているのか！？
けっこうええ感じやん！

# 思ったこと
- 仮想環境の作成と、ファイル操作回りができてるんだったら、ローカルLLMを駆使して、自作Code Interpretterもできるのでは

- 各エージェントに検索機能（検索生成AIもしくは検索エンジンのAPIをラップした関数）を使えば、様々な形式・容量のデータセットもダウンロードできたり、最新の論文拾ってきて、最適なモデルを提案することもできそう

- 各エージェントに使用するモデルをより性能の良いものにしていけば、AIエージェントとしての性能も上がっていくのはすごく楽でいい

- エラーが出ても止めずに、それすらもコンテキストで与え、解決していくところを見て、ヤバいなと思った（ChatGPTのエラー文のコピペとやってることはほぼ同じであるが、勝手にやってくれるところにエージェント感を感じた）

# さいごに
かなりごり押しにはなりましたが、簡単なAIであれば、AIにAIを作らせることが可能であることがわかりました。

AIエージェント駆使したら、できることがいろいろありそうで、むしろ人間のユースケースを考える力がボトルネックになっていると思いました。

おそらく、今のAIの性能でできることはいろいろあるはずです。

その2では、フロントエンドとも統合し、各エージェントの活動状況を見える化したり、最終的にはユーザはモデルを動かすことを想定し、手順書を作成するテックライターエージェントを追加し、本システムをさらに洗練されたものにしていきます。