Dify Workflow 実装仕様 (Phase 2a)
⚠️ 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.mdStep 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 |
| Temperature | 0.0 |
| プロンプト | prompts/01_triage.md v0.2 |
| Input | user_input |
| Output | triage_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(推奨) |
| Temperature | 0.0 |
| プロンプト | prompts/02_scoring.md v0.1 |
| Input | user_input + triage.mode |
| Output | scoring_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(推奨) |
| Temperature | 0.2(軽い創造性) |
| プロンプト | prompts/06_adr_body.md v0.1 |
| Input | user_input + triage + scoring |
| Output | adr_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(軽量で十分) |
| Temperature | 0.0 |
| プロンプト | prompts/07_slug.md v0.1 |
| Input | triage.suggested_title |
| Output | adr_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) |
| Output | payload_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 |
| Method | POST |
| URL | {{CLOUDFLARE_WORKER_URL}}/dispatch (環境変数化) |
| Headers | Content-Type: application/jsonX-Dify-Signature: {{HMAC_SIGNATURE}} |
| Body | {{payload_json}} |
| Timeout | 30s |
| リトライ | 自動リトライ 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_URL | https://decision-pipeline.bizlp.workers.dev | Worker のエンドポイント 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 直後) |
推奨手順
- Phase 1 ワークフローを 複製 して
Decision Pipeline v2aという新ワークフローを作る(Phase 1 は単体検証用に残す) - N4 (Scoring) の後ろに N5 以降を順に配置
- N1 の前に N2 を挿入し、N3(早期終了)を分岐
- 環境変数
CLOUDFLARE_WORKER_URLDIFY_WEBHOOK_SECRETを Workspace に登録 - テスト用に Worker をローカル(
wrangler dev)で立ち上げて疎通確認 - TC-07 を流し込み、エンドツーエンドで PR が立つことを確認
- TC-06 を流し込み、Webhook が送信されないことを確認
- 本番 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 返却 → N12 | N12 で「署名エラー」表示 |
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 実装着手前のリファレンス仕様) |