チャットボットを構築してサイトに公開する
篠原 隆司
アフィリエイト広告を利用しています
このページの内容が役に立ったら X (旧twitter) でフォローして頂けると励みになります
挨拶や報告は無しで大丈夫です
ナレッジベースの準備が完了しました。この記事では、Dify でチャットフローアプリを作成し、ナレッジベースを接続してプロンプトを設定し、テスト画面で動作を確認して、サイトへの埋め込みコードを取得するまでの作業をまとめています。この記事の手順どおりに進めると、サイト上で動くチャットボットが完成します。
【広告】 XServer VPS には Dify のアプリイメージが用意されています。
VPS上に独立した Claude Code 環境を簡単に構築することができます。
VPSでいろいろ試すなら『XServer VPS』
Chatflowアプリを作成する
最初から作成する
Dify のスタジオ画面で「最初から作成」を選択します。アプリタイプは「チャットフロー」を選びます。チャットフローは、メモリ機能(会話の履歴を保持)とチャットボットインターフェースを備えたワークフローです。
| 項目 | 設定値 |
|---|---|
| アプリタイプ | チャットフロー |
| アプリ名 | (サイトに合わせた名前) |
| 説明 | (任意) |
作成すると、ワークフロービルダーが開きます。初期状態では「ユーザー入力(START)」→「LLM」→「回答(ANSWER)」の3ノードが配置されています。
ナレッジベースを接続する
検索パラメータの設定
「ユーザー入力」と「LLM」の間に「知識検索」ノードを追加し、前回の記事で作成したナレッジベースを紐づけます。完成後のワークフローは以下の4ノード構成になります。
知識検索ノードの設定項目は以下のとおりです。
| 項目 | 設定値 | 説明 |
|---|---|---|
| クエリテキスト | sys.query | ユーザーの質問文をそのまま検索に使う |
| ナレッジベース | (作成したナレッジベースを選択) | 前回アップロードした FAQ データ |
| 検索設定 | ベクトル検索 | 意味的に近いチャンクを返す |
| メタデータフィルタ | 無効 | 今回は使用しない |
LLM ノードでは、コンテキストの「変数値を設定」から知識検索ノードの出力(result)を選択します。これでナレッジベースの検索結果が LLM に渡される状態になります。
LLM のモデルは gpt-5.4-nano を選択します。エイリアス版(gpt-5.4-nano)とスナップショット版(gpt-5.4-nano-2026-03-17)がありますが、エイリアス版を選んでおけば OpenAI がモデルを更新したときに自動で最新版に切り替わります。
プロンプト(システム指示文)を設計する
役割と応答ルールを定義する
LLM ノードの SYSTEM プロンプトに、チャットボットの役割と応答ルールを定義します。ポイントは3つです。
- 何のアシスタントか — 対象サイト・コンテンツを明記する
- コンテキストの変数を挿入する — ナレッジベースの検索結果をプロンプト内に埋め込む
- 答えられない場合のルール — コンテキストに情報がないときの応答を指定する
特に重要なのが コンテキスト変数の挿入 です。知識検索ノードの出力を LLM のコンテキストに紐づけただけでは不十分で、SYSTEM プロンプトの中にコンテキスト変数を明示的に記述する必要があります。これを忘れると、ナレッジベースの情報が LLM にまったく渡らず、すべての質問に「お答えできません」と回答してしまいます。
プロンプトの例を示します。
あなたは(サイト名)に関する質問に回答するアシスタントです。
以下の情報をもとに回答してください。
{{#context#}}
ルール:
- 上記の情報のみをもとに回答してください
- 情報がない質問には「この質問にはお答えできません」と回答してください
- 回答は簡潔に、日本語で行ってください
{{#context#}} の部分が、知識検索ノードの検索結果に自動的に置き換わります。プロンプト入力欄で { を打つと変数の候補が表示されるので、そこから選択できます。
回答のトーンを指定する
プロンプトに回答のトーンを追加で指定できます。EC サイトであれば「丁寧な敬語で」、社内ツールであれば「簡潔に」など、用途に応じて調整します。プロンプトのルール部分に1行追加するだけです。
回答できない質問の対処フローを設計する
メールフォームへの誘導など
チャットボットが答えられない質問への対処は、プロンプトの中で制御しています。コンテキストに情報がない場合は「お答えできません」と返す指示をプロンプトに含めました。
本番運用では、この拒否メッセージにお問い合わせフォームへのリンクを含めると、お客様が次のアクションを取りやすくなります。例えば「この質問にはお答えできません。お問い合わせフォームからご連絡ください」のような文面です。これもプロンプトのルールに1行追加するだけで対応できます。
テスト画面で動作を確認する
想定どおりの回答が返るか
ワークフロービルダーの右上にある「プレビュー」ボタンからテストチャットを開きます。FAQ に含まれる質問を、言い回しを変えていくつか投げてみます。
FAQ の情報をもとに正確な回答が返ってくれば、ナレッジベースの接続・プロンプト・コンテキスト変数がすべて正しく設定されている証拠です。
想定外の質問への挙動
FAQ に含まれない質問も投げてみます。「明日の天気は?」のようなまったく関係ない質問に対して、プロンプトで指定したとおり「お答えできません」と返ることを確認します。
もし FAQ にある内容なのに「お答えできません」と返ってしまう場合は、以下を確認してください。
- SYSTEM プロンプトにコンテキスト変数が入っているか —
{{#context#}}がないと FAQ が LLM に渡らない - LLM のコンテキストに知識検索の result が紐づいているか — コンテキストの「変数値を設定」を確認
- ナレッジベースの検索テストで該当の FAQ がヒットするか — 前回の記事の検索テストに戻って確認
サイトに埋め込む
埋め込みコードを取得する
テストで問題がなければ、「公開する」ボタンをクリックします。公開後、「サイトに埋め込む」を選択すると、3種類の埋め込み方法が表示されます。
EC サイトでは「チャットバブル」が一般的です。全ページの右下にボタンが表示され、クリックするとチャット画面が開きます。
HTMLテンプレートに設置する
表示された埋め込みコード(iframe タグまたは script タグ)をコピーして、サイトの HTML テンプレートに貼り付けます。WordPress であればテーマの footer.php や、ショートコードで特定のページにだけ設置するなど、設置方法はサイトの構成に合わせて選びます。
チャットバブル方式で埋め込む場合、発行されるコードは以下のような構成です。
<script>
window.difyChatbotConfig = {
token: 'xxxxxxxxxxxxxxxxxxxx',
inputs: {},
systemVariables: {},
userVariables: {},
}
</script>
<script
src="https://udify.app/embed.min.js"
id="xxxxxxxxxxxxxxxxxxxx"
defer
>
</script>
<style>
#dify-chatbot-bubble-button {
background-color: #1C64F2 !important;
}
#dify-chatbot-bubble-window {
position: fixed !important;
bottom: 50px;
width: 500px !important;
height: 700px !important;
max-width: 90dvw !important;
max-height: 90dvh !important;
}
</style>
token と id には、Dify の管理画面で発行された値が入ります。<style> 部分はチャットウィンドウの見た目を調整する CSS です。バブルボタンの色、ウィンドウのサイズや位置をサイトに合わせて変更します。
CSS で調整しているポイントは以下のとおりです。
| セレクタ | プロパティ | 説明 |
|---|---|---|
| #dify-chatbot-bubble-button | background-color | 右下のバブルボタンの色 |
| #dify-chatbot-bubble-window | bottom | ウィンドウの下端位置 |
| #dify-chatbot-bubble-window | width / height | ウィンドウのサイズ |
| #dify-chatbot-bubble-window | max-width / max-height | スマホ等の小画面での上限 |
iPhone Safari のズーム問題と回避策
iPhone Safari では、入力欄の font-size が 16px 未満の場合、フォーカス時にページが自動ズームされます。Dify のチャットウィンドウは cross-origin の iframe で描画されているため、外側の CSS で font-size を変更できません。
回避策として、チャットウィンドウが開いている間だけ viewport の maximum-scale=1 を設定し、閉じたら元に戻す JavaScript を追加します。これにより、チャット操作中のみズームが抑制され、通常のページ閲覧にはまったく影響しません。
なお、根本的に解決するなら、Dify の API と公式フロントエンドテンプレート(MIT ライセンス、Next.js ベース)を使って自前のチャット UI を構築する方法もあります。iframe を使わないため CSS を自由に制御できます。詳しくはシリーズ最終回で紹介しています。
チャット外クリックで閉じる
Dify のデフォルトでは、チャットウィンドウを閉じるにはバブルボタン(×)をクリックする必要があります。チャットウィンドウの外側をクリックしても閉じないため、ユーザー体験としてはやや不便です。
これも JavaScript で対処します。ドキュメント全体のクリックを監視し、クリックがバブルボタンにもチャットウィンドウにも属さなければ、ボタンをクリックして閉じる仕組みです。
公開後の初期確認
実際のサイトでの見え方
埋め込み後、実際のサイトでチャットボットの表示を確認します。Dify のデフォルトスタイルがそのまま適用されるため、サイトのデザインと合わない場合があります。見た目の調整は CSS やプロンプトの修正で対応します。
また、サイトの CSP(Content Security Policy)設定によっては、Dify の埋め込みスクリプトがブロックされることがあります。ブラウザの開発者ツール(コンソール)でエラーが出ていないか確認してください。
Dify管理画面でのカスタマイズ
見た目の調整は CSS だけでなく、Dify の管理画面からも行えます。公開したアプリの「カスタマイズ」画面を開くと、以下の項目を設定できます。
| 項目 | 説明 |
|---|---|
| Web App アイコン | チャットウィンドウ内のアイコンをカスタム画像に差し替えられる |
| 言語 | チャット UI の言語を設定(日本語対応) |
| テーマカラー | チャットウィンドウのアクセントカラーを変更 |
| ボットの名前 | チャット画面に表示されるアシスタントの名前 |
| 説明文 | チャット開始時に表示されるガイダンスメッセージ |
CSS によるオーバーライドと Dify 管理画面のカスタマイズは、それぞれ役割が異なります。
| 調整方法 | 向いている用途 |
|---|---|
| Dify 管理画面 | アイコン、名前、言語、テーマカラーなどの基本設定 |
| CSS オーバーライド | バブルボタンの差し替え、ウィンドウのサイズ・位置など細かいレイアウト調整 |
まず Dify の管理画面で基本的な見た目を整えてから、CSS で細かい調整を加えるのが効率的です。
デフォルトのバブルボタンは青い丸にチャットアイコンですが、オペレーターのイラストに差し替えると、訪問者に「ここに質問できる」と直感的に伝わりやすくなります。
Dify 管理画面での基本設定と、CSS によるアイコン差し替え、JavaScript による iPhone ズーム対策・チャット外クリック閉じを合わせた、カスタマイズ後のコードの例を示します。
<style>
/* --- 閉じている状態(カスタムアイコン表示) --- */
#dify-chatbot-bubble-button {
background-color: transparent !important;
background-image: url('data:image/svg+xml;base64,...') !important;
background-size: contain !important;
background-repeat: no-repeat !important;
background-position: center !important;
width: 100px !important;
height: 112px !important;
border-radius: 0 !important;
box-shadow: none !important;
}
#dify-chatbot-bubble-button #openIcon {
display: none !important;
}
/* --- 開いている状態(デフォルトの閉じるボタンに戻す) --- */
#dify-chatbot-bubble-button:has(#openIcon[style*="none"]) {
background-image: none !important;
background-color: #1C64F2 !important;
width: 50px !important;
height: 50px !important;
border-radius: 25px !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
}
/* --- チャットウィンドウ --- */
#dify-chatbot-bubble-window {
position: fixed !important;
bottom: 50px;
width: 500px !important;
height: 700px !important;
max-width: 90dvw !important;
max-height: 90dvh !important;
}
</style>
<script>
/**
* Dify チャットボット — カスタマイズスクリプト
*
* 1. iPhone ズーム抑制
* チャットが開いている間だけ viewport の maximum-scale=1 を設定
*
* 2. チャット外クリックで閉じる
* ウィンドウとボタン以外をクリックしたら閉じる
*/
(function() {
'use strict';
const meta = document.querySelector('meta[name="viewport"]');
const original = (meta) ? meta.getAttribute('content') : null;
/** @returns {boolean} */
const isChatOpen = function(btn) {
const openIcon = btn.querySelector('#openIcon');
if (!openIcon) {
return false;
}
return openIcon.style.display === 'none';
};
const observer = new MutationObserver(function() {
if (!meta) { return; }
if (!original) { return; }
const btn = document.getElementById('dify-chatbot-bubble-button');
if (!btn) { return; }
if (isChatOpen(btn)) {
meta.setAttribute('content', original + ', maximum-scale=1');
} else {
meta.setAttribute('content', original);
}
});
/** @param {MouseEvent} e */
const onDocumentClick = function(e) {
const btn = document.getElementById('dify-chatbot-bubble-button');
if (!btn) { return; }
if (!isChatOpen(btn)) { return; }
const win = document.getElementById('dify-chatbot-bubble-window');
if (!win) { return; }
if (btn.contains(e.target)) { return; }
if (win.contains(e.target)) { return; }
btn.click();
};
const wait = setInterval(function() {
const btn = document.getElementById('dify-chatbot-bubble-button');
if (!btn) { return; }
clearInterval(wait);
observer.observe(btn, {
subtree: true,
attributes: true,
attributeFilter: ['style'],
});
document.addEventListener('click', onDocumentClick);
}, 500);
})();
</script>
background-image の url('data:image/svg+xml;base64,...') には、オペレーターの SVG を Base64 エンコードした文字列が入ります。:has() セレクタにより、チャットウィンドウが開いている間はカスタムアイコンが消え、デフォルトの閉じるボタン(×)に自動で切り替わります。
JavaScript は2つの機能を担当しています。MutationObserver で #openIcon の表示状態を監視し、チャットが開いたら viewport に maximum-scale=1 を追加、閉じたら元に戻します。また、ドキュメント全体の click を監視し、チャットウィンドウとボタンの外側がクリックされたらチャットを閉じます。
テスト質問で最終チェック
実際のサイト上で、Dify のプレビューで試したのと同じ質問を投げてみます。プレビューと同じ回答が返ってくれば、チャットボットの公開は完了です。
次の記事では、公開後の会話ログを確認しながら、回答精度の改善に取り組みます。
【広告】 XServer VPS には Dify のアプリイメージが用意されています。
VPS上に独立した Claude Code 環境を簡単に構築することができます。
VPSでいろいろ試すなら『XServer VPS』
このページの内容が役に立ったら X (旧twitter) でフォローして頂けると励みになります
本ページの内容は可能な限り正確な情報を提供するよう努めていますが、内容の正確性・最新性・安全性を保証するものではありません。本情報を利用して生じたいかなる損害についても、当方は一切の責任を負いません。実施にあたっては必ずご自身の判断と自己責任にてお願いいたします。