速度の問題ではない。異なる視点を持つ複数のエージェントに一つの決定について議論させ、どのエージェント単独よりも優れた結論に到達させることだ。オーケストレーター、エキスパート、マージの完全なコード付き。
一つのモデルに決定を評価させると、通常は平均的で慎重な一つの見解が返ってくる。同意し、丸め込み、バランスを取ろうとする傾向がある。それが問題だ:重要な決定は一つの平均的な見解で評価できるものではなく、異なる側面から攻撃されなければならない。
エージェントのスワームはこれを構造的に解決する。複数のエキスパートを作成し、それぞれに固定された役割とバイアスを与える:一人はお金だけを考え、別の一人は技術的リスクだけを、三人目はユーザーだけを考える。彼らは一つの決定を独立に分析し、異なる結論に達し、その後それらの結論の調整を強制する。ここでの価値は速度ではなく、意見の不一致が構造に組み込まれていることだ。単一のエージェントは自分自身で集団思考に陥りがちだが、役割のスワームはそうならない。
この記事では、そのようなスワームをコード付きで構築する方法を示す。3 つのパートをカバーする:役割を割り当てるオーケストレーター、独立に分析するエキスパート、そしてそれらを一つの結論に調整するマージ。
アーキテクチャ:オーケストレーター、エキスパート、マージ
分析のためのスワームは 3 つのコンポーネントからなる。
オーケストレーターはタスクを受け取り、どのエキスパートの役割が必要かを決定する。製品ローンチの評価では、投資家、エンジニア、製品スペシャリスト、セキュリティ担当者などが考えられる。オーケストレーター自体は分析せず、役割を割り振る。
エキスパートは並行して独立に動作する。各エキスパートは同じ決定を、しかし自身のレンズを通して見る。重要なのは、お互いの結論を見ないことだ。そうしないと同調が生じる。独立性こそが異なる視点を生み出す。
マージはエキスパートの結論を収集し、調整する:どこで一致し、どこで矛盾し、すべての角度から見た最終的な評決は何か。これは平均化ではなく、不一致をシグナルとして保持する合成である。

ステップ 1:基本クライアント
モデルへのシンプルなクライアントから始める。OpenAI 互換のメッセージフォーマットを使用しており、ほとんどのプロバイダーやローカルの Ollama で動作する。
1import requests2import json3from concurrent.futures import ThreadPoolExecutor45API = "http://localhost:11434/api/chat" # Ollama、またはプロバイダーのエンドポイント6MODEL = "qwen2.5:32b"78def ask(system, user, temperature=0.7):9 resp = requests.post(API, json={10 "model": MODEL,11 "messages": [12 {"role": "system", "content": system},13 {"role": "user", "content": user},14 ],15 "temperature": temperature,16 "stream": False,17 }, timeout=120)18 resp.raise_for_status()19 return resp.json()["message"]["content"]
ステップ 2:オーケストレーターが役割を割り当てる
オーケストレーターはタスクを受け取り、どのエキスパートが必要かを決定する。役割を事前にハードコードせず、モデルに特定のタスクに合わせて選択させることで、スワームを汎用的にする。厳密な JSON で返すように要求する。
1ORCHESTRATOR_SYSTEM = """あなたは分析スワームのオーケストレーターです。2タスクに対して、決定に対して最大限に異なり、矛盾した見解を与える 3 〜 5 のエキスパート役割を定義してください。3役割は互いの利益で衝突する必要があり、補完し合ってはいけません。4各役割について以下を提供してください:name(名前)、focus(何に固執するか)、bias(何に偏っているか、何を過大評価しがちか)。5説明なしで JSON 配列のみを返してください:6[{"name": "...", "focus": "...", "bias": "..."}, ...]7"""89def plan_roles(task):10 raw = ask(ORCHESTRATOR_SYSTEM, f"Task to analyze:\n{task}",11 temperature=0.9) # 役割の多様性のために高い温度を設定12 # モデルが周囲にテキストを追加した場合に備えて JSON を切り出す13 start, end = raw.find("["), raw.rfind("]") + 114 return json.loads(raw[start:end])
ステップ 3:エキスパートが並行して独立に分析する
各エキスパートは自身の役割と同じ決定を受け取る。重要なのは:並行して実行され、お互いの結論を見ないこと。ここでの並列処理は速度のためだけでなく、独立性を保証する。エキスパートは物理的に他人の意見に合わせることができない。
1EXPERT_SYSTEM = """あなたは役割 {name} を持つエキスパートです。2あなたの焦点:{focus}。3あなたのバイアス:{bias}。それと戦わないでください。それが分析におけるあなたの価値です。45決定を厳密にあなたの立場から分析してください。バランスを取ろうとせず、他の視点を考慮しようとしないでください。他のエキスパートがそれを行います。あなたの仕事は自分の角度を限界まで押し進めることです。67以下を提供してください:8- あなたの立場からの評決(賛成 / 反対 / 条件付き)9- あなたの角度から具体的に 2 〜 3 の主な論点10- あなたの立場から最も見えやすく、他の人が見逃すリスク 1 つ11簡潔に、厳しく、無駄なものはなし。"""1213def run_expert(role, task):14 system = EXPERT_SYSTEM.format(**role)15 opinion = ask(system, f"Decision to analyze:\n{task}", temperature=0.7)16 return {"role": role["name"], "opinion": opinion}1718def run_swarm(roles, task):19 # 並列起動:独立性と速度の両立20 with ThreadPoolExecutor(max_workers=len(roles)) as pool:21 futures = [pool.submit(run_expert, role, task) for role in roles]22 return [f.result() for f in futures]
ステップ 4:マージが結論を調整する
これでいくつかの鋭く一方的な意見が得られた。マージはそれらを一つの評決にまとめるが、平均化ではない。エキスパートが同意した箇所(強いシグナル)、矛盾した箇所(決定を必要とするリスクゾーン)、何が何を上回るかを探す。
1MERGE_SYSTEM = """あなたは分析スワームの統合者です。異なるバイアスを持つ複数のエキスパートの意見が一つの決定について与えられています。23あなたの仕事は平均化することではありません。あなたの仕事は:41. 一致:異なる立場にもかかわらずエキスパートが同意した点。これが最も信頼できるシグナルなので、強調すること。52. 対立:エキスパートが直接矛盾する点。それを曖昧にせず、対立を明示し、各立場のコストを述べること。63. 盲点:一人のエキスパートだけが挙げたが、重要なリスク。74. すべてを考慮した最終評決:賛成 / 反対 / 条件付き、および条件が変わる場合。89密度高く書け。不一致を情報として保持し、隠すな。"""1011def merge_opinions(task, opinions):12 block = "\n\n".join(13 f"### Expert: {o['role']}\n{o['opinion']}" for o in opinions14 )15 user = f"Decision:\n{task}\n\nExpert opinions:\n{block}"16 return ask(MERGE_SYSTEM, user, temperature=0.4) # 冷静な統合のために低い温度を設定
ステップ 4.5:偽の合意に対する悪魔の代弁者
静かな危険がある:時々、エキスパートが同意するのは決定が良いからではなく、惰性で皆が同じ方向を見ているからだ。これは偽の合意であり、明白な対立よりも危険である。なぜなら、自信のように見えるからだ。これに対抗するために、特別なエージェントを一つ追加する:悪魔の代弁者。その唯一の仕事はコンセンサスを攻撃することだ。すべてのエキスパートの意見を見て、なぜ彼ら全員が同時に間違っている可能性があるかを見つける義務がある。スワームが全会一致で「賛成」と投票した場合、代弁者はそれが大惨事になるシナリオを探す。
1DEVIL_SYSTEM = """あなたは分析スワームの悪魔の代弁者です。エキスパートの意見が与えられています。あなたの唯一の仕事:彼らの合意を攻撃すること。23もしエキスパートが何かに収束した場合、なぜ彼ら全員が同時に間違っている可能性があるかを見つけてください。共通の盲点を探してください:誰も確認せずに受け入れた前提、誰も都合が悪いので考慮しなかったシナリオ。45礼儀正しくする必要はありません。あなたの価値は、グループが聞きたくないことを言うことです。以下を提供してください:6- エキスパートの最も危険な共通の前提7- スワームの全会一致の意見が致命的に間違っていることが判明するシナリオ8- グループが注意深く避けた質問9もし合意がなく、エキスパートが本当に意見を異にしている場合は、それを率直に述べ、最も鋭い未解決の対立を指摘してください。"""1011def run_devil(task, opinions):12 block = "\n\n".join(13 f"### {o['role']}\n{o['opinion']}" for o in opinions14 )15 user = f"Decision:\n{task}\n\nSwarm opinions:\n{block}"16 return ask(DEVIL_SYSTEM, user, temperature=0.8)
ステップ 4.6:対立を鋭くするための討論ラウンド
最初のエキスパートパスは独立であり、多様性のために正しい。しかし意見が収集された後、一つの討論ラウンドを与えることができる:各エキスパートに他の意見の要約を見せ、反論させる。これにより対立が鋭くなり、弱い議論は脱落し、強い議論は強化される。
1DEBATE_SYSTEM = """あなたは分析の第 2 ラウンドのエキスパート {name} です。2あなたの元の立場:3{own_opinion}45今、他のエキスパートの意見を見ています。圧力に屈してはいけませんが、強力な議論を無視してもいけません。以下を提供してください:6- 他人の議論があなたの立場に本当に影響を与えた場合、正直に認めること7- どこで自分の立場を守り、なぜ彼らの反論が弱いか8- 討論後に評決を変更したかどうか、変更した場合はどのように変更したか9簡潔に。これは最初の意見の繰り返しではなく、反対者への反応です。"""1011def debate_round(roles, task, opinions):12 others_map = {}13 for o in opinions:14 others = "\n\n".join(15 f"### {x['role']}\n{x['opinion']}" for x in opinions if x is not o16 )17 others_map[o["role"]] = others1819 def rebut(o):20 system = DEBATE_SYSTEM.format(name=o["role"], own_opinion=o["opinion"])21 user = (f"Decision:\n{task}\n\n"22 f"Opponents' opinions:\n{others_map[o['role']]}")23 return {"role": o["role"], "opinion": ask(system, user, temperature=0.6)}2425 with ThreadPoolExecutor(max_workers=len(opinions)) as pool:26 return list(pool.map(rebut, opinions))
ステップ 5:すべてをまとめる
1def analyze(task, debate=True):2 print("オーケストレーターが役割を選んでいます...")3 roles = plan_roles(task)4 for r in roles:5 print(f" - {r['name']}: {r['focus']}")67 print(f"\n{len(roles)} 人のエキスパートを並行して起動しています...")8 opinions = run_swarm(roles, task)9 for o in opinions:10 print(f"\n[{o['role']}]\n{o['opinion']}")1112 # オプションの討論ラウンド:エキスパートが互いに反論13 if debate:14 print("\n討論ラウンド:エキスパートが互いに反論しています...")15 opinions = debate_round(roles, task, opinions)1617 # 悪魔の代弁者がスワームの合意を攻撃18 print("\n悪魔の代弁者が合意の亀裂を探しています...")19 devil = run_devil(task, opinions)20 print(f"\n[悪魔の代弁者]\n{devil}")2122 # マージが結論と代弁者の攻撃を統合23 print("\nマージが結論を調整しています...")24 opinions_plus = opinions + [{"role": "Devil's advocate", "opinion": devil}]25 verdict = merge_opinions(task, opinions_plus)26 print(f"\n=== 最終評決 ===\n{verdict}")27 return verdict2829if __name__ == "__main__":30 analyze(31 "無料プランを廃止し、製品を完全有料化(14 日間のトライアル付き)にしたい。"32 "実行すべきか?"33 )
これを実行すると、完全なパイプラインが見える:オーケストレーターが役割を選び、エキスパートが各角度から真実を切り取り、討論ラウンドで互いに議論し、代弁者が合意を攻撃し、マージが攻撃を含むすべてを考慮した評決を出す。同じ質問に対して単一のエージェントは「対象者によります」という曖昧な答えを出すが、スワームは対立が明示され、コンセンサスがストレステストされた構造化された分析を提供する。
このスワームを機能させるもの
有用なスワームをエージェントの劇場から区別する 3 つの要素がある。
役割は衝突しなければならず、補完してはならない。もしエキスパートが「マーケター、SMM スペシャリスト、コンテンツマネージャー」なら、彼らの利益は一致するためほぼ同じ答えを出す。本当の価値は利益が衝突するときにある:成長 vs 持続可能性、スピード vs 品質、今のお金 vs 後の信頼。利益の衝突が決定を切り開く。
エキスパートはお互いを見てはならない。一方が他方の意見を見るとすぐに同調が始まり、調整が入る。独立性は実装の詳細ではなく、動作条件である。並列起動がそれを無料で提供する。
マージは平均化せず、対立を保持する。悪い合成は 5 つの鋭い意見を 1 つの歯のない要約にする。良い合成は対立を可視化したままにする。なぜなら、対立は最も価値のある情報だからだ:それは決定が本当にリスクのある場所を示し、誰もがただうなずく場所ではない。
拡張の方向性
このスケルトンは明らかな方向に拡張できる。最初のマージの後、要約をエキスパートに見せて反論させる討論ラウンドを追加できる。エキスパートよりも強力なモデルに審判を置いて議論を評価させることができる。繰り返し発生する種類の決定に対して役割を永続化し、毎回生成しないようにすることもできる。
しかし基本原則は変わらない:異なるレンズ、独立した分析、不一致を尊重する合成。スワームが分析に有用なのは、多くのエージェントがいるからではなく、彼らが異なる見方をし、お互いを共通分母に滑り込ませないからだ。今まさに一人で頭の中で考えている決定を、このようなスワームにかけてみてほしい。今まで持っていなかった角度が見えるだろう。





