「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 │ │ │ │ │
│ │ │ │ │ ← 命令・役割・出力フォーマット定義 │ │ │ │ │
│ │ │ │ └─────────────────────────────────────┘ │ │ │ │
│ │ │ └───────────────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
LLM に渡す system prompt の設計。役割・禁止ルール・出力フォーマットを定義する。この層だけでは情報の不足・ハルシネーション・再現性のなさを制御できない。
モデルが推論に必要な外部知識を正しい形で届ける設計。KB 検索・rerank・チャンク整形がここに入る。「何を渡すか」「どう整形するか」が精度を左右する。
LLM の出力を別の LLM が検証・制御するレイヤー。QA ノードが根拠のない数値や出典誤りを検出し、後段の出力ノードに修正指示を渡す。
ノード間の依存グラフ(DAG)設計。依存のないノードを並列実行してレイテンシを削減し、合流点で複数の入力を統合する。Dify の edges 定義がそのままこの層に対応する。
検証→修正→再検証のサイクルを反復させる設計。条件分岐で「問題あり」の場合に提案生成ノードへ戻るループを形成する。今回のワークフローでは未実装(後述)。
今回作ったDifyワークフローの全体像
実装したのは「AI技術相談アシスタント」だ。顧客や社内からの技術相談テキストを入力として受け取り、過去事例KB・技術情報KBを参照しながら提案レポートを生成する。
DAGの全体構造
このワークフローが実装している層は Layer 1〜4 の4層だ。start から3方向に並列ファンアウトし、proposal_llm で合流、そこから verify_llm と output_llm に分岐してふたたび output_llm で合流する。シーケンシャルな7ステップではなく、DAG により実質的に3フェーズで完結する。
Layer 1: Prompt Engineering — 各LLMノードのsystem prompt設計
このワークフローには4つのLLMノードがある。それぞれの system prompt が果たす役割は異なる。
出典ルールと「(要確認)タグ」の設計意図
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
rerank 前の候補取得数。多すぎるとノイズが増え、少なすぎると関連チャンクを取りこぼす。KB のドキュメント規模が中小(数百チャンク以下)なら 6 が現実的な出発点。大規模KBは 10〜15 から試す。
rerank 後のスコアカットライン。0.5 未満のチャンクは LLM に渡さない。閾値を下げると関連性の薄い情報が混入し、上げると「該当なし」になりやすい。実際の運用では RAG精度評価 で調整する。
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-Judge や Verifier 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_llm と output_llm が起動する。ただし output_llm は verify_llm の完了も待つ(verify-to-output エッジがあるため)。結果として verify_llm と output_llm はシーケンシャルに動く。
また past_cases_text と tech_info_text は verify_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 が受けて修正するだけだ。
真のLoop Engineering は「verify → 問題あり → proposal 再実行 → verify → …」を条件が満たされるまで繰り返すアーキテクチャだ。Dify でループを実装するには、verify_llm の出力テキストをパースして「問題あり / なし」を判定する条件分岐ノードを追加し、「問題あり」のエッジを proposal_llm に戻す必要がある。
ただし、これには注意点が2つある。
Loop Engineering の詳細な実装パターン(条件分岐ノードの設定・反復カウント変数の管理・停止条件の設計)は次回記事で解説する予定だ。
このYAMLを使い始めるための3ステップ
✅ Dify バージョンについて このYAMLは Dify v1.x(YAML schema version: 0.7.0)で動作確認している。v0.x系では互換性がないためインポートに失敗する。最新バージョンは GitHub Releases で確認してほしい。
まとめ
5層フレームワークとこのワークフローの対応を整理する。
今回のワークフローはプロンプトを書くだけの段階から一歩踏み出し、コンテキストの設計・品質の検証・グラフの並列化という3つの軸を同時に実装したものだ。Loop Engineering は未実装だが、1パスの自己補正だけでも「根拠のない数値が素通りする」問題は大幅に減らせる。次回は条件分岐ノードと反復カウント変数を使ったループ実装を扱う。
明日から使えるチェックリスト
- Dify の KB を「事例系」と「仕様系」で分ける(混在させない)
score_threshold: 0.5、top_k: 6を出発点に精度評価で調整する- template-transform で chunks を整形してから LLM に渡す
- proposal_llm の system prompt に「価格断定禁止」「(要確認)タグルール」を必ず入れる
- verify_llm はタグ漏れとタグ乱用の両方向をチェックする観点で設計する
- edges 定義を書く前に DAG を手書きで確認する(合流点と並列点を明示する)
- ループを入れる場合は最大反復回数と強制出口を先に設計する