TL;DR(このノードは何をする・専門語ゼロ): 提案された技術判断の下書きを受け取り、起案者が見落としている弱点・反論・将来の失敗の芽を機械的にあぶり出すノード。あえて反対役に回る AI と、半年後に大失敗した前提で原因を逆算する AI を同時に走らせ、その指摘を別の AI がまとめて重要度順に整理する。ここでは合否を出さず、見つかった指摘を変更提案 (PR) の本文に載せて、最終判断は人間に委ねる。

実装: drp/src/nodes/socratic.ts プロンプト: 現在は socratic.ts 内 inline 定義 (DA / PM / Judge の 3 system prompt)。Phase 2 で prompts/production/gate1-da/ 等に外出し予定 テスト: drp/test-tc-socratic.mjs 基盤 ADR: ADR-0071 / ADR-0019 / ADR-0033 (モデル選定)


1. 役割と位置づけ

起案者ドラフトの 盲点検出。Devil's Advocate (DA)〔悪魔の代弁者=あえて反対役に回り反論を出す役〕と Pre-mortem (PM)〔事前検死=将来失敗した前提で原因を逆算する役〕の 2 本並列 + Judge/Aggregator〔判定・集約役=両者の指摘をまとめ重要度を裁定する役〕による non-interactive DAG〔有向非巡回グラフ=一方向に流れる処理の依存図〕で、起案者が見落としているリスク・反論・失敗シナリオを機械的に洗い出す。

  • 解決する課題: RQ Synthesis の 3 モデル合意が同じ訓練データバイアスで盲点を共有するリスク、および Proposer=Reviewer のソロ運用で confirmation bias が構造的に排除できないリスク (ADR-0071 §1.3)。
  • 設計思想: 情報提供型 (差戻しなし)。findings を PR body の Blind-spot Report に表示し、人間 (代表取締役) が最終判断する。Pipeline の pass/fail 判定には使用しない。
  • 前身: 旧 Socratic〔ソクラテス式問答=前提・代替案を問い直して盲点を洗い出す手法〕Node (情報充足チェック・インタラクティブ追加質問) は RQ Synthesis 導入により dead node 化したため、ADR-0071 で本 DAG に再定義。

2. フロー図

flowchart LR
    T[triage] -->|needsAdr=true| S[socratic
blind-spot detection] S -->|socraticPass=true
blindSpotFindings| B[body_generation] S -.->|全ノード失敗時
skip-through| B

Gate 1 は常に socraticPass=true を返す (差戻しなし)。全ノード (DA+PM) が API エラー等で失敗した場合のみ、空の findings で skip-through する。


3. トリガー条件

条件挙動
triage から needsAdr=true実行
triage から needsAdr=falseスキップ (triage が END に飛ばす)

buildPreGraph() (triage のみ) では実行されない。buildGraph() / buildGraphWithWebhook() の main graph 内でのみ実行される (ADR-0071 で socratic が 30s wall time を超えるため preGraph から除外)。


4. 入力 (State)

State フィールド用途
title任意
context必須 (背景・決定内容)
options任意 (検討した代替案)
blindSpotFindings既に findings がある場合 (len > 0) は再実行をスキップ

5. 処理ロジック

1. blindSpotFindings が既にある場合 → 即リターン (冪等性保証)
2. userInput を組み立て (title + context + options)
3. DA + PM を Promise.allSettled で並列実行 (各 90s timeout)
   3a. Devil's Advocate (T=0.95): 反論 3-5 件生成
   3b. Pre-mortem (T=0.8): 4 カテゴリ強制の失敗シナリオ生成
4. rawFindings を集約 (片方失敗時は成功側のみ)
5. 全ノード失敗時 → skip-through (socraticPass=true, findings=[])
6. Judge/Aggregator (T=0.2):
   - rawFindings + 原文を入力
   - 重複排除 (dedupe_group)
   - severity × actionability 分類
   - 提案の context で既に言及されている指摘を除外
   - 最大 10 件に絞り込み
7. Judge 失敗時 → rawFindings をそのまま fallback (actionability=monitor)

3 ノードの役割

ノード役割Temperature出力
Devil's Advocate (DA)提案への反論・反証 3-5 件0.95{findings: [{title, severity, evidence, suggested_action}]}
Pre-mortem (PM)6 ヶ月後の破滅的失敗シナリオ (技術/運用/ビジネス・規制/認知の 4 カテゴリ強制)0.80{findings: [{category, title, severity, probability, evidence, suggested_action}]}
Judge/AggregatorDA+PM の findings を統合・重複排除・severity×actionability 分類0.20{findings: [{source, title, severity, actionability, evidence, suggested_action, dedupe_group}], summary}

6. LLM 設定

ノードモデルTemperature根拠
DAclaude-sonnet (MODELS.socratic)0.95多様な反論生成のため高 T (ADR-0056 準拠、Silverio/Smit TMLR 2026)
PMclaude-sonnet (MODELS.socratic)0.80失敗シナリオの多様性確保
Judgeclaude-sonnet (MODELS.socratic)0.20分類・統合の安定性重視
timeout各ノード 90 秒 (SAMPLE_TIMEOUT_MS)async 実行 (Queue consumer 15 分予算) 内で動作するため余裕あり
コスト目安DA ($0.03) + PM ($0.03) + Judge ($0.04) ≈ $0.10/ADR

7. 副作用

なし。LLM 呼出のみ。


8. 出力 (State)

正常完了

{
  socraticPass: true,
  socraticQuestions: [],  // 後方互換性のため空配列を維持
  blindSpotFindings: [
    {
      source: 'devil_advocate' | 'premortem' | 'both',
      title: 'short title',
      severity: 'critical' | 'high' | 'medium' | 'low',
      actionability: 'block' | 'revise_adr' | 'add_to_risks' | 'monitor',
      evidence: 'consolidated evidence',
      suggestedAction: 'specific recommendation',
      dedupeGroup: 'group key',
    },
    // ... 最大 10 件
  ],
}

全ノード失敗時 (skip-through)

{ socraticPass: true, socraticQuestions: [], blindSpotFindings: [] }

Judge 失敗時 (fallback)

{
  socraticPass: true,
  socraticQuestions: [],
  blindSpotFindings: [
    // rawFindings をそのまま出力、actionability は全て 'monitor'
  ],
}

UI 側 (public/chat.html) は blindSpotFindings を読み取って Blind-spot Report として表示する。webhook node は findings を PR 本文に転記する。


9. 分岐 (次ノード)

.addConditionalEdges('socratic', (s) => s?.socraticPass ? 'body_generation' : END)
条件次ノード備考
socraticPass=true (常態)body_generationfindings の有無にかかわらず通過
socraticPass=false/nullEND現実装では発生しない (常に true を返す) が、conditional edge の防御として残置

buildPreGraph には socratic を含まない (triage → END)。


10. エラー時の挙動

障害パターン挙動
DA のみ失敗PM の findings のみで Judge 実行 → 正常完了
PM のみ失敗DA の findings のみで Judge 実行 → 正常完了
DA + PM 両方失敗skip-through (findings=[], socraticPass=true)
Judge 失敗rawFindings をそのまま fallback (actionability=monitor)
90s timeoutPromise.race で reject → 該当ノードを失敗扱い

エラーは console.error で JSON 構造化ログ出力 (event: 'blindspot_node_failed' / 'blindspot_judge_failed')。


11. 既知の弱点・運用注意

  • DA inauthentic dissent: Devil's Advocate の 4.9% は形式的反論 (Silverio/Smit TMLR 2026)。Judge の dedupe + severity 分類で軽減するが完全排除は不可。
  • false positive: severity 分類の誤り (medium を critical に格上げ等) によるアラート疲労リスク。Judge の actionability フィールドで block は極めて稀に限定。
  • Judge 誤分類: PR body の Blind-spot Report に全 findings を severity 付きで表示し人間が最終判断。Pipeline の pass/fail 判定には使用しない。
  • レイテンシ: DA+PM 並列 + Judge で 30-90 秒。buildPreGraph() からは除外済みで、Queue consumer (15 分予算) 内の main graph でのみ実行。
  • Adjacent Domain (AD) 未実装: ADR-0071 Phase 2 で web search 連携と共に追加予定。

12. テストケース

ID内容期待
TC-BS01情報十分な ADR ドラフト (ADR-0056 等)blindSpotFindings.length >= 3, severity 分布あり
TC-BS02DA API エラー注入PM findings のみで Judge 正常完了
TC-BS03DA + PM 両方エラー注入skip-through (findings=[], socraticPass=true)

実行: node drp/test-tc-socratic.mjs


13. 過去の設計判断ログ

日時変更経緯
2026-05-12Phase 2b 実装 (PR #591)方式 A (ステートレス) で初期実装 (インタラクティブ追加質問)
ADR-0033 採択時gemini-flash → claude-sonnet に upgrade判定一貫性向上のため
2026-05-27ADR-0071 採択 + PR #1042インタラクティブ Socratic → 盲点検出エンジン (DA+PM+Judge) に再定義。RQ Synthesis 導入で追加質問が dead node 化したため
2026-05-27preGraph を triage のみに縮小 (commit 15d9c0c)盲点検出 DAG が 30s wall time を超えるため preGraph から除外

14. 関連リンク

実装更新 (2026-05-28)

  • Constitution 観点ランダム選択 (ADR-0071 Phase 1 補完 / PR #1091): DA ノードに CONSTITUTION_PERSPECTIVES (22 観点) からランダム 3 観点を選択するロジックを組込み。選択は constitution_perspectives_selected イベントとしてログ記録 (再現性確保)。loadPrompt で取得した DA プロンプトに [Constitution] セクションを append
  • DA / PM / Judge の 3 ノードを loadPrompt(env, 'gate1-da' / 'gate1-pm' / 'gate1-judge', FALLBACK_PROMPT) に切替 (PR #1090)
  • ChatOpenAI インスタンスに maxRetries=3 (exponential backoff) を付与