2023年12月13日より、開発者や企業は、Googleの生成AI「Gemini API 」を介して Gemini Pro にアクセスできるようになりました
3つのモデルのうち、2023年12月13日から「Gemini Pro」だけ使えるようになっています!「Google×生成AI」これは使うしかないでしょ!
また、2023年12月19日から、Bard でGoogle のアプリやサービス(Gmail、Google ドキュメント、Google ドライブ、Google マップ等…)と連携できる拡張機能が日本語版でも公開されました
今後、「Google×AI」の連携がドンドン増えていくこと間違いなしです!
前回の記事で作ったLINE botを会話ができるようもう一段階レベルアップさせてみましょう!
この機会に、皆さんも話題の「Gemini Pro API」を使ってみてね!
この記事を読めば、会話ができるオリジナルLINE botが完成します!
かなりワクワクする内容だと思うので、是非読んでいってください!
GASを極めたい方や、業務の効率化を図りたい方は、ぜひこの記事を読んください!
難しいことはGASに任せて、我々人間は楽しちゃいましょう!
当ブログでは実際に仕事でGASを扱っている私が、GASの魅力について徹底的に取り上げていきます!
前回作った生成AI「Gemini Pro」を使ったオリジナルのLINE botを進化させよう!
完成形のイメージはこんなカンジ!
前回作ったものからレベルアップして会話ができるLINE botの完成を目指します!
【前編】まで終えてから本記事を読んでね!
【STEP1】会話を実現するために入力テキストと出力テキストを保存する
GASには短時間のデータ保存に役立つCacheService
があります
今回はこれを活用して、LINEに入力した文字列およびGemini Proからの回答文字列を保存し、会話に利用します
CacheService
は以下のように3種類ありますが、今回はScriptCache
を使用します
メソッド | 説明 |
---|---|
getDocumentCache() | 現在のドキュメントとスクリプトをスコープとするキャッシュインスタンスを取得します |
getScriptCache() | スクリプトをスコープとするキャッシュインスタンスを取得します |
getUserCache() | 現在のユーザーとスクリプトをスコープとするキャッシュインスタンスを取得します |
getScriptCache()
は、Google Apps Script内でスクリプト全体のスコープで使えるキャッシュを取得するメソッドです
「スクリプト全体のスコープ」とは、同じスクリプト内の異なる関数や処理の間でデータを共有できるという意味です
通常、関数内で変数を宣言すると、その変数はその関数内でのみ有効で、他の関数では利用できません
しかし、getScriptCache()
を使うと、スクリプト全体で使えるキャッシュを取得できます
このキャッシュを使うことで、スクリプト内の異なる場所でデータを共有できるようになります
const sCache = CacheService.getScriptCache();
/**
* LINEのトークでメッセージが送信された際に起動するメソッド
* @param {EventObject} e - イベントオブジェクト
*/
function doPost(e){
// イベントデータはJSON形式となっているため、parseして取得
const eventData = JSON.parse(e.postData.contents).events[0]
, repToken = eventData.replyToken
, msgType = eventData.message.type;
// テキストメッセージのときのみ
if (msgType=='text') {
let uText = eventData.message.text
, gemini = getGeminiProAnswerTxt(uText);
replyTxt(repToken, gemini);
sCache.put('user', uText.slice(0, 10000));
sCache.put('model', gemini.slice(0, 10000));
}
}
上記のようにScriptCache
を使用するための宣言を行い、入力テキストと出力テキストを保存します
【STEP2】保存したテキストにより会話の継続性を実現
改めて、Gemini Pro APIの仕様を確認しましょう
こちらで確認できるようrole
を指定し、複数のパラメタを設定することでMulti-turn conversations (chat)
を実現できることがわかります
curl https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=$API_KEY \
-H 'Content-Type: application/json' \
-X POST \
-d '{
"contents": [
{"role":"user",
"parts":[{
"text": "Write the first line of a story about a magic backpack."}]},
{"role": "model",
"parts":[{
"text": "In the bustling city of Meadow brook, lived a young girl named Sophie. She was a bright and curious soul with an imaginative mind."}]},
{"role": "user",
"parts":[{
"text": "Can you set it in a quiet village in 1600s France?"}]},
]
}' 2> /dev/null | grep "text"
つまり、これまではcontents
には'parts'
> 'text'
のみの設定でしたが、そこにrole
ごとの会話を設定することで、会話の継続性を実現できるということです
それでは実装していきましょう!
/**
* LINEのトークに送信されたメッセージをGemini Pro APIに渡して回答を得るメソッド
* @param {String} txt - 送信されたメッセージ
*/
function getGeminiProAnswerTxt(txt) {
let contentsStr = '';
// キャッシュにuidに紐づく情報が存在した場合、情報には過去の質問文が入っているためそれを取得
if (sCache.get('user')) {
contentsStr += `{
"role": "user",
"parts": [{
"text": ${JSON.stringify(sCache.get('user'))}
}]
},
{
"role": "model",
"parts": [{
"text": ${JSON.stringify(sCache.get('model'))}
}]
},`
}
contentsStr += `{
"role": "user",
"parts": [{
"text": ${JSON.stringify(txt)}
}]
}`
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${GEMINI_API}`
, payload = {
'contents': JSON.parse(`[${contentsStr}]`)
}
, options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(payload)
};
const res = UrlFetchApp.fetch(url, options)
, resJson = JSON.parse(res.getContentText());
if (resJson && resJson.candidates && resJson.candidates.length > 0) {
return resJson.candidates[0].content.parts[0].text;
} else {
return '回答を取得できませんでした。';
}
}
過去のユーザーの入力テキスト (user
) と、Gemini Pro の前回の応答テキスト (model
) を ScriptCache
から取得します
このようにすることでsCache.get('user')
がある、つまりは前回の入力テキストがある場合は、入力テキストをrole:user
に出力テキストをrole:model
に設定したうえで、今回の入力テキストをrole:user
に設定し、Gemini Pro APIに依頼を出せるようになります
ただし、上記のやり方はあくまでも1回前の入出力を設定するだけです。そのため、複数回にわたるトーク履歴を設定したいのであれば、それをScriptCache
に保存し、role:user
およびrole:model
を構築すればばっちりです
ただし、1回前の入出力だけでも大抵の場合は会話が成り立ちます
会話というのは単純なものですね笑
JSON.stringify()メソッドは、JavaScriptオブジェクトをJSON形式の文字列に変換します
これにより、データをサーバーに送信する前にデータを整形することができます
これは、文字列の中に変数やオブジェクトを埋め込む際に便利な機能です
【STEP3】再デプロイする
const GEMINI_API = '**「Gemini Pro」のAPI keyを記載する**'
, REPLY_URL = 'https://api.line.me/v2/bot/message/reply'
, LINEAPI_TOKEN = '**取得したLINEのチャネルアクセストークンを記載する**';
const sCache = CacheService.getScriptCache();
/**
* LINEのトークでメッセージが送信された際に起動するメソッド
* @param {EventObject} e - イベントオブジェクト
*/
function doPost(e){
// イベントデータはJSON形式となっているため、parseして取得
const eventData = JSON.parse(e.postData.contents).events[0]
, repToken = eventData.replyToken
, msgType = eventData.message.type;
// テキストメッセージのときのみ
if (msgType=='text') {
let uText = eventData.message.text
, gemini = getGeminiProAnswerTxt(uText);
replyTxt(repToken, gemini);
sCache.put('user', uText.slice(0, 10000));
sCache.put('model', gemini.slice(0, 10000));
}
}
/**
* LINEのトークに送信されたメッセージをGemini Pro APIに渡して回答を得るメソッド
* @param {String} txt - 送信されたメッセージ
*/
function getGeminiProAnswerTxt(txt) {
let contentsStr = '';
// キャッシュにuidに紐づく情報が存在した場合、情報には過去の質問文が入っているためそれを取得
if (sCache.get('user')) {
contentsStr += `{
"role": "user",
"parts": [{
"text": ${JSON.stringify(sCache.get('user'))}
}]
},
{
"role": "model",
"parts": [{
"text": ${JSON.stringify(sCache.get('model'))}
}]
},`
}
contentsStr += `{
"role": "user",
"parts": [{
"text": ${JSON.stringify(txt)}
}]
}`
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${GEMINI_API}`
, payload = {
'contents': JSON.parse(`[${contentsStr}]`)
}
, options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(payload)
};
const res = UrlFetchApp.fetch(url, options)
, resJson = JSON.parse(res.getContentText());
if (resJson && resJson.candidates && resJson.candidates.length > 0) {
return resJson.candidates[0].content.parts[0].text;
} else {
return '回答を取得できませんでした。';
}
}
/**
* LINEのトークにメッセージを返却するメソッド
* @param {String} token - メッセージ返却用のtoken
* @param {String} text - 返却テキスト
*/
function replyTxt(token, txt){
const message = {
'replyToken' : token,
'messages' : [{
'type': 'text',
'text': txt
}]
}
, options = {
'method' : 'post',
'headers' : {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + LINEAPI_TOKEN,
},
'payload' : JSON.stringify(message)
};
UrlFetchApp.fetch(REPLY_URL, options);
}
プログラムの修正が終わったら、再デプロイしましょう!
これで完成です!お疲れ様でした!
(補足)相談Botを複数人で使いたい場合
これはあくまでも”あなただけの“相談Botなので、ScriptCache
のキーはuser
とmodel
と固定文字です
そのため、もしこの相談Botを複数人で使いたいという場合には、キーに対する値は他人の入出力により更新されてしまいます
これでは会話の継続性がまた失われてしまいますよね
そういった場合は、以下のようにLINEのuserIdをprefixに設定したりすれば解決できます。
sCache.put(eventData.source.userId+'user', uText.slice(0, 10000));
sCache.put(eventData.source.userId+'model', gemini.slice(0, 10000));
私が作った「LINE bot」です!
良かったらお友だちになってください!
まとめ
今回は「前回作ったLINE botに会話を継続できるようバージョンアップしよう!」というテーマで解説を行いました
どうでしょう、もうGASはなんでもできる! は疑いようがないですね!!!
ぜひ、皆さんも実業務での活用に取り組んでみてください
引き続き、GASを楽しんでいきましょう!!
…ところで「Gemini」はマルチモーダル式で作成されていて、画像の理解に長けている生成AIです
LINE上でも「Gemini」に「画像」を認識させたくない?
させたい!
させたい!
というワケで後編は、LINE bot上で「画像」も扱えるように設定します!
GAS×LINE APIだとログが出力されない!と質問を受けたので、外部サービスと連携する際にログを出力する方法についてまとめました!
X(旧:Twieer)にて、ブログの更新やQiita記事の更新、GAS情報をお届けしますので、是非フォローしてください!
おかげさまで今年5月に起業しました!
GASやGoogleサービス、プログラミング全般のご相談承ります!
YouTubeでGoogleサービスをより
快適に使う方法をご紹介しています!
見て頂けたらうれしいです
コメント