Gemini × GASの最終形態!?1つのスプレッドシートで全タスクを操る方法【コード公開!】
こんにちは。Edocです。
note公式の投稿コンテストがあったので初めて参加してみました!
開発経緯
スプレッドシートで扱っているテキストをAIに投げて、要約したり分類したり──。
そんな作業をしていると、毎回こう思っていました。
「いちいちコピペしてGeminiに貼り付けるの、面倒すぎる…!」
特に、大量の行データを扱う時は手間が何倍にも膨れます。
「じゃあ、スプレッドシート上から直接Geminiを呼び出せるようにすればいいじゃないか」と思い立ったのが開発のきっかけです。
GASとは?
Google Apps Script(GAS)は、Googleスプレッドシートやドキュメント、GmailなどGoogleサービスを自動化・拡張できるスクリプト言語です。
Excelのマクロ(VBA)のGoogle版のようなもの、と言えばイメージしやすいでしょう。
Geminiとの統合の親和性
GASとGemini(Googleの生成AI)を組み合わせるメリットは大きく3つあります。
Google製サービス同士のスムーズな連携
スプレッドシート内のデータをGAS経由で簡単に取得でき、そのままGemini APIに渡せます。インターフェース不要
Webアプリやフォームをわざわざ作らなくても、スプレッドシート上のボタンやメニューから直接実行できます。柔軟なタスク切り替え
タスク内容をセルで指定するだけで、要約・分類・翻訳など、複数種類の処理を一つの仕組みで回せます。
実装内容
今回のシート構成は以下の通りです。
content列:AIに渡したい元データ
task列:Geminiに実行してほしいタスク(例:「要約」「分類」)
target列:実行対象かどうかを指定
result列:Geminiの実行結果を出力
さらに、GASで「Gemini操作」マクロボタンを作り、クリックすると
task列とcontent列、target列をセットでGeminiに送り、result列に結果を自動で書き込みます。
試してみた結果
横に長いため、見づらくてすみません…


↓

要約タスク:長文もスッキリと簡潔にまとめてくれた。
分類タスク:ユーザーコメント(サンプル)の感情分類ができた。
翻訳タスク:和訳も問題なくできた。
シートから直接Geminiを操作できるのは想像以上に快適で、日常の小さなタスクを一瞬で片付けられるようになりました。
活用例
マーケティング:SNS投稿案の要約・タグ付け
業務改善:問い合わせ内容の分類・テンプレ自動提案
学習支援:長文資料の要約、難解な文の簡易化
多言語対応:文章を一括翻訳して海外チームと共有
「一つの機能だけ」ではなく、task列を書き換えるだけで用途が変わるのが魅力です。
失敗談
最初はセルに直接 =Gemini_Call(args) と書く関数型の方式にしましたが、これが大失敗。
改行やセル編集のたびにAPIが走り、利用料金が加速度的に増加…。
心臓に悪かったので、今回の「ボタンクリック方式」に切り替えました。
まとめ
GASとGeminiを組み合わせれば、スプレッドシートが「AIタスク実行マシン」に早変わりします。
普段の業務で「AIに頼みたい作業」があるなら、わざわざブラウザや別ツールを開かずに、手元のシートからポチッと実行できる仕組みを作ってみると、作業効率は確実に上がります。
参考)実装コード
今回紹介したGASの実装コードです!
スプレッドシートを立ち上げて"拡張機能"→"Apps Script"に、
以下記載のコードを貼り付けてください。
スプレッドシートは1行目を列名、2行目以降から実行したい内容を記載すれば、記載コードのまま使用可能となります!
APIキーについて
GeminiAPIは自分で取得したAPIkeyをGASのスクリプトプロパティに登録して使ってください。
■スクリプトプロパティ
プロパティ:GEMINI_API_KEY
値:皆さんが取得したAPIkey
※APIは絶対に公開してはいけないので、上記スクリプトプロパティで実行してください!!!
お願い
※無断での転載はご遠慮いただきますようお願いいたします。
※GeminiAPIは設定によっては無料利用枠を超えると従量課金に移行します。コードの実行や利用によって生じたいかなる問題についても、当方は一切の責任を負いかねます。 ご自身の責任においてご利用ください。m(_ _)m
可変推奨パラメータ
1.モデル
今回は簡易タスクだったため安価なgemini-1.5-flashを利用しました。必要に応じてモデルを変更してください。
const model = 'gemini-1.5-flash';↓モデルリスト
2.出力トークン数
test用のため出力トークン数を50に制限しています。
必要に応じて変更してください。
"maxOutputTokens": 50コード
#JavaScript
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Gemini操作')
.addItem('タスクを実行する', 'executeTasks')
.addToUi();
}
function executeTasks() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const lastRow = sheet.getLastRow();
const apiKey = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
const ui = SpreadsheetApp.getUi();
if (!apiKey) {
ui.alert('GEMINI_API_KEYが設定されていません。');
return;
}
// 列の指定(必要に応じて変えてください)
const taskCol = 2; // B列 タスク
const targetCol = 3; // C列 実行対象セルアドレス
const resultCol = 4; // D列 結果
const model = 'gemini-1.5-flash';
const endpoint = 'https://generativelanguage.googleapis.com/v1beta/models/' + model + ':generateContent?key=' + apiKey;
let processedCount = 0;
for (let row = 2; row <= lastRow; row++) { // 1行目はヘッダー想定
const prompt = sheet.getRange(row, taskCol).getValue();
const targetCellAddress = sheet.getRange(row, targetCol).getValue();
if (!prompt || !targetCellAddress) {
// どちらか空ならスキップ
continue;
}
let inputText = '';
try {
inputText = sheet.getRange(targetCellAddress).getValue();
} catch (e) {
sheet.getRange(row, resultCol).setValue('対象セル指定エラー');
continue;
}
const fullPrompt = prompt + ":" + inputText;
const requestBody = {
"contents": [
{
"parts": [
{"text": fullPrompt}
]
}
],
"generationConfig": {
"maxOutputTokens": 50,
"temperature": 1.0
}
};
const options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(requestBody),
'muteHttpExceptions': true
};
try {
const response = UrlFetchApp.fetch(endpoint, options);
const code = response.getResponseCode();
const text = response.getContentText();
if (code < 200 || code >= 300) {
sheet.getRange(row, resultCol).setValue("APIエラー: HTTP " + code);
continue;
}
const result = JSON.parse(text);
let out = "";
if (result.candidates && result.candidates.length > 0) {
const parts = result.candidates[0].content && result.candidates[0].content.parts;
if (parts && parts.length > 0) {
out = parts.map(p => p.text || "").join("");
}
}
sheet.getRange(row, resultCol).setValue(out.trim());
processedCount++;
// API負荷軽減のために少し待つ(必要に応じて調整)
Utilities.sleep(300);
} catch (e) {
sheet.getRange(row, resultCol).setValue("例外: " + e.message);
}
}
ui.alert('処理完了。' + processedCount + ' 件実行されました。');
}
