上級 10 分マルチモデル

LLM Council:複数のモデルに審議させる locaux

LLM Councilは、Andrej Karpathyが2025年末に公開した小規模なプロジェクトです。1つのモデルに質問を送る代わりに複数のモデルに質問を送り、各モデルが誰の回答かを知らずに他の回答を順位付けし、「議長」モデルが最終的な要約を作成します。リポジトリはOpenRouter経由のクラウドモデルを対象としていますが、その仕組みは約100行に収まり、Ollamaによって提供されるローカルモデルにも移植できます。このガイドでは3つのステップを説明し、個人用マシンで再現する方法、メモリと時間にかかるコスト、そして評議会が単独の優れたモデルより優れた回答を返すと想定してはいけない理由を詳しく解説します。

著者 Léa B.·更新 2026-10-12·Windows・macOS・Linuxでテスト済み

#LLM Council:3段階の仕組み

このリポジトリでは、三段階の固定されたフローが説明されています。各段階はツールもメモリも使わない通常のモデル呼び出しです。唯一の工夫は、最初の段階の出力を後の二段階にどのように渡すかにあります。

  1. 01
    最初の意見
    ユーザーの質問は、そのまま各評議会メンバーに個別に送信されます。各メンバーは他のメンバーの回答を見ずに回答します。元のインターフェースでは、これらの回答はモデルごとに一つのタブに表示されます。
  2. 02
    校閲と順位付け
    続いて各メンバーは、他のメンバーの匿名化された回答(「回答A」、「回答B」などのラベル付き)を受け取り、正確さと深さを基準に、最良の回答から最も低い回答まで順位付けします。匿名化は、モデルが既知の競合相手をえこひいきしたり、不利に扱ったりするのを避けるためのものです。
  3. 03
    議長による要約
    議長として指定されたモデルは、質問、すべての回答、すべての順位を受け取り、ユーザーに提示する単一の最終回答を作成します。

この構成では異なるモデルは必要ありませんが、それこそが要点です。学習の多様性に頼ることで、あるモデルの誤りを別のモデルが見つけられるようにします。議長はメンバーの一つでも、別のモデルでも構いません。通常は一覧中で最も能力の高いモデルにします。

i
製品ではなく、探索のためのプロジェクト
Karpathyは告知メッセージの中で、LLM Councilをコードアシスタントが大部分を書いた週末のプロジェクトとして紹介しており、今後メンテナンスする予定はないと述べています。モデル同士がうまく順位付けできるかどうかについて彼が共有している印象は、定性的かつ時点依存のものです。リポジトリには数値評価が伴っておらず、このガイドも追加していません。

#Karpathyのリポジトリに含まれるもの

ローカルエージェントキット

あなたのマシンで動作するエージェント:エージェント型 Cline、MCP、n8n + Ollama、ローカル自動化。

  • 永久に利用できるオンラインスペース
  • PDF + ファイル
  • 生涯アップデート

github.com/karpathy/llm-councilリポジトリは、一般的なチャットインターフェースに似たローカルWebアプリケーションです。執筆時点のREADMEと設定ファイルによれば、次の要素で構成されています。

Pythonバックエンド(FastAPI)
backend/フォルダーには、3つのステップのロジック、OpenRouterのAPI用HTTPクライアント、data/フォルダー内のJSONファイルへの会話保存が含まれています。依存関係はuvで管理されます。
Reactフロントエンド(Vite)
frontend/ ディレクトリ:個々の回答、ランキング、要約を表示するインターフェースです。
設定ファイル
backend/config.pyには、評議会のメンバー(COUNCIL_MODELS)と議長(CHAIRMAN_MODEL)がOpenRouterの識別子として、呼び出されるAPIのアドレスとともに記載されています。
起動スクリプト
start.sh はバックエンドとフロントエンドを同時に起動します。.env ファイルには OPENROUTER_API_KEY が必要です。

デフォルトで設定されているモデルは、クローズドなクラウドモデルです(公開版ではGPT、Gemini、Claude、Grokの各ファミリー)。ローカル版にするには、ここを変更します。APIがOpenAIによって普及した「chat completions」形式を扱える限り、残りのコードは誰が回答するかを気にしません。このガイドに従う前にREADMEとconfig.pyを読み直してください。プロジェクトは2026年10月12日以降に変わっている可能性があります。

#ローカル版の前提条件

Ollamaがインストールされ、動作している状態
サーバーはデフォルトで http://localhost:11434 をリッスンします。ollama list コマンドでモデルが表示されることを確認してください。
異なるファミリーの3〜4モデル
同じファミリーに由来するモデルだけで構成された評議会では、多様性の利点が失われます。例えば、Qwen、Llama、Mistral、Gemmaを、近いサイズで組み合わせることを目指してください。
適切なメモリ
Q4_K_Mでは、7Bモデル1つあたり約5 GB、14Bモデル1つあたり9 GBを見込んでください。メンバーはすべてを同時にメモリへ収める必要はありませんが、快適さはメモリ容量に左右されます(コストに関するセクションを参照)。
Python 3.10以降
このガイドの最小スクリプトではrequestsパッケージを使います。元のリポジトリでは、さらにuvとNode.jsが必要です。
Patience
一回の審議には、メンバー数の少なくとも二倍に加えて一つ、つまりそれ以上の生成が必要です。これは即時チャット用のツールではありません。

#ステップ1:評議会と議長を選ぶ

メンバーの選択では、多様性とメモリのバランスが重要です。一方、議長には長いコンテキストを正確に読む能力が特に求められます。議長は質問、すべての回答、すべての順位を受け取るため、容易に数千トークンになります。この役割には、処理が遅くても、お使いのマシンがサポートする中で最も高性能なモデルを選んでください。1ターンにつき一度しか呼び出されないためです。

ターミナル
# Exemple de conseil pour une carte 12 Go (noms à vérifier avec `ollama search` ou sur ollama.com/library)
ollama pull qwen3:8b
ollama pull llama3.1:8b
ollama pull mistral:7b
ollama pull gemma3:12b   # président : plus gros, appelé une seule fois par tour
ollama list

7Bから8Bのサイズなら、12 GBのカード上のメンバーに適しています。一つだけならコンテキスト用の余裕を残してメモリに収まり、Ollamaがそれらを切り替えます。24 GB、または48 GBのユニファイドメモリを搭載したMacなら、メンバーを14Bに上げるか、二つから三つのモデルを同時に読み込めるため、再読み込みをなくせます。

→
可能であれば推論モデルを追加してください
回答する前に「考える」ことができるメンバーは、直接回答するモデルとは異なる観点をもたらすことがよくあります。ただし、順位付けの段階では思考フェーズを無効にするか制限してください。そうしないと、この段階が一巡の中で最も長くなります。

#手順2:複数のモデル向けにOllamaを設定する

ここではOllamaに関する2つの設定が重要です。1つ目はメモリに保持するモデル数で、OLLAMA_MAX_LOADED_MODELS変数で指定します。メモリが許せば、すべてのメンバーをロードしたままにすることで、メンバーを切り替えるたびの再ロードを避けられます。2つ目はコンテキストウィンドウのnum_ctxで、Ollamaではデフォルトが短く設定されています。順位付けのステップと議長による要約では複数の完全な回答を読み込むため、コンテキストが小さすぎるとプロンプトの冒頭がひそかに切り捨てられます。

ターミナル(Linux、systemdサービス)
sudo systemctl edit ollama
# puis dans le bloc [Service] :
# Environment="OLLAMA_MAX_LOADED_MODELS=2"
# Environment="OLLAMA_KEEP_ALIVE=30m"
sudo systemctl restart ollama

macOSでは、アプリケーションを再起動する前にlaunchctl setenvを使って同じ変数を設定します。OLLAMA_MAX_LOADED_MODELSの値は、使用可能なメモリと整合させる必要があります。コンテキストを含めると、Q4の8Bモデル2つだけですでに約10 GBを占有します。コンテキスト自体は、リクエストごと(APIのoptions.num_ctxフィールド)または派生したModelfileで設定します。次のセクションでは、これを各呼び出しに渡します。

!
並列処理:デフォルトでは逐次実行
元のコードはメンバーを並列で起動します。これはクラウドAPIでは合理的です。ローカル環境で4つの異なるモデルに4件のリクエストを同時に送ると、Ollamaはそれらを順番にロードとアンロードすることになり、場合によっては一部をプロセッサに移すことになります。すべてのメンバーがメモリに収まる場合を除き、1つずつ呼び出してください。

#ステップ3:Pythonによる最小限の評議会

以下のスクリプトは、インターフェースなしでOllamaのネイティブAPIを使い、評議会の三段階を再現します。ラベルから著者が推測されないよう、順位付けの前に回答の順序を混ぜ、各メンバーに短い順位付けを求めてから、すべてを議長に委ねます。モデル名はお使いのマシンにあるものに合わせてください。

council.py
import random
import requests

OLLAMA = "http://localhost:11434/api/chat"
COUNCIL = ["qwen3:8b", "llama3.1:8b", "mistral:7b"]
CHAIRMAN = "gemma3:12b"
NUM_CTX = 16384


def ask(model, prompt):
    r = requests.post(OLLAMA, json={
        "model": model,
        "messages": [{"role": "user", "content": prompt}],
        "stream": False,
        "options": {"num_ctx": NUM_CTX, "temperature": 0.3},
    }, timeout=1800)
    r.raise_for_status()
    return r.json()["message"]["content"].strip()


def council(question):
    # Étape 1 : premières opinions, une par membre, sans se voir
    answers = {m: ask(m, question) for m in COUNCIL}

    # Anonymisation : ordre mélangé, étiquettes A, B, C...
    order = list(COUNCIL)
    random.shuffle(order)
    labels = {m: chr(65 + i) for i, m in enumerate(order)}
    packed = "\n\n".join(
        f"### Réponse {labels[m]}\n{answers[m]}" for m in order
    )

    # Étape 2 : chaque membre classe les réponses (les siennes incluses)
    rankings = {}
    for m in COUNCIL:
        rankings[m] = ask(m,
            f"Question : {question}\n\n{packed}\n\n"
            "Classe ces réponses de la meilleure à la moins bonne selon leur "
            "justesse et leur profondeur. Pour chacune, une ligne de justification. "
            "Termine par la ligne 'CLASSEMENT :' suivie des lettres dans l'ordre.")

    # Étape 3 : le président rédige la synthèse finale
    votes = "\n\n".join(
        f"### Avis du juge {labels[m]}\n{rankings[m]}" for m in COUNCIL
    )
    final = ask(CHAIRMAN,
        f"Question : {question}\n\n{packed}\n\n{votes}\n\n"
        "Tu présides ce conseil. En t'appuyant sur les réponses et les classements, "
        "rédige une réponse finale unique, précise et sans redite. Signale les points "
        "sur lesquels les réponses se contredisent.")
    return answers, labels, rankings, final


if __name__ == "__main__":
    import sys
    q = " ".join(sys.argv[1:]) or "Quelle quantification choisir pour un modèle 14B sur 12 Go de VRAM ?"
    answers, labels, rankings, final = council(q)
    for m in COUNCIL:
        print(f"\n===== {m} (Réponse {labels[m]}) =====\n{answers[m]}")
    for m in COUNCIL:
        print(f"\n===== Classement par {m} =====\n{rankings[m]}")
    print(f"\n===== Synthèse ({CHAIRMAN}) =====\n{final}")
ターミナル
python3 -m venv .venv && source .venv/bin/activate
pip install requests
python council.py "Faut-il un reranker dans un pipeline RAG local ?"

元のリポジトリはさらに踏み込み、各判定役の順位付け行を抽出して回答ごとの平均順位を計算し、タブの横に表示します。各出力の「CLASSEMENT:」行を読み取れば、数行で追加できます。ただし、小規模モデルはこの形式に必ずしも従わないため、この行がない場合のフォールバックを用意してください。

→
メンバーが自分自身を評価する
このスクリプトでもリポジトリでも、各判定役は匿名ラベルの下で、他の回答の中にある自分自身の回答を見ます。これは意図的なものです。モデルが自分の文体を認識して、自分を有利に扱うかどうかを確認できます。除外したい場合は、各判定役に送るブロックからその回答を削除してください。ただし、順位付けの対象が同じ集合ではなくなり、平均値も読み取りにくくなります。

#ステップ4:元のリポジトリをOllamaに適応させる

Webインターフェースが必要なら、最短の方法は、Ollama のOpenAI互換エンドポイントを使用することです。このエンドポイントは http://localhost:11434/v1/chat/completions で提供されています。リポジトリのバックエンドはすでにこの形式でOpenRouterにリクエストを送信しているため、原則として backend/config.py のAPIアドレスとモデル識別子を置き換え、ダミーキー Ollama を設定するだけで済みます。これは認証ヘッダーを無視します。

ターミナル
git clone https://github.com/karpathy/llm-council
cd llm-council
uv sync
cd frontend && npm install && cd ..
echo 'OPENROUTER_API_KEY=ollama' > .env
backend/config.py(適用用の抜粋。定数名はお使いのバージョンで確認してください)
COUNCIL_MODELS = [
    "qwen3:8b",
    "llama3.1:8b",
    "mistral:7b",
]
CHAIRMAN_MODEL = "gemma3:12b"

# Ollama expose une API compatible OpenAI sur /v1
OPENROUTER_API_URL = "http://localhost:11434/v1/chat/completions"
ターミナル
./start.sh
# backend sur le port 8001, interface Vite sur http://localhost:5173 (valeurs du README à vérifier)

手作業で対処すべき点が二つ残っています。まずコンテキストです。OpenAI互換のエンドポイントにはnum_ctxフィールドがないため、コンテキストを固定するModelfileでモデルのバリエーションを作るか、オプションを受け付ける/api/chatのネイティブAPIにクライアントを切り替える必要があります。次に、先ほど述べた並列性です。第一段階では呼び出しを同時に開始するため、メンバーをすべてメモリに収められない場合は逐次実行に切り替える価値があります。

ターミナル(コンテキスト固定版)
cat > Modelfile.qwen-council <<'EOF'
FROM qwen3:8b
PARAMETER num_ctx 16384
EOF
ollama create qwen3-council -f Modelfile.qwen-council

複数のコミュニティ製フォークが、Ollama やその他のローカルサーバー向けにこれらの変更を行っています。いずれかを採用する前に、最後のコミット日、元のリポジトリと比べて変更されたファイル数を確認し、モデルを呼び出すコードを読んでください。短いコードであり、違いが現れるのはそこです。このガイドでは特定のフォークを推奨していません。状態の変化が速すぎるためです。


#メモリと時間のコスト

取り組む前に、この計算を確認しておく価値があります。N人のメンバーによる審議では、完全な一回の処理にステップ1でN回、ステップ2でN回、議長用に一回の生成が必要で、合計2N + 1回の呼び出しになります。ステップ2と議長の呼び出しは、プロンプトにすべての回答が含まれるため、最初の呼び出しより処理に時間がかかります。プロンプトの読み取り時間が生成時間に加わるためです。

三人のメンバー
質問ごとに七回生成します。お使いのマシンで各回答に半分ほどの時間がかかる場合、再読み込みを除いても一巡に三分を超えます。
最小メモリ
評議会で最大のモデルとそのコンテキストです。Q4の8Bメンバーと16 000トークンのコンテキストなら、読み込むモデルを一つだけにすることを条件に、12 GBのカードに収まります。
余裕を持って使えるメモリ容量
すべてのメンバーを同時に読み込みます。必要なのは、各モデルのサイズの合計に、モデルごとのコンテキストキャッシュを加えた容量です。Q4の8Bモデル三つには、最低でも24 GBのGPUまたは32 GBのユニファイドメモリを搭載したMacが必要です。
Rechargements
メンバーを一緒に収められない場合、モデルを切り替えるたびにディスクからの読み込みで数秒かかります。一巡では最大2N回発生します。NVMe SSDならこのコストを許容できますが、遅いディスクでは負担になります。

これらの概算値は、ハードウェア、量子化、回答の長さによって異なります。本記事ではターンの処理時間を測定しておらず、上記の数値は計算上の目安であって結果ではありません。自分のケースを評価するには、各メンバーで単純な生成にかかる時間を計測し、式を適用してください。

#評議会の限界とバイアス

この仕組みが魅力的なのは、ピアレビューに似ているからです。しかし、優れた単独モデルより必ず正確だと考えるべきではありません。リポジトリにはそれを裏付ける測定結果がなく、モデルを評価者として使う際に知られている複数のバイアスも、この仕組みに不利に働きます。

不完全な匿名化
ラベルは名前を隠しますが、文体までは隠しません。モデルは自分独自の書き方、あるいは知っているモデルファミリーの書き方を認識し、それに応じて順位付けする可能性があります。Karpathy自身も、定性的な観察として、モデルはしばしば自分の回答より別のモデルの回答を好んだと記しています。これは観察であり、保証ではありません。
位置バイアスと長さバイアス
例えば、Zheng et al. (2023)によるMT-Benchの論文など、審判モデルに関する研究では、最初に読んだ回答や長い回答を好む傾向が記録されています。順序を混ぜることで前者のバイアスは弱められますが、後者はうまく補正できません。
相関したエラー
近いコーパスで学習したモデルは、同じような間違いをすることがよくあります。小規模モデルによる評議会が、誤った回答に全会一致で投票し、議長がそれを採用する可能性もあります。
議長だけが残る
統合結果は完全に議長モデルに依存します。議長が幻覚を起こせば、評議会は何も変えられません。重要な विषयでは個々の回答を読んでください。これは、元のインターフェースにタブがある理由でもあります。
小さなモデル、長い指示
順位付け用プロンプトには、三つまたは四つの文章を読み、判断し、根拠を示し、形式を守ることが求められます。7B未満では出力が不規則になり、平均順位の意味が失われます。
!
測定なし、約束なし
評議会が結果を改善するか知りたい場合は、答えが分かっている質問の小さなセットを作り、最良のモデル単独と評議会で実行して、ブラインドで比較してください。自分のケースでこのテストをしなければ、「複数の意見は一つより優れている」という直感は仮説にすぎません。

#ローカルLLM Councilの実際のユースケース

2N + 1回の生成による追加コストは、質問がまれで重要な場合には正当化できますが、雑談には向きません。ローカルでこのパターンを取り入れた人々からは、三つの状況が浮かび上がります。

機密性の高いテキストまたはコードの校閲
契約関連の文書、手順、重要な関数では、メンバーがそれぞれ異なる問題を指摘し、議長がそれらを一つのリストにまとめます。ここでは、順位付けよりも指摘の多様性が重要です。
根拠のある技術的判断
「このデータベースを移行すべきか」、「このエクスポートにはどの形式がよいか」など、回答の食い違い自体が情報になります。議長には、意見の相違を平滑化するのではなく、見える形にするよう求めてください。
単一モデルの品質管理
評議会を審査員として使い、二つのプロンプト版または同じモデルの二つの量子化を比較するには、別の場所で生成した出力を順位付けさせます。これは手順1を省いた、手順2の直接的な応用です。

これらすべてのケースで、ローカル版の本当の根拠は機密性です。元の版がOpenRouter経由で四つの異なるプロバイダーに送るのに対し、読み込む文書はマシンから外に出ません。待ち時間が数分になることを受け入れられるなら、内部データを扱うチームにとって、このパターンが興味深いものになる理由です。

#トラブルシューティング

順位付けで1つの回答が無視される
コンテキストが短すぎ、プロンプトの冒頭が切り詰められています。num_ctx(またはModelfile)を増やし、Ollamaのログに切り詰めに関する警告がないことを確認してください。
手順1の呼び出しが非常に遅い
メンバーは並列に呼び出されるため、メモリ上で競合します。逐次実行に切り替えるか、VRAMに同時に収まる範囲まで評議会を縮小してください。
議長が論点から外れた回答をする
このモデルのコンテキストは、今回の処理で最も多く使用されています。より大きなモデルを割り当てるか、ランキングを要約してから渡してください。
順位付け形式がない
小型モデルは「CLASSEMENT :」の行を忘れます。プロンプトの末尾で形式を再度示し、温度を下げるか、根拠を手作業で読むことを受け入れてください。
メンバーの回答が空、または英語になっている
モデルが思考フェーズだけを返していないことを確認し、質問内で期待する言語を明示してください。
数回の一巡後にメモリが飽和する
OLLAMA_MAX_LOADED_MODELS がマシンに対して高すぎます。1に戻し、Ollama に切り替えさせてください。

#さらに詳しく

LLM Councilはエージェントのフレームワークではなく、審議のパターンです。ツール、役割、メモリは管理しません。必要が意見の統合を超える場合は、サイト上のこれらのガイドで関連する構成要素を扱っています。

Ollamaを使ったCrewAI
同じ回答を評価するのではなく、モデル同士で役割を分担し、タスクを順番に進める必要がある場合。https://quelllm.fr/guide/crewai-ollama-multi-agents
ローカルゲートウェイとしてのLiteLLM
例えば、二台のマシンに分散した評議会のように、複数のモデルサーバーをOpenAI互換の単一APIの背後に公開するためのものです。https://quelllm.fr/guide/litellm-proxy-gateway-local
ローカルAIエージェントのアーキテクチャ
モデルにツールとメモリを与えたいとき、その周囲に組み合わせるコンポーネントです。https://quelllm.fr/guide/agent-ia-local-architecture
ローカルネットワーク上でOllamaサーバーを共有する
評議会のメンバーをより大きなマシンでホストし、お使いの端末から呼び出します。https://quelllm.fr/guide/ollama-serveur-reseau-local-partage

KarpathyのGitHubリポジトリとOllamaのドキュメントに基づき、2026年10月12日に作成したガイドです。この記事では時間も品質も測定していません。メモリの目安はQ4_K_Mでのおおよその値です。定数名、ポート、モデル名は変わるため、コマンドをコピーする前に以下のソースを読み直してください。

このガイドは役に立ちましたか?

ご意見、誤りのご指摘、補足はありますか?ぜひお知らせください。皆さんにとってより良いガイドにするために役立ちます。