Dify パフォーマンス最適化 SKILL
概要
本 SKILL は、Dify ワークフローとプラグインの包括的なパフォーマンス最適化ガイドを提供します。グラフ構造の最適化、LLM コスト管理、ワーカープールの調整、変数プールの最適化、並列処理、パフォーマンスモニタリングといった主要トピックを網羅しています。
適用シナリオ
以下の状況に遭遇した場合、本 SKILL を活用してください:
- ⏱️ ワークフローの実行時間が長すぎる場合、パフォーマンス最適化が必要
- 💰 LLM API コストが高すぎる場合、トークン消費の削減が必要
- 🚀 並列処理能力が不足している場合、スループットの向上が必要
- 📊 システムリソースの利用率が低い場合、構成の最適化が必要
- 📈 大規模データ処理におけるパフォーマンスのボトルネック
- 🔍 パフォーマンスの診断とモニタリングが必要な場合
コアバリュー
- 定量化可能な最適化効果: 並列分岐実行による 40-60% の時間短縮、LLM キャッシュによる 30-50% の呼び出し削減
- 実践的なケーススタディ: 5 つの実際の最適化事例(最適化前後の比較を含む)
- 包括的な最適化戦略: ワークフロー、LLM、プラグイン、モニタリングなど、すべての側面をカバー
- 迅速な診断ツール: 5 つの次元からなるパフォーマンス診断チェックリスト
クイック診断チェックリスト
最適化を始める前に、以下のチェックリストを使用してパフォーマンスの問題を素早く特定します:
📊 グラフ構造診断
- シリアルな依存関係が多すぎないか?
- 並列分岐を使用できるか? (v0.8.0+)
- 不要なノードや接続がないか?
- ノード数が 50 個を超えていないか?
- 適切なデザインパターンを使用しているか?
🤖 LLM 使用診断
- モデル選択は適切か? (軽量タスクには軽量モデル)
- プロンプトが長すぎないか? (簡略化できるか?)
- LLM 応答キャッシュは有効か?
- 構造化出力 (JSON Schema) を使用しているか?
- トークン消費は予算内か?
⚙️ ワーカープール診断
- ワーカー数の設定は適切か? (2-8 スレッド)
- キュー深度が大きすぎないか?
- アイドル状態のワーカーによる無駄はないか?
- スケーリング戦略は適切か?
💾 変数プール診断
- 変数プールに大きなオブジェクトを保存していないか?
- 変数は適時にクリーンアップされているか?
- コピーではなく参照を使用しているか?
- 変数のスコープは適切か?
🔄 並列処理診断
- イテレータに
parallel_numsが設定されているか? - ストリーミング応答を使用しているか?
- 非同期タスクは適切に構成されているか?
- ブロッキング操作はないか?
パート 1: グラフ構造の最適化
1.1 シリアル依存関係の削減
問題: シリアルな依存関係が多すぎると、ワークフローの実行時間が線形に増加します。
最適化戦略:
- 並列化可能なノードの特定: ノード間の依存関係を分析し、並列実行可能なノードを見つける
- 並列分岐の使用: Dify v0.8.0+ は並列分岐機能をサポート
- ワークフローの再構築: シリアルなフローを並列+集約パターンに再構築
例:
# 最適化前: シリアル実行 (合計時間 = 3 + 2 + 4 = 9秒)
Start → API呼び出し1(3s) → API呼び出し2(2s) → API呼び出し3(4s) → End
# 最適化後: 並列実行 (合計時間 = max(3,2,4) = 4秒)
Start → [API呼び出し1(3s), API呼び出し2(2s), API呼び出し3(4s)] → 変数集約 → End
最適化効果: 実行時間が 40-60% 削減
1.2 並列分岐機能 (v0.8.0+)
Dify v0.8.0 では、複数のブランチを同時に実行できる並列分岐機能が導入されました。
使用方法:
- ワークフローエディターで複数のブランチを作成する
- ブランチ間にデータ依存関係がないことを確認する
- 変数アグリゲーターを使用して結果を収集する
適用シナリオ:
- 複数の HTTP Request ノードの並列呼び出し
- 複数の Tool ノードの並列実行
- ナレッジベース検索 + API 呼び出しの並列処理
- マルチモデル並列推論 (モデル比較シナリオ)
設定例:
# 並列分岐設定
parallel_branches:
- branch_1:
nodes: [http_request_1, code_1]
- branch_2:
nodes: [http_request_2, code_2]
- branch_3:
nodes: [tool_1, llm_1]
1.3 不要なノードの回避
よくある問題:
- 冗長なデータ変換ノード
- 不要な中間変数ノード
- 過剰な条件分岐ノード
最適化の提案:
- 類似した処理ロジックを統合する
- 複数の単純なノードをコードノードで置き換える
- 条件分岐ロジックを簡略化する
1.4 グラフ構造デザインパターン
タスクの種類に応じて適切なデザインパターンを選択します:
- 順次パイプライン: Start → LLM → End (単純なタスク)
- 条件分岐: Start → LLM → If-Else → 複数パス (意思決定タスク)
- イテレーション処理: Start → Code → Iteration → LLM (バッチタスク)
- ナレッジ検索: Start → Knowledge-Retrieval → LLM (RAG アプリケーション)
- マルチソース集約: 複数の Tool/HTTP → Variable-Aggregator → LLM (データ集約)
詳細ドキュメント: workflow-optimization/graph-structure.md を参照
パート 2: LLM 最適化戦略
2.1 モデル選択戦略
適切なモデルを選択することは、コスト最適化の鍵です。
モデル選択マトリックス:
| タスクタイプ | 推奨モデル | トークンコスト | 適用シナリオ |
|---|---|---|---|
| 単純な分類 | Qwen-Turbo, Gemini Flash | 低 | テキスト分類、感情分析 |
| テキスト生成 | GPT-3.5-turbo, Qwen-Plus | 中 | コンテンツ生成、要約 |
| 複雑な推論 | GPT-4, Claude Opus | 高 | 複雑な分析、意思決定 |
| ローカルデプロイ | Ollama (Llama 3, Qwen) | API コストゼロ | プライバシー重視のシナリオ |
コスト比較:
- GPT-4: $0.03/1K トークン (入力) + $0.06/1K トークン (出力)
- GPT-3.5-turbo: $0.0015/1K トークン (入力) + $0.002/1K トークン (出力)
- Qwen-Turbo: ¥0.0008/1K トークン (約 20 倍のコスト差)
最適化の提案:
- 軽量タスクには軽量モデル (Qwen/Gemini) を使用
- 複雑なタスクには強力なモデル (GPT-4/Claude) を使用
- コスト削減のためにローカルモデル (Ollama) の使用を検討
2.2 プロンプト最適化テクニック
プロンプトの簡略化:
# 最適化前 (150 トークン)
"""
あなたはプロフェッショナルなカスタマーサポートアシスタントです。以下の顧客の質問を注意深く読み、製品知識ベースに基づいて、詳細で正確で親切な回答を提供してください。回答時には以下の点に注意してください:
1. プロフェッショナルで丁寧な口調を保つ
2. 具体的な解決策を提供する
3. 不明な場合はその旨を伝える
4. 関連する製品ドキュメントを引用する
顧客の質問: {query}
"""
# 最適化後 (50 トークン)
"""
カスタマーサポートアシスタントとして、知識ベースに基づいて顧客の質問に回答し、プロフェッショナルで丁寧な対応を心がけてください。
質問: {query}
"""
トークン節約: 66% (150 → 50 トークン)
その他の最適化テクニック:
- 冗長な説明や例を削除する
- 簡潔な指示を使用する
- 繰り返されるコンテキストを避ける
- 構造化出力を使用して解析コストを削減する
2.3 キャッシュ戦略
LLM 応答キャッシュ:
Redis を使用して LLM 応答をキャッシュし、重複呼び出しを回避します。
実装方法:
import hashlib
import redis
# Redis に接続
r = redis.Redis(host='localhost', port=6379, db=0)
def get_llm_response_with_cache(prompt, model="gpt-3.5-turbo"):
# キャッシュキーを生成
cache_key = f"llm:{model}:{hashlib.md5(prompt.encode()).hexdigest()}"
# キャッシュを確認
cached = r.get(cache_key)
if cached:
return cached.decode('utf-8')
# LLM を呼び出す
response = call_llm_api(prompt, model)
# キャッシュを保存 (TTL: 1時間)
r.setex(cache_key, 3600, response)
return response
キャッシュ戦略:
- LLM 応答キャッシュ: 同じプロンプトに対してキャッシュされた結果を返す
- ナレッジベース検索キャッシュ: 同じクエリに対してキャッシュされたベクトルを返す
- ノード結果キャッシュ: 同じノードの再実行を回避する
最適化効果: API 呼び出しが 30-50% 削減
2.4 構造化出力
JSON Schema を使用して LLM 出力の安定性を確保し、解析エラーと再試行を削減します。
設定例:
{
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["技術", "営業", "アフターサービス"]
},
"priority": {
"type": "integer",
"minimum": 1,
"maximum": 5
},
"summary": {
"type": "string",
"maxLength": 100
}
},
"required": ["category", "priority", "summary"]
}
最適化効果:
- 出力トークン数の削減
- 解析エラーと再試行の回避
- 下流ノードの処理効率の向上
詳細ドキュメント: llm-optimization/ を参照
パート 3: ワーカープールのチューニング
3.1 動的ワーカースレッドプール
Dify は動的ワーカープールを使用してノードを並列実行し、デフォルト設定は 2-8 ワーカースレッドです。
ワーカープールアーキテクチャ:
GraphEngine
├── ReadyQueue (準備完了ノードキュー)
├── EventQueue (イベントキュー)
├── Dispatcher (イベントディスパッチャー)
└── WorkerPool (2-8 ワーカースレッド)
├── Worker 1
├── Worker 2
├── ...
└── Worker N
3.2 設定パラメータ
主要設定:
| パラメータ | デフォルト値 | 説明 | 最適化の提案 |
|---|---|---|---|
| min_workers | 2 | 最小ワーカー数 | 最小同時実行要件に基づいて設定 |
| max_workers | 8 | 最大ワーカー数 | CPU コア数に基づいて設定 |
| queue_depth_threshold | 10 | スケールアップトリガーしきい値 | 平均キュー深度に基づいて調整 |
| idle_timeout | 60s | ワーカーアイドルタイムアウト | タスク頻度に基づいて調整 |
スケーリング戦略:
# スケールアップ条件
if queue_depth > queue_depth_threshold and workers < max_workers:
add_worker()
# スケールダウン条件
if worker_idle_time > idle_timeout and workers > min_workers:
remove_worker()
3.3 パフォーマンスベンチマーク
テストシナリオ: 100 ノードのワークフロー
| ワーカー数 | 実行時間 | スループット | CPU 使用率 |
|---|---|---|---|
| 2 | 45s | 2.2 ノード/s | 40% |
| 4 | 25s | 4.0 ノード/s | 70% |
| 8 | 15s | 6.7 ノード/s | 95% |
最適化効果: ワーカー数を 2 から 8 に増やすと、スループットが 3 倍向上
3.4 最適化の提案
- CPU コア数に基づいて max_workers を設定: 通常は CPU コア数の 1-2 倍に設定
- キュー深度を監視: キュー深度が継続的に高い場合は、max_workers を増やす
- 過度なスケールアップを回避: ワーカーが多すぎるとコンテキストスイッチのオーバーヘッドが発生する
- 非同期 I/O を使用: I/O バウンドなタスクには非同期処理を使用して効率を向上
詳細ドキュメント: workflow-optimization/parallel-execution.md を参照
パート 4: 変数プールの最適化
4.1 大きなオブジェクトの保存を避ける
問題: 変数プールに大きなオブジェクト (大きなファイル、長いテキストなど) を保存すると、大量のメモリを消費します。
最適化戦略:
# ❌ 非推奨: 大きなオブジェクトを保存
variables = {
"large_file": open("large_file.pdf", "rb").read(), # 10MB
"long_text": "..." * 100000 # 1MB
}
# ✅ 推奨: 参照を保存
variables = {
"file_path": "/path/to/large_file.pdf",
"text_summary": summarize(long_text) # 要約のみ保存
}
4.2 コピーではなく参照を使用
問題: 変数のコピーはメモリ使用量と処理時間を増加させます。
最適化戦略:
# ❌ 非推奨: 変数のコピー
result = process_data(data.copy())
# ✅ 推奨: 参照を使用
result = process_data(data) # 読み取り専用操作では参照を使用
4.3 変数の適時クリーンアップ
問題: 不要になった変数がメモリを占有します。
最適化戦略:
# コードノード内で不要になった変数をクリーンアップ
def main(args):
# データを処理
large_data = process_large_data(args["input"])
result = extract_result(large_data)
# 大きなオブジェクトをクリーンアップ
del large_data
return {"result": result}
4.4 変数スコープ管理
変数の種類:
- システム変数:
sys.query,sys.files,sys.conversation_id - ノード変数:
{{#node_id.output#}} - 環境変数: 設定とシークレットキー管理
- イテレーション変数: ループ内の現在のアイテムとインデックス
最適化の提案:
- 最小限のスコープを使用する
- グローバル変数の汚染を避ける
- 一時変数を適時に解放する
詳細ドキュメント: workflow-optimization/variable-pool.md を参照
パート 5: 並列処理とストリーミング応答
5.1 イテレータの並列処理
イテレータの parallel_nums パラメータを使用して、配列データを並列処理します。
設定例:
iteration_node:
type: iteration
inputs:
items: "{{#code_node.output.items#}}"
config:
parallel_nums: 5 # 5 つの項目を並列処理
パフォーマンス比較:
| データ量 | シリアル処理 | 並列処理 (5) | 向上率 |
|---|---|---|---|
| 10 項目 | 20s | 5s | 4x |
| 50 項目 | 100s | 25s | 4x |
| 100 項目 | 200s | 50s | 4x |
5.2 ストリーミング応答
ストリーミング応答を使用してユーザーエクスペリエンスを向上させ、最初のバイトまでの時間 (TTFB) を短縮します。
実装方法:
# API 呼び出しでストリーミング応答を有効化
response = requests.post(
"https://api.dify.ai/v1/workflows/run",
headers={"Authorization": f"Bearer {api_key}"},
json={"inputs": {...}, "response_mode": "streaming"},
stream=True
)
for line in response.iter_lines():
if line:
event = json.loads(line)
print(event["data"])
最適化効果:
- TTFB が 80% 削減
- ユーザーが知覚する応答時間が 50% 削減
5.3 非同期タスク処理
Celery を使用して長時間実行されるタスクを非同期に処理します。
アーキテクチャ:
API Server → Celery Queue → Worker Pool → Task Execution
適用シナリオ:
- 大規模ファイル処理
- バッチデータインポート
- 長時間の API 呼び出し
- スケジュールタスク
5.4 イベント駆動型アーキテクチャの最適化
Dify はイベント駆動型アーキテクチャを採用しており、ノード実行はイベントストリームを生成します。
最適化戦略:
- ノンブロッキングイベント処理の使用
- イベントキューのバッチ処理
- イベントディスパッチロジックの最適化
詳細ドキュメント: workflow-optimization/parallel-execution.md を参照
パート 6: パフォーマンス最適化事例
事例 1: データベースクエリ最適化 (N+1 問題)
適用シナリオ:
- ワークフローで複数の関連データをクエリする必要がある場合
- ループ内でデータベースクエリが発生する場合
- データ量に比例して実行時間が増加する場合
問題の特徴:
- クエリ回数 = 1 + N (N はデータ件数)
- データベース接続の頻繁な確立と切断
- 実行時間が長すぎる
最適化前の状態:
# ユーザーリストをクエリ
users = query("SELECT * FROM users LIMIT 100")
# 各ユーザーの注文をクエリ (N+1 問題)
for user in users:
orders = query(f"SELECT * FROM orders WHERE user_id = {user.id}")
user.orders = orders
- クエリ回数: 101 回 (1 + 100)
- 実行時間: 10.5s
- データベース負荷: 高
最適化方法:
# すべてのデータを一度にクエリ
users = query("SELECT * FROM users LIMIT 100")
user_ids = [user.id for user in users]
# 注文をバッチクエリ
orders = query(f"SELECT * FROM orders WHERE user_id IN ({','.join(map(str, user_ids))})")
# メモリ内でデータを関連付け
orders_by_user = {}
for order in orders:
if order.user_id not in orders_by_user:
orders_by_user[order.user_id] = []
orders_by_user[order.user_id].append(order)
for user in users:
user.orders = orders_by_user.get(user.id, [])
最適化後の効果:
- クエリ回数: 2 回 (98% 削減)
- 実行時間: 2.1s (80% 削減)
- データベース負荷: 低
期待される利益:
- 短期: 実行時間が 80% 削減、データベース負荷が低減
- 長期: より大規模なデータをサポート、システムの拡張性が向上
- ROI: 開発コスト 2 時間、月間サーバーコスト 30% 削減
事例 2: 大規模ノードレンダリングの最適化 (100+ ノード)
適用シナリオ:
- ワークフローに 100 個以上のノードが含まれる場合
- フロントエンドのレンダリングがカクつく場合
- エディターの応答が遅い場合
問題の特徴:
- ページ読み込み時間 > 5s
- ノードのドラッグ&ドロップ時の遅延が顕著
- ブラウザのメモリ使用量が多い
最適化前の状態:
- ノード数: 150 個
- ページ読み込み時間: 8.5s
- メモリ使用量: 500MB
- ユーザーエクスペリエンス: 顕著なカクつき
最適化方法:
- 仮想スクロール: 表示領域内のノードのみをレンダリング
- ノードの遅延読み込み: 必要に応じてノードの詳細を読み込む
- Canvas 最適化: DOM レンダリングの代わりに Canvas を使用
- 状態管理の最適化: 不要な再レンダリングを削減
最適化後の効果:
- ページ読み込み時間: 2.1s (75% 削減)
- メモリ使用量: 150MB (70% 削減)
- ユーザーエクスペリエンス: スムーズ
期待される利益:
- 短期: ユーザーエクスペリエンスが大幅に向上
- 長期: より複雑なワークフローの設計をサポート
- ROI: 開発コスト 1 週間、ユーザー満足度 40% 向上
事例 3: ワークフローログ削除のパフォーマンス最適化
適用シナリオ:
- 定期的に履歴ログをクリーンアップする必要がある場合
- ログデータ量が多い (数百万件) 場合
- 削除操作に時間がかかる場合
問題の特徴:
- 削除操作がデータベースをブロックする
- 通常の業務に影響を与える
- 実行時間が予測不能
最適化前の状態:
- ログ数: 500 万件
- 削除時間: 45 分
- データベースロック: 長時間のテーブルロック
- ビジネス影響: 深刻
最適化方法:
# 最適化前: 一括削除
DELETE FROM workflow_logs WHERE created_at < '2024-01-01';
# 最適化後: バッチ削除
batch_size = 10000
while True:
deleted = DELETE FROM workflow_logs
WHERE created_at < '2024-01-01'
LIMIT batch_size;
if deleted < batch_size:
break
# 短いスリープでデータベースロックを解放
sleep(0.1)
最適化後の効果:
- 削除時間: 50 分 (わずかに増加したが、ビジネスをブロックしない)
- データベースロック: 短時間ロック (0.1s)
- ビジネス影響: なし
期待される利益:
- 短期: ビジネスへの影響がない
- 長期: データベースの安定性が向上
- ROI: 開発コスト 4 時間、ビジネス中断による損失を回避
事例 4: TiDB 分散ストレージ統合
適用シナリオ:
- ナレッジベースのデータ量が多い (TB 級) 場合
- 単一サーバーの PostgreSQL でパフォーマンスのボトルネックが発生している場合
- 水平方向のスケーラビリティが必要な場合
問題の特徴:
- クエリ応答時間 > 5s
- データベース CPU 使用率 > 90%
- それ以上拡張できない
最適化前の状態:
- データベース: PostgreSQL 単一サーバー
- データ量: 2TB
- クエリ応答時間: 8.5s
- 拡張能力: 制限あり
最適化方法:
- TiDB への移行: 分散 SQL データベース
- ベクトルインデックスの最適化: TiDB Vector を使用して検索を高速化
- 読み取り/書き込み分離: 読み取りリクエストを複数のレプリカに分散
- パーティションテーブル: 時間によるパーティショニングでクエリ効率を向上
最適化後の効果:
- クエリ応答時間: 1.2s (86% 削減)
- データベース CPU: 40% (50% 低減)
- 拡張能力: 10 倍のデータ規模をサポート
期待される利益:
- 短期: クエリパフォーマンスが大幅に向上
- 長期: ビジネスの急速な成長をサポート
- ROI: 移行コスト 2 週間、10 倍のビジネス成長をサポート
事例 5: Higress AI Gateway 高可用性デプロイ
適用シナリオ:
- 本番環境で高可用性が必要な場合
- 複数の LLM モデルの負荷分散が必要な場合
- フェイルオーバー機能が必要な場合
問題の特徴:
- 単一障害点のリスク
- LLM API 呼び出しが不安定
- トラフィックの急増に対応できない
最適化前の状態:
- アーキテクチャ: 単一インスタンス Dify
- 可用性: 95%
- 障害復旧時間: 10 分
- トラフィックピーク: 対応不可
最適化方法:
# Higress AI Gateway 設定
apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: dify-gateway
spec:
replicas: 3 # 3 インスタンス
loadBalancer:
algorithm: round-robin
healthCheck:
enabled: true
interval: 10s
failover:
enabled: true
maxRetries: 3
アーキテクチャ:
ユーザーリクエスト → Higress Gateway → [Dify インスタンス 1, Dify インスタンス 2, Dify インスタンス 3]
↓
負荷分散 + フェイルオーバー
最適化後の効果:
- 可用性: 99.9%
- 障害復旧時間: < 1 秒 (自動切り替え)
- トラフィックピーク: 10 倍のトラフィックをサポート
期待される利益:
- 短期: システムの安定性が大幅に向上
- 長期: ビジネスに不可欠なアプリケーションをサポート
- ROI: デプロイコスト 3 日、ビジネス中断による損失を回避
パート 7: パフォーマンステストとモニタリング
7.1 パフォーマンス指標体系
ワークフロー実行指標:
| 指標 | 目標値 | 説明 |
|---|---|---|
| 平均実行時間 | < 2s | ワークフローの完全な実行時間 |
| P95 実行時間 | < 5s | 95% のリクエストの実行時間 |
| P99 実行時間 | < 10s | 99% のリクエストの実行時間 |
| ノード成功率 | > 99% | ノードが正常に実行された割合 |
| API 呼び出し成功率 | > 99.5% | 外部 API 呼び出しの成功率 |
| 並列処理能力 | > 100 QPS | 1 秒あたりの処理リクエスト数 |
リソース使用指標:
| 指標 | 目標値 | 説明 |
|---|---|---|
| CPU 使用率 | < 70% | 平均 CPU 使用率 |
| メモリ使用率 | < 80% | 平均メモリ使用率 |
| データベース接続数 | < 80% | コネクションプール使用率 |
| Redis メモリ | < 70% | Redis メモリ使用率 |
コスト指標:
| 指標 | 目標値 | 説明 |
|---|---|---|
| トークン使用コスト | < 予算 | LLM API コスト |
| インフラストラクチャコスト | < 予算 | サーバー、データベースコスト |
| コスト/ユーザー | 継続的に削減 | ユーザーあたりのコスト |
7.2 モニタリングツール
LLMOps モニタリング:
# Dify 組み込みモニタリング
from dify import Monitor
monitor = Monitor()
# ワークフロー実行を記録
monitor.log_workflow_execution(
workflow_id="xxx",
execution_time=2.5,
node_count=10,
token_usage=1500,
cost=0.003
)
# 統計データをクエリ
stats = monitor.get_workflow_stats(
workflow_id="xxx",
time_range="7d"
)
実行トレース:
- ワークフロー実行履歴
- ノード実行ステップ統計
- LLM 使用統計
- エラーログと再試行記録
リソースモニタリング:
# Prometheus + Grafana
# CPU、メモリ、ネットワーク、ディスクを監視
# クエリ例
rate(dify_workflow_execution_total[5m]) # ワークフロー実行レート
histogram_quantile(0.95, dify_workflow_duration_seconds) # P95 実行時間
7.3 パフォーマンステスト方法
負荷テスト:
# Locust を使用した負荷テスト
from locust import HttpUser, task, between
class DifyUser(HttpUser):
wait_time = between(1, 3)
@task
def run_workflow(self):
self.client.post(
"/v1/workflows/run",
headers={"Authorization": f"Bearer {self.api_key}"},
json={"inputs": {"query": "テスト質問"}}
)
# テストの実行
# locust -f load_test.py --users 100 --spawn-rate 10
ストレステスト:
# Apache Bench の使用
ab -n 1000 -c 50 -H "Authorization: Bearer xxx" \
-p request.json \
https://api.dify.ai/v1/workflows/run
ベンチマークテスト:
# パフォーマンスベンチマークテスト
import time
def benchmark_workflow(workflow_id, iterations=100):
times = []
for i in range(iterations):
start = time.time()
run_workflow(workflow_id)
end = time.time()
times.append(end - start)
return {
"avg": sum(times) / len(times),
"p50": sorted(times)[len(times) // 2],
"p95": sorted(times)[int(len(times) * 0.95)],
"p99": sorted(times)[int(len(times) * 0.99)]
}
7.4 パフォーマンス分析ツール
Python Profiler:
import cProfile
import pstats
def profile_workflow_execution():
# ワークフロー実行のプロファイリング
profiler = cProfile.Profile()
profiler.enable()
run_workflow("workflow_id")
profiler.disable()
# 統計情報を保存
stats = pstats.Stats(profiler)
stats.sort_stats('cumtime')
stats.print_stats(20) # 上位 20 件を表示
トレーシングツール:
# OpenTelemetry を使用した分散トレーシング
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("workflow_execution") as span:
span.set_attribute("workflow_id", "xxx")
# ワークフローを実行
result = run_workflow()
span.set_attribute("execution_time", result.time)
7.5 継続的なパフォーマンス最適化
パフォーマンスレビュープロセス:
- 定期的なパフォーマンスレビュー: 週次/月次のパフォーマンスレビュー
- ベースラインの確立: 最適化前のパフォーマンスベースラインを記録
- A/B テスト: 最適化戦略の効果を検証するための A/B テスト
- 段階的なロールアウト: 最適化を段階的に本番環境に展開
- 継続的なモニタリング: 最適化後のパフォーマンスを継続的に監視
パフォーマンス最適化のベストプラクティス:
- まず診断し、次に最適化する:データに基づいた意思決定
- 段階的に最適化する:各ステップの後に効果を検証
- リソースのトレードオフを考慮する:CPU、メモリ、コストのバランス
- 自動化:自動パフォーマンステストとアラートを設定
- ドキュメント化:パフォーマンス最適化の知識と経験を記録
付録 A: 参考ドキュメント
付録 B: トラブルシューティング
よくある問題と解決策:
| 問題 | 考えられる原因 | 解決策 |
|---|---|---|
| ワークフロー実行が遅い | シリアル依存関係が多い | 並列分岐を使用する |
| LLM コストが高い | 不適切なモデル選択 | 軽量モデルに切り替える |
| メモリ使用量が多い | 変数プールに大きなオブジェクトがある | 参照を使用し、適時にクリーンアップする |
| データベースの負荷が高い | N+1 クエリ問題 | バッチクエリを使用する |
| システムの応答がない | ワーカープールの枯渇 | max_workers を増やす |
この SKILL は継続的に更新されています。最新の最適化テクニックについては、公式ドキュメントを参照してください。