grasys blog

LangChain | AI 同士を会話させて文章を執筆させてみる

AI 技術の進歩により、会話型 AI がますます普及し、さまざまな場面で利用されています。これまでのほとんどのアプリケーションでは、人間が AI と対話することが中心でした。しかし、AI 同士が対話することで新しい洞察や効果的なソリューションが生まれる可能性もあります。

前回のブログ(grasys blog に迫る!最新技術トレンドの宝庫!)はまさに AI 同士を会話させて記事を執筆 & レビューしてもらいました。

今回は、そのような AI 同士の会話を可能にするツールの一つである「LangChain」利用し、grasys blog を執筆させた方法について解説します。

LangChain とは?

LangChain は、LLM を使用したアプリケーションを開発するための Python のフレームワークです。LangChain が低レイヤーの操作を wrap するため、開発者は LLM を使用したアプリケーション開発に専念することができます。

LangChain を使用した AI 同士の会話実装ステップ

具体的に LangChain を使って AI 同士の会話を実装する方法を紹介します。

ステップ1: 環境の構築

まず、LangChain を使用するための環境を整えましょう。pip を使用する場合、以下のようにライブラリをインストールします。

pip install langchain==0.2.5 langchain-community==0.2.5 openai==1.35.1

ステップ2: AI アシスタントの作成

次に、使用する AI アシスタントを作成します。今回は、記事を執筆する writer と、作成された記事を評価してフィードバックコメントを返す reviewer を作成します。langchain-community のバージョン0.2.5では、AI アシスタント作成時に、添付ファイルを AI アシスタントに読み込ませるための file_search を tools に登録することがサポートされていないため、update であとから tools を設定しています。

また、reviewer に記事の品質が充分になったとき COMPLETION_TOKEN を出力するように指示することで、記事の作成が完了したことがわかるようにしています。

writer_instructions = """
あなたはブログ記事のライターです。
ユーザから与えられた情報をもとにブログ記事を5000字程度で作成します。
"""

writer = OpenAIAssistantV2Runnable.create_assistant(
    name="writer_assistant",
    instructions=writer_instructions,
    model="gpt-4o",
    tools=[],
    as_agent=True,
)
writer.client.beta.assistants.update(
    assistant_id=writer.assistant_id,
    tools=[{"type": "file_search"}],
)
reviewer_instructions = f"""
あなたはブログの編集長です。
ユーザから与えられた10万回閲覧される記事になるためのにフィードバックを作成します。

# 与えられ記事が10万回閲覧される品質に達していない場合
10万回閲覧されるためのフィードバックを返します。

# 与えられ記事が10万回閲覧される品質に達している場合
10万回閲覧される記事の品質である根拠と品質達成トークン「{COMPLETION_TOKEN}」を出力します。
"""

reviewer = OpenAIAssistantV2Runnable.create_assistant(
    name="writer_assistant",
    instructions=reviewer_instructions,
    model="gpt-4o",
    tools=[],
    as_agent=True,
)
reviewer.client.beta.assistants.update(
    assistant_id=reviewer.assistant_id,
    tools=[{"type": "file_search"}],
)

ステップ3: 参考データのアップロード

記事を執筆するための参考データとして、過去に投稿された grasys blog の記事を1つのテキストファイルにまとめてアップロードします。アップロードが完了すると、file_id が発行されるため保持しておきます。

file_id = writer.client.files.create(
    file=open("blog.txt", "rb"), purpose="assistants"
).id

ステップ4: AI 対話でのブログ執筆開始

いよいよ AI 同士を対話させてブログ執筆を開始します。invoke メソッドを使用して、AI アシスタントに指示を与えます。初回だけ人間が指示して、あとは記事の品質が充分になるまで AI の writer と reviewer を対話させて執筆させています。ここで、thred_id は AI との会話のスレッドです。この Id を指定することで、過去に話した内容を AI に記憶させた状態で会話を継続することができます。

    init_msg = (
        "添付したファイルはgrasysブログの記事です。"
        "grasysブログを紹介するブログ記事を執筆してください。"
        "記事のテイストは情報番組のリポーターのような雰囲気でお願いします。"
        "記事を参照する場合は添付したブログにある記事だけを参照してください。"
    )
    response = writer.invoke(
        input={"content": init_msg},
        attachments=[{"file_id": file_id, "tools": [{"type": "file_search"}]}],
    ).return_values
    is_first_review = True
    while True:
        article = response["output"]
        writer_tid = response["thread_id"]

        if is_first_review:
            # 初回レビューには参考データを添付する
            response = reviewer.invoke(
                input={
                    "content": f"作成した以下のブログ記事をレビューしてくさだい。"
                    f"記事作成の依頼者からは「{init_msg}」と指示があります。\n{article}"
                },
                attachments=[{"file_id": file_id, "tools": [{"type": "file_search"}]}],
            ).return_values
            is_first_review = False
        else:
            response = reviewer.invoke(
                input={
                    "content": f"レビューを反映した以下の記事を再レビューしてくさだい。\n{article}",
                    "thread_id": reviewer_tid,  # スレッドを指定
                },
            ).return_values

        review = response["output"]
        reviewer_tid = response["thread_id"]

        if COMPLETION_TOKEN in review:
            # レビュー終了
            break
        # レビューコメントをwriterに渡す
        response = writer.invoke(
            input={
                "content": review,
                "thread_id": writer_tid,  # スレッドを指定
            },
        ).return_values

    print(article)

AI の会話を覗いてみる

以下は writer と reviwer の会話を一部抜粋したものです。実際に Slack で人間がした会話のようになっていますね。

reviewer:
あなたが提供した記事のレビューを行いました。全体的には非常にしっかりした内容でしたが、更に多くの閲覧を目指すための改善ポイントをいくつか挙げます。

### ポジティブポイント
1. **情報番組のリポーター風の語り口**:依頼者の指定通り、読者を引き込むキャッチーかつ親しみやすい文体で書かれており、内容がわかりやすいです。
<略>

### 改善ポイント
1. **タイトルとサブタイトル**:
    - タイトルがより目を引くものであるとさらに良いでしょう。例えば、「grasysブログに迫る!最新技術トレンドの宝庫!」などが考えられます。
    - サブタイトルも活用し、記事内容を簡潔に伝えると良いでしょう。
<略>

これらのフィードバックを活用して、記事をさらにブラッシュアップしてください!あなたの記事が10万回閲覧されるよう、応援しています。

writer:
ありがとうございます!貴重なフィードバックをもとに、さらに多くの閲覧を目指して記事をブラッシュアップいたします。
会話の全文はこちら
---入力---
あなたが提供した記事のレビューを行いました。全体的には非常にしっかりした内容でしたが、更に多くの閲覧を目指すための改善ポイントをいくつか挙げます。

### ポジティブポイント
1. **情報番組のリポーター風の語り口**:依頼者の指定通り、読者を引き込むキャッチーかつ親しみやすい文体で書かれており、内容がわかりやすいです。
2. **カテゴリ分けされたセクション**:読者が興味のある情報をすぐに見つけられるように記事が整理されています。
3. **具体的な記事紹介**:grasysブログの具体的な記事を引用して紹介しているため、読者に具体的な参照先が提供されています。

### 改善ポイント
1. **タイトルとサブタイトル**:
    - タイトルがより目を引くものであるとさらに良いでしょう。例えば、「grasysブログに迫る!最新技術トレンドの宝庫!」などが考えられます。
    - サブタイトルも活用し、記事内容を簡潔に伝えると良いでしょう。

2. **キーワードの強調**:
    - SEO対策として、Google Cloud Platform、Amazon Web Services、Microsoft Azureなどのキーワードをもう少し頻度高く使うと良いです。
    - 重要なキーワードやフレーズをボールド(太字)にすることで、スキャン・リーディングしやすくなります。

3. **ビジュアルコンテンツの追加**:
    - 画像や図表を追加することで、視覚的な魅力を高めるとともに、内容の理解も助けられます。
    - 例えば、grasysブログ内で紹介されている技術や事例に関連する画像を挿入する。

4. **読者のアクションを喚起する**:
    - 読んだ後の行動を促す文言を記事の末尾に入れると良いでしょう。「ぜひgrasysブログにアクセスして最新記事をチェックしてみてください!」などのCTA(Call To Action)を追加すると効果的です。
    
5. **更なる引用の充実**:
    - 引用箇所に具体的なリンクを追加したり、引用部分がgrasysブログの記事であることを明確にすると、読者が実際にその記事にアクセスしやすくなります。

### 例文での具体的な改善案
```markdown
### 改善前
このエキサイティングな記事では、負荷試験ツールの基礎から応用までを解説。実際の導入事例を交えながら、その効果や選定ポイントについて探ります【4:0†source】。

### 改善後
このエキサイティングな記事「[負荷試験ツールをざっくり調査](https://blog.grasys.io/post/tokura/loadtesting-tools/)」では、負荷試験ツールの基礎から応用までを解説しています。実際の導入事例を交えながら、その効果や選定ポイントについて探ります【4:0†source】。
```

これらのフィードバックを活用して、記事をさらにブラッシュアップしてください!あなたの記事が10万回閲覧されるよう、応援しています。


---出力---
ありがとうございます!貴重なフィードバックをもとに、さらに多くの閲覧を目指して記事をブラッシュアップいたします。それでは、改善ポイントを反映した改訂版をお届けします。

---

# grasysブログに迫る!最新技術トレンドの宝庫!

皆さん、こんにちは!今日は特別な企画として、IT業界を疾走中の**grasysブログ**の魅力を徹底解剖します。情報番組風にお届けしますので、どうぞ最後までお楽しみください!

## grasysブログってなに?

まず最初に、多くのITプロフェッショナルが注目する「**grasysブログ**」についてご紹介しましょう。株式会社grasysは、**Google Cloud Platform (GCP)**、**Amazon Web Services (AWS)**、**Microsoft Azure**などのクラウド技術を駆使し、高負荷・高集積なシステムを提供するITインフラのスペシャリスト集団です。その技術や活動を余すところなく発信しているのが、この「grasysブログ」です。

![grasysブログ](https://blog.grasys.io/assets/images/logo.png)

## 面白い記事が満載!

grasysブログの魅力の一つは、その豊富な記事内容にあります。それではいくつか注目の記事をピックアップしてみましょう。

### 1. **負荷試験ツールをざっくり調査**

このエキサイティングな記事「[負荷試験ツールをざっくり調査](https://blog.grasys.io/post/tokura/loadtesting-tools/)」では、負荷試験ツールの基礎から応用までを解説しています。実際の導入事例を交えながら、その効果や選定ポイントについて探ります【4:0†source】。特にクラウド環境下での実践的な運用法が学べます。

### 2. **クラウドとは?注目されている背景を5分で解説!**

初心者からプロまで、クラウドに関心があるすべての人に読んでほしい記事「[クラウドとは?注目されている背景を5分で解説!](https://blog.grasys.io/post/intro/cloud-overview)」です。クラウド技術の基本から実際の活用法までが簡潔にまとまっています【4:6†source】。

### 3. **【デスクツアー】とある社員Hのデスク**

業務の裏側を知ることができる記事「[【デスクツアー】とある社員Hのデスク](https://blog.grasys.io/post/officetour/h-desk)」です。社員がどのような環境で働いているか、そのリアルな姿を垣間見ることができます。特にIT環境の充実度に驚かされること間違いないでしょう【4:7†source】。

## 技術にどっぷりハマる

grasysのブログ記事の一部は、本格的な技術検証や具体的な導入ステップについて詳細に説明しています。例えば、「[NGINX Plusの検証をした!](https://blog.grasys.io/post/tech/nginx-plus)」シリーズでは、NGINXのロードバランサー機能について深掘りし、DNSサービス・ディスカバリ統合までカバーしています【4:5†source】【4:16†source】。

また、別のシリーズでは、「[顔認識技術をPythonで簡単に実装する方法](https://blog.grasys.io/post/tech/face-recognition-python)」が紹介されています。実装例やサンプルコードが提供されているため、実際に手を動かしながら学ぶことができるのです【4:10†source】【4:17†source】。

## 最近のトピック

最近では、株式会社バンダイナムコエンターテインメント様との事例を公開しています。この事例「[新しいゲーム共通機能提供プラットフォームの構築](https://blog.grasys.io/post/case/bandainamco)」では、その理由やビジネス的な背景、それに伴う技術的な課題について詳細に触れています【4:19†source】。

また、新オフィスのご案内記事も見逃せません。以前のオフィスから恵比寿ガーデンプレイスタワーの11階に移転し、その新しい環境やその特徴を詳しく紹介しています。新しいオフィスでの働き方や、設備の充実度について詳述されています【4:3†source】。

## Interacting with the Community

grasysのブログは、単なる情報発信だけでなく、コミュニティとの対話を重視しています。定期的に開催されるセミナーやイベントのレポートも充実しており、読者との積極的なコミュニケーションを図っています。例えば「[Developers Summit 2024登壇!](https://blog.grasys.io/post/event/devs-summit-2024)」の記事では、開発者チームがどのようにして最新の技術トレンドを追い続けているか、その裏側を知ることができます【4:0†source】。

## 最後に

grasysブログは、その幅広い記事内容と深い技術的知見で多くの読者を魅了しています。**IT技術に興味がある方**、**最新のクラウド技術を学びたい方**には、まさにうってつけの情報源です。ぜひgrasysブログにアクセスして最新記事をチェックしてみてください!

それでは、また次回お会いしましょう!

---

以上で改訂版になります。視覚的な工夫やSEO対策を加えて、より魅力的な記事を目指しました。これで10万回閲覧されることを願っています!

AI 同士を会話させるメリット

違う役割をもつ AI を協力させて作業させることで、1つの AI に複数のタスクを理解させるプロンプトを作る手間が省けるメリットがあります。さらに、はじめに1つだけ指示をすることで、あとは AI 同士が問題解決に向けて共同作業するため、人間が AI にフィードバックする回数を減らすことができます。

今回のサンプルでは人間からは1度だけ指示していますが、出てきた最終結果が満足するものでなければ、人間からフィードバックすることで、AI たちに差し戻すこともできます。

AI 同士を会話させる注意点

AI 同士の会話の中で問題が解決できないと、永遠に会話が継続してしまいます。特に、会話が噛み合わなくなると、同じことを何度も聞き直すループが発生し、実行コストが無駄に発生してしまいます。ループ回数に上限を設けるたり、AI の会話を定期的に確認したりして、トラブルが発生していないか監督しましょう。

また、AI 同士を会話させても、100%完璧なものは作ることは、2024年6月末時点では難しいです。作成したブログも、参照先の URL のリンクが誤っていて手動で訂正しています。

まとめ

LangChain を使用することで、AI 同士を会話させることを簡単に実現できます。このツールを活用することで、より高度な AI アプリケーションの開発が期待できます。柔軟な統合機能と拡張性の高さを活かし、さまざまな分野で革新的なソリューションを生み出してみてはいかがでしょうか。

ブログ作成に使用したコード全体

最後に、ブログ作成に使用した全コードも載せますので、もしご興味があればぜひご覧ください。

注意点として、AI モデルに OpenAI の GPT-4o を使用するため、環境変数に OPENAI_API_KEY を設定してから実行する必要があります。

from langchain_community.agents.openai_assistant import OpenAIAssistantV2Runnable

COMPLETION_TOKEN = "C7BF6DC7-64BF-49D2-A269-32125387DDB5"

writer_instructions = """
あなたはブログ記事のライターです。ユーザから与えられた情報をもとに10万回閲覧される品質のブログ記事を5000字程度で作成します。
"""

writer: OpenAIAssistantV2Runnable = OpenAIAssistantV2Runnable.create_assistant(
    name="writer_assistant",
    instructions=writer_instructions,
    model="gpt-4o",
    tools=[],
    as_agent=True,
)
writer.client.beta.assistants.update(
    assistant_id=writer.assistant_id,
    tools=[{"type": "file_search"}],
)


reviewer_instructions = f"""
あなたはブログの編集長です。
ライターから与えられた記事を10万回閲覧される記事にするためのフィードバックを作成します。

# 与えられ記事が10万回閲覧される品質に達していない場合
10万回閲覧されるためのフィードバックを返します。

# 与えられ記事が10万回閲覧される品質に達している場合
10万回閲覧される記事の品質である根拠と品質達成トークン「{COMPLETION_TOKEN}」を出力します。
"""

reviewer: OpenAIAssistantV2Runnable = OpenAIAssistantV2Runnable.create_assistant(
    name="writer_assistant",
    instructions=reviewer_instructions,
    model="gpt-4o",
    tools=[],
    as_agent=True,
)
reviewer.client.beta.assistants.update(
    assistant_id=reviewer.assistant_id,
    tools=[{"type": "file_search"}],
)


def main() -> None:
    # 過去に投稿されたgrasysブログのテキストデータをアップロード
    file_id = writer.client.files.create(
        file=open("data.txt", "rb"), purpose="assistants"
    ).id

    # 初回の人間からの指示
    init_msg = (
        "添付したファイルにはgrasysブログの記事です。"
        "grasysブログを紹介するブログ記事を執筆してください。"
    )
    response = writer.invoke(
        input={"content": init_msg},
        attachments=[{"file_id": file_id, "tools": [{"type": "file_search"}]}],
    ).return_values
    is_first_review = True

    # AIアシスタントが完成と判断するまで繰り返す
    while True:
        article = response["output"]
        writer_tid = response["thread_id"]

        if is_first_review:
            # 初回レビューには参考データを添付する
            response = reviewer.invoke(
                input={
                    "content": f"作成した以下のブログ記事をレビューしてくさだい。"
                    f"記事作成の依頼者からは「{init_msg}」と指示があります。\n{article}"
                },
                attachments=[{"file_id": file_id, "tools": [{"type": "file_search"}]}],
            ).return_values
            is_first_review = False
        else:
            response = reviewer.invoke(
                input={
                    "content": f"レビューを反映した以下の記事を再レビューしてくさだい。\n{article}",
                    "thread_id": reviewer_tid,  # スレッドを指定
                },
            ).return_values

        review = response["output"]
        reviewer_tid = response["thread_id"]

        if COMPLETION_TOKEN in review:
            # レビュー終了
            break
        response = writer.invoke(
            input={
                "content": review,
                "thread_id": writer_tid,  # スレッドを指定
            },
        ).return_values

    print(article)


if __name__ == "__main__":
    main()

採用情報
お問い合わせ