2
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?

【脱・予約忘れ Vol.3】GASの中に「キャンプ専用Gem」を埋め込む。最新Gemini 3.0 Proで完全自動化へ

Last updated at Posted at 2025-12-02

はじめに

こんにちは!キャンプ歴4年目のズボラキャンパーです。

これまで2回の記事で、GASとスプレッドシートを使って「キャンプ予約メールを自動でカレンダー登録する仕組み」を作ってきました。

これで十分便利……と思っていたのですが、Googleから最新モデル 「Gemini 3.0」 が登場しました。
今回はこの最新モデルと、APIの機能である 「System Instruction(システム指示)」 を使って、GASのコード内に 「キャンプ予約解析専門のAI人格(Gem)」 を作り出します。

これにより、たまに起きていたAIの「お茶目な回答(余計な挨拶など)」を完全に封じ込め、エラー率ほぼゼロの最強システムとして完成させます。
※APIからGemを動かすことはできなかったです💦残念:sleepy:

「Gem」をコードで再現するとは?

ブラウザ版のGeminiには、自分好みのAIを作れる「Gem」という機能がありますよね。
API開発において、あれと同じことを実現するのが 「System Instruction」 という機能です。

  • これまでのやり方:
    • 毎回「情報を抽出してください」とお願いする(AIは毎回初対面の状態)。
  • 今回のやり方(Gem化):
    • 「お前は予約解析システムだ」という 人格設定(Gem) をあらかじめ定義し、それを強制する。

これに加えて、Gemini 3.0の 「JSONモード」 を組み合わせることで、システムとしての安定性を極限まで高めます。

GASコードの修正(完成形)

スプレッドシート連携(Vol.2)のコードをベースに、Geminiを呼び出す部分(parseEmailWithGemini)を書き換えます。
今回は 「コード自体が仕様書」 になるよう、コメントを細かめに入れています。

main 関数や getAllowListFromSheet 関数はVol.2のままでOKです!

変更する箇所:Gemini呼び出し関数

/**
 * 最新のGemini 3.0 Pro APIを叩く関数(Gem化・JSONモード搭載)
 * * @param {string} emailBody - 解析対象のメール本文
 * @return {object} 解析結果のJSONオブジェクト(title, startTime, endTime, memo)
 */
function parseEmailWithGemini(emailBody) {
  // スクリプトプロパティからAPIキーを取得
  const apiKey = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
  
  // =================================================================
  // 1. モデルの指定
  // =================================================================
  // 最新の「Gemini 3.0 Pro」を指定します。
  // "preview" はプレビュー版を意味し、最新機能がいち早く使えます。
  const model = 'gemini-3-pro-preview';
  const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;

  // =================================================================
  // 2. System Instruction(Gemの定義)
  // =================================================================
  // ここが「Gem」の本体です。
  // ユーザーの入力(メール本文)とは別に、「AIとしての振る舞い」を厳格に定義します。
  const systemInstruction = {
    parts: [{
      text: `
        あなたはキャンプ場の予約メールを解析するプロフェッショナルなシステムです。
        ユーザーから送られたメール本文から、カレンダー登録に必要な情報を抽出してください。
        
        【重要ルール:思考プロセス】
        1. メール本文から「キャンプ場名」「チェックイン日時」「チェックアウト日時」を探す。
        2. 日時が曖昧な場合(例: "午後")は、チェックイン13:00、チェックアウト11:00と補完する。
        3. 年月日が省略されている場合は、メールの送信日時(今の年)を基準に補完する。
        
        【重要ルール:出力形式】
        - 必ず以下のJSONフォーマットのみを出力すること。
        - JSON以外のテキスト(挨拶やmarkdown記号)は一切出力禁止。
        
        {
          "title": "キャンプ場名",
          "startTime": "YYYY-MM-DD HH:mm",
          "endTime": "YYYY-MM-DD HH:mm",
          "memo": "サイト名やプラン名など"
        }
      `
    }]
  };

  // =================================================================
  // 3. ユーザープロンプト
  // =================================================================
  // 役割は上記で定義したので、ここではシンプルに「解析対象のデータ」を渡すだけです。
  const userPrompt = {
    parts: [{ text: emailBody }]
  };

  // =================================================================
  // 4. 設定(JSONモード)
  // =================================================================
  // "application/json" を指定することで、API側が強制的にJSON形式で返答します。
  // これにより、パースエラー(解析失敗)が劇的に減ります。
  const generationConfig = {
    responseMimeType: "application/json"
  };

  // リクエストデータの構築
  const payload = {
    systemInstruction: systemInstruction,
    contents: [userPrompt],
    generationConfig: generationConfig
  };

  // HTTPリクエストのオプション設定
  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true // エラー時も例外を投げずにレスポンスを確認できるようにする
  };

  try {
    // APIへのリクエスト実行
    const response = UrlFetchApp.fetch(url, options);
    const json = JSON.parse(response.getContentText());
    
    // エラーハンドリング:API側でエラーが返ってきた場合
    if (json.error) {
      console.error('Gemini API Error:', json.error.message);
      return null;
    }

    // =================================================================
    // 5. 結果の抽出
    // =================================================================
    // JSONモードのおかげで、replace等の文字列操作なしにそのままパースできます。
    const contentText = json.candidates[0].content.parts[0].text;
    return JSON.parse(contentText);

  } catch (e) {
    // 通信エラーやJSONパースエラーの捕捉
    console.error('Gemini解析中の予期せぬエラー:', e);
    // デバッグ用にレスポンスの中身をログに出す
    if (typeof response !== 'undefined') {
      console.error(response.getContentText());
    }
    return null;
  }
}

システム構成の進化

Vol.1〜2の構成と、今回のVol.3(Gemini 3.0 Pro)の構成を比較してみましょう。
「ただAIモデルが変わった」 だけでなく、データの流れと信頼性が劇的に変わっているのがわかります。

💡 ここが進化ポイント!

  1. 「ゴミ取り処理」の撤廃

    • Before: AIが気を利かせて「解析しました!こちらです」などの前置きを入れてくるため、GAS側で無理やり削除する処理が必要でした。

    • After: JSONモードにより、AIはJSONデータ以外を一切出力しなくなりました。これによりコードがシンプルになり、予期せぬエラーが激減しました。

  2. 「人格」の分離 (System Instruction)

    • Before: プロンプトで「あなたはシステムです」と言い聞かせていましたが、メール本文の内容によってはAIがつられて普通の会話をしてしまうことがありました。

    • After: APIレベルで「役割」を固定したため、どんなメールが来ても 「私はただの解析マシーンである」 というスタンスを崩しません。これが 「Gemをコードで定義する」 ということです。

  3. 推論能力の向上 (Gemini 3.0 Pro)

    • 最新モデルにより 「文脈を読む力」 が上がっています。例えば「明日の午後イチ」のような曖昧な表現も、メールの日付から推測して正確な日時データに変換してくれます。

さいごに(シリーズ完結)

Vol.1からVol.3を通して、以下のシステムが完成しました。

  1. GASがメールを監視
  2. スプレッドシートのリストに基づいて対象を絞る
  3. Gemini 3.0 Pro(Gem化済み) が超高精度にデータを選別
  4. Googleカレンダーに登録する

ここまで作り込めば、もはや 「自分専用のSaaS(Webサービス)」 と言ってみたくなりました。
初期費用0円、維持費0円で作れる最強のキャンプ相棒として、ぜひ育ててみてください!

それでは、また次のキャンプ場でお会いしましょう:tent:

2
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
2
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?