【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から、アカウントを登録しましょう。
住所者氏名の他に、支払情報の入力が必要です。
ただし、これから設定しまていきますが、Geminiは無料枠で収まるに設定します。
そのため、支払情報を入力しても課金はされないので、安心してください。
プロジェクトの作成
ログインが完了すると、以下の様な画面になります。

GCPでは、「プロジェクト」という単位で、ひとつのアカウントで利用するサービスのリソースをまとめています。
アカウント作成後に開いた画面では「My First Project」というプロジェクトが自動で生成されていますが、今回は新しいプロジェクトを作ります。


②Google AI Studioの設定
Google AI Studioは、開発者向けに提供されているAI開発プラットフォームです。
Geminiの様々なモデルが無料で利用できることに加え、GeminiAPIキーの取得もできます。
キーの取得はここで行ないますが、キーの管理はGCPの各プロジェクトに紐づく形になっているので、前もってGCPのプロジェクトの作成が必要です。
アカウント作成
まずは、アカウントを作成しましょう。
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つの関数でできています。
- function callGeminiForReceipt(imagePath)
- 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の転記」になります!