🏗️

システム全体構成

このメール一括送信システムは、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で権限追加・同意