システム全体構成
このメール一括送信システムは、3つの主要サービスで構成されています。それぞれの役割と連携を理解することで、トラブルシューティングや機能拡張が容易になります。
╔════════════════════════════════════════════════════════════════════════════════╗
║ メール一括送信システム アーキテクチャ ║
╠════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ ┌─────────────────────────────────────────────────────────────────────────┐ ║
║ │ ブラウザ(フロントエンド) │ ║
║ │ ┌─────────────────────────────────────────────────────────────────┐ │ ║
║ │ │ index.html (SPA) │ │ ║
║ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ ║
║ │ │ │ CSVロード │ │メール作成 │ │ 送信実行 │ │ 停止リスト管理 │ │ │ ║
║ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │ ║
║ │ └─────────────────────────────────────────────────────────────────┘ │ ║
║ └─────────────────────────────────────────────────────────────────────────┘ ║
║ │ │ │ ║
║ │ ①認証 │ ②データ保存 │ ③AI生成 ║
║ ▼ ▼ ▼ ║
║ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ║
║ │ │ │ │ │ │ ║
║ │ Azure AD │ │ Firebase │ │ Dify API │ ║
║ │ (認証基盤) │ │ (データベース) │ │ (AI生成) │ ║
║ │ │ │ │ │ │ ║
║ └──────┬───────┘ └──────┬───────┘ └──────────────┘ ║
║ │ │ ║
║ │ ④アクセストークン │ ║
║ ▼ │ ║
║ ┌──────────────┐ │ ║
║ │ │ │ ║
║ │ Microsoft │◄──────────┘ ⑤履歴保存 ║
║ │ Graph API │ ║
║ │ │ ║
║ └──────┬───────┘ ║
║ │ ║
║ │ ⑥メール送信 ║
║ ▼ ║
║ ┌──────────────┐ ║
║ │ │ ║
║ │ Office 365 │ ─────────► 📧 受信者へメール配信 ║
║ │ Exchange │ ║
║ │ │ ║
║ └──────────────┘ ║
║ ║
╚════════════════════════════════════════════════════════════════════════════════╝
Azure AD(Microsoft Entra ID)の役割
| 機能 | 説明 |
|---|---|
| ユーザー認証 | Microsoft アカウントでのシングルサインオン(SSO)を提供。ポップアップまたはリダイレクト方式でログイン。 |
| アクセストークン発行 | Graph API を呼び出すためのOAuth 2.0 アクセストークンを発行。有効期限は約1時間。 |
| 権限管理 | メール送信(Mail.Send)、メール読み取り(Mail.Read)などのスコープを管理。 |
| セキュリティ | 多要素認証(MFA)、条件付きアクセス、監査ログなどの企業セキュリティ機能。 |
【認証フロー】
ユーザー ブラウザ Azure AD Graph API
│ │ │ │
│ 1.ログイン │ │ │
│ ──────────────►│ │ │
│ │ 2.認証リクエスト │ │
│ │ ────────────────►│ │
│ │ │ │
│ │◄────────────────│ │
│ │ 3.認証画面表示 │ │
│ 4.ID/PW入力 │ │ │
│ ──────────────►│ ────────────────►│ │
│ │ │ 5.検証 │
│ │ 6.トークン返却 │ │
│ │◄────────────────│ │
│ │ │ │
│ │ 7.API呼び出し(トークン付き) │
│ │ ────────────────────────────────►│
│ │ │ │
│ │◄────────────────────────────────│
│ │ 8.メール送信完了 │
│◄──────────────│ │ │
Firebase / Firestore の役割
| 機能 | 説明 |
|---|---|
| 停止リスト保存 | 配信停止されたメールアドレスをクラウドに保存。チーム間で共有され、重複送信を防止。 |
| 返信監視データ | バウンス(配信失敗)や配信停止希望の検出結果を保存。承認/却下のワークフロー管理。 |
| 送信履歴 | 各送信バッチの詳細(送信日時、件数、成功/失敗)を記録。監査やレポートに使用。 |
| 設定共有 | Dify API Keyなどの設定をチーム間で共有。個別設定の手間を削減。 |
【データ構造】
📁 Firestore Database
│
├── 📂 stopList/ ← 配信停止リスト
│ ├── 📄 {auto-id}
│ │ ├── email: "user@example.com"
│ │ ├── reason: "バウンス検出"
│ │ ├── source: "返信監視"
│ │ └── addedAt: Timestamp
│ └── ...
│
├── 📂 replyMonitoring/ ← 検出された異常メール
│ ├── 📄 {auto-id}
│ │ ├── email: "bounced@example.com"
│ │ ├── type: "bounce"
│ │ ├── status: "pending" ← pending → approved/rejected
│ │ ├── reason: "配信不能"
│ │ └── detectedAt: Timestamp
│ └── ...
│
├── 📂 sendHistory/ ← 送信履歴
│ ├── 📄 {batch-id}
│ │ ├── subject: "〇〇のご案内"
│ │ ├── totalCount: 150
│ │ ├── successCount: 148
│ │ ├── errorCount: 2
│ │ └── sentAt: Timestamp
│ └── ...
│
└── 📂 settings/ ← 共有設定
└── 📄 difyApiKey
├── apiKey: "app-xxx..."
└── updatedAt: Timestamp
Dify API の役割
| 機能 | 説明 |
|---|---|
| AI文章生成 | GPT-4やClaude等のLLMを使用して、メール本文を自動生成。プロンプトエンジニアリングで品質管理。 |
| トーン調整 | ビジネスフォーマル、カジュアル、多言語(日/英/中)など、状況に応じた文体で生成。 |
| ストリーミング応答 | 生成中のテキストをリアルタイムで表示。ユーザー体験を向上。 |
| プレースホルダー埋め込み | {名前}、{会社名}などの変数を文章内に配置。差し込み印刷のパーソナライズに対応。 |
【AI生成フロー】
ユーザー アプリ プロキシ Dify API LLM
│ │ │ │ │
│ 1.生成リクエスト │ │ │
│ ──────────────►│ │ │ │
│ │ │ │ │
│ │ 2.API呼び出し │ │ │
│ │ ──────────────►│ │ │
│ │ (CORS回避) │ 3.転送 │ │
│ │ │ ───────────────►│ │
│ │ │ │ 4.LLM呼び出し │
│ │ │ │ ─────────────►│
│ │ │ │ │
│ │ │ │◄─────────────│
│ │ │◄───────────────│ 5.生成結果 │
│ │◄──────────────│ 6.ストリーミング │
│◄──────────────│ 7.リアルタイム表示 │ │
│ │ │ │ │
│ 8.確認・編集 │ │ │ │
│ ──────────────►│ │ │ │
メール送信フローチャート
メール一括送信の完全なワークフローです。各ステップでの処理と判断を示します。
【メール一括送信 フローチャート】
┌─────────────┐
│ 開始 │
└──────┬──────┘
│
▼
┌─────────────────────────────┐
│ Step 1: 送信リストCSV読込 │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ メール列を選択 │
│ (email, メールアドレス等) │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ 停止リスト読込 │
│ (CSV + Firestore) │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ Step 2: メール内容作成 │
└──────────────┬──────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 手動作成 │ │ AI生成(Dify) │ │ テンプレート │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└───────────────────┼───────────────────┘
│
▼
┌─────────────────────────────┐
│ 配信停止案内を追加 │
│ (オプション) │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ Step 3: 送信確認 │
│ ・宛先一覧表示 │
│ ・停止リスト該当者を強調 │
└──────────────┬──────────────┘
│
▼
╔═════════════╗
║ 送信開始? ║
╚══════┬══════╝
│
┌──────────────────┼──────────────────┐
│ Yes │ │ No
▼ │ ▼
┌─────────────────┐ │ ┌─────────────────┐
│ 受信者ループ │ │ │ 編集に戻る │
└────────┬────────┘ │ └─────────────────┘
│ │
▼ │
╔═════════════════╗ │
║ 停止リストに ║ │
║ 該当するか? ║ │
╚════════┬════════╝ │
│ │
┌──────────────┼──────────────┐ │
│ Yes │ │ No │
▼ │ ▼ │
┌─────────────────┐ │ ┌─────────────────┐
│ スキップ │ │ │ プレースホルダー │
│ (ログに記録) │ │ │ 置換 │
└─────────────────┘ │ └────────┬────────┘
│ │
│ ▼
│ ┌─────────────────┐
│ │ Graph API送信 │
│ │ POST /sendMail │
│ └────────┬────────┘
│ │
│ ▼
│ ╔═════════════════╗
│ ║ 送信成功? ║
│ ╚════════┬════════╝
│ │
│ ┌─────────┼─────────┐
│ │ Yes │ │ No
│ ▼ │ ▼
│ ┌───────┐ │ ┌──────────┐
│ │成功+1 │ │ │エラー+1 │
│ └───────┘ │ │リトライ? │
│ │ └──────────┘
│ │
└────────────┼────────────────────────────────┐
│ │
▼ │
╔═════════════════╗ │
║ 次の受信者 ║ │
║ あり? ║ │
╚════════┬════════╝ │
│ │
┌───────────────┼───────────────┐ │
│ Yes │ │ No │
│ │ │ │
│ ┌────────────┘ ▼ │
│ │ ┌─────────────────┐ │
│ │ │ 送信結果保存 │ │
│ │ │ (Firestore) │ │
│ │ └────────┬────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────────┐ │
│ │ │ 完了レポート表示 │ │
│ │ │ ・成功: N件 │ │
│ │ │ ・失敗: N件 │ │
│ │ │ ・スキップ: N件 │ │
│ │ └────────┬────────┘ │
│ │ │ │
│ │ 500ms待機 ▼ │
│ │ (レート制限) ┌─────────────────┐ │
│ └────────────────►│ 終了 │ │
│ └─────────────────┘ │
│ │
└────────────────────────────────────────────────┘
データフロー概要
① 認証フロー(Azure AD)
ユーザー → ブラウザ → Azure AD → アクセストークン取得 → Graph API利用可能に
② データ保存フロー(Firebase)
操作(停止追加/送信完了) → Firestore に保存 → チーム間で共有
③ AI生成フロー(Dify)
生成リクエスト → プロキシ(CORS回避) → Dify API → LLM → ストリーミング応答
④ メール送信フロー(Graph API)
受信者リスト → 停止リストチェック → プレースホルダー置換 → Graph API送信 → 履歴保存
セットアップ優先順位
| 優先度 | システム | 理由 |
|---|---|---|
| 1. 必須 | Azure AD | メール送信の認証基盤。これがないとログインできない。 |
| 2. 推奨 | Firebase | 停止リスト共有・履歴保存。なくても動作するが、データが失われる。 |
| 3. オプション | Dify API | AI生成機能。手動でメール作成する場合は不要。 |
Azure AD (Microsoft Entra ID) 概要
Microsoft Graph API を使用してメール送信を行うため、Azure AD にアプリケーションを登録します。
┌─────────────────────────────────────────────────────────────────┐
│ メール送信フロー │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ブラウザ Azure AD Microsoft 365 │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ │ 1. ログイン │ │ │ │ │
│ │ アプリ │ ──────────► │ 認証 │ │ メール │ │
│ │ │ │ │ │ │ │
│ │ │ ◄────────── │ │ │ │ │
│ │ │ 2. トークン │ │ │ │ │
│ │ │ └────────┘ │ │ │
│ │ │ │ │ │
│ │ │ 3. メール送信 (Graph API) │ │ │
│ │ │ ─────────────────────────────────► │ │ │
│ └────────┘ └────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
| 必要なスコープ | 用途 | 必須 |
|---|---|---|
User.Read |
ログインユーザー情報の取得 | 必須 |
Mail.Send |
自分のアカウントからメール送信 | 必須 |
Mail.Read |
受信メールの読み取り(バウンス検出) | 任意 |
Mail.Send.Shared |
共有メールボックスからの送信 | 任意 |
Step 1: アプリケーション登録
1 Azure Portal にアクセス
- Azure Portal にログインします
- 検索バーで「アプリの登録」と入力して開きます
- 「新規登録」をクリックします
2 アプリ情報を入力
- 名前:
BulkEmailApp(任意の名前) - サポートされているアカウントの種類:
→ 組織内のみ: 「この組織ディレクトリのみに含まれるアカウント」
→ 個人も含む: 「任意の組織ディレクトリ + 個人のMicrosoftアカウント」 - リダイレクト URI: 一旦スキップ(後で設定)
- 「登録」をクリック
3 クライアントIDを取得
- 登録完了後、「概要」ページを開きます
- アプリケーション (クライアント) ID をコピーして保存
💡 ヒント
クライアントIDは
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 形式のGUIDです。
これをアプリの設定画面に入力します。
Step 2: API アクセス許可の設定
1 APIのアクセス許可を開く
- 左メニューから「APIのアクセス許可」をクリック
- 「アクセス許可の追加」をクリック
- 「Microsoft Graph」を選択
- 「委任されたアクセス許可」を選択
2 必要な権限を追加
- 検索で
Mail.Sendを探してチェック - 検索で
Mail.Readを探してチェック(返信監視用) - 検索で
User.Readを探してチェック - 「アクセス許可の追加」をクリック
3 管理者の同意を付与
- 「〇〇に管理者の同意を与えます」をクリック
- 確認ダイアログで「はい」をクリック
- ステータスが緑のチェックマークになることを確認
⚠️ 注意
管理者の同意がないと、一般ユーザーはログインできません。
組織の管理者に依頼してください。
Step 3: リダイレクト URI の設定
1 認証設定を開く
- 左メニューから「認証」をクリック
- 「プラットフォームの追加」をクリック
- 「シングルページ アプリケーション」を選択
2 リダイレクト URI を入力
アプリを開くURLを入力します(複数追加可能):
# ローカル開発用
http://localhost:8000
http://127.0.0.1:8000
http://localhost:5500
# 本番環境(SharePoint など)
https://your-domain.sharepoint.com/sites/your-site
- 「構成」をクリックして保存
ℹ️ SPAモードについて
シングルページアプリケーション (SPA) を選択すると、PKCE認証フローが使用されます。
これはクライアントシークレット不要で、よりセキュアです。
Step 4: 動作確認
チェックリスト
- ✅ クライアントIDをコピーした
- ✅ Mail.Send, Mail.Read, User.Read の権限を追加した
- ✅ 管理者の同意を付与した(緑チェック)
- ✅ リダイレクトURIを設定した
アプリでのテスト
- index.html を開く
- 「クライアントID」に取得したIDを入力
- 「Microsoft でログイン」をクリック
- ポップアップで権限を許可
- ログイン成功後、ユーザー名が表示されることを確認
🚨 よくあるエラー
- AADSTS50011: リダイレクトURIが一致しません → 正確なURLを登録
- AADSTS65001: 管理者の同意が必要 → 管理者に依頼
- AADSTS70011: スコープが無効 → 権限設定を確認
Firebase 概要
Firebaseを使用して、停止リスト・送信履歴・API Key などのデータを保存します。
┌─────────────────────────────────────────────────────────────────┐
│ データフロー │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ブラウザ Firestore Cloud Functions │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ │ 読み書き │ │ │ │ │
│ │ アプリ │ ──────────► │ データ │ │ API │ │
│ │ │ │ │ │ Proxy │ │
│ │ │ ◄────────── │ ・停止 │ │ │ │
│ │ │ リアルタイム │ リスト │ │ │ │
│ │ │ │ ・履歴 │ └───┬────┘ │
│ │ │ │ ・設定 │ │ │
│ │ │ └────────┘ │ │
│ │ │ │ │
│ │ │ Dify API呼び出し(CORS回避) ▼ │
│ │ │ ─────────────────────────────────► [外部API] │
│ └────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
| コレクション | 用途 |
|---|---|
stopList |
配信停止メールアドレスの管理 |
replyMonitoring |
バウンス・配信停止検出の管理 |
sendHistory |
送信履歴の保存 |
settings |
API Keyなどの共有設定 |
Step 1: Firebase プロジェクト作成
1 Firebase Console にアクセス
- Firebase Console にGoogleアカウントでログイン
- 「プロジェクトを追加」をクリック
- プロジェクト名を入力(例:
bulk-email-system) - Google Analytics は任意(オフでOK)
- 「プロジェクトを作成」をクリック
2 Web アプリを追加
- プロジェクト概要で「</>」(Webアイコン)をクリック
- アプリのニックネームを入力(例:
bulk-email-web) - 「Firebase Hosting」は任意
- 「アプリを登録」をクリック
3 設定情報を取得
表示される設定をコピーして保存します:
const firebaseConfig = {
apiKey: "AIzaSy...",
authDomain: "your-project.firebaseapp.com",
projectId: "your-project-id",
storageBucket: "your-project.appspot.com",
messagingSenderId: "123456789",
appId: "1:123456789:web:abc123"
};
💡 ヒント
この設定はいつでも「プロジェクト設定」→「全般」→「マイアプリ」から確認できます。
Step 2: Firestore Database 設定
1 Firestore を有効化
- 左メニューから「Firestore Database」をクリック
- 「データベースを作成」をクリック
- モード: 「本番モード」を選択(後でルール設定)
- ロケーション:
asia-northeast1(東京)を選択 - 「有効にする」をクリック
2 コレクション構造
アプリが自動的に以下のコレクションを作成します:
📁 stopList/
└── {docId}
├── email: "user@example.com"
├── reason: "バウンス検出"
├── source: "返信監視"
└── addedAt: Timestamp
📁 replyMonitoring/
└── {docId}
├── email: "user@example.com"
├── type: "bounce" | "unsubscribe"
├── reason: "配信不能メール検出"
├── status: "pending" | "approved" | "rejected"
└── detectedAt: Timestamp
📁 settings/
└── difyApiKey
├── apiKey: "app-xxxx..."
├── updatedAt: Timestamp
└── updatedBy: "admin@example.com"
Step 3: セキュリティルール設定
1 ルールタブを開く
- Firestore Database ページで「ルール」タブをクリック
- 既存のルールを以下に置き換え
2 セキュリティルールを設定
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// 停止リスト - 全ユーザーが読み書き可能
match /stopList/{document=**} {
allow read, write: if true;
}
// 返信監視 - 全ユーザーが読み書き可能
match /replyMonitoring/{document=**} {
allow read, write: if true;
}
// 設定 - 全ユーザーが読み書き可能
match /settings/{document=**} {
allow read, write: if true;
}
// 送信履歴 - 全ユーザーが読み書き可能
match /sendHistory/{document=**} {
allow read, write: if true;
}
}
}
- 「公開」をクリックしてルールをデプロイ
⚠️ セキュリティについて
上記ルールは社内利用を想定した簡易設定です。
外部公開する場合は、認証チェックを追加してください。
より厳格なルール(参考)
// 認証済みユーザーのみアクセス可能
match /{document=**} {
allow read, write: if request.auth != null;
}
Step 4: Cloud Functions(オプション)
Dify APIなど外部APIをCORS回避で呼び出す場合に使用します。
1 Firebase CLI をインストール
# Node.js が必要
npm install -g firebase-tools
# ログイン
firebase login
# プロジェクトディレクトリで初期化
firebase init functions
2 プロキシ関数を作成
functions/index.js にプロキシ関数を追加:
const functions = require('firebase-functions');
exports.difyProxy = functions.https.onRequest(async (req, res) => {
// CORSヘッダー
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.set('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
res.status(204).send('');
return;
}
// 外部API呼び出し
const response = await fetch('https://api.dify.ai/...', {
method: 'POST',
headers: { 'Authorization': req.body.apiKey },
body: JSON.stringify(req.body)
});
res.json(await response.json());
});
3 デプロイ
firebase deploy --only functions
ℹ️ ローカル開発の場合
Cloud Functionsの代わりに
local_proxy.js を使用できます。
node local_proxy.js で起動します。
Dify AI 概要
Difyは、AIアプリケーション開発プラットフォームです。このシステムでは、メール本文のAI生成機能に使用します。
┌─────────────────────────────────────────────────────────────────┐
│ AI生成フロー │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ブラウザ プロキシ Dify API LLM │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ │ 1.要求 │ │ 2.転送 │ │ │ │ │
│ │ 生成UI │ ─────► │ CORS │ ─────► │ Dify │──►│ GPT │ │
│ │ │ │ 回避 │ │ │ │ Claude │ │
│ │ │ │ │ │ │ │ │ │
│ │ │◄─────│ │◄─────│ │◄──│ │ │
│ │ │ 4.表示 │ │ 3.返却 │ │ └────────┘ │
│ └────────┘ └────────┘ └────────┘ │
│ ストリーミング ローカル API Key │
│ リアルタイム表示 or Functions 認証 │
│ │
└─────────────────────────────────────────────────────────────────┘
| 機能 | 説明 |
|---|---|
| メール本文生成 | 目的・トーン・言語を指定してAIが適切な文章を生成 |
| ストリーミング応答 | 生成中の文章をリアルタイムで表示 |
| プレースホルダー対応 | 生成した文章に{名前}などの変数を埋め込み |
| HTML/テキスト切替 | リッチテキストまたはプレーンテキストで生成 |
Step 1: Dify アカウント作成
1 アカウント登録
- Difyの登録ページにアクセス(社内環境の場合は管理者に確認)
- 本システムでは
https://chatbot.aimeet.jpを使用 - メールアドレスとパスワードで登録
- メール認証を完了
ℹ️ 注意
Difyは複数のデプロイ方法があります:
- クラウド版: https://cloud.dify.ai(公式)
- セルフホスト版: 社内サーバーにデプロイ
- 本システム: https://chatbot.aimeet.jp(カスタム環境)
2 ワークスペース作成
- ログイン後、ワークスペースを作成(既存の場合はスキップ)
- ワークスペース名を入力(例:
Techsor Email System)
Step 2: アプリケーション作成
1 新規アプリを作成
- ダッシュボードで「アプリを作成」をクリック
- アプリタイプ: テキスト生成(Text Generator)を選択
- アプリ名:
Email Content Generator - アイコンと説明を任意で設定
2 プロンプトを設定
以下のプロンプトテンプレートを使用します:
あなたはプロフェッショナルなメール文章作成アシスタントです。
【入力情報】
- 用件: {{query}}
- 目的: {{purpose}}
- トーン: {{tone}}
- 言語: {{language}}
- 出力形式: {{input_format}}
- 追加要望: {{extra_requests}}
【出力形式】
- {{input_format}} が "html" の場合: HTML形式で出力(<p>, <br>, <strong> などを使用)
- {{input_format}} が "text" の場合: プレーンテキストで出力
【トーンの定義】
- very_polite: 非常に丁寧(拝啓・敬具、謹んで、等を使用)
- standard: ビジネス標準(です・ます調)
- casual: カジュアル(親しみやすい表現)
【言語】
- ja: 日本語
- en: 英語
- zh: 中国語
プレースホルダー({名前}、{会社名}など)が必要な場合は適切に配置してください。
追加要望がある場合は必ず反映してください。
それでは、上記の条件に従ってメール本文を生成してください。
件名や挨拶文、署名は含めず、本文のみを出力してください。
3 変数を設定
以下の変数をアプリに追加します:
| 変数名 | タイプ | 必須 | 説明 |
|---|---|---|---|
query |
Text | ✅ | メールの主な内容 |
purpose |
Text | メールの目的 | |
tone |
Select | ✅ | very_polite / standard / casual |
language |
Select | ✅ | ja / en / zh |
input_format |
Select | ✅ | html / text |
extra_requests |
Text | 追加の要望 |
4 モデルを選択
- 推奨モデル: GPT-4 または Claude 3.5 Sonnet
- 温度(Temperature):
0.7(創造性とバランス) - 最大トークン数:
2000
5 公開とAPI Key取得
- 右上の「公開」ボタンをクリック
- 「API」タブを開く
- 「API Keyを作成」をクリック
app-xxxxxxxxxxxxxx形式のキーをコピー
⚠️ セキュリティ
API Keyは秘密情報です。GitHubにコミットしないでください。
Step 3: システム連携
1 API Key をアプリに登録
- メール送信アプリを開く
- 「AI生成」ボタンをクリック
- 初回は「API Key設定」ダイアログが表示される
- 取得したAPI Keyを入力して「保存」
💡 チーム共有
API KeyはFirestoreに保存され、チーム全体で共有されます。
一度設定すれば、他のメンバーは設定不要です。
2 API エンドポイント確認
システムで使用するエンドポイント:
# 本番環境
POST https://chatbot.aimeet.jp/v1/completion-messages
# ヘッダー
Authorization: Bearer {API_KEY}
Content-Type: application/json
3 リクエスト形式
{
"inputs": {
"query": "新製品のご案内メールを作成",
"purpose": "製品プロモーション",
"tone": "standard",
"language": "ja",
"input_format": "html",
"extra_requests": "{名前}と{会社名}のプレースホルダーを含める"
},
"response_mode": "streaming",
"user": "user-identifier"
}
4 動作テスト
- Step 2 の「メール本文」欄で「AI生成」をクリック
- 用件やトーンを入力
- 「生成」ボタンをクリック
- ストリーミングで文章が生成されることを確認
- 生成された文章が本文欄に挿入される
Step 4: プロキシ設定(CORS回避)
ブラウザから直接Dify APIを呼び出すとCORSエラーが発生するため、プロキシを使用します。
方法1: ローカルプロキシ(開発用)
local_proxy.js の内容:
const http = require('http');
const https = require('https');
const PORT = 8787;
const server = http.createServer(async (req, res) => {
// CORS設定
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.statusCode = 204;
res.end();
return;
}
if (req.method === 'POST' && req.url === '/difyProxy') {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', async () => {
try {
const data = JSON.parse(body);
// Dify APIへリクエスト転送
const response = await fetch('https://chatbot.aimeet.jp/v1/completion-messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${data.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
// ストリーミングレスポンスを転送
const { Readable } = require('stream');
const stream = Readable.fromWeb(response.body);
stream.pipe(res);
} catch (error) {
res.statusCode = 500;
res.end(JSON.stringify({ error: error.message }));
}
});
}
});
server.listen(PORT, () => {
console.log(`Proxy server running at http://localhost:${PORT}`);
});
起動コマンド:
node local_proxy.js
# → http://localhost:8787/difyProxy
方法2: Firebase Cloud Functions(本番用)
functions/index.js にプロキシ関数を追加:
const functions = require('firebase-functions');
const fetch = require('node-fetch');
exports.difyProxy = functions.https.onRequest(async (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.set('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
res.status(204).send('');
return;
}
const response = await fetch('https://chatbot.aimeet.jp/v1/completion-messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${req.body.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(req.body)
});
res.json(await response.json());
});
デプロイ:
firebase deploy --only functions:difyProxy
ℹ️ アプリ側の設定
アプリは自動的に環境を判定します:
- ローカル開発時:
http://localhost:8787/difyProxy - 本番環境: Firebase Functions URL
トラブルシューティング
| エラー | 原因 | 解決策 |
|---|---|---|
| CORS error | プロキシが起動していない | local_proxy.js を起動 |
| 401 Unauthorized | API Key が無効 | Difyで新しいキーを生成 |
| 429 Rate Limit | API呼び出し制限超過 | しばらく待ってから再試行 |
| タイムアウト | ネットワーク遅延 | プロキシログを確認 |
ベストプラクティス
1. プロンプトの最適化
- 具体的な指示を含める(文字数、構成、含めるべき情報)
- トーンを明確に定義する(敬語レベル、フォーマル度)
- 出力例を示す(Few-shot learning)
2. コスト管理
- 長い生成は高コスト → 必要最小限の長さに制限
- GPT-3.5-turbo は GPT-4 より安価(品質は劣る)
- Difyのダッシュボードで使用量を定期確認
3. 生成品質の向上
- 温度(Temperature)を調整: 0.3-0.5(安定)/ 0.7-0.9(創造的)
- 事前にサンプルで動作確認
- 生成後に人間が必ず確認・修正
⚠️ 重要な注意
AIが生成した文章は必ず人間が確認してから送信してください。
不適切な表現や誤情報が含まれる可能性があります。
ローカル開発環境
1 必要なツール
| ツール | 用途 | インストール |
|---|---|---|
| Node.js 18+ | JavaScript実行環境 | nodejs.org |
| VS Code | コードエディタ | code.visualstudio.com |
| Live Server | ローカルHTTPサーバー | VS Code 拡張機能 |
2 設定ファイルの準備
config.example.json をコピーして config.json を作成:
{
"azureClientId": "your-client-id-here",
"firebaseConfig": {
"apiKey": "your-api-key",
"authDomain": "your-project.firebaseapp.com",
"projectId": "your-project-id"
}
}
3 ローカルプロキシの起動(Dify API用)
# プロジェクトフォルダで実行
node local_proxy.js
# → http://localhost:8787 で起動
4 アプリの起動
# 方法1: Python
python -m http.server 8000
# 方法2: VS Code Live Server
# index.html を右クリック → "Open with Live Server"
# → http://localhost:8000 または http://127.0.0.1:5500
✅ 完了
ブラウザでアプリを開き、Microsoftアカウントでログインしてください。
トラブルシューティング
よくある問題と解決策
| 症状 | 原因 | 解決策 |
|---|---|---|
| ログインポップアップが開かない | ポップアップブロック | ブラウザでポップアップを許可 |
| AADSTS50011エラー | リダイレクトURIの不一致 | Azure ADで正確なURLを登録 |
| Firestoreアクセスエラー | セキュリティルール | ルールを確認・更新 |
| Dify APIエラー | CORSまたはプロキシ未起動 | local_proxy.js を起動 |
| メール送信失敗 | Mail.Send権限なし | Azure ADで権限追加・同意 |