【AppSheet×AI×GAS】超時短!転記処理を完全に自動化しよう!~写真転記編~

【AppSheet×AI×GAS】超時短!転記処理を完全に自動化しよう!~写真転記編~

はじめに

今回は、AIを使って転記作業を自動化する方法を解説します。

内容としては二部構成になっており、

第一部:「写真を転記」
第二部:「PDFを転記」

となっています。

今回の記事は「写真を撮って転記」する方法を解説しますので、「スキャンしたPDFを転記したい!」という方は第二部をご覧ください。

※第一部、第二部ともに結構難しい内容になります。できるだけわかりやすく解説するつもりですが、もし不明な点があれば、Youtubeや問い合わせを活用してください。

今回つくるシステム

それでは、早速つくっていきましょう

今回つくるシステムは「領収書転記システム」です。

領収書は何かと量が多くなりがちで、それを転記するのがとてもダルい。

時間もとられるし、単純作業でしんどい、というのが僕の経験でもあります。

そのため、AppSheetで自動化して負担を減らしましょう!

システムの全体像

完成したシステムは以下の様な感じです。

写真を撮って保存→GASでAI呼び出し→AI起動→転記開始

という流れになります。

(上記の動画ではパソコンを使っているので、アップロード形式になっていますが、スマホからであれば写真を撮影でできます。)

前準備:Gemini APIの取得

今回使うAIは「Gemini2.5Flash」です。

Googleが提供しているAIで、高性能かつ無料で使える範囲が広いため、今回はGeminiを使います。

Geminiを使うために、APIを取得してGASの操作で使えるように設定します。

①GCPの設定

まず最初に、GCP(Google Cloud)を設定します。

GCPとは、Googleが提供している高度なサービスひとまとめに管理・利用するためのサービスです。

普段は使いませんが、AppSheetの関連でいくと、今回使うGeminiAPIやBigQueryを使う場合はGCPを設定するので、覚えておきましょう。

アカウント作成

まず、下記のURLから、アカウントを登録しましょう。

Google Cloud

住所者氏名の他に、支払情報の入力が必要です。

ただし、これから設定しまていきますが、Geminiは無料枠で収まるに設定します。

そのため、支払情報を入力しても課金はされないので、安心してください。

プロジェクトの作成

ログインが完了すると、以下の様な画面になります。

GCPでは、「プロジェクト」という単位で、ひとつのアカウントで利用するサービスのリソースをまとめています。

アカウント作成後に開いた画面では「My First Project」というプロジェクトが自動で生成されていますが、今回は新しいプロジェクトを作ります。

②Google AI Studioの設定

Google AI Studioは、開発者向けに提供されているAI開発プラットフォームです。

Geminiの様々なモデルが無料で利用できることに加え、GeminiAPIキーの取得もできます。

キーの取得はここで行ないますが、キーの管理はGCPの各プロジェクトに紐づく形になっているので、前もってGCPのプロジェクトの作成が必要です。

Google AI Studioはこちら

アカウント作成

まずは、アカウントを作成しましょう。

GCPで作ったプロジェクトと同じGoogleアカウントでサインインしてください。

APIキーの取得

左側にあるダッシュボードを下にスクロールすると、「Get API key」というのがあるので、これをクリックします。

次に、右上にある「APIキーを作成」のボタンをクリックします。

キー名はわかりやすいものを適宜つけてください。あとから変更可能です。ここでは、「OCR-1」とつけました。

「インポートしたプロジェクトを選択」をクリックし、一番上にある「プロジェクトをインポート」を選択すると、GCPで作ったプロジェクトがでてきますので、それを選択します。

これで、APIキーの取得が完了しました。

画像の白色で隠している「キー」の列の部分がAPIキーになります。

APIキーは他者に知られるとかなり大変なことになるので、絶対にネット上で公開してください。

GASの作成

次は、GASを作成してきますが、その前にテーブルを作成しておきます。

テーブルの作成

  • プロンプトテーブル
  • 一覧テーブル

プロンプトテーブルには、AIに投げる指示文を書きます。

一覧テーブルにある「領収書」の写真とプロンプトテーブルにある「プロンプト」をGASで取得してAIに渡し、その戻り値を一覧テーブルの各レコードに格納していくという流れです。

スクリプトプロパティの設定

スクリプトプロパティとは、APIキーを格納しておく箱のようなものです。

これから書いていくGASのコードには、APIキーを記述しますが、直接記述するのはリスクが高いです。

そのため、スクリプトプロパティを使って変数に格納することで、安全性と保守性を高めます。

左側にある「プロジェクトの設定」をクリックします。

最下部までスクロールします。

「プロパティ」に任意の名前(ここではGEMINI_API_KEY)を、「値」にAPIキーを入力します。

APIキーは、Google AI Studioからコピーできます。

これで、GASのコードには「プロパティ」に記述した「GEMINI_API_KEY」という名前を使うことで、APIキーを直接記述しなくてもよくなります。

GASのコード作成

ここまで設定できたら、いよいよGASのコードを作成します。

コードの詳細については省きますので、要点だけ解説します。

function callGeminiForReceipt(imagePath) {
  const SS_ID = '自身のスプレッドシートのID';
  const MODEL_NAME = 'gemini-2.5-flash';
  
  try {
    // 1. 画像ファイルの取得
    const fileName = imagePath.split('/').pop(); 
    const files = DriveApp.getFilesByName(fileName);
    
    if (!files.hasNext()) {
      throw new Error(`画像ファイルが見つかりませんでした: ${fileName}`);
    }
    
    const file = files.next();
    const mimeType = file.getMimeType();
    const imageBlob = file.getBlob();
    const base64Image = Utilities.base64Encode(imageBlob.getBytes());

    // 2. プロンプトの取得 (プロンプトテーブル B2セル)
    const ss = SpreadsheetApp.openById(SS_ID);
    const promptSheet = ss.getSheetByName('プロンプトテーブル');
    const promptText = promptSheet.getRange('B2').getValue();

    if (!promptText) {
      throw new Error('プロンプトが取得できませんでした。');
    }

    // 3. Gemini APIの呼び出し
    const apiResponse = callGeminiAPI(MODEL_NAME, promptText, base64Image, mimeType);
    
    // 4. 結果のJSONパース
    let cleanJson = apiResponse.replace(/```json/g, '').replace(/```/g, '').trim();
    const resultData = JSON.parse(cleanJson);

    // 5. AppSheetに返すオブジェクト(税込金額を追加)
    return {
      "status": resultData["status"] || "",
      "date": resultData["date"] || "",
      "merchant": resultData["merchant"] || "",
      "amount_8": Number(resultData["amount_8"]) || 0,
      "amount_10": Number(resultData["amount_10"]) || 0,
      "amount_excl_tax": Number(resultData["amount_excl_tax"]) || 0,
      "amount_incl_tax": Number(resultData["amount_incl_tax"]) || 0
    };

  } catch (e) {
    console.error(e.toString());
    return {
      "status": "Error: " + e.message,
      "date": "",
      "merchant": "",
      "amount_8": 0,
      "amount_10": 0,
      "amount_excl_tax": 0,
      "amount_incl_tax": 0
    };
  }
}

/**
 * Gemini APIへのリクエストを実行する関数
 */
function callGeminiAPI(model, prompt, base64Image, mimeType) {
  const apiKey = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
  const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;

  const payload = {
    "contents": [
      {
        "parts": [
          { "text": prompt },
          {
            "inline_data": {
              "mime_type": mimeType,
              "data": base64Image
            }
          }
        ]
      }
    ],
    "generationConfig": {
      "response_mime_type": "application/json"
    }
  };

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

  const response = UrlFetchApp.fetch(apiUrl, options);
  const responseCode = response.getResponseCode();
  const jsonResponse = JSON.parse(response.getContentText());

  if (responseCode !== 200) {
    throw new Error(`Gemini API Error: ${jsonResponse.error && jsonResponse.error.message ? jsonResponse.error.message : response.getContentText()}`);
  }

  return jsonResponse.candidates[0].content.parts[0].text;
}

利用するGeminiのモデルは2.5-flash

const MODEL_NAME = 'gemini-2.5-flash';

まず、Geminiのモデルについてですが、2.5-flashを使うようにして下さい。

ただ、私が数カ月前(2025年11月)に使っていたのは2.0-flashでしたが、今使おうとするとエラーが出ます。

今後、新しいモデルが出るたび使えるものが変わる可能性があるので、その際は最新情報を参考にモデルを変更していってください、

2つの関数を使用

コードは大きく分けて以下の2つの関数でできています。

  1. function callGeminiForReceipt(imagePath)
  2. function callGeminiAPI(model, prompt, base64Image, mimeType)

1がメインの関数で、この中で2の関数の呼び出て、戻り値を設定しています。戻り値というのは、処理結果を変数として外部に出力するもので、AppSheetに設定します。

2はGeminiAPIを取得する関数で、APIを呼び出すときの決まった形(細かいとこは違いますが)になっています。他のシステムでGeminiAPIを使うときは、2と同じような記述のコードを使うので、これを機に勉強してみても良いかもしれませんね。

戻り値の確認

function callGeminiForReceipt(imagePath)には戻り値は以下です。各カラムと対応するようになっています。

戻り値

レコード名

status

処理判定

date

利用日

merchant

店舗名

amount_8

8%金額

amount_10

10%金額

amount_excl_tax

税抜金額

amount_incl_tax

税込金額

各戻り値が、対応するカラムに入力されていくというイメージです。

AppSheetの作成

次に、AppSheetの設定に入っていきます。

テーブルの設定

テーブルは以下の様に設定してください。

  • 一覧テーブル

「処理判定」はTEXT設定ですが、「Suggested values」

{"処理完了", "不備あり"}

と入力してください。本来であればEnumが良いのですか、今回の場合はうまくいかない場合があるので、このようにします。

  • プロンプトテーブル

プロンプトテーブルは、「Updates」のみにチェックを入れてください。

ビューの設定

ビューは今回の本旨ではないので、最低限の設定とします。

  • 領収書一覧
  • プロンプト
  • 一覧テーブル_Form(system generated)

Column orderの「領収書_image」を一番上に持ってきます。

Botの設定(超重要)

ここは超重要ポイントです。

引数に加えて戻り値の設定もあるので、順を追って解説します。

①トリガーの設定

まずはトリガーを設定しいきます。

今回は、一覧テーブルに写真をアップロードした時、つまりレコードが追加されたときに発火するため、「Adds」にチェックを付けます。

②GASと引数の設定

次に、「Create a new step」から「Call a a script」を選択し、「Apps Script Project」には先ほど作ったGASファイル(OCR_領収書転記)を選択します。

「Function Name」は「callGeminiForReceipt(imagePath)」を選択し、「Function Parameters」の「imagePath」には「[領収書_image]」と記述してください。

③AppSheetと戻り値の紐づけ

今回の肝である戻り値を設定するために、Return Valueをオンにします。Return value typeは「Object」です。

次に、「Specific type」を設定しますが、再度、以下に戻り値とカラムの対応表を出します。

戻り値

レコード名

status

処理判定

date

利用日

merchant

店舗名

amount_8

8%金額

amount_10

10%金額

amount_excl_tax

税抜金額

amount_incl_tax

税込金額

この戻り値の名前を、一言一句間違えずNameに入力してください。

これにより、戻り値とAppSheetの紐づけることができます。

④戻り値をデータに入力する

最後に、戻り値をデータに入力する設定をします。

「Add a step」で「Run a date action」を選択します。

Seetingsで「Set row values」を選択し、「Set these column(s)」の各カラムに以下の様に入力してください。

[GAS起動].[Name]

[GAS起動]の部分は、③で設定したPROCESSの名前です。

[Name]の部分は、③で設定した各戻り値の名前です。

例えば、戻り値「status」は、[GAS起動].[status]記述します。

また、データ型はテーブルのデータ型と一致するようにしてください。

プロンプトの入力

AIに読み込ませるプロンプトを作成します。

僕は以下の様なプロンプトとしました。

あなたは高精度のOCRアシスタントです。 提供された領収書画像を解析し、以下の情報を抽出してJSON形式で出力してください。

### 出力項目(JSONのキー) 
1. "status": 領収書として内容が読み取れた場合は "処理完了"、読み取れない・判別不能な場合は "不備あり" としてください。 
2. "date": 日付を "YYYY/MM/DD" 形式で出力してください。
3. "merchant": 店舗の正式名称を出力してください。 
4. "amount_8": 軽減税率(8%)対象の合計金額を数値(integer)で出力してください。8%の対象金額であり、税込金額ではない点に注意してください。該当がない場合は 0 としてください。 
5. "amount_10": 標準税率(10%)対象の合計金額を数値(integer)で出力してください。10%の対象金額であり、税込金額ではない点に注意してください。該当がない場合は 0 としてください。 
6. "amount_excl_tax": 税抜の合計金額を数値(integer)で出力してください。税込金額ではありません。支払額は税込金額なので、注意してください。 
7. "amount_incl_tax"": 支払いの合計金額(税込)を数値(integer)で出力してください。支払金額は税込金額です。 

### 制約事項 - 必ずJSON形式のみを出力してください。
- 金額フィールドには「円」やカンマ「,」を含めず、純粋な数値のみにしてください。 - 画像が領収書でない場合や読み取れない場合、金額はすべて 0 に設定してください。 

### JSON形式例 
{
"status": "処理完了", 
"date": "2023/10/01", 
"merchant": "セブンイレブン 〇〇店",
"amount_8": 500, "amount_10": 1000,
"amount_excl_tax": 1500, 
"amount_incl_tax":1640 
}

ポイントになるのは、「出力項目」と「JSON形式例」の2つですね。

「出力項目」は一つずつ具体的に記述することが大切です。

また、出力形式は必ずJSON形式になりますので、「JSON形式例」としてあえて提示しましょう。

完成後の動き

それでは、実際に動かしてみましょう

いかがでしょうか?

もし、出力されている値が意図しているものと違う場合は、プロンプトを変えてみてください。

次回は、「PDFの転記」になります!