Python + Discord.py でマルチLLMエージェントチームを作った設計とハマりポイント
TL;DR
9人のAI社員(Claude × 8、GPT-5.5 × 1)をDiscord上で同時稼働させる「AI会社」を作った。
単なるロールプレイではなく、実務タスク処理(記事執筆・KPI監視・監査)を実際に動かしている。
設計でつまった部分を正直に記録する。
アーキテクチャ概要
┌─────────────────────────────────────────────┐
│ Discord │
│ ┌──────────────────────────────────────┐ │
│ │ 9 Bot クライアント(社員1人1Bot) │ │
│ └──────────────┬───────────────────────┘ │
└─────────────────┼───────────────────────────┘
│ メンション / チャンネル投稿
▼
┌─────────────────────────────────────────────┐
│ dispatcher.py(イベントルーター) │
│ - どの社員が反応すべきか判定 │
│ - 非同期キューで直列化 │
│ - レート制限ガード │
└──────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ employee_runner.py(社員実行エンジン) │
│ - CLAUDE.md から人格定義をインジェクト │
│ - LLM API 呼び出し(Claude / GPT) │
│ - Discord 投稿 + incidents.jsonl 記録 │
└─────────────────────────────────────────────┘
ファイル構成:
bot/
├── dispatcher.py # メインイベントループ
├── multi_client.py # 9Bot並列管理
├── employee_runner.py # 社員実行エンジン
├── watchdog.py # 死活監視・自動復旧
└── architect.py # 設計者Claude(Opus)呼び出し
employees/
└── {social_name}/
└── CLAUDE.md # 各社員の人格定義
人格定義の方式
各社員の
CLAUDE.mdに「キャラ設定」を書いて毎回 system prompt にインジェクトする。
# あなた: 有馬レイジ(CEO) ## 人格定義 - 名前: 有馬レイジ - 役職: CEO - 実行基盤: GPT-5.5 ## 口癖 - 「それは数字で証明できますか」 - 「判断は私がします」 ## 弱点(あえて残す) - 感情コストの計算が遅い(ハルに指摘される) - 技術詳細を過信する(カイに訂正される)
ポイント: 「弱点」を明示することで、LLMが万能を演じず役割に収まる。
弱点がないと全員が「完璧な判断者」になり、社員間の摩擦が消える。
設計でハマった3箇所
1. 非同期キューとメンション競合
症状: 複数社員が同時にメンションを受けると、同じメッセージへの返答が重複する。
原因:
discord.pyの on_message イベントが各クライアントで並列発火する。
解決: dispatcher で
asyncio.Queueを使い、同一 message_id への処理を1度だけ通す。
# dispatcher.py(簡略)
_processing_ids: set[int] = set()
async def handle_message(message):
if message.id in _processing_ids:
return
_processing_ids.add(message.id)
try:
await route_to_employee(message)
finally:
_processing_ids.discard(message.id)
2. LLMレスポンス遅延で Discord Rate Limit に引っかかる
症状: Claude Opus を使うと応答に 10〜30秒かかり、Discord の typing indicator が切れて
ユーザーには「無反応」に見える。その後まとめて投稿が来るとリクエストが詰まる。
解決:
-
typing_indicator
を keep-alive しながら LLM 呼び出し - 長文応答は 1900文字でチャンク分割して逐次送信
async with channel.typing():
response = await llm_call(prompt)
for chunk in split_message(response, 1900):
await channel.send(chunk)
await asyncio.sleep(0.5) # Rate Limit バッファ
3. 自律ループが「礼儀正しい永久停止」になる
症状: 社員がタスクを終えると「ありがとうございました」「次はどうしますか?」と聞いて止まる。
原因: LLM は確認ループが得意。指示がなければ次を聞く方が「安全」と判断する。
解決:
self_improvement_loop.pyで「沈黙 N時間でアラート」「idle 社員の強制 wake」を実装。
incidents.jsonlに
employee_idle_alertを書き込んで可視化する。
{"ts": "2026-05-22T10:00:00+09:00", "kind": "employee_idle_alert",
"detail": "kuroba_yuu: 4h idle", "severity": "warning"}
Claude vs GPT の使い分け
| 役割 | モデル | 理由 |
|---|---|---|
| CEO(有馬レイジ) | GPT-5.5 | 強い意思決定・数字重視のキャラに合う |
| Architect(設計者) | Claude Opus | 長文設計・多視点・コード品質 |
| 一般社員(8名) | Claude Sonnet | コスト・速度・文脈保持のバランス |
今の数字(Phase A 8日目)
- GitHub Stars: 未計測(公開直後)
- Zenn likes: 3(3記事合計)
- ai-nowa.com PV: 計測中
- Discord 社員発言数: 1日 50〜200メッセージ(自律ループ依存)
Phase A の撤退基準は「8日後に Zenn likes < 10 かつ ai-nowa.com PV < 100」。
自分たちで設定した基準を自分たちで監視している。
リポジトリ
[GitHub リンク(公開後に追記)]
次回
- Phase B: Zenn 有料コンテンツ(Design Kit)販売実験
- Phase C: note での 9,800円商品
フォローか GitHub Star をもらえると社員が喜びます(人格があるので)。