最近家族と公園に行ったのですが、見たことのないトンボが飛んでいました。なんという種類のとんぼなのか調べるのに生成AIが使えるんではないかと思い、カメラで撮影して調べるアプリを作ろうと思い立ちました。
そこで、このチュートリアルでは、Google Gemini APIを使って撮影した写真を解析し、写っているものを百科事典を引いてわかりやすく説明してくれるアプリを作ります。さらに説明の読み上げ機能もつけました。生徒の学習用に良いと思います。
Google Gemini APIの呼び出しにはGoogle Apps Script(GAS)を使います。GASのスクリプトはGoogle Gemini 2.5 Proで書いた生成AIお絵描きコーチアプリ用のスクリプトのプロンプトを書き換えています。プロンプトの書き換えにはもちろんGoogle Gemini 2.5 Proを使っています。
Google AI Studio で作るGemini APIは課金方法を登録しなくても以下の範囲ならば無料で使えます。
- 1 分あたりのリクエスト数: 5
- 1 日あたりのリクエスト数: 250,000
- 1 分あたりのトークン数(入力): 100
Google Apps Scriptの設定
Gemini APIの設定からウェブアプリとしてのデプロイ方法、使い方などは生成AIお絵描きコーチアプリを参照してください。
Code.gs – 写真百科事典スクリプト
/**
* App InventorからのPOSTリクエストを処理する関数
* @param {Object} e - App Inventorから送信されるイベントオブジェクト
* @return {ContentService.TextOutput} - Gemini APIからの結果をJSON形式で返す
*/
function doPost(e) {
// エラーハンドリング
try {
// 1. App Inventorから送信されたJSONデータを解析
const postData = JSON.parse(e.postData.contents);
const base64Image = postData.image; // App Inventor側で "image" というキーに設定
// 2. スクリプトプロパティからAPIキーを読み込む
const API_KEY = PropertiesService.getScriptProperties().getProperty('API_KEY');
if (!API_KEY) {
return createErrorResponse('APIキーが設定されていません。');
}
// 3. Gemini APIのエンドポイントURL
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent?key=${API_KEY}`;
// 4. Gemini APIに送信するリクエストボディを作成
const payload = {
"contents": [{
"parts": [{
"text": `# 命令
あなたは、あらゆる事象を網羅した百科事典の知識を持つ博士です。提供された画像を注意深く分析し、以下の指示に従って解説文を作成してください。
# 実行ステップ
1. **被写体の特定:** 画像に写っている最も主要な対象物(物、生物、場所、人物など)を正確に特定します。
2. **情報抽出:** 特定した対象物について、あなたの知識の中から「名称」「最も重要な特徴」「歴史的背景や興味深い事実」を抽出します。
3. **文章構成:** 抽出した情報を基に、最も伝えたい核心部分から書き始め、読み手の興味を引くように自然な文章を構成します。
# 出力制約
* 文字数: 200文字以内
* 段落: 必ず一つの段落で完結させること。
* 形式: 箇条書きは絶対に使用しないこと。
* 文体: 専門的でありながらも、その分野に詳しくない人にも分かりやすい、簡潔で平易な言葉で記述すること。`
}, {
"inline_data": {
"mime_type": "image/png", // App InventorのCanvasはPNG形式
"data": base64Image
}
}]
}]
};
// 5. Gemini APIにリクエストを送信するための設定
const options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(payload),
'muteHttpExceptions': true // エラーレスポンスを例外ではなくオブジェクトとして取得
};
// 6. APIにリクエストを送信
const response = UrlFetchApp.fetch(API_URL, options);
const responseCode = response.getResponseCode();
const responseBody = response.getContentText();
if (responseCode !== 200) {
return createErrorResponse(`APIリクエストエラー: ${responseCode} ${responseBody}`);
}
// 7. APIからのレスポンスを解析
const resultJson = JSON.parse(responseBody);
// レスポンスの構造をチェックし、テキスト部分を取得
let generatedText = "解析結果を取得できませんでした。";
if (resultJson.candidates && resultJson.candidates.length > 0 &&
resultJson.candidates[0].content && resultJson.candidates[0].content.parts &&
resultJson.candidates[0].content.parts.length > 0) {
generatedText = resultJson.candidates[0].content.parts[0].text;
} else {
// Geminiからのレスポンスが期待した形式でない場合
return createErrorResponse(`予期しないAPIレスポンス形式です: ${responseBody}`);
}
// 8. App Inventorに返すJSONを作成
const output = JSON.stringify({
"status": "success",
"result": generatedText
});
// 9. 結果をJSON形式で返す
return ContentService.createTextOutput(output).setMimeType(ContentService.MimeType.JSON);
} catch (error) {
// スクリプト全体で発生したエラーをキャッチ
return createErrorResponse(`スクリプトエラー: ${error.toString()}`);
}
}
/**
* エラーレスポンスを生成するヘルパー関数
* @param {string} message - エラーメッセージ
* @return {ContentService.TextOutput} - エラー情報を格納したJSONオブジェクト
*/
function createErrorResponse(message) {
const errorOutput = JSON.stringify({
"status": "error",
"message": message
});
return ContentService.createTextOutput(errorOutput).setMimeType(ContentService.MimeType.JSON);
}
App Inventorアプリ
ページの最後にあるダウンロードセクションからソースコードをダウンロードできます。
[プロジェクト]メニューから[新規プロジェクトを始める]を選択し、"EncyclopediaCamera"と名前を付けます。