0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIディベートシステムを作ったら、LLMの「性格」と格闘することになった話

Posted at

はじめに

複数のAI(GPT-4、Claude、Gemini)に議論させるWebアプリを開発しました。技術スタックはCloudflare Workers + Next.jsで、ほぼ無料枠で動いています。

この記事では、LLMに「立場」を守らせるために試行錯誤した内容を共有します。

作ったもの

スクリーンショット 2025-08-12 20.07.06.png

2~4体のAIエージェントが、あるトピックについて賛成・反対・中立などの立場から議論するシステムです。

システム仕様

スクリーンショット 2025-08-12 20.07.36.png

  • エージェント数: 2-4体(選択可能)
  • 議論ラウンド数: 1-5回
  • 同時実行セッション: 最大10件(Stage 1の制限)
  • 処理時間: 60-180秒(エージェント数とラウンド数による)

スクリーンショット 2025-08-12 20.10.09.png
最終的に要点をまとめたレポートが出力されます。

議論の例

トピック:「リモートワークを継続すべきか」

3エージェント構成の場合

  • Agent1 (GPT-4): 賛成派
  • Agent2 (Claude): 反対派
  • Agent3 (Gemini): 中立派

4エージェント構成の場合

  • Agent1 (GPT-4): 賛成派
  • Agent2 (Claude): 反対派
  • Agent3 (Gemini): 中立派
  • Agent4 (GPT-4): ユーザー視点代弁者

問題:AIが立場を守らない

現象1: Claudeの寝返り

賛成派に設定したClaudeが、2ラウンド目で突然こんな発言:

「前回の反対派の指摘は的を射ています。私も再考すると...」

賛成派なのに反対派に同調してしまう問題が頻発しました。

現象2: Geminiの哲学モード

中立派のGeminiが議論をまとめる際:

「しかし、人生とは選択の連続であり、我々は皆...」

具体的な結論を避けて抽象論に逃げる傾向がありました。

現象3: GPT-4の優等生病

反対派のGPT-4が必ず最後に:

「ただし、これは一つの視点に過ぎず、賛成派の意見にも一理あります」

と付け加えてしまい、議論が成立しません。

解決策:モデル別の対処法

1. プロンプトでの立場強制

単に「賛成の立場で」と指示するだけでは不十分でした。以下のような多重の制約が必要:

const STANCE_RULES = {
  supporting: `
    あなたは絶対的な賛成派です。
    以下のルールを厳守してください:
    
    1. 冒頭で必ず「私は賛成の立場から述べます」と宣言
    2. 反対意見には必ず反論する
    3. 結論は必ずポジティブにする
    4. 「しかし」「ただし」で始まる譲歩文を使わない
    5. 最後に「以上が賛成派としての主張です」で締める
  `,
  opposing: `
    あなたは絶対的な反対派です。
    以下のルールを厳守してください:
    
    1. 冒頭で必ず「私は反対の立場から述べます」と宣言
    2. 賛成意見の欠点を必ず指摘する
    3. 結論は必ずネガティブにする
    4. 部分的な同意も表明しない
    5. 最後に「以上が反対派としての主張です」で締める
  `
};

2. モデル別パラメータ調整

各LLMの「性格」に合わせた細かい調整が必要でした:

const MODEL_SETTINGS = {
  'gpt-4': {
    temperature: 0.3,  // 低めで一貫性を保つ
    top_p: 0.8,        // 多様性を制限
    frequency_penalty: 0.5,  // 同じ表現の繰り返しを防ぐ
  },
  'claude-3': {
    temperature: 0.4,  // GPTより少し高め
    top_p: 0.9,        
    // Claudeは特に明示的な指示が有効
    system: "You must argue your position strongly. No hedging."
  },
  'gemini-pro': {
    temperature: 0.2,  // 最も低くして具体性を保つ
    top_p: 0.7,
    // 抽象化を防ぐ
    system: "Be specific. Use numbers and facts. No metaphors."
  }
};

3. レスポンスの後処理

それでも完全には防げないので、後処理で修正:

function cleanResponse(text: string, role: string, model: string): string {
  let cleaned = text;
  
  if (role === 'supporting') {
    // 譲歩表現を削除
    cleaned = cleaned.replace(/ただし.*?。/g, '');
    cleaned = cleaned.replace(/一方で.*?。/g, '');
  }
  
  if (model.includes('gemini')) {
    // 哲学的な締めを削除
    cleaned = cleaned.replace(/人生とは.*$/g, '');
    cleaned = cleaned.replace(/結局のところ.*$/g, '');
  }
  
  if (model.includes('claude')) {
    // 過度な慎重さを削除
    cleaned = cleaned.replace(/重要なのは.*?という点です/g, '');
  }
  
  return cleaned;
}

効果測定

100回の議論セッション(2-4エージェント、3-5ラウンド)で立場維持率を測定:

モデル 対策前 対策後
GPT-4 72% 94%
Claude-3 58% 89%
Gemini Pro 65% 91%

※立場維持率:設定した立場を最後まで守った割合

実装上の工夫

メモリ効率の改善

Cloudflare Workersはメモリ制限が厳しいため、議論の文脈を効率的に管理:

// 直近2ラウンドのみ保持(エージェント数に応じて動的調整)
function getRecentContext(
  history: Message[], 
  maxRounds: number = 2,
  agentCount: number = 3
): string {
  const messagesToKeep = maxRounds * agentCount;
  const recentMessages = history.slice(-messagesToKeep);
  return recentMessages
    .map(m => `${m.agent}: ${m.content.substring(0, 200)}...`)
    .join('\n');
}

エラーハンドリング

LLM APIは不安定なので、リトライ処理は必須:

async function callLLMWithRetry(
  params: any, 
  maxRetries: number = 3
): Promise<string> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await callLLM(params);
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      
      // 指数バックオフ
      await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
    }
  }
  throw new Error('Max retries exceeded');
}

今後の改善点

  1. コンテキスト管理の最適化

    • 現在は単純な文字列結合
    • ベクトル埋め込みで重要部分を抽出したい
  2. 動的なプロンプト調整

    • 議論の流れに応じてプロンプトを変更
    • 立場が弱まったら強化する
  3. より多様なモデル対応

    • Mistral、Llama系の追加
    • 日本語特化モデルの検討
  4. Stage 2への移行準備

    • WebSocket対応で リアルタイム更新
    • 同時実行数の制限解除
    • より高度な分析機能

まとめ

LLMに「役割」を演じさせるのは予想以上に難しく、モデルごとの「性格」を理解して対処する必要がありました。

プロンプトエンジニアリングだけでなく、パラメータ調整と後処理を組み合わせることで、ようやく安定した議論が可能になりました。

現在はCloudflare無料枠の制限内で動作していますが、実用レベルのパフォーマンスを実現できています。

次回は、このシステムでキャッシュ戦略によりAPI費用を70%削減した方法について書く予定です。

※キャッシュの詳細実装(2層構造:メモリ24時間 + DB 7日間永続化)は次回記事で解説します。

βテスト実施中なの是非使ってみてください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?