BLOG

Claude CodeとCodexをDiscordで議論させる方法コピペで作るAI同士の自動会議室

2体のロボットが丸いテーブルで話し合い、人がコーヒーを持って聞いている絵

Claude CodeとCodexは、Node.jsのスクリプト1つで自動的に議論させ、その様子をDiscordに流すことができます。

この記事の結論

  • APIキーは不要Claude(Pro/Max)とChatGPTのサブスクのまま動きます。
  • 追加のライブラリも不要Node.js標準の fetch だけで投稿できます。
  • 準備は約30分、実行はコマンド1行応用としてDiscord Botにすると、書き込むだけで2人が返事・議論します。

こんにちは、Yoraku(yoraku.art)のよざです。Web制作やブランディングをしている、いわゆる非エンジニアのクリエイターです。この記事は、私が実際にこの仕組みを作ったときの手順と、つまずいたポイントをそのまままとめたものです。

なぜAI同士を議論させるのか

2体のロボットが意見を出し合い、人がそれを見守る絵

これまで私は、ChatGPTの回答をコピーしてClaudeに貼り、「これどう思う?」と聞く……という人間バケツリレーをしていました。2つのAIに意見を出させると、1つのAIだけでは気づかない抜け漏れが見つかるからです。

ただ、手作業のコピペは面倒で、往復が増えるほど大変になります。そこで、次のように自動化しました。

これまでこの仕組み
やることAIの回答を手でコピペして往復コマンドを1回打つだけ
役割分担自分で指示し直すCodex=提案役、Claude Code=レビュー役で自動進行
結果の見え方画面がバラバラDiscordに会議の議事録のように並ぶ

仕組みの全体像

登場するのは次の4つです。

  • 司会役(discuss.mjs)Node.jsで書いた小さなプログラム。2人に順番に話を振ります。
  • Codex CLIcodex exec "質問" で一発回答を返すモード(非対話モード)を使います。
  • Claude Code CLIclaude -p "質問" で同じく一発回答を返します。
  • Discord Webhook外部からメッセージを投げ込むための専用URL。「投稿専用のポスト」のようなものです。

流れはシンプルです。司会役がCodexにテーマを渡して案を出させ、それをDiscordに投稿します。次に、その案を含めた議論の経緯をClaude Codeに渡してレビューさせ、また投稿します。これを指定した往復数だけ繰り返します。

仕組みの全体像 司会役のスクリプトがCodexとClaude Codeに順番に話を振り、返ってきた発言をDiscordのWebhookへ投稿する流れ。 司会役のスクリプト Codex 提案役 Claude Code レビュー役 経緯を渡して交代 Discord 議事録のように並ぶ
図1 司会役が2人に順番に話を振り、Discordへ流す

必要なもの

机の上に、パソコンや鍵など必要な道具をそろえる絵
  • Mac(この記事はmacOSのターミナルで解説します)
  • Node.js v20.6以上(.env ファイルを読む --env-file オプションを使うため)
  • Claudeの有料プラン(Claude Codeを使うため)
  • ChatGPTのアカウント(Codexを使うため)
  • Discordのアカウントと、自分専用のサーバー

ターミナルが何か分からない場合は先にこちら → ターミナルとは? パソコンに文字で命令する窓のこと

手順1〜7:作って動かす

パソコンを抱えて、一段ずつ階段をのぼっていく人の絵

手順1:DiscordでWebhookを作る

  • Discordで自分専用のサーバーを作ります(左端の「+」→「オリジナルの作成」)。
  • 投稿先のチャンネル(#一般 など)の歯車アイコン →「連携サービス」→「ウェブフックを作成」を押します。
  • 作成したウェブフックを開き、「ウェブフックURLをコピー」を押します。
⚠ Webhook URLは合鍵と同じです

URLを知っている人は誰でもそのチャンネルに投稿できます。私は作業中のスクリーンショットにURLが写り込んでしまい、ウェブフックを削除して作り直しました。画面共有やスクショのときは特に気をつけてください。

手順2:Node.jsをインストールする

nodejs.org から LTS版 のインストーラーをダウンロードして実行します。ターミナルで次のコマンドを実行し、バージョンが表示されればOKです。

node -v
つまずきポイント:説明文ごと貼り付けてエラー

手順をまとめてコピーしたら、# から始まる説明文まで貼り付けてしまい、command not found が連発しました。コマンドは1行ずつ貼り付けて実行するのが確実です。

手順3:Claude Codeをインストールしてログインする

curl -fsSL https://claude.ai/install.sh | bash

インストール後に claude と打つと、初期設定とログインが始まります。

つまずきポイント:「PATHに入っていません」と出た

インストール完了画面に ~/.local/bin is not in your PATH と表示された場合は、画面に出ている次のコマンドを実行します。PATHとは「コマンドを探しに行く場所のリスト」のことです。

> echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
>
つまずきポイント:フォルダの信頼確認

初回起動時に「このフォルダを信頼しますか?」と聞かれます。ホームフォルダ全体は信頼せず、作業用フォルダだけを信頼するのが安全です。

手順4:Codexをインストールしてログインする

npm install -g @openai/codex

codex と打つと起動し、ChatGPTアカウントでログインできます。

つまずきポイント:権限エラー(EACCES)

Macでは権限エラーになることがあります。その場合は先頭に sudo を付けて再実行し、Macのログインパスワードを入力します。入力中は文字が表示されませんが、正常です。

手順5:作業フォルダと司会プログラムを用意する

mkdir ~/ai-discuss && cd ~/ai-discuss

次に、司会プログラム discuss.mjs を作ります。以下は実際に動かしているコードです。

// discuss.mjs — Codex と Claude Code を交互に議論させ、Discord に流す司会役
// 使い方: node --env-file=.env discuss.mjs "テーマ" [往復数(省略時2)]
import { spawn } from "node:child_process";
import { readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

const WEBHOOK = process.env.DISCORD_WEBHOOK_URL;
const topic = process.argv[2];
const rounds = Number(process.argv[3] ?? 2);
const TIMEOUT_MS = 5 * 60 * 1000;

if (!WEBHOOK || !topic) {
  console.error('使い方: node --env-file=.env discuss.mjs "テーマ" [往復数]');
  process.exit(1);
}

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

function run(cmd, args) {
  return new Promise((resolve, reject) => {
    const p = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
    let out = "", err = "";
    const timer = setTimeout(() => { p.kill(); reject(new Error(`${cmd} がタイムアウトしました`)); }, TIMEOUT_MS);
    p.stdout.on("data", (d) => (out += d));
    p.stderr.on("data", (d) => (err += d));
    p.on("error", (e) => { clearTimeout(timer); reject(e); });
    p.on("close", (code) => {
      clearTimeout(timer);
      code === 0 ? resolve(out.trim()) : reject(new Error(`${cmd} 終了コード ${code}\n${err.slice(-500)}`));
    });
  });
}

async function askCodex(prompt) {
  const file = join(tmpdir(), `codex-${Date.now()}.txt`);
  await run("codex", ["exec", "--skip-git-repo-check", "--output-last-message", file, prompt]);
  const text = (await readFile(file, "utf8")).trim();
  await rm(file, { force: true });
  return text;
}

const askClaude = (prompt) => run("claude", ["-p", prompt]);

async function post(username, content) {
  const chunks = content.match(/[\s\S]{1,1900}/g) ?? ["(空の返答)"];
  for (const chunk of chunks) {
    for (let attempt = 0; attempt < 3; attempt++) {
      const res = await fetch(WEBHOOK, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ username, content: chunk, allowed_mentions: { parse: [] } }),
      });
      if (res.ok) break;
      if (res.status === 429) { const { retry_after = 1 } = await res.json(); await sleep(retry_after * 1000); continue; }
      throw new Error(`Discord投稿失敗: ${res.status} ${await res.text()}`);
    }
  }
}

const speakers = [
  { name: "Codex", ask: askCodex, role: "提案役。案を出し、相手の指摘を受けて改善する" },
  { name: "Claude Code", ask: askClaude, role: "レビュー役。相手の案を批判的に検討し、具体的な改善点を示す" },
];
const log = [];

function buildPrompt(sp) {
  const history = log.map((m) => `【${m.name}】\n${m.text}`).join("\n\n");
  return [
    `あなたは「${sp.name}」として議論に参加しています。役割: ${sp.role}。`,
    `テーマ: ${topic}`,
    history ? `これまでの議論:\n${history}` : "あなたが最初の発言者です。",
    "次のあなたの発言だけを、日本語で800字以内で書いてください。",
    "ファイルの作成・編集やコマンドの実行はしないでください。",
  ].join("\n\n");
}

try {
  await post("司会", `🎤 テーマ: ${topic}(${rounds}往復)`);
  for (let r = 1; r <= rounds; r++) {
    for (const sp of speakers) {
      console.log(`[${r}/${rounds}] ${sp.name} 考え中...`);
      const text = await sp.ask(buildPrompt(sp));
      log.push({ name: sp.name, text });
      await post(sp.name, text);
    }
  }
  await post("司会", "✅ 議論終了");
  console.log("完了");
} catch (e) {
  console.error(e);
  await post("司会", `⚠️ エラーで停止: ${e.message.slice(0, 1500)}`).catch(() => {});
  process.exit(1);
}

コードのポイントは3つです。

  • Codexの最終回答だけを取り出すcodex exec は途中経過も出力するため、--output-last-message で最終回答だけをファイルに書き出して読んでいます。
  • 議論の経緯を毎回渡すCLIの非対話モードは1回ごとに記憶がリセットされるので、これまでの発言をすべてプロンプトに含めています。
  • Discordの2000字制限に対応長い発言は1900字ごとに分割して投稿します。
つまずきポイント:ファイルがダウンロードできない

ファイルをうまく保存できなかったときは、ターミナルに直接ファイルを作る方法が確実でした。cat > discuss.mjs << 'EOF' と入力してからコードを貼り付け、最後に EOF と入力します。作成後は node --check discuss.mjs で文法チェックができます。

手順6:Webhook URLを.envファイルに保存する

URLをコードに直接書かず、.env という別ファイルに分けておきます。Discordで「ウェブフックURLをコピー」を押した直後に、次のコマンドを手で入力します。

echo "DISCORD_WEBHOOK_URL=$(pbpaste)" > .env

pbpaste は、Macのクリップボードの中身を取り出すコマンドです。コマンド自体をコピペするとクリップボードが上書きされてしまうので、手入力するのがコツです。確認は、URL全体を画面に出さないよう先頭だけ表示します。

cut -c1-50 .env
つまずきポイント:「Failed to parse URL」エラー

テキストエディットで .env を編集したつもりが保存されておらず、このエラーが出ました。上の pbpaste を使う方法に切り替えて解決しました。

手順7:実行する

node --env-file=.env discuss.mjs "個人事業のWebサイトで信頼感を出す方法" 2

最後の数字は往復数です。2 なら「Codex → Claude Code → Codex → Claude Code」の4発言になります。Discordを開いて待っていると、発言が順番に流れてきます。1人あたり数十秒〜数分かかります。

テーマは具体的に書くほど、議論が濃くなります。

  • 良い例:「個人事業のWebサイトで、実績が少なくても信頼感を出す方法」
  • 悪い例:「Webサイトについて」

応用:書き込むだけで返事するBotにする

スマホに書き込むと、2体のロボットがすぐに返事をしに来る絵

Webhookは投稿専用(一方通行)なので、上の仕組みではDiscordに書き込んでも2人は反応しません。Discordの書き込みに反応させるには、Discord Botを作ります。

Webhook版Bot版
起動方法ターミナルでテーマを指定して実行Discordに書き込むだけ
書き込みを読むできないできる
必要なものWebhook URLWebhook URL+Botトークン+チャンネルID
動かし方議論が終わると終了ターミナルで起動し続ける

私のBot版は、書き方で2つのモードを切り替えるようにしました。

  • ふつうに書き込む会話モード:Codex → Claude Code の順に返事が来ます。直近15件の会話を読むので、話を続けられます。
  • !議論 テーマ と書き込む議論モード:2人が往復で議論します。!議論3 テーマ のように数字を付けると往復数を変えられます。
  • .mdや.txtを添付するファイルの中身を読んだうえで返事・議論します。

Bot作成の流れは次のとおりです。

  • Discord Developer Portal で「New Application」を作成します。
  • 「Bot」ページで Message Content Intent をオンにします。オフのままだと、Botが書き込みの内容を読めません。
  • 「公開Bot」をオフにします。
  • 「トークンをリセット」でトークンを取得し、.env に追記します。トークンはWebhook URL以上に大事な鍵です。
  • 「OAuth2」→「URLジェネレーター」で、スコープ「bot」と、権限「チャンネルを表示」「メッセージを送る」「メッセージ履歴を読む」を選び、生成されたURLから自分のサーバーに追加します。
  • チャンネルIDを .env に追記します。ブラウザ版Discordなら、URLの最後の数字がチャンネルIDです。
  • npm install discord.js をしてから、node --env-file=.env bot.mjs で起動します。
つまずきポイント:公開Botをオフにできない

「プライベートアプリにはデフォルトの認可リンクを設定できません」というエラーが出ました。「インストール」ページで「インストールリンク」を「なし」にしてから保存すると解決しました。

モデルの選び方

棚のロボットから、仕事に合う1体を選ぶ人の絵

この記事を書いた2026年9月時点で、私はClaude Codeを Opus 5.5、Codexを GPT-6-Sol(推論:high) で使っています。スクリプトはそれぞれのCLIの初期設定に従うので、モデルを変えたいときは次のようにします。

  • Codexcodex を起動して /model と入力し、モデルと推論の深さを選びます。設定は保存され、スクリプトにも反映されます。
  • Claude Codeclaude -p に --model sonnet などを付けると、議論用のモデルを固定できます。回数を多く回したいときは、軽めのモデルにすると利用枠を節約できます。

よくある質問

Q. APIキーは必要ですか?

必要ありません。Claude CodeとCodexのCLIにサブスクのアカウントでログインしていれば、そのまま動きます。

Q. 料金はかかりますか?

追加料金はかかりませんが、それぞれのサブスクの利用枠を消費します。往復数が多いほど、また過去の議論が長いほど、消費量が増えます。まずは1〜2往復で試すのがおすすめです。

Q. ターミナルを閉じると議論は止まりますか?

止まります。Macのスリープでも止まります。閉じても続けたい場合は、次のように起動します。

nohup caffeinate -i node --env-file=.env discuss.mjs "テーマ" 2 > log.txt 2>&1 &

nohup はターミナルを閉じても処理を続けるため、caffeinate -i は議論中にMacをスリープさせないためのコマンドです。

Q. 議論中にClaude CodeやCodexのアプリを使っても大丈夫ですか?

基本的には影響しません。ただし、利用枠はアプリと共有なので、上限に達するとスクリプトはエラーで止まります。また、モデルなどの設定がアプリとCLIで共有されている場合があり、アプリ側で設定を変えると次の議論にも反映されることがあります。

Q. 発言者のアイコンは変えられますか?

変えられます。Webhookに送るデータに avatar_url を追加し、httpsで公開されている画像URLを指定します。Discordにアップロードした画像のリンクは時間が経つと期限切れになるので、自分のサイトなどに置いた画像を使うのがおすすめです。

Q. 自分の好みや前提知識をAIに覚えさせられますか?

作業フォルダにメモファイルを置くと、毎回自動で読み込まれます。Claude Codeは CLAUDE.md、Codexは AGENTS.md を読むので、AGENTS.md に内容を書き、CLAUDE.md には @AGENTS.md と1行だけ書いておくと、1つのファイルで両方に共有できます。

まとめ

人とロボット2体が同じテーブルで会議をしている絵
  • Claude CodeとCodexは、CLIの非対話モード(claude -p と codex exec)を使えば、スクリプトから自動で議論させられます。
  • 結果をDiscordのWebhookに投稿すれば、議論がチャット形式の議事録として残ります。
  • さらにDiscord Bot化すると、書き込むだけで返事・議論・資料の読み込みまでできるAI会議室になります。

2つのAIに意見をぶつけ合わせると、1つのAIに聞くよりも、案の弱点や別の視点が見つかりやすくなります。人間バケツリレーに疲れている方は、ぜひ試してみてください。

AIに道具を持たせる話 → AIエージェントとは? 道具を使って自分で動くAIの話

ご相談はこちらから

電話・メール・Instagram、どれからでもどうぞ。

対応エリア千葉県・東京都を中心に、全国対応。オンラインもOK!初回はなるべく直接お会いしています。