1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【完全版活用ガイド】GAS × Gemini APIでメール業務を丸ごと自動化する完全ガイド — 抽出・要約・分類をノーコストで実現

1
Posted at

0. はじめに

こんな作業、手でやっていませんか?

Gmailに届いた案件メールを開く → 内容を読む → クライアント名、案件名、実施月などを手作業でスプレッドシートに転記する → 次のメール…

問題: 転記ミスが起きる / 担当者が休むと止まる / 毎日30分〜1時間がこの作業に消える / メールが多いと追いつかない

これ、GAS(Google Apps Script) × Gemini API で自動化できます。しかも Geminiの無料枠 で。

GeminiAPI_Wagtial_RD2-V01.original.jpg

この記事では、「Gmailに届いたメールをAIで自動解析 → スプレッドシートに記録」する仕組みを、STEP形式でゼロから構築していきます。

この記事はClaudeと一緒に書きました。GASは触ったことがある・AI APIは初めてという方を想定しています。

本記事で扱う内容

# トピック ひとこと セクション
1 完成イメージ 何ができるようになるか 1. 完成イメージ
2 事前準備 APIキー取得・シート準備 2. 事前準備
3〜5 STEP 1〜3: 基盤構築 シート設計・GAS作成・メール取得 3. STEP 1: スプレッドシート準備
6〜7 STEP 4: Gemini API APIの叩き方とプロンプト設計 6. STEP 4: Gemini APIを叩く
8〜9 STEP 5〜6: 書き込みと分類 シート記録・メール分類 8. STEP 5: スプレッドシートに書き込む
10 全体コード コピペで動く完成版 10. 全体コード
11〜12 デバッグと本番化 動作確認・トリガー設定 11. 動作確認とデバッグ
13〜16 応用・注意点 コスト・セキュリティ・比較 13. やりがちな失敗
17 まとめ 要点とBefore/After 17. まとめ

それでは、まず完成イメージから見ていきましょう。


1. 完成イメージ — 何ができるようになるか

一言で言うと

Gmailにメールが届くだけで、AIが中身を読み取り、スプレッドシートに自動で整理してくれる

処理の全体フロー

Gmail受信 → GASが定期検知 → Gemini APIで解析 → スプレッドシートに自動記録

Before / After

Before(手作業) After(GAS × Gemini)
転記作業 メールを開いて目視でコピペ 自動で抽出・記録
所要時間 1通あたり3〜5分 数秒(API応答時間のみ)
転記ミス 人間だから起きる JSONスキーマで構造化、ミスなし
担当者不在 止まる 止まらない(トリガーで自動実行)
分類・要約 手動で判断 AIが自動分類・要約
コスト 人件費(月数万円相当) Gemini無料枠で0円

2. 事前準備 — 必要なもの一覧

必要なもの 準備方法
Googleアカウント 個人アカウントでOK。Workspace環境でも可
Gemini APIキー Google AI Studio で無料発行
Googleスプレッドシート 新規作成(次のSTEPで列構成を設計)

Gemini APIキーの取得手順

  1. Google AI Studio にアクセス
  2. Googleアカウントでログイン → 利用規約に同意
  3. 左メニューの「Get API key」をクリック
  4. 「Create API key」でキーを発行
  5. 発行されたキーをコピーして安全な場所に保存

Gemini APIはクレジットカード不要で無料枠を利用できます。個人アカウントであれば通常すぐに発行可能です。

Workspace(組織)アカウントの場合、管理者設定によってはAPI利用が制限されていることがあります。発行できない場合は組織の管理者に確認してください。


3. STEP 1: スプレッドシートを準備する

まずは、抽出結果を記録するスプレッドシートを作成します。Googleドライブから新規スプレッドシートを開き、以下の列構成で1行目にヘッダーを入力してください。

ヘッダー名 内容
A 受信日時 メールの受信日時
B 差出人 メールの送信者
C クライアント名 AIが抽出
D 案件名 AIが抽出
E 投稿条件 AIが抽出
F 実施月 AIが抽出
G 要約 AIが要約
H 分類 AIが分類(案件依頼/見積もり/問い合わせ/その他)
I メール全文 原本保持(後述)

I列の「メール全文」は必ず残してください。 AIの抽出結果にハルシネーション(事実と異なる出力)がないか、後から原本で検証できるようにするためです。


4. STEP 2: GASプロジェクトを作成する

スプレッドシートのメニューから「拡張機能」→「Apps Script」を開き、エディタを起動します。

まずは動作確認として、最小限のコードを実行してみましょう。

function hello() {
  Logger.log("GASが動きました!");
}

エディタ上部の「▶ 実行」を押し、実行ログに「GASが動きました!」と表示されればOKです。初回実行時はGmailやスプレッドシートへのアクセス許可を求められるので、許可してください。


5. STEP 3: Gmailからメールを取得する

次に、GASでGmailのメールを取得します。

function getLatestEmail() {
  // デバッグ用: 既読・未読問わず最新1件を取得
  const threads = GmailApp.search("label:INBOX", 0, 1);

  // 本番用: 未読メールのみ取得する場合は上の行を下に置き換える
  // const threads = GmailApp.search("is:unread label:INBOX", 0, 1);

  if (threads.length === 0) {
    Logger.log("対象メールなし");
    return;
  }

  const messages = threads[0].getMessages();
  const latest = messages[messages.length - 1];

  Logger.log("件名: " + latest.getSubject());
  Logger.log("差出人: " + latest.getFrom());
  Logger.log("本文: " + latest.getPlainBody());
}

テスト段階では label:INBOX (全メール)で動作確認し、本番では is:unread label:INBOX (未読のみ)に切り替えてください。 全メールを対象にすると、過去のメールまで繰り返し処理されてしまいます。


6. STEP 4: Gemini APIを叩く

ここが本記事の核心です。GASからGemini APIを呼び出してメール本文を解析します。

APIキーの安全な管理

APIキーをコードに直書きするのはNGです。スクリプトプロパティに保存しましょう。

  1. Apps Scriptエディタの左メニュー → 「⚙ プロジェクトの設定」
  2. 「スクリプトプロパティ」セクションで「プロパティを追加」
  3. プロパティ名: GEMINI_API_KEY、値: 取得したAPIキーを入力

Gemini APIを呼び出すコード

function callGemini(emailBody) {
  // スクリプトプロパティからAPIキーを安全に取得
  const apiKey = PropertiesService.getScriptProperties()
    .getProperty("GEMINI_API_KEY");

  // Gemini 2.0 Flash(高速・無料枠あり)
  const model = "gemini-2.0-flash";
  const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;

  // プロンプト: JSON形式で抽出・要約・分類を指示
  const prompt = `
あなたはメール解析アシスタントです。
以下のメール本文から情報を抽出し、**必ず以下のJSON形式のみ**で回答してください。
JSON以外のテキスト(説明文、マークダウンの装飾など)は一切含めないでください。

{
  "client": "クライアント名(不明なら'不明')",
  "project": "案件名(不明なら'不明')",
  "conditions": "投稿条件(不明なら'不明')",
  "month": "実施月(不明なら'不明')",
  "summary": "メール全体の要約(2〜3文)",
  "category": "以下から1つ選択: 案件依頼 / 見積もり / 問い合わせ / その他"
}

--- メール本文 ---
${emailBody}
`;

  const payload = {
    contents: [{ parts: [{ text: prompt }] }]
  };

  const options = {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify(payload),
    muteHttpExceptions: true  // エラー時もレスポンスを取得
  };

  const response = UrlFetchApp.fetch(url, options);
  const json = JSON.parse(response.getContentText());

  // Geminiの回答テキストを取得
  const rawText = json.candidates[0].content.parts[0].text;

  // マークダウンの ```json ... ``` を除去してクリーンなJSONにする
  const cleanText = rawText
    .replace(/```json\s*/gi, "")
    .replace(/```\s*/g, "")
    .trim();

  return JSON.parse(cleanText);
}

Geminiは回答を ```json ... ``` で囲んで返すことがあります。この除去処理を忘れると JSON.parse でエラーになるのが最もよくあるハマりポイントです。


7. プロンプト設計のコツ

STEP 4のプロンプトがなぜあの形なのか、設計のポイントを解説します。

良いプロンプト / 悪いプロンプト

悪い例 良い例
出力形式 「情報を抽出して」(形式を指定していない) 「以下のJSON形式のみで回答」(スキーマを明示)
不明時の処理 指定なし(AIが勝手に推測してしまう) 「不明なら'不明'と出力」(ハルシネーション防止)
分類のカテゴリ 「適切に分類して」(曖昧) 「以下から1つ選択: 案件依頼 / 見積もり / 問い合わせ / その他」(選択肢を限定)

ポイント3つ

  1. JSONスキーマを例示する — Geminiに「この形で返して」と見本を見せるのが最も確実
  2. 「不明なら'不明'」を明示する — これがないとAIが勝手に推測して嘘の情報を出力する
  3. 分類カテゴリは選択式にする — 自由記述だと表記ゆれが起き、スプレッドシートでフィルタしにくい

8. STEP 5: スプレッドシートに書き込む

Geminiの解析結果をスプレッドシートに1行追加する関数です。

function appendToSheet(data, message) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet()
    .getSheetByName("シート1");

  if (!sheet) {
    Logger.log("エラー: 「シート1」が見つかりません");
    return;
  }

  sheet.appendRow([
    message.getDate(),                  // A列: 受信日時
    message.getFrom(),                  // B列: 差出人
    data.client     || "不明",          // C列: クライアント名
    data.project    || "不明",          // D列: 案件名
    data.conditions || "不明",          // E列: 投稿条件
    data.month      || "不明",          // F列: 実施月
    data.summary    || "",              // G列: 要約
    data.category   || "その他",        // H列: 分類
    message.getPlainBody()              // I列: メール全文(原本)
  ]);
}

9. STEP 6: メール分類の活用

STEP 4のプロンプトで category フィールドを含めたことで、メールが自動分類されます。

分類 説明 使い方の例
案件依頼 新規案件の依頼メール 案件管理ボードに自動追加
見積もり 金額に関する問い合わせ 経理チームに転送
問い合わせ 一般的な質問 FAQで対応可能か判断
その他 上記に当てはまらない 手動確認キューへ

カテゴリは自分の業務に合わせて変更してください。プロンプト内の選択肢を書き換えるだけです。


10. ここまでの全体コード

STEP 1〜6を統合したコピペで動く完成版です。

// ============================================================
// Gmail × Gemini API メール自動解析スクリプト
// 概要: Gmailの未読メールをGemini APIで解析し、
//       スプレッドシートに自動で追記する
// ============================================================

// 使用するGeminiモデル
const GEMINI_MODEL = "gemini-2.0-flash";

// 書き込み先のシート名
const SHEET_NAME = "シート1";


// ============================================================
// メイン関数: メール取得 → Gemini解析 → シート書き込み
// ============================================================
function main() {
  // デバッグ用: 既読・未読問わず最新1件を取得
  const threads = GmailApp.search("label:INBOX", 0, 1);

  // 本番用: 未読メールのみ対象にする場合は上をコメントアウトし下を有効化
  // const threads = GmailApp.search("is:unread label:INBOX", 0, 5);

  if (threads.length === 0) {
    Logger.log("対象のメールが見つかりませんでした。");
    return;
  }

  for (const thread of threads) {
    const messages = thread.getMessages();
    const latest = messages[messages.length - 1];
    const body = latest.getPlainBody();

    Logger.log("--- 処理中: " + latest.getSubject() + " ---");

    // Gemini APIで解析
    const parsed = callGemini(body);

    if (!parsed) {
      Logger.log("解析失敗。スキップします。");
      continue;
    }

    // スプレッドシートに書き込み
    appendToSheet(parsed, latest);
    Logger.log("書き込み完了: " + (parsed.project || "不明"));

    // 本番用: 処理済みメールを既読にする
    // thread.markRead();
  }
}


// ============================================================
// Gemini APIを呼び出してメール本文を解析する関数
// ============================================================
function callGemini(emailBody) {
  const apiKey = PropertiesService.getScriptProperties()
    .getProperty("GEMINI_API_KEY");

  if (!apiKey) {
    Logger.log("エラー: GEMINI_API_KEYが設定されていません。");
    return null;
  }

  const url = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${apiKey}`;

  const prompt = `
あなたはメール解析アシスタントです。
以下のメール本文から情報を抽出し、**必ず以下のJSON形式のみ**で回答してください。
JSON以外のテキスト(説明文、マークダウンの装飾など)は一切含めないでください。

{
  "client": "クライアント名(不明なら'不明')",
  "project": "案件名(不明なら'不明')",
  "conditions": "投稿条件(不明なら'不明')",
  "month": "実施月(不明なら'不明')",
  "summary": "メール全体の要約(2〜3文)",
  "category": "以下から1つ選択: 案件依頼 / 見積もり / 問い合わせ / その他"
}

--- メール本文 ---
${emailBody}
`;

  const payload = {
    contents: [{ parts: [{ text: prompt }] }]
  };

  const options = {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    const statusCode = response.getResponseCode();

    if (statusCode !== 200) {
      Logger.log("APIエラー (HTTP " + statusCode + "): " + response.getContentText());
      return null;
    }

    const json = JSON.parse(response.getContentText());
    const rawText = json.candidates[0].content.parts[0].text;
    Logger.log("Gemini応答: " + rawText);

    // ```json ... ``` の除去
    const cleanText = rawText
      .replace(/```json\s*/gi, "")
      .replace(/```\s*/g, "")
      .trim();

    return JSON.parse(cleanText);

  } catch (error) {
    Logger.log("Gemini API呼び出しエラー: " + error.message);
    return null;
  }
}


// ============================================================
// スプレッドシートに1行追記する関数
// ============================================================
function appendToSheet(data, message) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet()
    .getSheetByName(SHEET_NAME);

  if (!sheet) {
    Logger.log("エラー: シート「" + SHEET_NAME + "」が見つかりません。");
    return;
  }

  sheet.appendRow([
    message.getDate(),                  // A列: 受信日時
    message.getFrom(),                  // B列: 差出人
    data.client     || "不明",          // C列: クライアント名
    data.project    || "不明",          // D列: 案件名
    data.conditions || "不明",          // E列: 投稿条件
    data.month      || "不明",          // F列: 実施月
    data.summary    || "",              // G列: 要約
    data.category   || "その他",        // H列: 分類
    message.getPlainBody()              // I列: メール全文(原本)
  ]);
}

11. 動作確認とデバッグ

実行方法

  1. Apps Scriptエディタで main 関数を選択
  2. 「▶ 実行」をクリック
  3. 実行ログ(Ctrl+Enter)でGeminiの応答と書き込み結果を確認

よくあるエラーと対処法

エラー 原因 対処
GEMINI_API_KEYが設定されていません スクリプトプロパティ未設定 プロジェクト設定 → スクリプトプロパティに追加
APIエラー (HTTP 400) プロンプトが不正 or モデル名のタイポ モデル名を gemini-2.0-flash に確認
APIエラー (HTTP 429) レートリミット超過 数分待って再実行。本番では Utilities.sleep(2000) を挿入
JSON.parse でエラー ```json ``` の除去漏れ or Geminiが不正なJSONを返した Logger.logで生のレスポンスを確認。プロンプトを調整
シートが見つかりません シート名の不一致 SHEET_NAME の値とシート名が一致しているか確認

12. 本番運用に切り替える

デバッグが通ったら、本番運用に切り替えます。コードの変更は3箇所だけです。

変更1: 未読メールのみを対象にする

// 変更前(デバッグ用)
const threads = GmailApp.search("label:INBOX", 0, 1);

// 変更後(本番用)
const threads = GmailApp.search("is:unread label:INBOX", 0, 5);

変更2: 処理済みメールを既読にする

// コメントアウトを外す
thread.markRead();

変更3: 時間ベーストリガーを設定する

  1. Apps Scriptエディタの左メニュー → 「⏰ トリガー」
  2. 「トリガーを追加」をクリック
  3. 実行する関数: main
  4. イベントのソース: 「時間主導型」
  5. 時間ベースのトリガーのタイプ: 「分ベースのタイマー」→ 「5分おき」
  6. 保存

これで5分おきにGmailをチェックし、未読メールがあれば自動で解析・記録されます。

レートリミットに注意。 Gemini 2.0 Flashの無料枠は1分あたり10〜15リクエスト程度です。大量のメールを一度に処理する場合は、ループ内に Utilities.sleep(2000) を挿入してください。


13. やりがちな失敗(アンチパターン)

失敗1: プロンプトでJSON形式を強制していない
「メールから情報を抽出して」だけだと、Geminiが自然言語で回答してしまいJSONパースできません。必ずスキーマを例示してください。

失敗2: ```json ``` の除去を忘れている
Geminiの応答は高確率でマークダウンのコードブロックで囲まれます。これを除去せずに JSON.parse すると必ずエラーになります。

失敗3: メール全文を保存していない
AIの抽出結果が正しいかどうか、後から検証する手段がなくなります。I列に原本を必ず残しましょう。

失敗4: APIキーをコードに直書きしている
ソースを共有したりGitにコミットした瞬間にAPIキーが漏洩します。必ずスクリプトプロパティを使ってください。


14. コストの話 — Gemini APIは実質無料?

Gemini 2.0 Flash の料金(2026年3月時点)

項目 無料枠 有料枠
入力 無料 $0.10 / 100万トークン
出力 無料 $0.40 / 100万トークン
レートリミット 10〜15 RPM / 1,000 RPD 2,000 RPM

実際にどのくらい処理できる?

メール1通あたりの平均トークン数を約500トークン(入力)+ 200トークン(出力)と仮定すると:

  • 無料枠: 1日1,000リクエスト → 1日1,000通のメール処理が可能
  • 一般的なビジネスで1日1,000通を超えることは稀

個人利用・小規模チームなら、ほぼ確実に無料枠で収まります。 クレジットカードの登録も不要なので、まずは無料で試してみてください。

参考ドキュメント


15. セキュリティ上の注意点

メール本文をGemini APIに送信するということは、メール内容がGoogleのサーバーに送られるということです。 以下の点を理解した上で利用してください。

注意すべきこと

項目 対策
機密情報 顧客の個人情報や契約金額が含まれるメールの送信には慎重に
無料枠のデータ利用 無料枠では、送信データがGoogleのサービス改善に使用される可能性がある
有料枠のデータ利用 有料枠にアップグレードすると、データがサービス改善に使用されない(エンタープライズグレードのプライバシー)
APIキー管理 スクリプトプロパティに保存。コードに直書きしない
スクリプト共有 GASプロジェクトの共有範囲は最小限に

16. 発展: 他のAI APIとの比較

この記事ではGemini APIを使いましたが、同じアーキテクチャで他のAI APIにも差し替え可能です。

比較項目 Gemini 2.0 Flash Claude (Haiku) GPT-4o mini
GASからの呼びやすさ UrlFetchAppだけでOK UrlFetchAppだけでOK UrlFetchAppだけでOK
無料枠 あり(1,000 RPD) なし なし
入力コスト(有料) $0.10 / 100万トークン $0.25 / 100万トークン $0.15 / 100万トークン
JSON出力の安定性 良い 良い 良い
Google連携の親和性 最高(同じGoogle基盤) 普通 普通

GASとの組み合わせならGeminiが最もスムーズです。 Google基盤同士の連携で認証周りがシンプルになります。他のAPIに興味がある場合は、callGemini 関数のURL・ヘッダー・レスポンス形式を差し替えるだけで対応できます。


17. まとめ

要点

やること 具体的に
GASで構築 Gmail取得 → Gemini API解析 → スプレッドシート記録
プロンプト設計 JSONスキーマを明示 + 「不明なら'不明'」 + カテゴリ選択式
安全なAPI管理 スクリプトプロパティにAPIキーを保存
本番化 未読フィルタ + markRead + 5分トリガー
コスト Gemini 2.0 Flashの無料枠で十分

Before / After

Before After
メール転記 1通3〜5分、手作業 自動、数秒
転記ミス 人間だから起きる 構造化JSON、ミスなし
担当者不在 業務が止まる トリガーで自動継続
分類・要約 手動判断 AI自動分類
月間コスト 人件費(数万円相当) 0円(無料枠)

最初の一歩

# ① Google AI Studio でAPIキーを取得(無料・カード不要)
https://aistudio.google.com/

# ② スプレッドシートを作成 → 拡張機能 → Apps Script

# ③ セクション10の「全体コード」をコピペ

# ④ スクリプトプロパティにAPIキーを設定

# ⑤ main関数を実行 → スプレッドシートに結果が入る!

まずは1通のメールで試してみてください。 動いたら、トリガーを設定して本番化するだけです。

参考リンク集

リソース URL
Google AI Studio(APIキー取得) https://aistudio.google.com/
Gemini API 料金ページ https://ai.google.dev/gemini-api/docs/pricing
Gemini API 公式ドキュメント https://ai.google.dev/gemini-api/docs
GAS GmailApp リファレンス https://developers.google.com/apps-script/reference/gmail
GAS UrlFetchApp リファレンス https://developers.google.com/apps-script/reference/url-fetch

ここまで読んでいただきありがとうございます。GAS × Gemini APIの組み合わせは、Google Workspaceユーザーにとって最もローコストで始められるAI自動化の入口です。ぜひ試してみてください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?