TL;DR(このノードは何をする・専門語ゼロ): 起案された技術判断の下書きを受け取り、あえて反対役に回って「反論・反例・見落としているリスク」を 3〜5 件抽出する Gate 1 の片肺。判定はせず、見つけた指摘を後段の統合役 Judge に渡すだけ。指摘の切り口は 22 個の攻撃テンプレから 3 個を原稿シードで決定的に選び、プロンプトに差し込む。

実装: drp/src/nodes/socratic.tsdaPromise ブロック プロンプト SSoT: prompts/production/gate1-da/prompt.mdKV デプロイ・ADR-0042 Type 1 攻撃観点 YAML: prompts/production/gate1-da/constitution.yaml — 全 22 観点 出力スキーマ: prompts/production/gate1-da/output_schema.json 観点選択ロジック: drp/src/nodes/perspective_selector.ts テスト: drp/test-tc-socratic.mjs 基盤 ADR: ADR-0071 / ADR-0033 / ADR-0042 / ADR-0056


1. 役割と位置づけ

Gate 1 の 反対役。起案者の盲点を severity 付きの指摘として 3〜5 件出すのが仕事。判定や差戻しはしない。

  • 解決する課題: ソロ起案 + 3 モデル合意の Synthesis では confirmation bias が構造的に残る。DA は反対の立場を強制してこの偏りを崩す(ADR-0071 §1.3)。
  • 設計思想: 情報提供型。出力は Judge が統合し PR 本文の Blind-spot Report に載る。pass/fail には使わない。
  • 観点ローテーション: 22 個の攻撃テンプレから 3 個を 原稿シード由来で決定的選択。同じ原稿の再投入では同じ 3 観点が選ばれ、Cross-Validation の収束が保たれる。
  • 兄弟ノード: 並列に走る Pre-mortem (PM) と、後段の Judge。本ノードを含む 3 ノードはグラフ上は socratic 1 ノードに統合されている。

2. フロー図

flowchart LR
    T[triage] -->|needsAdr=true| S[socratic 統合ノード]
    S --> DA[gate1-da
Devil's Advocate
反論 3-5 件] S --> PM[gate1-pm
Pre-mortem] DA --> J[gate1-judge
統合・重複排除] PM --> J J -->|socraticPass=true
blindSpotFindings| B[body_generation] style DA fill:#fdd,stroke:#c33

DA と PM は Promise.allSettled で並列実行。DA 単独で API エラーが起きても PM が成功すれば Judge は PM の findings だけで実行され、パイプラインは止まらない。


3. トリガー条件

条件挙動
triageneedsAdr=true を返した実行
triageneedsAdr=false を返したスキップ — triage が END に飛ばす
blindSpotFindings.length > 0スキップ — 冪等性のため再実行しない

buildPreGraph() には含まれない。buildGraph() / buildGraphWithWebhook() の main graph 内でのみ動く(30s wall time 超過対策・ADR-0071)。


4. 入力

State から

State フィールド用途
title任意。あれば Title: ... 行で先頭に付く
context必須。背景・決定内容
options任意。Alternatives considered: ... として末尾に付く

LLM への user message

Title: {title}                ← title 空なら省略

Context:
{context}

Alternatives considered:      ← options 空なら省略
{options}

system prompt の組み立て

loadPrompt(env, 'gate1-da', DA_FALLBACK_PROMPT) の本文末尾に、選択された 3 観点を [Constitution — focus on these perspectives this run] セクションとして append する。


5. 処理ロジック

1. 観点選択: selectRandomPerspectives(3, deterministic ? userInput : undefined)
   - deterministic 既定 ON (SOCRATIC_DETERMINISTIC="false" でロールバック)
   - 決定性: FNV-1a 32bit ハッシュ + mulberry32 で原稿シード → 3 観点を再現可能に選ぶ
   - 結果は event: 'constitution_perspectives_selected' で構造化ログ
2. プロンプト合成: gate1-da プロンプト本文 + 選択観点ブロック
3. LLM 呼出: claude-sonnet (MODELS.socratic), temperature=0.95, 90s timeout
4. JSON 抽出: parseJson() でコードフェンス除去 + JSON.parse
5. 整形: findings に source='devil_advocate' を付与し Judge への raw findings として返却

選択された観点は次回以降の調査・障害分析でも参照できるよう、ログにインデックス配列で残す(観点 YAML を差し替えても過去の選択を再現可能)。


6. LLM 設定

項目根拠
モデルclaude-sonnet (MODELS.socratic)ADR-0033 — Gate 1 は判定一貫性より多様性重視
temperature0.95ADR-0056 — 反論の多様性確保。Silverio/Smit TMLR 2026
seed未設定原稿シードを観点選択側で吸収するため、本ノードでは LLM の sampling を制約しない
maxRetries3createLlm の exponential backoff
timeout90 秒SAMPLE_TIMEOUT_MS — Queue consumer 15 分予算内で余裕あり
コスト目安約 $0.03 / ADR入出力合計 約 2〜4 KB
レイテンシ10〜30 秒PM と並列実行のため Gate 1 全体は max(DA, PM) + Judge

7. 副作用

なし。LiteLLM Gateway 経由の LLM 呼出のみ。KV / GitHub API / Webhook には触れない。


8. 出力

Judge への raw findings(内部受渡し型)

{
  source: 'devil_advocate',
  title: string,            // 60 字目安・「(状況) すると、(具体的に何が起きるか)」形式
  severity: 'critical' | 'high' | 'medium' | 'low',
  evidence: string,         // 最大 3 文・「いま何がどうなっている → だから何が起きる → 根拠」
  suggestedAction: string,  // 動詞始まり 1-2 文
}[]                         // 3-5 件

LLM 生 JSON(DA プロンプト直結の生出力)

{
  "findings": [
    {
      "title": "...",
      "severity": "critical | high | medium | low",
      "evidence": "...",
      "suggested_action": "..."
    }
  ]
}

JSON スキーマは prompts/production/gate1-da/output_schema.json を SSoT とする。

観点選択ログ

{
  "event": "constitution_perspectives_selected",
  "selected_indices": [3, 11, 18],
  "selected": ["撤退条件が...", "...", "..."],
  "deterministic": true
}

9. 分岐(次サブノード)

DA は socratic ノード内の Promise.allSettled([daPromise, pmPromise]) で集約される内部処理。LangGraphaddConditionalEdges での分岐はない。

DA 状態次の扱い
成功findings を rawFindings に追加 → Judge へ
失敗PM の findings のみで Judge を実行
DA + PM 両方失敗skip-through — socraticPass=true, blindSpotFindings=[] で body_generation へ

10. エラー時の挙動

障害パターン挙動影響
90s timeoutPromise.race で reject → DA を失敗扱いPM の findings のみで Judge が動く
Gateway 不達maxRetries=3 の exponential backoff 後に reject同上
JSON 以外を返すparseJson が throw → DA を失敗扱い同上
観点 YAML 読込失敗perspective_selector の fallback で空配列 → プロンプトに観点ブロックが付かない観点指定なしで実行(精度低下のリスク)

エラーは event: 'blindspot_node_failed' で構造化ログ出力。


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

  • inauthentic dissent 4.9%: Silverio/Smit TMLR 2026 によれば DA の出力には形式的な反論が一定割合混入する。Judge の dedupe + severity 分類で軽減するが、完全排除は不可。
  • 観点 YAML の偏り: constitution.yaml の 22 観点は GAS / Cloudflare / 会計の 4 観点を含むが、他ドメインの観点不足。新領域の ADR では一般観点に頼ることになる。
  • temperature 0.95 の副作用: たまに同じ原稿でも文体が大きくブレる。severity の振れも観測されているため、最終分類は Judge に任せる設計。
  • 観点ローテーションの可観測性: selected_indices を週次で集計し、特定観点に偏っていないかを監視する(22 観点 × 3 選出なので長期的には均等分布が期待値)。
  • Light モードでの抑制: 出力件数自体は変えないが、後段の Judge で critical/high のみ最大 5 件に絞られる(ADR-0102 フェーズ③)。DA 単体では Light/Standard/Critical の差は付けない。

12. テストケース

ID内容期待
TC-BS01-DA情報十分な ADR ドラフトfindings.length が 3〜5 件・全件に evidence あり
TC-BS02-DADA API エラー注入event: 'blindspot_node_failed' がログに出る・PM 単独で Judge が動く
TC-BS-DET同一原稿を 2 回投入selected_indices が両回で一致(決定性確認)

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


13. 過去の設計判断ログ

日時変更経緯
2026-05-27ADR-0071 採択 + PR #1042Socratic を盲点検出 DAG に再定義・DA を新設
2026-05-28プロンプト KV 化(PR #1090)loadPrompt(env, 'gate1-da', ...) に切替。ADR-0085
2026-05-28Constitution 観点ランダム選択(PR #1091)DA プロンプトに 22 観点からランダム 3 観点を append
2026-06 ごろ決定的観点選択に切替Cross-Validation 収束のため SOCRATIC_DETERMINISTIC 既定 ON 化(FNV-1a + mulberry32 / perspective_selector.ts に切出し)
2026-06ADR-0102 フェーズ③Light モードで Judge 側 cap 導入。DA 出力自体は変更なし

14. 関連リンク

  • 上位ノード: 02_socratic.md — Gate 1 統合ノード
  • 兄弟サブノード: 22_gate1_judge.md / 23_gate1_pm.md
  • 前ノード: 01_triage.md
  • 次ノード: 03_body_generation.md
  • プロンプト SSoT: prompts/production/gate1-da/
  • State 定義: drp/src/state.ts
  • グラフ定義: drp/src/graph.ts
  • 観点選択モジュール: drp/src/nodes/perspective_selector.ts
  • 関連 ADR:
    • ADR-0071 — Gate 1 盲点検出エンジン再定義
    • ADR-0033 — ノード別モデル選定
    • ADR-0042 — プロンプト SSoT (KV) ライフサイクル
    • ADR-0056 — Temperature / Sampling 戦略
    • ADR-0085 — 全ノード KV 経由読込
    • ADR-0102 — Light モード盲点 cap(Judge 側で発動)