13. ABC Screen Node (Gate 0b)
TL;DR(このノードは何をする・専門語ゼロ): 起案を読んで「この問題はどんな実害を引き起こすか」を A / B / C の 3 つに分けて、後段の審査の重みを決める。A は後から取り返せない実害なので審査を厚くする。B は放置すると複利的に悪化するので実測データを要求する。C は軽微なので人間に「今は着手しない」と伝える。どれを引いても差し戻しはしない。判定は LLM ではなくコードが下し、根拠は本文からの逐語引用と機械照合する。
実装:
drp/src/nodes/abc_screen.ts判定コード (pure module):drp/src/nodes/abc_decide.tsプロンプト SSoT (KV デプロイ):prompts/production/gate0b-abc-screen/prompt.mdmigrate:drp/migrate-v12-abc-screen.sql基盤 ADR: ADR-0157 位置づけ: triage → recall_pre_gate → abc_screen → cost_gate → problem_space_pregate → socratic 本番稼働: 2026-06-22 (週次集計監視中)
1. 役割と位置づけ
Gate4 の 10 軸は 9 軸が解決策側で、問題側は痛みの記述精度を見る軸 1 つだけだった。本ノードは「問題の解く価値」を harm の性質で機械分類し、入口から審査の重みを決める。
- 解決する課題: 論証だけ立派な非問題が高得点を取れてしまう問題と、不可逆 harm の取りこぼし。Gate4 だけでは捕まらない。
- 設計思想: harm 性質 (severity) と triage の mode (priority) を 別軸 として駆動する。LLM は意味分類と evidence 抽出だけを担い、A/B/C 確定と routing はコードが下す。ADR-0142 (pregate) と同型の二段構成。
- rejected は絶対に立てない: C も人間終端で差戻しではない。C のメッセージは「現時点で取り組む優先度が低い理由」を伝える HITL の旗。
- 由来: ADR-0157 (Existence/Executive・Standard・2026-06-18 受理)。
2. フロー図
flowchart LR
R[recall_pre_gate] --> ABC[abc_screen
gemini-flash 1 call]
ABC -->|A 不可逆| FC[force-Critical
triageMode を Critical へ昇格]
ABC -->|B 慢性流血| RM[require-measurement
実測必須タグ]
ABC -->|C 軽微| DN[do-nothing
人間終端の旗]
ABC -->|INVALID| PASS[pass]
FC --> CG[cost_gate]
RM --> CG
DN --> CG
PASS --> CG
すべての分岐が cost_gate へ流れる (差戻しはしない)。
3. トリガー条件
| エントリポイント | 起動グラフ | 説明 |
|---|---|---|
POST /draft | buildGraphWithWebhook | recall_pre_gate 直後に実行 |
POST /chat/run | buildGraph | webhook を除く full pipeline |
スキップ条件:
- 既に判定済み (
state.abcVerdict != null) は冪等化で no-op。 - env
ABC_SCREEN_ENABLED=falseで素通し。state はPASSを入れる。
4. 入力 (State)
| State フィールド | 型 | 必須 | 用途 |
|---|---|---|---|
title | string | 任意 | 起案タイトル |
context | string | 必須 | 背景・目的の自由記述 |
options | string | 任意 | 検討した代替案 |
triageMode | enum | 既定値 | 昇格判定のため読む |
入力は buildPregateInput() を流用する。pregate と同形のテキスト 1 本を作って LLM へ渡す。
5. 処理ロジック
0. 冪等化: state.abcVerdict が決定済みなら no-op
1. env ABC_SCREEN_ENABLED='false' なら素通し (PASS で state 注入)
2. プロンプト (gate0b-abc-screen) を KV から取得し gemini-flash で 1 call
- 出力: harm_domain / irreversibility / urgency / blast_radius /
workaround_exists / inaction_cost_measured / evidence_spans
- 各 evidence span に axis (どの軸の根拠か) と text (本文逐語引用) を付す
3. JSON parse 失敗 / enum 不正は INVALID で素通し (差戻しはしない)
4. decide() (abc_decide.ts) がコードで判定
① evidence span 照合: 全 span を NFKC + 空白畳みで正規化して本文に存在するか機械照合
- 最初の不一致で短絡しない (全 span を評価)
- 不一致 span は unmatchedSpans に記録 (INVALID にはしない)
② irreversibility で switch:
- 'irreversible' → verdict='A'
- harm_domain が 'other' なら force-critical せず Confidence flag (人間レビュー)
- 影響範囲 evidence が照合できない場合は fail-safe で force-critical + Confidence
- 顧客級以上なら forceCritical=true, route='force-critical'
- 顧客級未満なら route='pass' (A タグだけ立てる)
- 'chronic_compounding' → verdict='B', route='require-measurement'
- 放置コスト未実測なら reasons に 'inaction_cost_not_measured'
- 'minor' → verdict='C', route='do-nothing'
③ 駆動 evidence (不可逆性) が照合できない A/B は Confidence flag (人間へ降格)
- A の forceCritical は降格しても下げない (過剰 escalation = #safe)
5. force-Critical 安全ラチェット:
- forceCritical=true かつ現 mode が Critical 未満なら triageMode を Critical へ昇格
- 上げるのみ (ユーザ modeOverride が Light/Standard でも A の不可逆性が優先)
- Critical 済なら no-op
6. abcMessage を組み立てる
- A force-critical → A_FORCE_CRITICAL_NOTE
- B → B_REQUIRE_MEASUREMENT_MESSAGE
- C → C_DO_NOTHING_MESSAGE
- Confidence flag があれば末尾に CONFIDENCE_NOTE を追記
7. state に全 abc* フィールドを書き込み終了 (rejected は触らない)
正規化の方針: NFKC + 空白畳み (全角/半角・合成/分解を吸収する方向)。
abc_decide.tsの正規化は ADR-0142 のnormalizeForQuoteMatchより緩く、A の取りこぼし防止#safeを優先する。シノニム (例「コンプラ」↔「法令対応」) は文字正規化では救えず、LLM の意味分類が解決する。ADR 本文との表記乖離 (注記): ADR-0157 §決定は "Unicode NFC" と書くが、実装は NFKC を採用している (詳細は
abc_decide.tsの冒頭コメント)。doc 側で "NFKC" へ更新を申し送る予定。
6. LLM 設定
| 項目 | 値 | 根拠 |
|---|---|---|
| モデル | gemini-flash (MODELS.triage) | 軽量分類 1 call で十分・最安・最速 |
| temperature / seed | 0 / 42 | 同一起案には同一判定 |
| 期待出力 | JSON object (7 フィールド) | system prompt + output schema で形式強制 |
| コスト目安 | 1 起案あたり 1〜3 円 | 月次 ~$1 未満 (ADR-0157 §コスト試算) |
| レイテンシ | 1〜3 秒 | パイプライン全体に対し無視できる |
7. 副作用
なし。LiteLLM Gateway 経由の LLM 呼出のみ。GitHub API / KV 書込 / Webhook には触れない。
構造化ログ:
event: 'abc_screen_skipped'— env OFF で素通しevent: 'prompt_loaded'— KV / FALLBACK の出所event: 'abc_screen_complete'— verdict / route / force_critical / confidence_flag / reasons / unmatched_countevent: 'abc_screen_error'— LLM 例外
8. 出力 (State)
PASS (env OFF・素通し)
{
abcVerdict: 'PASS',
abcRoute: 'pass',
abcReasons: [],
}
INVALID (JSON parse 失敗・enum 不正)
{
abcVerdict: 'INVALID',
abcRoute: 'pass',
abcConfidenceFlag: false,
abcReasons: ['schema_parse_fail'],
abcUnmatchedSpans: ['<schema_parse_fail>'],
}
INVALID は審査深度を上げも下げもしない素通し。rejected は立てない。
A / B / C (decide が確定したケース)
{
abcVerdict: 'A' | 'B' | 'C',
abcClass: HarmDomain, // harm_domain (compliance 等)
abcRoute: 'force-critical' | 'require-measurement' | 'do-nothing' | 'pass',
abcConfidenceFlag: boolean, // true=人間レビュー要
abcForcedCritical: boolean, // triageMode を Critical へ昇格したか
abcOriginalMode: 'Light' | 'Standard' | 'Critical' | null,
abcMessage: string, // 起案者向けメッセージ
abcReasons: string[],
abcEvidenceSpans: string[], // 照合できた span
abcUnmatchedSpans: string[], // 照合できなかった span
triageMode?: 'Critical', // force-critical 時のみ昇格
}
rejected は どの分岐でも絶対に立てない (C は人間終端)。webhook が PR 本文に abcMessage を転記する。
9. 分岐 (次ノード)
graph.ts の edge:
.addEdge('abc_screen', 'cost_gate')
無条件で cost_gate へ流れる。abc_screen 自身は差し戻しを起こさない。
abcVerdict | 次ノード | 後段への影響 |
|---|---|---|
A (force-critical) | cost_gate | triageMode が Critical に昇格済み。下流 gate の閾値が上がる |
A (sub-customer) | cost_gate | A タグのみ。コスト試算が A の場合は cost_gate で A_AWARE_PASS 扱い |
B | cost_gate | 実測必須タグ・abcMessage が PR 本文に転記される |
C | cost_gate | Do Nothing 旗・人間終端 (rejected ではない) |
PASS / INVALID | cost_gate | 触らない |
10. エラー時の挙動
| 失敗パターン | 挙動 | 影響 |
|---|---|---|
| LLM 例外 / JSON parse 失敗 | INVALID で通過させる (abcRoute='pass') | telemetry で別集計 |
| evidence span 照合失敗 | INVALID にしない・Confidence flag で人間レビューへ | A の取りこぼし防止 (#safe) |
| irreversibility=irreversible なのに harm_domain=other | force-critical せず Confidence flag | 不確かな A は人間判定へ降格 |
| 影響範囲 evidence が照合できない A | fail-safe で force-critical + Confidence | 過剰 escalation は許容 (#safe) |
設計方針は rejected を立てない fail-safe に倒す。pregate と違って差戻し権限がない。
11. 既知の弱点・運用注意
ADR-0157 §5.2.1 盲点 6 件 + §撤退条件から抜粋:
- C メッセージの読まれ方リスク: 「Do Nothing 旗」が「あなたの問題は無価値」と読まれ起案を萎縮させうる。再申請フロー・人間レビュー要求オプションが必要。再起案率を週次で監視する。
- Confidence flag 起案の人間キュー滞留: 週次上限件数・SLA 日数・担当ロール・エスカレーション先を本番反映 2 週以内に数値で確定する (未確定時は env フラグ停止を発動)。
- 日本語表記ゆれと言い換え: 全角/半角や LLM のシノニム言い換えで span 照合が失敗しうる。NFKC + 空白畳みで吸収するが、シノニムは LLM の意味分類に任せる。
- 同一 harm の文体差: 婉曲表現 (例「GDPR 違反」→「個人情報の取り扱い改善」) で A 以外に着地しうる。複数ライター golden eval で verdict 一致率を検証する。
- 法域依存・時間的割引: FMEA AP lookup-table が EU AI Act vs 国内規制や「3 年後に不可逆」パターンに適合しない恐れ。該当パターンは Confidence flag に自動分類する。
- 新興ドメイン (AI 規制・新規 CVE): 四半期更新の空白で C 誤分類されうる。イベントドリブン更新を Confirmation に追加。
- 撤退条件: A の人間 downgrade 率が 4 週連続 30% 超 (単週 50% 超は即時停止) / Confidence flag 率 20% 超 / Do Nothing 旗の人間覆し率 50% 超 で再較正。
12. テストケース
- golden eval (A/B/C 各クラス): prompt-cicd フローで PR ごと CI ゲート化。FP / FN を追跡。
abc_decide.tsunit test:decide()と同型の純粋関数 test (worker / DO / langchain 非依存・vitest pool-workers 安全)。- 複数ライター golden: 同一問題を異なる文体で記述したサンプルで verdict 一致率を測る。一致率 80% 未満なら
#reliableK.O. でシグナル定義を再較正。 - 四半期再較正: 新規起案から無作為サンプリングして golden セットを更新。
実行: prompt-cicd フローの一部。詳しくは /Users/ts_kuma/projects/bizlp/doc/prompts/production/gate0b-abc-screen/ 配下の eval セット。
13. 過去の設計判断ログ
| 日時 | 変更 | 経緯 |
|---|---|---|
| 2026-06-18 | ADR-0157 受理 (PR #2123 マージ) | Gate4 軸偏りと不可逆 harm 取りこぼしへの対処として起案。Pipeline 通常 flow Standard 42/50 通過 |
| 2026-06-18 | drp PR #2152 で本番反映 (worker v c2d51b1a) | abc_screen ノード + abc_decide.ts pure module + プロンプト + telemetry 列追加 |
| 2026-06-18 | ADR-0158 (コストゲート独立ノード) を同梱起票・実装 | A の ROI 免除を分離。cost_gate_verdict='A_AWARE_PASS' で A は cost 未記載でも通過 |
| 2026-06-22 | 本番稼働開始・撤退条件監視 4 週開始 | A downgrade 率 / Confidence flag 率 / C 覆し率を週次集計 |
14. 関連リンク
- 前ノード: 15_recall_pre_gate.md
- 次ノード: cost_gate (Gate 0c・ADR-0158) → 12_problem_space_pregate.md
- 同じ受付バンド: 12_problem_space_pregate.md (構造を見る別検査・並立)
- State 定義:
drp/src/state.ts§ Gate 0b - 判定コード (pure):
drp/src/nodes/abc_decide.ts - migrate:
drp/migrate-v12-abc-screen.sql - 関連 ADR: