04. Scoring Node (Gate 4)
TL;DR(このノードは何をする・専門語ゼロ): 出来上がった判断記録の原稿を受け取り、10 個の観点を各 5 点・合計 50 点満点で採点する品質の関所。判断の重みに応じた合格ラインに届かなければ「ここを足してください」と具体的な助言を添えて差し戻す。点数はあくまで材料で、最後に進めると決める責任は起案者と代表取締役が持つ。
実装:
drp/src/nodes/scoring.tsプロンプト設計ドキュメント:prompts/02_scoring.mdプロンプト SSoT:drp/prompts/production/gate4-scoring/prompt.mdテスト:drp/test-tc.mjs(TC-06 / TC-07) 基盤 ADR: ADR-0020 (閾値根拠) / ADR-0033 (モデル選定)
1. 役割と位置づけ
生成された ADR〔Architecture Decision Record=技術判断の記録〕本文を 10 項目 × 5 点 = 50 点満点 で採点し、mode〔モード=判断の重みを軽い・普通・重大の 3 段階に仕分けする区分〕別閾値 (Light 35 / Standard 40 / Critical 45) 未満なら差戻す 品質の関所。Pipeline 全体で「40 点未満は PR 化させない」原則の実体。
- 解決する課題: 起案者が「とりあえず形だけ」の ADR を提出すると、レビュアー (代表取締役) のレビュー負荷が膨大になる。低品質を機械的に弾くことでレビュー対象を高品質に絞り込む。
- 設計思想: 差戻し型ゲート。
pass=falseでrejected=trueを立て END へ。feedback_for_userには「何を追加すべきか」を 150 字以内で具体的に書かせる。 - 「AI に責任逃れさせない」原則: スコアは判断材料であり、最終的に起案者が「私はこの決定で進めたい」と PR 概要欄に書く義務を負う (operator_guide 参照)。
2. フロー図
flowchart LR
B[body_generation] --> SC[scoring]
SC -->|rejected=false| CV[cross_validation]
SC -->|rejected=true
feedback_for_user を返却| END([END])
3. トリガー条件
| 条件 | 挙動 |
|---|---|
body_generation から adrBody を受け取り | 必ず実行 (無条件 edge) |
4. 入力 (State)
| State フィールド | 用途 |
|---|---|
adrBody | 採点対象の Markdown 本文 |
triageMode | 閾値判定 (Light 35 / Standard 40 / Critical 45)。null なら Standard |
scoringEffort | claude-opus の reasoning_effort (high / xhigh / max)。デフォルト max |
5. 処理ロジック
1. createLlmWithEffort(env, MODELS.scoring='claude-opus', state.scoringEffort='max')
- temperature=1 (Claude 4.x thinking 必須値)
- reasoning_effort=max (Adaptive Thinking 最大)
2. user msg: "mode: {Light|Standard|Critical}\nadr_draft:\n{adrBody}"
3. invoke → JSON 抽出
4. total を 3 段階で集計:
a. result.scores オブジェクトから (各項目の {score, comment}.score を加算)
b. (a) が 0 なら result.score_summary_md の Markdown テーブル 2 列目を加算
c. それでも 0 なら result.total を使う
5. pass = (total >= threshold)
6. pass=false → score / scoringDetail / rejected=true / rejectionMsg=feedback_for_user
7. pass=true → score / scoringDetail / rejected=false / rejectionMsg=feedback_for_user (警告として継続)
Self-Consistency サンプリング (N>1): LLM パラメータは loadLlmParams(env, 'gate4-scoring', { temperature: 1.0, sampling_n: 1 }) で KV から取得する。KV の sampling_n が 2 以上のとき、同一入力を Promise.allSettled で N 回並列採点し多数決で判定する (ADR-0056 温度サンプリング戦略):
- 各サンプルの
totalからmean/stdDevを算出 majorityPass = (合格サンプル数 > N/2)→rejected = !majorityPass- 代表スコア =
Math.round(mean)、出力にscoringSamples/scoringMean/scoringStdDevを付与 (§8) - 全 N サンプル失敗時のみ
All N scoring samples failedで停止 sampling_n <= 1(既定) のときは上記の単発採点パス
採点 10 項目 (各 0〜5 点):
| キー | 観点 |
|---|---|
1_problem_definition | 問題定義の明確さ (現状の痛みの具体性・数値) |
2_alternatives | 代替案の網羅性 (最低 2 案 + 却下理由) |
3_decision_criteria | 判断基準の明示 (評価軸) |
4_past_adr_consistency | 過去決定との整合性 (参照 / Supersede / Conflict) |
5_impact_scope | 影響範囲の特定 (ファイル / モジュール / データ / ステークホルダー) |
6_operational_pitfalls | 運用上の罠の検証 |
7_rollback_strategy | 失敗時のロールバック性 |
8_cost_estimate | コスト試算 (時間 / 金銭、粗くても数値) |
9_completion_criteria | 検証可能な完了条件 (観測可能な指標) |
10_long_term_impact | 長期的影響 (半年後の負債化リスク・Review After) |
閾値の根拠: 学術的・業界標準的な絶対値はなく、Phase 1 検証 (TC-06=3/50・TC-07=45/50) で弁別力を実証した組織内ヒューリスティクス (ADR-0020 §4 / Basili GQM 1994)。
6. LLM 設定
| 項目 | 値 | 根拠 |
|---|---|---|
| モデル | claude-opus (claude-opus-4-7) | ADR-0033 で gpt-4.1 から upgrade。10 項目を critical に評価する判断力 |
| temperature | 1 | Claude 4.x の thinking 有効化必須値 (createLlmWithEffort が固定) |
| reasoning_effort | max (デフォルト) | Adaptive Thinking 最大。state.scoringEffort で上書き可 |
| コスト目安 | 0.30〜0.80 USD / 起案 | opus + thinking max。Pipeline 最大 |
| レイテンシ | 30〜90 秒 | thinking でモデル内推論時間が長い |
createLlmWithEffort は LiteLLM の _map_reasoning_effort 経由で claude-opus 向け thinking + output_config を自動生成する (gateway.ts:42-47)。
7. 副作用
なし。LLM 呼出のみ。
8. 出力 (State)
通過
{
score: number, // 例: 47 (N>1 時は Math.round(mean))
scoringDetail: string, // Markdown 表 (PR 本文転記用)
rejected: false,
rejectionMsg: '合格点ですが、〇〇は改善余地あり...',
// --- Self-Consistency (N>1) 時のみ実値。単発採点 (sampling_n<=1) では null/[] ---
scoringSamples: Array<{ total: number, pass: boolean }>, // 各サンプルの結果
scoringMean: number | null, // 平均点 (小数1桁)
scoringStdDev: number | null, // 標準偏差 (小数1桁、ばらつき監視用)
}
weakest_items(最低点項目キーの配列) は LLM 出力ScoringResultに含まれ、rejectionMsg/feedback_for_userの生成に使われる (scoring.ts プロンプト rule 6)。
差戻し
{
score: number, // 例: 32
scoringDetail: string,
rejected: true,
rejectionMsg: '〇〇を追加してください。〇〇の数値が無い...', // 150 字以内
}
scoringDetail の形式 (system prompt で強制):
| 採点項目 | 点数 | コメント |
|---------|------|---------|
| 問題定義 | 4 | ... |
| 代替案 | 3 | ... |
...
9. 分岐 (次ノード)
.addConditionalEdges('scoring', (s) => s.rejected ? END : 'cross_validation')
rejected | 次ノード |
|---|---|
false | cross_validation (Gate 4 通過 → 盲点×Must 軸の交差検証へ) |
true | END (feedback_for_user を起案者に返却) |
N>1 サンプリング時は
rejected = !majorityPass(下記 §5 参照)。
10. エラー時の挙動
- LLM 失敗 / JSON parse 失敗: catch なし → 例外スロー → グラフ全体失敗
- total 集計のフォールバック: 3 段階 (scores → score_summary_md → result.total) で吸収。0 のまま落ちることは少ないが、全て失敗すれば total=0 で必ず差戻し (実用上は防御層)
- モデル別の出力形式ブレ: scoring プロンプトは scores object 形式を期待するが、claude が score_summary_md だけ返す場合に備え Markdown テーブルからも集計可
11. 既知の弱点・運用注意
- モデル upgrade のコスト影響: gpt-4.1 → claude-opus + thinking max でコストが 3〜5 倍。Pipeline 全体コストの 60%+ を scoring が占める。
- 「Critical=45 点」の運用負担: 月次決算 Critical ADR が 45 点に届かず差戻され続けるケースあり → 起案者が Standard に格下げして再起案する経路が必要 (現状は手動)。
- Markdown テーブル列順依存:
cols[1](2 列目) を点数列として読む。LLM が「| コメント | 項目 | 点数 |」順で出力すると誤集計 → プロンプトで列順を明示。 scoringEffortの活用: 通常はmaxで十分。コスト削減目的で個別起案だけhighに下げる UI は未実装。- 「合格でも feedback を返す」設計: pass=true でも
feedback_for_userは捨てず PR 本文に転記される (改善余地として起案者に共有)。
12. テストケース
| ID | 内容 | 期待 |
|---|---|---|
| TC-06 | 情報希薄な ADR (一般論のみ) | 3〜10 / 50 で差戻し |
| TC-07 | 情報豊富な ADR (数値・代替案・撤退条件あり) | 45〜50 / 50 で通過 |
実行: node drp/test-tc.mjs --scoring-only
注意: テストは OpenAI 直接呼出 (gpt-4o) で本番 (claude-opus) とは厳密一致しないが、線形性 (低情報 → 低スコア / 高情報 → 高スコア) は確認可能。
13. 過去の設計判断ログ
| 日時 | 変更 | 経緯 |
|---|---|---|
| 2026-05-04 | v0.1 初版 (Phase 1) | 10 項目・mode 別閾値の設計確定 |
| 2026-05-11 | LangGraph TS 移行 | Dify 退役、プロンプト本文は踏襲 |
| ADR-0033 採択 | gpt-4.1 → claude-opus + thinking max | 採点の論理深度向上 |
| (実装途中) | total 集計フォールバック 3 段階導入 | LLM が scores object 形式を返さないケースに対処 |
14. 関連リンク
- 前ノード: 03_body_generation.md
- 次ノード: 05_cross_validation.md
- プロンプト設計: prompts/02_scoring.md
- 起案者ガイド: operator_guide.md
- 関連 ADR:
実装更新 (2026-05-28)
- 閾値 KV 外部化 (ADR-0084 / PR #1088):
loadThresholds(env)をllm/prompt_loader.tsに追加。KV キーbizlp:prompts:gate4-scoring:thresholdsから{Light, Standard, Critical}を取得し、未設定時は{35, 40, 45}にフォールバック。閾値変更手順は operator_guide_langgraph.md §7.4 参照 SYSTEM_PROMPTをFALLBACK_PROMPTにリネーム +loadPrompt(env, 'gate4-scoring', FALLBACK_PROMPT)に切替 (PR #1090)ChatOpenAIインスタンスにmaxRetries=3(exponential backoff) を付与