アーキテクチャ · 設計

Context・Harness・Graph Engineering を Dify で実装する — YAML全文付き

「Dify で LLM ノードを並べたが、出力がブレる。根拠のない数値が混入する。品質を担保する仕組みがない」という状況は、単一プロンプトのまま規模を大きくすれば必ず起きる。プロンプトだけで品質を制御しようとするのは、設計の問題ではなくアーキテクチャの問題だ。

2025年ごろから X(旧Twitter)では Context Engineering という言葉が広まった。Andrej Karpathy が「Prompt Engineering は LLM に渡す文字列を最適化することだが、Context Engineering はモデルが判断に必要な情報すべてを正しい形で提供するアーキテクチャ設計だ」(2025-06-25 X 投稿の趣旨を要約)と述べたのが起点のひとつだ。本記事ではそこに Harness Engineering(出力を検証・制御する安全機構)と Graph Engineering(ノード間の依存と並列性を設計する)を加えた5層フレームワークを用いる。Prompt Engineering と Context Engineering は業界で広く使われる概念だが、Harness / Graph / Loop Engineering という呼称は著者が Dify 実装を整理するために用いた名前であり、業界共通の標準用語ではない点に注意してほしい。

本記事ではこの5層を、実際の Dify Workflow YAML(dify-ai-consulting-v1.yaml)を引用しながら具体的に解説する。YAMLは末尾のツールカードからダウンロードできる。


プロンプトだけでは足りない時代 — 5層フレームワーク

5層は「外側が内側を包む」入れ子構造として概念的に整理するとわかりやすい。ただし実装上は必ずしもこの入れ子が成立するわけではない(Dify では Loop は Graph の中のノードとして実装される)。あくまで設計の思考順序を示した図解として読んでほしい。内側の層が機能しなければ外側の層を追加しても意味がない。

┌─────────────────────────────────────────────────────────────┐
│  Layer 5: Loop Engineering   ← 反復・自己改善サイクル          │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  Layer 4: Graph Engineering   ← DAG・並列実行制御        │  │
│  │  ┌─────────────────────────────────────────────────┐  │  │
│  │  │  Layer 3: Harness Engineering  ← 検証・安全機構   │  │  │
│  │  │  ┌───────────────────────────────────────────┐  │  │  │
│  │  │  │  Layer 2: Context Engineering  ← 知識注入  │  │  │  │
│  │  │  │  ┌─────────────────────────────────────┐  │  │  │  │
│  │  │  │  │  Layer 1: Prompt Engineering        │  │  │  │  │
│  │  │  │  │  ← 命令・役割・出力フォーマット定義   │  │  │  │  │
│  │  │  │  └─────────────────────────────────────┘  │  │  │  │
│  │  │  └───────────────────────────────────────────┘  │  │  │
│  │  └─────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
✍️
Layer 1: Prompt Engineering
最も内側の層

LLM に渡す system prompt の設計。役割・禁止ルール・出力フォーマットを定義する。この層だけでは情報の不足・ハルシネーション・再現性のなさを制御できない。

🗂️
Layer 2: Context Engineering
知識注入・RAG

モデルが推論に必要な外部知識を正しい形で届ける設計。KB 検索・rerank・チャンク整形がここに入る。「何を渡すか」「どう整形するか」が精度を左右する。

🛡️
Layer 3: Harness Engineering
検証・安全機構

LLM の出力を別の LLM が検証・制御するレイヤー。QA ノードが根拠のない数値や出典誤りを検出し、後段の出力ノードに修正指示を渡す。

🔀
Layer 4: Graph Engineering
DAG・並列・合流

ノード間の依存グラフ(DAG)設計。依存のないノードを並列実行してレイテンシを削減し、合流点で複数の入力を統合する。Dify の edges 定義がそのままこの層に対応する。

🔄
Layer 5: Loop Engineering
反復・自己改善

検証→修正→再検証のサイクルを反復させる設計。条件分岐で「問題あり」の場合に提案生成ノードへ戻るループを形成する。今回のワークフローでは未実装(後述)。


今回作ったDifyワークフローの全体像

実装したのは「AI技術相談アシスタント」だ。顧客や社内からの技術相談テキストを入力として受け取り、過去事例KB・技術情報KBを参照しながら提案レポートを生成する。

DAGの全体構造

start相談入力requirements_llm① 要件整理Prompt Engineeringpast_cases② 過去事例KB検索Context Engineeringtech_info③ 技術情報KB検索Context Engineeringpast_cases_textchunks → テキスト整形tech_info_textchunks → テキスト整形proposal_llm④ 提案方針生成Graph Engineeringverify_llm④-2 QA検証Harness Engineeringoutput_llm⑤ 最終整理Graph Engineeringend提案レポートLayerPrompt EngineeringContext EngineeringGraph EngineeringHarness EngineeringPhase 1 — 並列実行Phase 2 — 合流Phase 3 — 検証 → 出力

このワークフローが実装している層は Layer 1〜4 の4層だ。start から3方向に並列ファンアウトし、proposal_llm で合流、そこから verify_llm と output_llm に分岐してふたたび output_llm で合流する。シーケンシャルな7ステップではなく、DAG により実質的に3フェーズで完結する。

関連ツール

AI技術相談アシスタント DSL

本記事で解説した5層設計を実装した Dify Workflow YAML。KBのIDを2箇所置き換えるだけで動作する。

YAMLをダウンロードツール一覧を見る

Layer 1: Prompt Engineering — 各LLMノードのsystem prompt設計

このワークフローには4つのLLMノードがある。それぞれの system prompt が果たす役割は異なる。

requirements_llm — 要件整理
入力テキストを10項目に構造化。「推測で事実を追加せず、不明なものは〈不明〉とする」が核心ルール。後続ノードへの入力品質を担保する前処理ノード。
構造化
proposal_llm — 提案方針生成
要件・過去事例・技術情報の3入力を受けて提案方針を生成。「価格・費用・金額は一切記載しない」「KB にない型番には〈要確認:KB記載なし〉タグを付ける」「同じ語句へのタグは最初の1箇所のみ」の3ルールが品質の軸。
生成
④-2
verify_llm — QA検証
提案方針の下書きを KB 検索結果と照合してチェックする安全機構ノード。Layer 3 の核心。問題がない場合は「重大な問題なし」と出力し、問題がある場合は該当箇所・語句・理由を具体的に箇条書きで示す。
検証
output_llm — 最終整理
提案方針(④)と QA 指摘(④-2)を受けて、配布可能な最終レポートを生成。QA で指摘されたタグ漏れを補い、タグ乱用を除去し、章立てを整える。「10. 社内QAメモ」として QA 結果を末尾に残す設計。
統合

出典ルールと「(要確認)タグ」の設計意図

proposal_llm の system prompt から該当箇所を引用する。

- id: proposal-system
  role: system
  text: |
    重要ルール:
    - 過去事例・技術情報KBにない事実は断定しない。
    - 技術の適合性に確信がない場合は「要確認」とする。
    - 具体的な価格・費用・金額・見積り数値は一切記載しない。
    - 技術・製品候補には出典を明記する。
      「技術情報KBより」または「一般的な業界知識より(KB記載なし)」
      のどちらかを必ず付記する。
    - KB に存在しない具体的な型番・製品名・数値には
      「(要確認:KB記載なし)」タグを付ける。
      ただし一般的な見込み・意見の表現にはタグを付けない。
    - 同じ語句へのタグは文書全体で最初の 1 箇所のみ。タグの乱用は禁止。

「タグの乱用は禁止」は意外に重要なルールだ。タグを付けすぎると読み手が「この提案は信頼できるのか」と感じてしまう。verify_llm は「タグ漏れ」と「タグ乱用」の両方向をチェックする設計になっている(後述)。


Layer 2: Context Engineering — Hybrid Search + Rerank でKBを2本使う

このワークフローでは KB を2本並列で検索する。「過去事例KB」と「技術情報KB」は性質が異なるため、あえて分離している。

並列取得の設定値

両KBのノード設定は同一パラメータだ。YAML から抜粋する。

multiple_retrieval_config:
  reranking_enable: true
  reranking_mode: reranking_model
  reranking_model:
    model: rerank-multilingual-v3.0
    provider: langgenius/cohere/cohere
  score_threshold: 0.5
  top_k: 6
top_k: 6

rerank 前の候補取得数。多すぎるとノイズが増え、少なすぎると関連チャンクを取りこぼす。KB のドキュメント規模が中小(数百チャンク以下)なら 6 が現実的な出発点。大規模KBは 10〜15 から試す。

score_threshold: 0.5

rerank 後のスコアカットライン。0.5 未満のチャンクは LLM に渡さない。閾値を下げると関連性の薄い情報が混入し、上げると「該当なし」になりやすい。実際の運用では RAG精度評価 で調整する。

retrieval_mode: multiple

Hybrid Search(ベクトル検索 + キーワード検索)の組み合わせモード。製品名・型番などのキーワードはキーワード検索が強く、意味的な類似はベクトル検索が強い。両方を組み合わせることで補完関係が生まれる。

Cohere rerank-multilingual-v3.0 を選んだ理由

日本語コンテンツを含む KB に対して日本語クエリで検索する場合、rerank モデルの多言語対応が必須だ。rerank-multilingual-v3.0 は日本語を含む100言語以上に対応しており、英語専用の rerank-english-v3.0 より日本語ドキュメントへの精度が高い。Dify での利用は langgenius/cohere プラグイン(バージョン 0.0.17)をマーケットプレイスから追加することで有効になる。なお、2026年8月時点では rerank-multilingual-v3.5(Rerank 3.5)が最新版として提供されており、新規に構築する場合は v3.5 も検討に値する。

template-transformノードでchunksをテキストに整形する理由

knowledge-retrieval ノードの出力は result(チャンクのオブジェクト配列)だ。LLM ノードはこのオブジェクトをそのまま受け取っても読みやすい形に解釈できないケースがある。そのため template-transform ノードで明示的に整形する。

template: |
  {% if chunks|length == 0 %}
  (該当する事例が見つかりませんでした)
  {% else %}
  {% for item in chunks %}■ {{ item.title }}
  {{ item.content }}

  {% endfor %}
  {% endif %}

「該当なし」の場合に LLM が存在しない情報を生成するのを防ぐため、空の場合のフォールバックメッセージも設定している。


Layer 3: Harness Engineering — verify_llm が安全機構になる

Harness Engineering の本質は「LLM の出力を別の LLM が検証する」という二重チェック構造だ。業界では LLM-as-a-JudgeVerifier pattern とも呼ばれる、確立されたパターンである。このワークフローでは verify_llm(ノードID: verify_llm)がその役割を担う。

QA検証ノードの6つのチェック観点

- id: verify-system
  role: system
  text: |
    チェック観点:
    1. 出典表記(「技術情報KBより」/「一般的な業界知識より」)が、
       実際の検索結果の内容と一致しているか。
       KB に存在しない技術・製品を「KBより」と誤表記していないか。
    2. 検索結果に記載のない具体的な数値・価格・費用・納期・SLA・性能値を
       断定的な事実として書いていないか(金額例は特に厳しくチェック)。
    3. KB 本文に存在しない具体的な型番・製品名が
       「(要確認:KB記載なし)」タグなしに使われていないか。
       既にタグが付いている箇所は「対応済み」として扱い、指摘しない。
    4. タグの乱用(一般的な見込み・意見表現にまでタグが付いている等)が
       起きていないか。
    5. 出力項目(0〜9)が過不足なく含まれているか。
    6. 相談内容に対して実際に答えになっているか。

観点3と観点4は一見矛盾するが、設計的には「タグ漏れを検出する」と「タグ乱用を検出する」の両方向チェックを行うことで、タグの付け方が適切な範囲に収まるよう誘導している。

verify_llm が output_llm に指摘を引き渡す流れ

verify_llm の出力テキスト({{#verify_llm.text#}})は output_llm のユーザープロンプトに「QAチェック結果」として渡される。output_llm の system prompt には次の指示が含まれている。

- id: output-system
  role: system
  text: |
    重要:
    - ④-2 の QA チェック結果で「KB記載なしのタグ漏れ」「根拠のない数値」として
      具体的に指摘された語句・数値は、本文(1〜9 章)で見つけ次第
      「(要確認:KB記載なし)」を付けるか、断定表現を「要確認」に弱めること。
    - 「タグの乱用」が指摘された場合は、対象外箇所から不要な「要確認」タグを削除し
      自然な文章に戻すこと。
    - 根拠のない製品名・数値・価格・納期は追加しないこと。

verify_llm が「重大な問題なし」と出力した場合でも output_llm には渡される。問題なしという情報自体が「確認済み」の根拠になる設計だ。

🐝 IroHive メモ verify_llm 自体も gpt-4o-mini で動いており、完璧ではない。今回の設計は「自動 QA でゼロにする」ではなく「人間が最終確認する前のフィルタを自動化する」という位置付けだ。社内QAメモが最終レポートに含まれているのはそのため。


Layer 4: Graph Engineering — DAGで3並列ファンアウト → 合流

Dify Workflow の graph.edges セクションがそのまま Graph Engineering の設計に対応する。エッジ定義を読めば、どのノードが並列で動き、どこで合流するかが分かる。

start → 3並列ファンアウト

edges:
- id: start-to-requirements
  source: start
  target: requirements_llm   # ① 要件整理

- id: start-to-past-cases
  source: start
  target: past_cases         # ② 過去事例KB検索

- id: start-to-tech-info
  source: start
  target: tech_info          # ③ 技術情報KB検索

start ノードから3方向に同時に実行が始まる。requirements_llm(LLM呼び出し)と2つの KB 検索は互いに依存しないため、並列実行できる。シーケンシャルに並べた場合と比べてレイテンシは概ね3分の1になる(3ブランチがほぼ等時間の場合の理論上限。実測では最遅ブランチに律速されるため、1/2〜1/1.5 程度に収束することが多い)。

proposal_llm への3入力合流

- id: requirements-to-proposal
  source: requirements_llm
  target: proposal_llm

- id: past-cases-text-to-proposal
  source: past_cases_text
  target: proposal_llm

- id: tech-info-text-to-proposal
  source: tech_info_text
  target: proposal_llm

proposal_llm は3つの上流ノードすべての完了を待って実行される。Dify はエッジで宣言した依存関係を自動解決するため、明示的な「待ち合わせ」設定は不要だ。

verify_llm と output_llm の並列分岐 → output_llm で合流

- id: proposal-to-verify
  source: proposal_llm
  target: verify_llm

- id: proposal-to-output
  source: proposal_llm
  target: output_llm

- id: verify-to-output
  source: verify_llm
  target: output_llm

proposal_llm の完了後、verify_llmoutput_llm が起動する。ただし output_llmverify_llm の完了も待つ(verify-to-output エッジがあるため)。結果として verify_llm と output_llm はシーケンシャルに動く。

また past_cases_texttech_info_textverify_llm にも直接接続されている。これにより verify_llm は proposal_llm の出力だけでなく、元の KB 検索結果を直接参照して照合できる。

✅ なぜシーケンシャルではなくDAGにするか シーケンシャルにすると「要件整理が終わるまで KB 検索が始まらない」。要件整理は LLM 呼び出しであり、数秒かかる。KB 検索の開始を待つ理由はない。DAG にすることで、依存のないノードを並列実行してエンドツーエンドのレイテンシを削減できる。

n8n でも同様の並列フロー設計ができる。n8nとDifyを連携させる記事では、n8n の Split In Batches や並列ブランチを使って Dify API を複数呼び出すパターンを解説している。


Loop Engineering はなぜ実装していないか — 次の進化

今回のワークフローは「1パスの自己補正」にとどまる。verify_llm が問題を検出したとしても、proposal_llm に戻って再生成するループは存在しない。verify の結果を output_llm が受けて修正するだけだ。

今回の実装(1パス自己補正)

proposal_llm → verify_llm → output_llm
verify が問題を指摘 → output が修正を適用
修正の品質は保証されない

真のLoop Engineering

proposal_llm → verify_llm
「重大な問題あり」→ proposal_llm に戻る
「問題なし」→ output_llm へ進む

真のLoop Engineering は「verify → 問題あり → proposal 再実行 → verify → …」を条件が満たされるまで繰り返すアーキテクチャだ。Dify でループを実装するには、verify_llm の出力テキストをパースして「問題あり / なし」を判定する条件分岐ノードを追加し、「問題あり」のエッジを proposal_llm に戻す必要がある。

ただし、これには注意点が2つある。

⚠️ 無限ループのリスク

症状:verify が常に「問題あり」と判定するとループが終わらない。
対処:最大反復回数(例: 3回)を設定し、超えたら強制的に output_llm に進む分岐を設ける。

⚠️ コストの増大

症状:ループ1回につき proposal_llm + verify_llm の2呼び出し分のトークンコストが発生する。
対処:verify の判定を「重大な問題のみ反復対象」に絞り、軽微な指摘は output が吸収する設計にする。

Loop Engineering の詳細な実装パターン(条件分岐ノードの設定・反復カウント変数の管理・停止条件の設計)は次回記事で解説する予定だ。


このYAMLを使い始めるための3ステップ

STEP 1
KBを2本準備する

Dify の「ナレッジ」で KB を2つ作成する。過去事例KB(提案書・議事録・案件メモ)と技術情報KB(製品カタログ・仕様書・FAQ)を分ける。KB 詳細画面の URL 末尾から KB ID をコピーしておく。

STEP 2
プラグインを追加する

Dify 管理画面 → プラグイン → マーケットプレイスから langgenius/cohere(v0.0.17)と langgenius/openai(v1.0.4)を追加する。それぞれの API キーを設定する。

STEP 3
YAMLのID2箇所を置換する

ダウンロードしたYAML内の YOUR_PAST_CASES_KNOWLEDGE_IDYOUR_TECH_INFO_KNOWLEDGE_ID を STEP 1 でコピーした KB ID に置き換えてインポートする。

✅ Dify バージョンについて このYAMLは Dify v1.x(YAML schema version: 0.7.0)で動作確認している。v0.x系では互換性がないためインポートに失敗する。最新バージョンは GitHub Releases で確認してほしい。


まとめ

5層フレームワークとこのワークフローの対応を整理する。

L1
Prompt Engineering
requirements_llm / proposal_llm / verify_llm / output_llm の system prompt 設計
✅ 実装済み
L2
Context Engineering
past_cases / tech_info の Hybrid Search + Cohere Rerank + template-transform
✅ 実装済み
L3
Harness Engineering
verify_llm による6観点QA検証 → output_llm への修正指示
✅ 実装済み
L4
Graph Engineering
start からの3並列ファンアウト → proposal_llm 合流 → output_llm 合流
✅ 実装済み
L5
Loop Engineering
verify → 問題あり → proposal 再実行のサイクル
⚠️ 次回実装

今回のワークフローはプロンプトを書くだけの段階から一歩踏み出し、コンテキストの設計・品質の検証・グラフの並列化という3つの軸を同時に実装したものだ。Loop Engineering は未実装だが、1パスの自己補正だけでも「根拠のない数値が素通りする」問題は大幅に減らせる。次回は条件分岐ノードと反復カウント変数を使ったループ実装を扱う。


明日から使えるチェックリスト

  • Dify の KB を「事例系」と「仕様系」で分ける(混在させない)
  • score_threshold: 0.5top_k: 6 を出発点に精度評価で調整する
  • template-transform で chunks を整形してから LLM に渡す
  • proposal_llm の system prompt に「価格断定禁止」「(要確認)タグルール」を必ず入れる
  • verify_llm はタグ漏れとタグ乱用の両方向をチェックする観点で設計する
  • edges 定義を書く前に DAG を手書きで確認する(合流点と並列点を明示する)
  • ループを入れる場合は最大反復回数と強制出口を先に設計する

参考資料