-----
・追記(2024-6-24)
・追記(2024-6-29)
Claude の LINE bot を作りました → 「GAS で Claude API を使った LINE bot を作る」
-----
とりあえず、継続的なやりとりや temperature の指定、画像処理もできるようにしました。
LINE Messaging API チャンネルの作成とアクセストークンの取得。及び、Gemini の API キーの取得は済んでる想定です。
それぞれ、GAS の「プロジェクトの設定」よりスクリプト プロパティとして追加・保存しておきます。プロパティ名は「Line_key」と「Gemini_key」としています。
また、スプレッドシート(サンプル)を用意して、「chat」シートと「temperature」シートを作っておきます。(作り方は「GAS で ChatGPT とやりとりできる簡単な LINE bot の作り方」を参照)
★ 機能一覧
- 文頭に「続き)」を付けることで会話を継続。(例:「続き)もっと詳細に教えて」)
- 文頭に「画像)」で画像の処理。(例:「画像)何ですか?」 → 画像を送信)
- 文末に「(厳密」「(創造」で temperature を指定。通常 0.5、厳密 0、創造 1。(例:「3行で小噺を作って(創造」)
コードは以下の通りです。
const spreadsheet = SpreadsheetApp.openById('スプレッドシート ID');
const chatSheet = spreadsheet.getSheetByName('chat');
let chatLastRow = chatSheet.getLastRow();
let chatRange = chatSheet.getRange(2, 1, chatLastRow, 2);
const tempSheet = spreadsheet.getSheetByName('temperature');
const tempRange = tempSheet.getRange(1, 1);
function doPost(e) {
try {
const event = JSON.parse(e.postData.contents).events[0];
let prompt = [];
let replyMessage = '';
switch(event.message.type) {
case 'text': // リクエストがテキストの場合
let messageContent = event.message.text;
if (messageContent.startsWith('画像)')) {
// 画像処理の場合
messageContent = messageContent.replace('画像)', '');
geminiRequestText(messageContent); // リクエスト(テキスト)を成型・保存
sendLineMessage(event.replyToken, '画像を送信してください');
} else {
// 画像処理以外の場合
prompt = geminiRequestText(messageContent);
replyMessage = getGeminiReply(prompt); // Gemini で回答
setAiMessage('model', replyMessage); // AI の返答をスプレッドシートに'role': 'model'で格納
sendLineMessage(event.replyToken, replyMessage);
}
break;
case 'image': // リクエストが画像の場合
prompt = geminiRequestImage(event.message.id);
replyMessage = getGeminiReply(prompt); // Gemini で回答
sendLineMessage(event.replyToken, replyMessage);
break;
default:
// リクエストがサポート外のデータ形式の場合
sendLineMessage(event.replyToken, 'サポート外のデータ形式です');
}
} catch {
// エラー発生時
sendLineMessage(event.replyToken, '不明なエラーが発生しました');
}
}
// リクエストデータ(テキスト)を生成する関数
function geminiRequestText(messageContent) {
// リクエスト内容の処理
if (messageContent.startsWith('続き)')) {
// 文頭に「続き)」で会話を継続
messageContent = messageContent.replace('続き)', '');
chatSheet.getRange(chatLastRow + 1, 1, 1, 2).setValues([['user', messageContent]]);
} else {
// temperature を取得してスプレッドシートにセット
// 文末に「(創造」「(厳密」で指定
if (messageContent.endsWith('(創造')) {
messageContent = messageContent.replace('(創造', ''); // 1
tempRange.setValue(1);
} else if (messageContent.endsWith('(厳密')) {
messageContent = messageContent.replace('(厳密', ''); // 0
tempRange.setValue(0);
} else {
tempRange.setValue(0.5);
}
chatRange.clear(); // 以前のチャット内容を削除
// リクエスト内容をスプレッドシートに格納
chatSheet.getRange(2, 1, 1, 2).setValues([['user', messageContent]]);
chatRange = chatSheet.getRange(2, 1, 1, 2);
}
// リクエストデータを生成
let contents = [];
let values = chatRange.getValues();
for(let i = 0; i < values.length; i++) {
contents.push({'role': values[i][0], 'parts': {'text': values[i][1]}});
}
return contents;
}
// Gemini でリクエストデータ(画像)を生成する関数
function geminiRequestImage(id) {
// 画像を取得
const response = UrlFetchApp.fetch('https://api-data.line.me/v2/bot/message/' + id + '/content',{
'headers': {
'Authorization': `Bearer ${PropertiesService.getScriptProperties().getProperty('Line_key')}`,
},
'method': 'get'
});
const imageBlob = response.getBlob().getAs('image/jpeg');
// 画像を Base64 にエンコード
const base64Image = Utilities.base64Encode(imageBlob.getBytes());
// リクエストデータを生成
let contents = [{"parts": [{"text": chatSheet.getRange(2, 2).getValue()}, {"inlineData": {"mimeType": "image/jpeg", "data": base64Image}}]}];
return contents;
}
// Gemini から回答を得る関数
function getGeminiReply(prompt) {
let url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${PropertiesService.getScriptProperties().getProperty('Gemini_key')}`;
const payload = {
'contents': prompt,
'generationConfig': {
'maxOutputTokens': 1024, // 生成する文章の最大トークン数
'temperature': tempRange.getValue() // 生成された文章のランダムさを制御するパラメータ。値が高いほど、よりランダムな文章が生成される
}
};
const options = {
'payload': JSON.stringify(payload),
'method' : 'POST',
'muteHttpExceptions': true,
'contentType':'application/json'
};
try {
const response = JSON.parse(UrlFetchApp.fetch(url, options).getContentText());
return response.candidates[0].content.parts[0].text; // Gemini の回答
} catch {
// エラー発生時
return 'エラー: Gemini の回答が得られませんでした';
}
}
// AI の返答をスプレッドシートに格納する関数
function setAiMessage(role, replyMessage) {
chatLastRow = chatSheet.getLastRow();
chatSheet.getRange(chatLastRow + 1, 1, 1, 2).setValues([[role, replyMessage]]);
}
// LINE メッセージを送信する関数
function sendLineMessage(replyToken, replyMessage) {
const linePayload = {
'method': 'post',
'headers': {
'Content-Type': 'application/json',
'Authorization': `Bearer ${PropertiesService.getScriptProperties().getProperty('Line_key')}`,
},
'payload': JSON.stringify({
'replyToken': replyToken,
'messages': [{
'type': 'text',
'text': replyMessage,
}],
}),
};
UrlFetchApp.fetch('https://api.line.me/v2/bot/message/reply', linePayload);
}
※ 「gemini-1.5-flash-latest」モデルを使ったコードに変更しました(2024-6-1)
1行目の「スプレッドシート ID」は書き換えてください。
コード作成後にウェブアプリとして公開、URL を LINE Messaging API チャンネルで Webhook として設定して完成です。
使い方は、GPT のものと同じです。(ただ、システム指示はできません)
***
今回 Gemini をはじめて触りました。GPT だけでなく、こちらもしっかり追っていければと思います。
LINE bot に関しては、GPT とGemini を切り替えて使えるようにしておくと便利そうな気がします。
![詳解! Google Apps Script完全入門 [第3版]](https://m.media-amazon.com/images/I/51FYffSOBcS._SL160_.jpg)
詳解! Google Apps Script完全入門 [第3版]
by SimpleImageLink
