⚠️ Status: Retired (2026-05-08, ADR-0019)

本仕様書は ADR-0019 により退役。N1〜N13 の Dify ノード定義は LangGraph StateGraph + channels モデルに再設計する。

  • 退役理由: docs/adr/0019-drp-migration.md
  • Dify N1〜N13 → LangGraph node 対応表: langgraph_migration/migration_overview.md § ノード対応表
  • LangGraph node 実装手順: langgraph_migration/main_workspace_checklist.md Step 2

履歴として保持。新規実装着手時は LangGraph 側ドキュメントを参照すること。


Status: Retired (2026-05-08) 対象: Phase 2a 実装担当(オーナー / Dify ワークフローを組む人) — 退役のため新規参照不要 前提: Phase 1 で Triage / Scoring の LLM ノードは検証済み


1. ゴール

Phase 1 の Dify ワークフロー(Triage + Scoring の 2 ノード)を、Phase 2a の Webhook 連携対応版に拡張する。

  • 入力: 起案者の自由記述(user_input)
  • 出力(成功時): GitHub PR URL を起案者に表示
  • 出力(差戻し時): スコアと改善ポイントを表示してパイプライン終了

2. ワークフロー全体構造

┌─────────────┐
│ 開始        │  user_input (string)
└─────┬───────┘
      ↓
┌─────────────────────┐
│ N1: Triage (LLM)    │  → triage_json
└─────┬───────────────┘
      ↓
┌─────────────────────┐
│ N2: 分岐: ADR 要否  │  triage.is_adr_worthy
└──┬───────────────┬──┘
   │ false         │ true
   ↓               ↓
┌──────────┐  ┌─────────────────────┐
│ N3: 終了 │  │ N4: Scoring (LLM)   │  → scoring_json
│ (対象外)  │  └─────┬───────────────┘
└──────────┘        ↓
              ┌─────────────────────┐
              │ N5: 分岐: pass?      │  scoring.pass
              └──┬───────────────┬──┘
                 │ false         │ true
                 ↓               ↓
            ┌──────────┐  ┌─────────────────────┐
            │ N6: 終了 │  │ N7: ADR 本文生成 LLM │  → adr_body_md
            │ (差戻し) │  └─────┬───────────────┘
            └──────────┘        ↓
                          ┌─────────────────────┐
                          │ N8: Slug 生成 LLM   │  → adr_slug
                          └─────┬───────────────┘
                                ↓
                          ┌─────────────────────┐
                          │ N9: Payload 構築    │  → payload_json
                          │ (Code Node)         │
                          └─────┬───────────────┘
                                ↓
                          ┌─────────────────────┐
                          │ N10: HTTP リクエスト │  → http_response
                          │ (Worker → GitHub)   │
                          └─────┬───────────────┘
                                ↓
                          ┌─────────────────────┐
                          │ N11: 分岐: 成功?    │  http.status_code
                          └──┬───────────────┬──┘
                             │ != 200        │ 200
                             ↓               ↓
                        ┌──────────┐  ┌──────────┐
                        │ N12: 終了│  │ N13: 終了│
                        │ (エラー) │  │ (成功)   │
                        └──────────┘  └──────────┘

ノード数: 13(うち LLM が 4、分岐が 3、終了が 4、Code 1、HTTP 1)


3. ノード詳細仕様

N1: Triage (LLM)

項目
ノード種別LLM
モデルClaude Sonnet 4.6(推奨) / GPT-4o
Temperature0.0
プロンプトprompts/01_triage.md v0.2
Inputuser_input
Outputtriage_json (JSON)
パースJSON.parse で構造化

抽出する変数:

  • triage.is_adr_worthy (boolean)
  • triage.mode ("Light" / "Standard" / "Critical" / null)
  • triage.reason (string, ≤80 字)
  • triage.suggested_title (string ≤30 字 / null)

N2: 分岐 — ADR 要否

項目
ノード種別If/Else(条件分岐)
条件triage.is_adr_worthy == true
True 分岐→ N4
False 分岐→ N3

N3: 終了(対象外)

項目
ノード種別End / 直接応答
表示メッセージ下記テンプレ
## 🛑 ADR 対象外と判定されました

**判定理由:**
{{triage.reason}}

通常の PR 概要欄に記載してください。設計判断を伴う場合は、より具体的に「何が問題で、どの選択肢を比較したか」を書き、再度この Decision Pipeline に投入することを推奨します。

N4: Scoring (LLM)

項目
ノード種別LLM
モデルClaude Sonnet 4.6(推奨)
Temperature0.0
プロンプトprompts/02_scoring.md v0.1
Inputuser_input + triage.mode
Outputscoring_json (JSON)

抽出する変数:

  • scoring.scores (object: 10 項目 × {score, comment})
  • scoring.total (number 0〜50)
  • scoring.mode (string)
  • scoring.threshold (number: Light=35 / Standard=40 / Critical=45)
  • scoring.pass (boolean)
  • scoring.weakest_items (array)
  • scoring.feedback_for_user (string)
  • scoring.score_summary_md (Markdown table)

N5: 分岐 — pass?

項目
ノード種別If/Else
条件scoring.pass == true
True 分岐→ N7
False 分岐→ N6

N6: 終了(差戻し)

## 📉 品質スコア {{scoring.total}}/50(閾値 {{scoring.threshold}} / Mode: {{scoring.mode}})

スコアが Mode の閾値に届かなかったため、PR は作成されませんでした。

### 改善ポイント
{{scoring.feedback_for_user}}

### スコア内訳
{{scoring.score_summary_md}}

### 弱点項目
{{scoring.weakest_items}}

このフィードバックを参考に起案ドラフトを書き直し、再度投入してください。

N7: ADR 本文生成 (LLM)

項目
ノード種別LLM
モデルClaude Sonnet 4.6(推奨)
Temperature0.2(軽い創造性)
プロンプトprompts/06_adr_body.md v0.1
Inputuser_input + triage + scoring
Outputadr_body_md (Markdown)

生成する Markdown 構造:

# ADR-{NNN}: {triage.suggested_title}

- **Status**: Proposed
- **Mode**: {triage.mode}
- **起案者**: {submitter.name}
- **起案日**: {today}
- **承認日**: -

## コンテキスト
(user_input と triage.reason を統合して再構成)

## 決定
(user_input から採用方針を抽出)

## 検討した代替案
(user_input から代替案を抽出。Light Mode は省略可)

## 影響
(正/負/リスクに分類)

## 撤退条件
(user_input から抽出。Light Mode は省略可)

## 参照
- 起案 PR: #PR_NUMBER(後続で挿入)

N7 では NNN は仮で XXX のままとし、GitHub Actions 側で連番採番後に書き換える。

N8: Slug 生成 (LLM)

項目
ノード種別LLM
モデルClaude Haiku 4.5(軽量で十分)
Temperature0.0
プロンプトprompts/07_slug.md v0.1
Inputtriage.suggested_title
Outputadr_slug (string)

プロンプト本体(v0.1 ドラフト):

ADR タイトルから snake_case のスラッグを生成せよ。

[ルール]
- 小文字英数字とアンダースコアのみ
- 20 文字以内
- 日本語は意味を保ったまま英訳
- 動詞は省略可("add_xxx" より "xxx" 推奨)
- 一般的な略語は使用可(cap, fn, ref, ddl など)

[出力フォーマット]
スラッグのみを返す。説明・コードブロック禁止。

[入力タイトル]
{{triage.suggested_title}}

例:

タイトルSlug
領収書 OCR を Gemini から Claude へ移行ocr_gemini_to_claude
11_mst_account に税効果科目フラグを追加mst_tax_effect_flag
Action B の決済日 < 発生日チェックを緩和actionb_date_check

N9: Payload 構築 (Code Node)

項目
ノード種別Code Execution(Python)
Input各種変数(triage / scoring / adr_body_md / adr_slug / submitter / user_input)
Outputpayload_json (JSON string)
import json
from datetime import datetime, timezone

def main(
    user_input: str,
    submitter_name: str,
    submitter_id: str,
    triage_is_adr_worthy: bool,
    triage_mode: str,
    triage_title: str,
    triage_reason: str,
    scoring_scores: dict,
    scoring_total: int,
    scoring_pass: bool,
    scoring_summary_md: str,
    adr_body_md: str,
    adr_slug: str,
) -> dict:
    payload = {
        "schema_version": "1.0",
        "submitter": {
            "name": submitter_name,
            "dify_user_id": submitter_id,
        },
        "user_input": user_input,
        "triage": {
            "is_adr_worthy": triage_is_adr_worthy,
            "mode": triage_mode,
            "title_summary": triage_title,
            "reason": triage_reason,
        },
        "scoring": {
            "scores": scoring_scores,
            "total": scoring_total,
            "pass": scoring_pass,
            "score_summary_md": scoring_summary_md,
        },
        "adr_draft": {
            "slug": adr_slug,
            "body_md": adr_body_md,
        },
        "submitted_at": datetime.now(timezone.utc).isoformat(),
    }
    return {"payload_json": json.dumps(payload, ensure_ascii=False)}

N10: HTTP リクエスト

項目
ノード種別HTTP Request
MethodPOST
URL{{CLOUDFLARE_WORKER_URL}}/dispatch (環境変数化)
HeadersContent-Type: application/json
X-Dify-Signature: {{HMAC_SIGNATURE}}
Body{{payload_json}}
Timeout30s
リトライ自動リトライ 1 回(5xx エラー時のみ)

HMAC 署名の生成:

Cloudflare Worker 側で payload の改ざん検知のため、Dify 側で HMAC-SHA256 署名を生成する。

# N9 の Code Node 内で計算
import hmac, hashlib
secret = "{{DIFY_WEBHOOK_SECRET}}"  # Dify 環境変数
signature = hmac.new(
    secret.encode(),
    payload_json.encode(),
    hashlib.sha256
).hexdigest()
# → headers の X-Dify-Signature に渡す

N11: 分岐 — HTTP 成功?

項目
ノード種別If/Else
条件http_response.status_code == 200
True 分岐→ N13
False 分岐→ N12

N12: 終了(HTTP エラー)

## ⚠️ PR 作成に失敗しました

GitHub への送信時にエラーが発生しました。

- **HTTP ステータス**: {{http_response.status_code}}
- **エラーメッセージ**: {{http_response.body}}

時間をおいて再度投入してください。問題が継続する場合はオーナーに連絡してください。

N13: 終了(成功)

## ✅ PR が作成されました

- **PR URL**: {{http_response.body.pr_url}}
- **ADR 番号**: {{http_response.body.adr_number}}
- **ブランチ**: {{http_response.body.branch}}
- **Mode**: {{triage.mode}}
- **スコア**: {{scoring.total}}/50

オーナーがレビューします。コメントが付いた場合は GitHub または Slack で対応してください。

### スコア内訳
{{scoring.score_summary_md}}

4. 環境変数

Dify ワークフローで使う環境変数(Workspace Settings で設定):

変数名用途
CLOUDFLARE_WORKER_URLhttps://decision-pipeline.bizlp.workers.devWorker のエンドポイント URL
DIFY_WEBHOOK_SECRET(ランダム 32 文字以上)HMAC 署名の共有秘密

DIFY_WEBHOOK_SECRET は Cloudflare Worker 側にも同じ値を Secret として登録する。


5. Cloudflare Worker からのレスポンス仕様

N10 の HTTP リクエストに対し、Worker は以下を返す:

成功時 (200 OK)

{
  "ok": true,
  "pr_url": "https://github.com/BizLinkPartners/bizlp-gas-accounting/pull/123",
  "pr_number": 123,
  "adr_number": "010",
  "branch": "claude/decision-010-ocr-gemini-to-claude"
}

失敗時 (4xx / 5xx)

{
  "ok": false,
  "error_code": "INVALID_SIGNATURE | SCHEMA_INVALID | GITHUB_API_ERROR | INTERNAL",
  "message": "人間が読めるエラー説明"
}

6. ADR 本文生成プロンプト(N7 用、新規 v0.1)

新規プロンプト prompts/06_adr_body.md v0.1 として作成する。

入出力

  • Input: user_input (string)、triage (JSON)、scoring.scores (JSON)、submitter.name (string)
  • Output: ADR テンプレート(docs/adr/_template.md)に準拠した Markdown

システムプロンプト本体(ドラフト)

あなたは ADR 起草アシスタントである。起案者の入力(user_input)と Triage / Scoring 結果を読み、
docs/adr/_template.md に準拠した ADR Markdown を生成せよ。

[ルール]
1. テンプレートのセクション順を厳守する: コンテキスト → 決定 → 検討した代替案 → 影響 → 撤退条件 → 参照
2. ヘッダ部分は以下の形式で開始:
   # ADR-XXX: {triage.suggested_title}
   - **Status**: Proposed
   - **Mode**: {triage.mode}
   - **起案者**: {submitter.name}
   - **起案日**: {today}
   - **承認日**: -
   ※ ADR 番号は XXX のまま。GitHub Actions 側で連番に置換される。
3. 起案者の言葉を尊重し、要約しすぎない。論理的な接続詞のみ追加してよい。
4. 起案者が書いていない情報を推測で補わない。「(記載なし)」と明示する。
5. Light Mode の場合のみ「検討した代替案」「撤退条件」を省略可。Standard / Critical では空でも見出しを残す。
6. 影響セクションは正/負/リスクの 3 つのサブ箇条書きに整理する。
7. Markdown のみを出力。前置き・解説禁止。

[入力]
- user_input: {{user_input}}
- triage: {{triage_json}}
- scoring.weakest_items: {{scoring.weakest_items}}  ← 弱点項目があるが補完不可。「(記載なし)」とする
- submitter.name: {{submitter.name}}
- today (YYYY-MM-DD): {{today}}

Phase 2a 起動前に TC-07(高品質起案)で N7 出力の品質を確認すること。


7. Phase 1 ワークフローからの移行手順

既存の Phase 1 ワークフロー(Triage + Scoring 2 ノード)からの差分:

操作ノード
既存維持N1 (Triage), N4 (Scoring)
新規追加N2, N3, N5, N6, N7, N8, N9, N10, N11, N12, N13
削除Phase 1 の最終出力ノード(N4 直後)

推奨手順

  1. Phase 1 ワークフローを 複製 して Decision Pipeline v2a という新ワークフローを作る(Phase 1 は単体検証用に残す)
  2. N4 (Scoring) の後ろに N5 以降を順に配置
  3. N1 の前に N2 を挿入し、N3(早期終了)を分岐
  4. 環境変数 CLOUDFLARE_WORKER_URL DIFY_WEBHOOK_SECRET を Workspace に登録
  5. テスト用に Worker をローカル(wrangler dev)で立ち上げて疎通確認
  6. TC-07 を流し込み、エンドツーエンドで PR が立つことを確認
  7. TC-06 を流し込み、Webhook が送信されないことを確認
  8. 本番 Worker URL に切替

8. テスト計画

テストケース期待動作確認ノード
TC-07 (高品質 Standard)N1→N4→N7→N8→N9→N10→N13 で PR 作成N13 で PR URL 表示
TC-06 (低品質 Standard)N1→N4→N6 で差戻しN6 でスコア詳細表示、N10 が走らないこと
TC-01 (UI 微修正・対象外)N1→N3 で対象外表示N3 で表示、N4 が走らないこと
TC-08 (境界線・閾値ぴったり)Mode が Standard、スコア 40 → 通過N5 で True 分岐
エラー: Worker 停止N10 タイムアウト → N12 でエラー表示N12 で再送信案内
エラー: HMAC 不一致Worker が 401 返却 → N12N12 で「署名エラー」表示

9. Phase 2a → 2b 候補

  • N7 ADR 本文生成プロンプトの精度改善(TC-07 で出力品質を実測してから)
  • Triage / Scoring の並列実行(モードによっては Scoring を Critical 用と Light 用で分岐)
  • HTTP リトライポリシーの強化(指数バックオフ)
  • N7 出力に「Review After 6 ヶ月」の自動挿入
  • Slug 生成 LLM を関数(決定論的アルゴリズム)に置換

変更履歴

日時変更内容
2026-05-07初版作成(Phase 2a Dify 実装着手前のリファレンス仕様)