前の記事で作ったディベートの仕組みを使って、私の作業の一覧にある未完了の24項目を、50回のディベートにかけました。
決めたのは私で、AIが作ったのは材料です。
この記事では、50回の結果、論者のモデルの差、Claudeの審判とまとめの作り、費用と時間、つまずいた所を書きます。
仕組みのおさらいは、次の図のとおりです。
くじで案と役を割り当て、2回の議論のあとにJevが1票を入れます。
これを50回くり返して票を数え、Claudeがまとめを書き、私が決めます。
1回の討論を「世界」と呼ぶのは、前の記事と同じです。破線の矢印は、50回のくり返しを表します。
議題ファイルの書き方
議題は、frontmatter(題と候補)と、前提の本文だけで書きます。候補は英字の名前と、1行の説明です。次は、発信先を決めた議題の候補の部分です。
---
title: Qiita・note の次に、最初に力を入れる発信先はどれか
options:
X: X(旧 Twitter)。短文で日々の気づきや記事の紹介を流す
LinkedIn: LinkedIn。仕事上のつながり向けに、実務の事例や考えを投稿する
blog: 自分のブログ(独自ドメイン)。記事をまとめて置き、資産として積み上げる
podcast: ポッドキャスト。音声で話す(台本づくりと録音が要る)
slides: 登壇資料の公開(SpeakerDeck など)。勉強会で話した資料を公開する
---
frontmatterにmode: rankと書くと、1つを選ぶ代わりに、全候補の順位づけになります。今回の24項目は、この形です。
本文には、決める人の前提(今の状態、決まっていること、使える時間など)を書きます。
前提は、ClaudeとJevのAPIに送られます。
そのため、個人情報、機密は書きません。論者に渡す文の形は固定で、議題、候補、前提の順に並べます。
50回回した結果
候補が24個なので、割り当ては「毎回全部を混ぜて3つずつ取り出す」形になります。50回で150席あり、推された回数は6〜7回にそろいました(150席÷24項目)。50回は8.0分で終わりました。
案ごとに、推された回数のうち何回勝ったかを数えました。
勝率が高かったのは、次の3つです。
- セキュリティまわりの、今すぐできる対処。6回中6回(100%)
- 下書きとレビューが済んだ連載記事の投稿。7回中6回(86%)
- 公開物の点検。6回中5回(83%)
一方で、勝った回数が0回の項目は、24項目のうち7つありました。
勝った役は、中間が22回、反論が17回、肩入れが11回です。
モデルの差が票に出る
論者はHaiku 2席とSonnet 1席です。
勝った論者のモデルは、Haikuが36回、Sonnetが14回でした。
席の数で割ると、Haikuは100席中36回で36%、Sonnetは50席中14回で28%です。
前の記事の個人ナレッジwikiの議題(19回)では、逆でした。
Sonnetが19席中11回で58%、Haikuが38席中8回で21%です。
どちらのモデルが有利かは、議題によって変わりました。
モデルの差が票に影響する以上、公平さを優先するなら、論者のモデルをそろえるほうがよいと考えています。
Claudeの審判は5回だけ
票を入れる審判はJevで、全部の回に入れています。
Claudeの審判は、50回のうち5回だけ加えました。審判のモデルはSonnetで、考える過程を使わせています。
5回をどう選ぶかで、結果が偏ります。
票の多い案ばかり選ぶと、Claudeが見る論が一部の案に集まるためです。
そこで、Jevが勝たせた案ごとに回をグループにして、グループを順に回って1つずつ取るようにしました。
groups = defaultdict(list)
for w in worlds:
groups[w["vote"]["option"]].append(w)
for g in groups.values():
rng.shuffle(g)
out = []
keys = sorted(groups, key=lambda o: -len(groups[o]))
while len(out) < min(k, len(worlds)):
for o in keys:
if groups[o] and len(out) < k:
out.append(groups[o].pop())
5回のうち4回は、ClaudeもJevと同じ論者を選びました。食い違った1回は、2人が別の項目を選んでいます。
まとめはClaudeが書いた
票を数えただけでは、なぜその案が勝ったのかが分かりません。そこで、Claudeにまとめを書かせました。まとめに渡したのは、次の3つです。
- 案ごとの、勝った回数と推された回数
- Claudeの審判が選んだ論と、その理由
- 反論の材料として、ほかの論者の最後の主張(最大12件)
出力の形も指定しました。
「早めに」「そのうち」「いつか」の3段に全項目を振り分け、各項目に1行の理由と勝率を付けます。
勝率と違う段に置いた項目には、その理由を書かせます。
最後に、私に確かめることを3つまで付けさせます。
合計は1500字以内です。
期限が決まっている、ある作業が終わらないと始められない、放置すると危ない、という3つのしばりも加味させました。
渡したものと返ったものの抜粋を載せます。項目名は記事の呼び方に置き換え、一部を省いています。
案ごとの、勝った回数/推された回数
セキュリティまわりの、今すぐできる対処 6勝/6回
連載記事の投稿 6勝/7回
公開物の点検 5勝/6回
(ほか21項目は省略)
Claudeの審判が選んだ論と、その理由
推す案:連載記事の投稿
理由:ある論者は、今すぐできる対処の中心部分は別の作業を待たずに
進められると指摘し、「それが別の作業の必須の下準備だ」という
主張を崩した。連載記事の投稿は下書き・レビュー済みで、週に
数時間でも終えられ、目的に直結する。「見るだけの確認を最優先
にする」主張は、作業の規模に合わない
反論の材料(ほかの論者の最後の主張、最大12件のうちの1件)
(推す案:公開物の点検)最後の主張の本文……
## 早めに
- セキュリティまわりの、今すぐできる対処(6/6=100%):危険度が高く、すぐできる分がある。
即時にできる分は、ほかの作業を待たず今すぐ可能。
- 連載記事の投稿(6/7=86%):下書き・レビュー済みで、読み直しと投稿だけで完了する。
- 公開物の点検(5/6=83%):最初の対処と作業が重なるので、まとめて進める。
## 勝率と違う段に置いた項目と理由
- 期限のある切り替えの判断を「早めに」:勝率は中位。動かせない期限があり、別の作業の前提になる。
- 見るだけの確認を「いつか」:勝率は0。ただし放置すると壊れに気づかないので、ほかの確認に同乗させる。
## 私に確かめること
(3つ。中身は省略)
勝率と違う段に置かれた項目は、6つでした。たとえば、勝率が中位でも、動かせない期限があり、別の作業の前提になる項目は、「早めに」に上がっています。勝率が0でも、放置すると壊れに気づかない項目は、別の確認に同乗させる扱いでした。
まとめを読んで決めたのは私です。
費用と時間
Claudeの呼び出しは312回で、約103万トークン、2.45ドルでした。これにまとめの0.05ドルを足して、約2.5ドルです。Sonnetの論者は、1回あたりの費用がHaikuの約3倍でした。
費用の上限は3ドルにしています。ただし、これは「新しい呼び出しを始めない」目安です。同時に動いている世界とまとめの分だけ、超える可能性があります。
if budget is not None and _spent >= budget:
raise BudgetExceeded(f"費用の上限 {budget} ドルに達した")
...
except BudgetExceeded as e:
stopped = str(e)
呼び出しの前に累計を見て、上限に達していれば止めます。失敗した呼び出しの費用も累計に足します。世界ごとにこの例外を受けるので、1つの世界が止まっても、全体は止まりません。
時間は、4並列で、1回の討論あたり約38秒でした。論者の呼び出し1回が平均で約5.6秒、1回の討論で6回呼ぶので、直列で約34秒です。実測とほぼ合います。
つまずいたこと
つまずいたのは、次の3つです。
- 論者の返事のJSONが、途中で切れる
- 「300字以内」の指示が、守られない
- 費用の上限が、目安にとどまる
JSONが切れる失敗は、20回に1〜2回出ました。そこで、読めなかったときに1回だけ呼び直し、必要な項目が欠けているときも同じ扱いにしました。
for attempt in range(2):
c = claude(prompt, model, think=think, budget=budget)
calls.append((kind, c))
try:
j = parse_json(c["text"])
for key, allowed in (need or {}).items():
v = j.get(key)
if not isinstance(v, str) or not v.strip() or (allowed and v.strip() not in allowed):
raise ValueError(f"項目 {key} が足りない・正しくない: {v!r}")
return j
except (ValueError, json.JSONDecodeError):
if attempt == 1:
raise
prompt += "\n\n(前の返事は JSON として読めなかった。指示された JSON だけを返してください)"
50回の本番では、論者の呼び出しが予定の300回に対して307回でした。差の7回が、呼び直しです。呼び直しても直らないことは、今もあります。その世界は集計から外れるだけで、全体は止まりません。
「300字以内」の指示は、守られませんでした。最後の主張は、平均が338字、最大が498字です。上限は、守らせる決まりというより、目安として効いていました。
費用の上限が目安にとどまる点は、前の節に書いたとおりです。
Orcaとの使い分け
私は普段、長い作業をOrcaで複数のエージェントに任せています。経緯はOrcaの連載の1本目に書きました。
使い分けは、次のとおりです。
- 長い作業(設計・下書き・実装)は、Orcaの子
- 短い判断(レビューの合否、ディベートの論、審判)は、軽い呼び出し
2つは混ぜません。Orcaで進めている作業の途中には、ディベートを挟みません。意見が割れたままの点は、私に見せます。ディベートにかけるかどうかは、私が別の依頼として決めます。
私がしたことと、AIがしたこと
仕組みは、AIが作りました。私がしたのは、次のことです。
- ディベートを、意思決定に使うと決めた
- 最初の設定を、論者はHaiku、20回と決めた
- 推す案は、議論の途中で変えないと決めた
- 審判は議論に加えないと決めた
- 票を数えるだけでなく、まとめを読んで判断すると決めた
- 最後に決めるのは人だと決めた
実行、集計、まとめの文章は、AIがしました。
このテーマのまとめ
複数の子エージェントなどを使って対話をさせて結論の品質を上げることに関しての記事は以下のとおりです。
ご参考いただけると嬉しいです。


