Contents
Difyとは — サービス概要と主要機能
ここではDifyの役割と実務で重要となる機能を整理します。Difyはノーコードのオーケストレーション機能とAPIを組み合わせ、プロトタイプから本番運用まで対応できる点が特徴です(公式ドキュメントによる機能説明は後述のリンクを参照してください)。
主要機能
主要機能の把握は設計方針の決定に直結します。以下は実務で押さえるべき機能群です。
- ノーコードのフローエディタ(トリガー、分岐、アクション、有人転送)
- ナレッジベース(PDF/CSV/HTML)取り込みと検索連携(RAG)
- 外部API呼び出し・Webhook・DB連携
- Web埋め込みウィジェットやSDK、各種チャネル接続(LINE/Slack等はWebhook経由が一般的)
- 会話ログ・分析ダッシュボード(フォールバック率、解決率の可視化)
想定ユースケース
想定ユースケースを明確にすると設計優先度が決まります。以下の用途に向きます。
- FAQ自動化/一次対応(根拠提示が必要なサポート)
- 社内ナレッジ検索(プライバシー制約がある場合のオンプレや限定公開)
- EC接客・レコメンド(外部API連携と有人切替の併用)
公式ドキュメント(参照): https://docs.dify.ai/ja-jp/guides/application-orchestrate/chatbot-application
事前準備とワークフロー設計
実装前に揃えるべき項目とワークフロー設計の考え方を示します。適切な準備は後工程の手戻りや運用コストを大きく減らします。
アカウントと開発環境の準備
必要な環境と秘密情報の管理方法を具体的に示します。
- ワークスペース/アプリを用途別に分ける(開発・ステージング・本番)
- APIキーは環境変数/シークレットマネージャで管理する(例は下に示します)
- 開発ツール:Git、CI(GitHub Actions等)、Node.js/Python LTS、Docker(任意)
- ローカルではモックサーバや契約済みテスト用データを用いる(PIIを含めない)
- リポジトリには .env を含めず、シークレットは外部に保管する
環境変数(例 .env)
|
1 2 3 4 |
DIFY_API_KEY=sk-xxxx... DIFY_API_BASE=https://api.dify.ai/v1 SESSION_TTL_SECONDS=86400 |
AWS Secrets Manager(Node.js 例)
|
1 2 3 4 5 6 7 8 9 10 |
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"; const client = new SecretsManagerClient({ region: process.env.AWS_REGION }); export async function getSecret(secretName) { const cmd = new GetSecretValueCommand({ SecretId: secretName }); const res = await client.send(cmd); return JSON.parse(res.SecretString); } |
GCP Secret Manager(Python 例)
|
1 2 3 4 5 6 7 8 |
from google.cloud import secretmanager import os def access_secret(secret_name: str): client = secretmanager.SecretManagerServiceClient() name = f"projects/{os.environ['GCP_PROJECT']}/secrets/{secret_name}/versions/latest" resp = client.access_secret_version(name=name) return resp.payload.data.decode('utf-8') |
鍵ローテーション方針:APIキーは短期ローテーションを計画し、アクセスログで異常を検知したら即ローテートする仕組みを作ってください。
会話ワークフロー設計とKPI
ワークフローは入口→主要パス→フォールバック→エスカレーションで整理します。主要KPIを初期に設定して改善サイクルを回してください。
- セッション管理(セッションID発行・TTLの決定)
- インテント定義と日本語サンプル文の集合(方言・敬語を考慮)
- コンテキスト保存ポリシー(短期メモリ:直近数往復、長期メモリ:匿名化した要約)
- エスカレーション条件:未解決回数、フォールバック連続回数、重要カテゴリでの自動転送
- KPI例:解決率、フォールバック率、p95/p99 レイテンシ、有人対応比率、セッション当たりコスト
設計段階で「測れる指標」を確実にログしておくと改善速度が上がります。
ノーコード導入とハンズオン
まずノーコードでプロトタイプを作り、UXと主要シナリオの妥当性を確認します。ここではワークスペース作成から公開までの典型的な流れと注意点を示します。
ワークスペース作成 → フロー設計 → テスト → 公開
実務での標準的な手順です。順番に構築して段階的に評価します。
- 管理画面でアプリを登録し、用途(FAQ/サポート/EC)を設定する。
- フローエディタでトリガー(キーワード/ボタン/Webhook)を定義し、分岐とアクションを組む。
- ナレッジをアップロード(PDF/CSV/HTML)し、チャンクとメタデータを付与する。
- フォールバック文言と有人切替トリガーを設定する(Slack通知など)。
- 組み込みテスターで典型パスとエッジケースを確認し、テストデータは合成データを使う。
- ステージングで負荷試験→ RBAC とバージョン管理を整えて本番公開する。
運用注意点:個人情報は取り込み前にマスキング/除去し、編集権限を制限してください。
コード実装と公式APIの使い方(Node.js / Python)
ノーコードで足りない場合はバックエンドを実装して細かい制御や外部連携を行います。次は期待されるアウトプットと、公式APIを想定した実装例・運用上の注意点です。APIの仕様は変更される可能性があるため、実装前に必ず公式ドキュメントを確認してください(公式ドキュメントによると各APIの正確なエンドポイントとフィールドはドキュメントで確認できます)。
公式API概要と呼び出しの注意
ここでは一般的に使われる呼び方の例を示します。DifyのAPIではBearerトークンでの認証が一般的です。実際のパス・フィールドは公式リファレンスを参照してください。
- 認証:HTTPヘッダ Authorization: Bearer
- ベースURL(例): https://api.dify.ai/v1 ※実際のエンドポイントは公式リファレンスを確認してください
- タイムアウトとリトライをクライアント側で実装すること(詳細は下)
以下のコード例は実務でそのまま使える形で、レスポンス確認やエラーハンドリング、PIIマスキングの流れを含みます。
Node.js(Express + axios) 完全な同期呼び出し例
この実装で期待する出力は「セッションID付きのボット応答」を返すAPIです。環境変数でキーとベースURLを渡します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
// server.js import express from 'express'; import axios from 'axios'; import dotenv from 'dotenv'; import crypto from 'crypto'; dotenv.config(); const app = express(); app.use(express.json()); const DIFY_API_BASE = process.env.DIFY_API_BASE || 'https://api.dify.ai/v1'; const DIFY_API_KEY = process.env.DIFY_API_KEY; if (!DIFY_API_KEY) throw new Error('DIFY_API_KEY is required'); const client = axios.create({ baseURL: DIFY_API_BASE, timeout: 10000, headers: { Authorization: `Bearer ${DIFY_API_KEY}`, 'Content-Type': 'application/json' }, }); // 簡易PIIマスキング function maskPII(text) { if (!text) return text; return text .replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g, '[email_removed]') .replace(/\b\d{13,19}\b/g, '[number_removed]') .replace(/\b\d{2,4}[-\s]?\d{2,4}[-\s]?\d{3,4}\b/g, '[phone_removed]'); } async function requestWithBackoff(url, body, retries = 3) { let attempt = 0; while (true) { try { return await client.post(url, body); } catch (err) { attempt++; const status = err.response?.status; if (attempt > retries || (status && status < 500)) throw err; const backoffMs = Math.min(1000 * 2 ** attempt, 10000) + Math.floor(Math.random() * 300); await new Promise(r => setTimeout(r, backoffMs)); } } } app.post('/message', async (req, res) => { try { const sessionId = req.headers['x-session-id'] || `s_${Date.now()}_${Math.random().toString(36).slice(2,8)}`; const userText = String(req.body.text || '').trim(); if (!userText) return res.status(400).json({ error: 'text is required' }); // マスキングして送信(ポリシーに応じて除去も検討) const safeText = maskPII(userText); // 例: Responses エンドポイントを想定 const payload = { model: 'dify/gpt-example', // 実装時は公式のモデル名を利用 input: safeText, // conversation metadata を含める場合はここに追加 }; const apiResp = await requestWithBackoff('/responses', payload, 3); const body = apiResp.data; // レスポンスの取り出し(APIの実際のスキーマに合わせて調整) const reply = (body?.outputs?.[0]?.content?.[0]?.text) || (body?.outputs?.[0]?.text) || '応答が得られませんでした'; res.json({ sessionId, reply }); } catch (err) { console.error('Error /message:', err?.response?.data || err.message); res.status(500).json({ error: 'internal_error' }); } }); app.listen(3000, () => console.log('listening on 3000')); |
上記は同期的な呼び出し例です。レスポンスの構造は必ず公式APIのレスポンス例に合わせてパースしてください(公式ドキュメント参照)。
Python(FastAPI + httpx) 完全な同期呼び出し例
FastAPIで同様の処理を実装した例です。期待される出力はNode.js例と同じくセッションID付き応答です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# app.py import os import uuid from fastapi import FastAPI, Request, HTTPException import httpx import re from pydantic import BaseModel DIFY_API_BASE = os.environ.get("DIFY_API_BASE", "https://api.dify.ai/v1") DIFY_API_KEY = os.environ.get("DIFY_API_KEY") if not DIFY_API_KEY: raise RuntimeError("DIFY_API_KEY is required") app = FastAPI() def mask_pii(text: str) -> str: text = re.sub(r'[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}', '[email_removed]', text) text = re.sub(r'\b\d{13,19}\b', '[number_removed]', text) text = re.sub(r'\b\d{2,4}[-\s]?\d{2,4}[-\s]?\d{3,4}\b', '[phone_removed]', text) return text class MessageIn(BaseModel): text: str @app.post("/message") async def message(msg: MessageIn, request: Request): user_text = msg.text.strip() if not user_text: raise HTTPException(status_code=400, detail="text is required") session_id = request.headers.get("x-session-id", str(uuid.uuid4())) safe_text = mask_pii(user_text) payload = {"model": "dify/gpt-example", "input": safe_text} async with httpx.AsyncClient(timeout=10.0) as client: try: r = await client.post(f"{DIFY_API_BASE}/responses", json=payload, headers={"Authorization": f"Bearer {DIFY_API_KEY}"}) r.raise_for_status() except httpx.HTTPError as e: raise HTTPException(status_code=502, detail=str(e)) data = r.json() reply = data.get("outputs", [{}])[0].get("content", [{}])[0].get("text", "応答が得られませんでした") return {"sessionId": session_id, "reply": reply} |
署名検証(チャネル受信の例:LINE)
チャネルからのWebhookは必ず署名検証を行ってください。LINEの検証例(Node.js):
|
1 2 3 4 5 6 7 |
import crypto from 'crypto'; function verifyLineSignature(body, signature, channelSecret) { const hash = crypto.createHmac('sha256', channelSecret).update(body).digest('base64'); return hash === signature; } |
Slackの署名検証(署名とタイムスタンプを利用)や、プロバイダごとの仕様に従ってください。DifyがWebhook署名方式を提供している場合は公式ドキュメントに従って検証を組み込んでください。
エラー処理・タイムアウト・リトライのポイント
- HTTPクライアントのタイムアウトを明示する(例:10秒)
- 5xx系は再試行対象、4xxは再試行しないのが一般的
- 再試行は指数バックオフ+ジッターを使う
- 再試行回数は短め(2〜3回)に留め、全体のレスポンスタイムSLAsを守る
RAGと外部データ連携設計と実装
RAG(Retrieval-Augmented Generation)で期待する成果は「根拠付きの応答」を安定して返すことです。ここではドキュメント取り込み〜検索〜プロンプト結合までの実務的フローを示します。
ドキュメント取り込みと前処理
取り込み時点で品質とプライバシーを担保します。前処理は一貫したパイプラインで実施してください。
- 対応フォーマット:PDF / DOCX / HTML / CSV / スプレッドシート
- OCRが必要な場合は品質チェックを入れる(言語モデルのトークン化を想定)
- 正規化:改行・全角半角統一、不要文字除去
- チャンク分割:目安は500〜1,000トークン(言語とモデルにより調整)。前後 20〜30% のオーバーラップを推奨。
- メタデータ:source, title, page, section, upload_timestamp, version を付与する
- PIIは取り込み前に除去または匿名化する
ベクタDB連携と検索パイプライン設計
ベクタDBはマネージド(Pinecone等)かセルフホスト(Milvus等)を選びます。選定はレイテンシ、耐障害性、運用コストで判断してください。
設計の要点:
- 埋め込みは一元化してキャッシュする(再生成コストを削減)
- Upsertはインクリメンタルに行いバージョンを付与する
- パイプライン:retrieve → filter(メタデータ)→ rerank(BM25/LLM)→ context 組み立て
- コンテキスト組立ではトークン制限を常に考慮し、上位3〜5件+要約を使う運用が多いです
短い擬似コード(Python)
|
1 2 3 4 5 6 7 8 |
# pseudo query_embedding = embed_fn(query) results = vector_db.query(query_embedding, top_k=5, filter={"lang":"ja"}) texts = [r['metadata']['text'] for r in results] context = "\n\n".join(texts) prompt = system_prompt + "\n\nContext:\n" + context + "\n\nUser:\n" + user_query response = call_llm_api(prompt) |
運用上は検索結果に必ず「出典」を添えて返す仕様を組むと誤情報リスクを下げられます。
プロンプト設計とコンテキスト管理
- Systemプロンプトでボットの制約・禁止事項を明文化する
- 履歴は短期履歴(直近数往復)と長期要約に分ける
- 長い会話は要約APIで圧縮してトークンを節約する
- RAGの応答には参照箇所(ドキュメントID/ページ)と「信頼度」を添える
RAGの設計はアプリケーション要件に依存します。Dify側のRAG内蔵機能の詳細は公式ドキュメントで確認してください。
運用・セキュリティ・法務・監視
本章のアウトプットは「安全に稼働する本番環境」を整備することです。鍵管理、PII対策、監視、負荷試験、法務対応まで網羅します。
認証・権限・鍵管理
シークレットは必ずシークレットマネージャで管理し、ローテーションと監査を自動化します。
- AWS Secrets Manager / GCP Secret Manager / HashiCorp Vault を利用する
- 最小権限(RBAC)で開発・ステージング・本番を分離する
- CI/CDで秘密が露出しないようにテンプレート化(.env はリポジトリ外)
- 監査ログ:シークレットアクセスのログを保持して定期レビューする
PII検出・マスキング・ログ方針
ログにPIIを残さないことが最重要です。ログはJSON形式でフィールド単位に保持し、出力前にマスキング/ハッシュ化してください。
Node.jsでの簡易マスキング例(winstonなどに組み込み)
|
1 2 3 4 5 6 7 8 |
function maskLog(obj) { if (obj.email) obj.email = '[email_removed]'; if (obj.phone) obj.phone = '[phone_removed]'; if (obj.message) obj.message = maskPII(obj.message); return obj; } logger.info('user_event', maskLog(event)); |
ログ保存期間の推奨(例)
- 会話トランスクリプト(非PII化):30日~90日
- サポートチケットなど法的根拠が必要なデータ:1年~7年(法規に従う)
DLPを導入できる場合は取り込み時点でスキャンし、該当箇所は取り込み拒否・自動匿名化します。
チャネル連携と署名検証
チャネル例ごとの注意点を実装に落とします。
- Webウィジェット:フロントでAPIキーを持たせずバックエンド経由で呼ぶ。匿名セッションIDで管理する。
- LINE:Webhook受信時にX-Line-Signatureを検証する(上記のコード例参照)。
- Slack:X-Slack-Signature / X-Slack-Request-Timestamp で検証する。
- 有人切替:切替時に会話履歴と参照したナレッジを渡し、有人側のUIで確認できるようにする(履歴は匿名化・要約で軽量化)。
監視・モニタリング(Prometheus / Grafana)
PrometheusでAPI呼び出しメトリクスを収集し、Grafanaで可視化・アラート設定するのが標準的です。
Node.js用 prom-client の例(簡易)
|
1 2 3 4 5 6 7 8 9 10 11 |
import client from 'prom-client'; const requestHistogram = new client.Histogram({ name: 'dify_api_request_duration_seconds', help: 'Dify API request duration', buckets: [0.1,0.3,0.5,1,2,5] }); app.get('/metrics', async (req, res) => { res.set('Content-Type', client.register.contentType); res.end(await client.register.metrics()); }); |
Prometheusの簡易アラート例(YAML)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
groups: - name: dify.rules rules: - alert: HighDifyLatency expr: histogram_quantile(0.95, sum(rate(dify_api_request_duration_seconds_bucket[5m])) by (le)) > 1 for: 5m labels: { severity: 'page' } annotations: summary: "API p95 latency > 1s" - alert: HighDifyErrorRate expr: sum(rate(dify_api_errors_total[5m])) / sum(rate(dify_api_requests_total[5m])) > 0.05 for: 10m labels: { severity: 'page' } |
Grafanaでは p50/p95/p99、エラーレート、フォールバック率、有人転送率をダッシュボード化してください。
負荷試験(k6 の例)とSLA設定
負荷試験は段階的に行い、本番想定負荷の1.5〜2倍で短時間のスパイクを試験します。k6の簡単なスクリプト例:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import http from 'k6/http'; import { check, sleep } from 'k6'; export let options = { stages: [ { duration: '1m', target: 50 }, { duration: '3m', target: 200 }, { duration: '2m', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<1000'], }, }; export default function () { const res = http.post('https://your-backend.example.com/message', JSON.stringify({ text: 'テスト' }), { headers: { 'Content-Type': 'application/json' }}); check(res, { 'status 200': r => r.status === 200 }); sleep(1); } |
SLAは p95/p99 のしきい値と稼働率を定義し、アラートを設けて運用体制を整えてください。
リトライとタイムアウト設定(コード例)
- Timeoutは10秒前後を目安にし、モデル呼び出しが重い場合は非同期処理に切り替える
- 再試行は指数バックオフ+ジッターで実装(上のNode.js例参照)
法務・コンプライアンス(同意テンプレ・DPAチェック)
同意取得の文言例(簡潔・日本語)
当チャットは会話内容をサービス改善とサポートのために収集します。個人情報は最小限に留め、保存期間は30日間(特段の理由がある場合を除く)とします。詳細はプライバシーポリシーをご確認ください。
DPA(データ処理契約)確認ポイント(実務)
- データ処理の目的・範囲が明確か。
- 第三者提供の有無と転送先(国際転送含む)を確認。
- セキュリティ対策の水準(暗号化・アクセス管理・監査ログ)を確認。
- 侵害発生時の通知ルールと対応時間を明確化する。
- 保存期間と削除手順を定める。
YMYL領域(医療・金融等)対応例
- 医療系:必ず「医師に相談してください」といった明確な免責・エスカレーションを表示する。
- 金融系:具体的な判断を下す助言は避け、専門家への相談を促す。
- いずれもログと会話履歴の保管は最小限にし、監査可能な同意を取ること。
まとめ
DifyはノーコードとAPI連携を組み合わせて会話アプリを素早く作れる一方、運用面では認証・キー管理・署名検証・PII対策・監視・負荷試験・法務対応が不可欠です。まずはノーコードでプロトタイプを作り、公式APIの仕様に従ってミドル層で認証・タイムアウト・再試行・ログマスキングを実装し、Prometheus/Grafanaで可視化、DPAや同意テンプレを整備したうえで本番移行するフローを推奨します。公式のAPIリファレンスを必ず参照して実装を確定してください(公式ドキュメント参照: https://docs.dify.ai/ja-jp/guides/application-orchestrate/chatbot-application)。
参考・リンク(抜粋)
- Dify公式(チャットボットガイド): https://docs.dify.ai/ja-jp/guides/application-orchestrate/chatbot-application
- ノーコード/ハンズオン記事(外部参考): Zenn / note 等
(注)本文中のAPIパスやレスポンス構造は、実装時に公式APIリファレンスで最新仕様を確認のうえ調整してください。