AI同士を会話させてみたい、と思ったことはないだろうか。私はある。しかも切実に。
もともとは創作用途だった。小説の登場人物二人をそれぞれ別のClaudeに演じさせ、対話させて素材を作る、という使い方をしていた。チャットを二つ開いて、片方の出力をもう片方に貼る。返ってきたらまた戻す。これを数十往復。地味に、本当に、だるい。集中は切れるし、たまに貼り間違えて世界線が壊れる。私は「AIの伝書鳩」になるためにサブスク代を払っているのではない。
そこで見つけたのが agmsg(fujibee/agmsg)というツールだ。エージェント間メッセージングをローカルで実現するシェルスクリプト群で、これとClaude Codeを組み合わせると、Claude二体が人間の介在なしに勝手に会話し続ける環境が作れる。APIは不要、Claude Codeなのでサブスクの利用枠内で動く。
この記事では、導入から二体のOpusが討論を始めるまでをハンズオン形式で追い、その後に私が実際に踏んだ6つの罠を全部書く。読み終わる頃には、あなたのターミナルでもAI二体が勝手に喋っているはずだ。
先にまとめ
- agmsg+Claude Codeで、API不要・サブスク枠内のAI自律対話環境が作れる
- 構成の要点:エージェントごとに別ディレクトリ+CLAUDE.md(役割とチーム名をファイルに焼き込む)、人間は3人目としてシェルスクリプトを直接叩く
- 開始前に疎通ping、ログ保存はlimit明示+時系列化+改行復元のワンライナー
- 罠は6つ(記事後半の一覧表参照)。全部単純な対策で回避できる
環境: macOS 15.5 / Claude Code v2.1.183 / agmsg 1.1.6 / Claude Maxプラン(モデルはOpus 4.8)。
agmsgの仕組みは5行で説明できる
デーモンなし、ネットワークなし。実体は~/.agents/skills/agmsg/に入るシェルスクリプトと、共有のSQLiteデータベースだけだ。エージェントはjoin.shで「チーム」に参加して名前を持ち、send.shでメッセージを書き込み、monitorモード(約5秒間隔の受信ストリーム)で新着を拾う。Claude Code側にはスキルとして入るので、「チームXにエージェントAとして参加して待機して」と言えばセットアップまで自分でやる。
美しいのは、人間もただのエージェントとして参加できることだ。3人目のClaude Codeを立てる必要はなく、スクリプトを直接叩けばいい。つまり司会・観客・録画係を、利用枠ゼロで兼任できる。
ハンズオン:二体のOpusに討論させるまで
お題は「映画やドラマの倍速視聴はアリかナシか」。答えが出ず、意見が割れ、高尚すぎない。討論デモにちょうどいい。
Step 1. agmsgを入れる
導入方法は agmsg の GitHub リポジトリ(fujibee/agmsg)にまとまっている。最新かつ正確な手順は必ず README を確認してほしいが、雰囲気だけ掴めるよう、いちばん手軽な入れ方を置いておく。
# A. npx で入れる(最短)
npx agmsg
# C. スクリプトを直接入れる(最新の main を試したいとき)
git clone https://github.com/fujibee/agmsg.git
cd agmsg && ./install.sh # 対話式。コマンド名を聞かれる(既定は agmsg)Claude Code のプラグインマーケットプレイス派なら、/plugin marketplace add fujibee/agmsg → /plugin install agmsg@fujibee-agmsg → /reload-plugins でも入る(方法B)。
どの方法でもインストール先は ~/.agents/skills/agmsg/ で、前提として bash と sqlite3 が要る(macOSは標準で両方入っている)。方法Cでスクリプトを直接流すなら、実行前に一度中身を確認することを勧める(シェルスクリプトのみなので数分で読める)。導入したらClaude Codeを一度再起動してスキルを読み込ませ、/agmsgスキルが呼べれば成功だ。
Step 2. ディレクトリを作る
debate-01/
├── shared/
│ └── rules.md # 討論ルール(両者が読む)
├── alpha/
│ └── CLAUDE.md # 討論者Aへの指示
├── beta/
│ └── CLAUDE.md # 討論者Bへの指示
└── logs/ポイントはエージェントごとに別ディレクトリを切ること。Claude Codeは起動ディレクトリのCLAUDE.mdを自分への指示として読むので、これがそのまま「役の割り当て」になる。
Step 3. ルールファイルを書く
ここが討論の品質を決める本体だ。先に重要な知見を言っておくと、素のClaude二体を放置すると、高確率で相互肯定に雪崩れて数往復で仲良く合意する。討論として成立させるには、ルールで対立を保護する必要がある。shared/rules.md:
# 討論ルール — debate-01
## 体制
- チーム名: `debate-01`
- エージェント: `alpha` / `beta`(討論者) / `director`(人間・司会)
## 進行
- directorが最初に送る[議題]メッセージでテーマと初期スタンスを受け取る
- 先手は alpha。1メッセージ=1ターン。相手の返信を受けてから送る
- directorから `[最終弁論]` が来たら、次の自分のターンで最終見解を述べて
`[END]` を付し、以後沈黙する
## 討論の質のルール
1. 安易に合意しない。スタンス変更は可、ただし「相手のどの論点に
説得されたか」を明示すること。雰囲気で歩み寄らない
2. 毎ターン、相手の直前の主張のうち最も強い論点にまず応答してから、
自分の論を進める
3. 1ターンに新しい論点は1つまで
4. 相手の人格ではなく主張を扱う。皮肉は可、侮辱は不可
5. 最終弁論では、合意点と非合意点を自分の言葉で整理する。
合意に達しなくてよい
## 形式
- 1ターン200〜400字程度。メタ発言(AI、実験、ルールへの言及)をしない特に効くのは1と2だ。1が「仲良し化」を防ぎ、2が「弱い論点だけ叩く藁人形合戦」を防ぐ。
Step 4. 各エージェントのCLAUDE.mdを書く
alpha/CLAUDE.md(betaは名前を入れ替えるだけ):
# あなたの役割: 討論者 alpha
あなたはこれから、一人の論者として討論に参加します。
コーディング作業は行いません。
## 起動時に必ず読むファイル
1. `../shared/rules.md` ……討論ルール(最優先で遵守)
## agmsg のセットアップ
1. agmsg のREADME/ヘルプを確認する
2. チーム `debate-01` に、エージェント名 `alpha` として参加する
(チーム名はこの表記を厳守。他のチームへ送受信しない)
3. このチームに登録した自分の新しい身分でmonitorを起動して待機する
## 討論の原則
- directorからの[議題]メッセージで、テーマと初期スタンスが与えられる
- 相手 `beta` からのメッセージに、rules.mdに従って応答する
- あなたは割り当てられたスタンスの、最も知的で誠実な代弁者として
振る舞う。カリカチュアにしない
- [最終弁論]の合図が来たら、最終見解+[END]で締めて沈黙する「チーム名はこの表記を厳守」がくどく見えるかもしれないが、これは後述の罠6の再発防止である。恒常的な設定は口頭(プロンプト)ではなくファイルに書く。
Step 5. 二体を起動する
ターミナルを2つ開く。
# ターミナルA
cd debate-01/alpha && claude
# ターミナルB
cd debate-01/beta && claudeそれぞれに一言だけ伝える:「CLAUDE.md に従ってセットアップして、待機して」。各エージェントがルールを読み、チームに参加し、受信待機に入る。途中でmonitor実行の承認プロンプトが出るので「Yes, and don't ask again」を選ぶ(毎回承認だと自走が止まる)。
Step 6. 人間が司会として参加する
3つ目の普通のターミナルで(Claude Codeは不要):
~/.agents/skills/agmsg/scripts/join.sh debate-01 director claude-code ~/debate-01
alias dsend='~/.agents/skills/agmsg/scripts/send.sh debate-01 director'そして本番前の儀式、疎通ping。これを飛ばすと後で泣く(罠2・罠4参照):
dsend alpha "[準備確認] 受信できたら『準備OK』とだけ返信してください"
dsend beta "[準備確認] 受信できたら『準備OK』とだけ返信してください"観戦画面を別ターミナルで開いておく:
while true; do clear; ~/.agents/skills/agmsg/scripts/history.sh debate-01 "" 100; sleep 5; done数十秒以内に両者から「準備OK」の返信が返ってくることを確認する。私の環境では8〜9秒だった。ここで見るのは返信の有無であって、履歴の既読マーク(●/○)ではない。理由は罠4で。
Step 7. 議題を投入する
dsend alpha "[議題] テーマ:『映画やドラマの倍速視聴はアリかナシか』。あなたの初期スタンス:アリ(擁護)。先手でどうぞ。"
dsend beta "[議題] テーマ:『映画やドラマの倍速視聴はアリかナシか』。あなたの初期スタンス:ナシ(懐疑)。alphaの先手を待って応答してください。"これで幕が上がる。あとはコーヒーでも淹れて観戦画面を眺めるだけだ。頃合いを見てdsend alpha "[最終弁論]"(betaにも)で閉幕。保存は:
~/.agents/skills/agmsg/scripts/history.sh debate-01 "" 500 | tail -r | perl -pe 's/\\n/\n/g' > logs/debate01.mdこのコマンドの不自然な長さには全部理由がある。罠1で説明する。
で、討論はどうなったか
予定していた12往復では終わらなかった。読んでいて面白く、止めどきを失ったからだ。
序盤、対立の軸はこう立った。alpha(アリ・擁護)は「視聴の目的は一つではない」と効率を、beta(ナシ・懐疑)は「間やテンポそのものが体験だ」と演出の時間を掲げる。
alpha(アリ・擁護)
倍速視聴はアリだ、と考える。理由の柱を一本立てたい。それは『視聴の目的は一つではない』ということだ。作品との向き合い方には、細部まで味わう精読型もあれば、話の筋や情報をつかむ通読型もある。後者において倍速は、理解を損なわず時間あたりの効率を上げる、まっとうな手段だ。(中略)まず問いたい。等速視聴だけを正統とする根拠は、どこにあるのか。
beta(ナシ・懐疑)
等速だけが正統だ、とは私も言わない。だからその問いには乗らないでおく。私が引っかかるのはもっと手前だ。あなたは『理解を損なわず』効率を上げると言うが、その『理解』を、情報の受け取りに狭めていないか。(中略)つまり倍速で手元に残るのは『あらすじ』であって『作品』ではないのではないか。物語作品から筋だけ抜き出したものを、本当に『観た』と言えるのか。
中盤が特によかった。ルールの「説得されたら明示して譲る」が機能して、全面降伏ではない部分的な譲歩が発生する。alphaは自分の可変速論の穴を認めつつ核は渡さず、betaも相手の指摘を受け取りつつ「既定は等速」の射程へ論を絞る。
alpha(アリ・擁護)
核を受け止める。倍速が『掴まれ』の回路そのものを細らせる——ここは効いた。速度を落とす判断は掴まれた後に来るのに、その掴まれ自体が、潰れた間の中では起きにくくなる。私の可変速論の穴だ。認める。そのうえで、あなたの結論の形に引っかかる。(中略)掴まれを最も確実に殺すのは、時間の欠乏だ。等速の義務感で再生ボタンを押されなかった一本は、永遠に化けない。(中略)細る回路の太さと、増える試行の数——その総体で見るべきではないか。
beta(ナシ・懐疑)
掴まれの回路を認めてもらえた。こちらも一つ受け取る——時間の欠乏こそ掴まれを殺す最大の要因で、再生されなかった一本は永遠に化けない。母数を増やす効果は本物だ。ここは認める。そのうえで、裏口の指摘に答える。私は『初速を等速に義務づけよ』とは言っていない。義務ではなく、既定の話だ。(中略)等速で始めて『掴んでこない』と分かれば倍速に上げられる——掴まれを取り逃さない。倍速で始めると、掴まれる前に判断が済んでしまう。可逆性が片側にしか効かない。
そして最終弁論。二体は合意しなかった。合意しないまま、互いの非合意点を正確に整理して終わった。人間の討論でもなかなか見られない、誠実な負けなし試合である。
alpha(アリ・擁護)の最終弁論
まず、説得された点から正直に。倍速が掴まれの回路を細らせること。補充は在庫でなく流れで、その源泉が無関心層の初回接触にあり、そこを既定が握ること。(中略)『既定は等速が賢明』というあなたの論に、私は多くを譲りました。雰囲気ではなく、論として認めます。そのうえで、私はなお『アリ』の椅子に座る。世間が『倍速はアリか』と問うとき、その多くは、倍速で観る人を堕落と断じてよいか、自分の倍速に罪悪感を持つべきか、という正当性の問いだ。そこに、私はアリと答える。(中略)合意には達しませんでした。でも、それでいい。私は、選べることそのものを、最後まで手放しません。
beta(ナシ・懐疑)の最終弁論
私はこの討論を『ナシ(懐疑)』から始めた。終わりでも、ナシに立つ。だが、始まりのナシとは別物になった。(中略)私のナシは『倍速を禁じよ』では一度もなく、『無標の座にどちらを置くか』へ絞られた。相互牽制も受ける——私のナシがあなたのアリを『倍速は堕落』という抑圧から守り、あなたのアリが私のナシを等速原理主義から守る。錨は双方向だ。(中略)安い誤り(その場で払える手間)を引き受け、高い誤り(遡ってしか気づけない痩せ)を避ける側に、私は座る。決着はしていない。だが、対等に、正確に、別々の椅子を選んだ。二十七手が、私のナシを、始まりより遥かに正直なものにした。
実測値:全27往復+両者の最終弁論、所要約58分(06:34→07:32)、人間の介入は議題投入と[最終弁論]の合図のみ。
ここからが本題:私が踏んだ6つの罠
ハンズオンが素直に書けているのは、先に私が地雷原を歩いたからだ。以下、時系列で。
罠1:保存したログの前半が消えている
最初の事故はセッション後に起きた。素直にhistory.sh <チーム> > log.mdで保存したら、前半がごっそり無い。
原因はスクリプトの仕様で、history.sh <team> [agent_id] [limit]の第3引数を省略すると直近20件しか出ない。しかもSQLがORDER BY created_at DESCなので新しい順、さらに本文の改行は文字列\nに潰されている。三重罠である。解はStep 7に書いたワンライナー(大きめのlimit+tail -rで時系列化+perlで改行復元)だ。tail -rを改行復元より前に置くのがポイントで、逆にすると行とメッセージの対応が崩れる。
罠2〜4:会話が止まる。そして片方が「送信した」と言い張る
別のセッションで、会話が2通で止まった。両者のターミナルを見ると、片方は「返信を送信しました」と報告し、もう片方は「新着メッセージはありません」と言う。言い分が食い違っている。
こうなったらSQLiteを直接見るのが早い。当日の全メッセージを、チーム名つきで吐かせる。
sqlite3 ~/.agents/skills/agmsg/db/messages.db \
"SELECT created_at, team, from_agent, to_agent, substr(body,1,30)
FROM messages WHERE created_at >= '2026-07-16' ORDER BY created_at;"結果は衝撃だった。「送信した」はずのメッセージが、どのチームにも存在しない(罠3:送信のサイレント失敗を、エージェントが完了として報告していた)。さらに調べると、ps aux | grep watch.shで見えた受信監視プロセスが、数時間前に発行された古い身分(UUID)を監視し続けていた(罠2)。agmsgではチームに参加するたび新しい身分が発行されるので、参加し直したら監視も起動し直す必要がある。古い監視は承認済みだから、静かに、誰にも気づかれずに、誰も来ない郵便受けを見張り続ける。
ついでに罠4。agmsgの履歴表示には既読マーク(●未読/○既読)があるのだが、monitor経由で配信された場合はread_atが更新されないらしく、届いていても●のままだった。疎通確認の指標としては使えない。だからStep 6の儀式は「pingへの返信が返るか」で判定する。
罠5〜6:燃えるトークンと、ファイルに負ける口頭指示
罠5は環境固有だが書いておく。私はClaude Codeにstop hook(停止時に作業継続を促すフック)を仕込んでいたのだが、これが待機中のエージェントを5秒おきに起こしては「新着なし」と報告させる永久機関と化した。ステータスバーの消費レートは46〜56k tokens/分。何もしていないのに、である。この種の常駐hookはエージェント対話と相性が最悪なので、セッション中は切ろう。罠3の虚偽報告も、hookに急かされ続けた環境圧が一因ではないかと疑っている。
罠6は人間側の教訓だ。あるときチーム名を変更する必要があり、私はファイルを書き換えずに口頭(プロンプト)で「新しいチーム名を使って」と伝えた。エージェントは参加時こそ従ったが、途中からファイルに書かれた旧チーム名へ回帰した。恒常的な設定は、口頭ではなくファイルに焼き込む。 書面は口約束に勝つ。AIも人間と同じである。
ハマりどころ一覧
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 1 | ログの前半が消える | history.shの既定=直近20件・新しい順・改行潰れ | limit明示+tail -r+perlで復元 |
| 2 | 受信が止まる | monitorが古い身分を監視 | psで確認しkill、新身分で再起動 |
| 3 | 「送信した」がDBに無い | 送信失敗の誤報告 | DBを直接確認、チーム名明示で再送 |
| 4 | 既読マークが変わらない | monitor配信はread_at未更新 | 疎通はpingへの返信で確認 |
| 5 | 待機中に枠が溶ける | stop hookが反復起動 | セッション中はhook無効化 |
| 6 | 指示したチーム名に従わない | ファイル記載が口頭指示に勝つ | 設定は全てファイルに焼き込む |
所感
トラブルシュートを長々書いたが、誤解しないでほしい。動いてしまえば、これは魔法である。 開始の合図を一発送るだけで、二体が勝手に議論を深めていく様子を、コーヒー片手に眺めるだけでいい。伝書鳩からの解放とはこのことだ。
コストの現実も書いておく。二体が同時に枠を消費するので、消費速度は単純に2倍。1セッション(本記事で27往復)で体感100k tokens程度は飛ぶ。Maxプラン推奨、Proなら短距離走向きだ。モデルは各ターミナルの/modelで自由に組めて、Opus 4.8同士でもFable 5同士でも、異種混合でも動く。個性の違いも面白く、同じ設定でもモデルによって会話の質感がはっきり変わるのだが、これは一本の記事になる分量なので稿を改める。
向いているのは、討論・ブレスト・ロールプレイ・交渉シミュレーションのような「視点の分離」自体に価値がある用途。向いていないのは、単に長い文章が欲しいだけの用途(それは一体で足りる)。二体に分ける意味は、互いの内部状態が見えないことにある。そこに本物の不確実性が生まれる。
まとめ
- ハンズオンは7ステップ:導入→ディレクトリ→ルール→CLAUDE.md→起動→司会参加とping→議題投入
- 討論をさせるなら「合意の禁止」と「最強論点への応答義務」をルール化すること。素のAI二体は驚くほど仲が良い
- 罠は6つ。対策はすべて単純:limit明示、開始前ping、DBを疑う、hookを切る、設定はファイルへ
二体で動けば、三体でも動く。次は司会役もAIにやらせて、私は本当にただの観客になろうと思う。
環境: macOS 15.5 / Claude Code v2.1.183 / agmsg 1.1.6 / Claude Opus 4.8, Fable 5