21. Gate 1 — Devil's Advocate (DA) サブノード
TL;DR(このノードは何をする・専門語ゼロ): 起案された技術判断の下書きを受け取り、あえて反対役に回って「反論・反例・見落としているリスク」を 3〜5 件抽出する Gate 1 の片肺。判定はせず、見つけた指摘を後段の統合役 Judge に渡すだけ。指摘の切り口は 22 個の攻撃テンプレから 3 個を原稿シードで決定的に選び、プロンプトに差し込む。
実装:
drp/src/nodes/socratic.ts内daPromiseブロック プロンプト SSoT:prompts/production/gate1-da/prompt.md— KV デプロイ・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. トリガー条件
| 条件 | 挙動 |
|---|---|
triage が needsAdr=true を返した | 実行 |
triage が needsAdr=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 は判定一貫性より多様性重視 |
| temperature | 0.95 | ADR-0056 — 反論の多様性確保。Silverio/Smit TMLR 2026 |
| seed | 未設定 | 原稿シードを観点選択側で吸収するため、本ノードでは LLM の sampling を制約しない |
| maxRetries | 3 | createLlm の exponential backoff |
| timeout | 90 秒 | 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]) で集約される内部処理。LangGraph の addConditionalEdges での分岐はない。
| DA 状態 | 次の扱い |
|---|---|
| 成功 | findings を rawFindings に追加 → Judge へ |
| 失敗 | PM の findings のみで Judge を実行 |
| DA + PM 両方失敗 | skip-through — socraticPass=true, blindSpotFindings=[] で body_generation へ |
10. エラー時の挙動
| 障害パターン | 挙動 | 影響 |
|---|---|---|
| 90s timeout | Promise.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-DA | DA API エラー注入 | event: 'blindspot_node_failed' がログに出る・PM 単独で Judge が動く |
| TC-BS-DET | 同一原稿を 2 回投入 | selected_indices が両回で一致(決定性確認) |
実行: node drp/test-tc-socratic.mjs
13. 過去の設計判断ログ
| 日時 | 変更 | 経緯 |
|---|---|---|
| 2026-05-27 | ADR-0071 採択 + PR #1042 | 旧 Socratic を盲点検出 DAG に再定義・DA を新設 |
| 2026-05-28 | プロンプト KV 化(PR #1090) | loadPrompt(env, 'gate1-da', ...) に切替。ADR-0085 |
| 2026-05-28 | Constitution 観点ランダム選択(PR #1091) | DA プロンプトに 22 観点からランダム 3 観点を append |
| 2026-06 ごろ | 決定的観点選択に切替 | Cross-Validation 収束のため SOCRATIC_DETERMINISTIC 既定 ON 化(FNV-1a + mulberry32 / perspective_selector.ts に切出し) |
| 2026-06 | ADR-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: