02. Blind-spot Detection Node (Gate 1) — ADR-0071
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/Aggregator | DA+PM の findings を統合・重複排除・severity×actionability 分類 | 0.20 | {findings: [{source, title, severity, actionability, evidence, suggested_action, dedupe_group}], summary} |
6. LLM 設定
| ノード | モデル | Temperature | 根拠 |
|---|---|---|---|
| DA | claude-sonnet (MODELS.socratic) | 0.95 | 多様な反論生成のため高 T (ADR-0056 準拠、Silverio/Smit TMLR 2026) |
| PM | claude-sonnet (MODELS.socratic) | 0.80 | 失敗シナリオの多様性確保 |
| Judge | claude-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_generation | findings の有無にかかわらず通過 |
socraticPass=false/null | END | 現実装では発生しない (常に 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 timeout | Promise.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-BS02 | DA API エラー注入 | PM findings のみで Judge 正常完了 |
| TC-BS03 | DA + PM 両方エラー注入 | skip-through (findings=[], socraticPass=true) |
実行: node drp/test-tc-socratic.mjs
13. 過去の設計判断ログ
| 日時 | 変更 | 経緯 |
|---|---|---|
| 2026-05-12 | Phase 2b 実装 (PR #591) | 方式 A (ステートレス) で初期実装 (インタラクティブ追加質問) |
| ADR-0033 採択時 | gemini-flash → claude-sonnet に upgrade | 判定一貫性向上のため |
| 2026-05-27 | ADR-0071 採択 + PR #1042 | インタラクティブ Socratic → 盲点検出エンジン (DA+PM+Judge) に再定義。RQ Synthesis 導入で追加質問が dead node 化したため |
| 2026-05-27 | preGraph を triage のみに縮小 (commit 15d9c0c) | 盲点検出 DAG が 30s wall time を超えるため preGraph から除外 |
14. 関連リンク
- 前ノード: 01_triage.md
- 次ノード: 03_body_generation.md
- 関連 ADR:
実装更新 (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) を付与