レギオンサバイヴの戦略(上級編)

今回はレギオンサバイヴの戦略シリーズ最後の上級編の内容となります。前回の中級編の記事を読みたい方は以下のリンクよりご確認下さい。


では、上級編の内容にいってみましょう!



1.対人戦は心理戦だと心得よ

何と言っても対人戦の醍醐味はこれでしょう!もちろん戦闘力はかなり勝敗に影響を及ぼすのですが、それだけでは同じくらいの強さの相手に太刀打ちできません。中級編で触れた内容に通じる部分もありますが、全てを例示するのは不可能ですので、いくつかを例示します。

a.相手の心が折れたら勝ち

やり方はいろいろありますが、相手が目標とする順位に全く届かなさそうと思わせたり、尽くユニットが負けたりすれば今回は諦めるかぁ…と思って今週のレギオンサバイヴを見なくなる人はいると思います。そうなれば1人脱落してあなたはかなり有利にバトルを進めることができるはずです。ただし「この人心折れた!」というのを察知できればの話になります。これを察知できるか否かでもバトルの優位性は大きく変わります。


b.諦めたらそこで試合終了ですよ

スラムダンクで安西先生も言ってますが、まさにこの通りです。よくあると思うのが、初日にログインできなくて2日目ログインしてみると目の前の拠点が敵に占拠されてて心折れそうになることがあると思います。しかし、ここで諦めずに攻撃してくる人の方が相手は厄介だと思うはずです。中心部の拠点の奪い合いで激戦してる最中に端っこの低レベル拠点を攻撃されても、他の5人もこの時は「こいつになら拠点取られても大丈夫か…」とあっさり明け渡す可能性が高いです。ノーマークだからこそ逆転できる可能性が高いです。

レギオンサバイヴはトップ以外もプレイすればポイントがもらえるので5位や4位でも取る価値はあります。


c.自分がされて嫌なことは相手も嫌

中級編の3番目と4番目の内容に通じる話ですが、自分がされて嫌だな…と思ったことはしっかりと覚えておき、次回それを敵にもやってみましょう。何度か練習して試して自分のものにしましょう。自分を相手に置き換えて、逆に相手を自分に置き換えて考える癖をつけましょう。



2.フェイントを上手く使え

これは中級編の1番目の話に通じることですが、弱いキャラクターは全然役に立たないかというとそうではありません。例えばレベル4拠点に4体セットされたユニットが攻撃しに来ていたらこれは取りにきてそうだから4体の最強ユニット置いておくか、と思うでしょう。そうして最強ユニットを置いておいたら4体ともめちゃくちゃ弱いユニットが来てたら「行動力とられた!」と思うのではないでしょうか。行動力が限られてるが故にフェイントがかなり効果が高いです。



3.ログから敵のユニット情報を収集せよ

これは相手の攻撃ユニットを読むためにかなり重要な情報です。敵の攻撃まで1時間の猶予がありますので、この間にログを見て相手のユニットを大まかに把握し、次はどんなユニットが来そうかということを予想します。これをやり始めると的中率が上がり、バトルを有利に進めることができます。

また、初級編の3番目にも繋がりますが、ログを見てると相手の戦闘力もほぼ分かりますので、戦ってはいけない相手かどうかもよく分かります。



4.好機を虎視眈々と待て

ここまで見られた方は、お前の考え方公開して自分が不利にならないの?と考える方もいらっしゃるかもしれませんが、私はこれまでのことはそこまで大きく上手さに関わる部分だとは思ってません。最も重要なのはこの好機を見極められるか?だと思っているからです。どんな戦いも一瞬のチャンスを逃さず取れれば一気に逆転できる可能性があると思っています。これはどんなゲームも共通する部分が大きいと思います。自分が上手いと言ってる訳では無くて、これまで敵に上手くやられたこともあるし、自分が上手くやれたと感じたこともあるということです。

また極端な例を出せば、1人の圧倒的に強い人が他の4人から同時に攻められている時、自分もその圧倒的に強い人を攻撃すればひょっとすると拠点が奪えるかもしれませんよね。なんか上手く言えませんがそういうことですw 戦いには流れを変えるレベルのチャンスがたまにやってきます。

とにかく6フェーズあるうちに1回現れるかどうかみたいな凄いチャンスを如何に掴み取れるか!というところがレギオンサバイヴの面白いところなのです!

レギオンサバイヴは6人もいるので、それぞれの思惑が絡み合ってとてつもないチャンスが巡ってくる可能性が高いと思っています。


まとめ

初級編から上級編までの内容を組み合わせて応用していくことで、更に勝てる確率が上がると思います。

とりあえず初級編から上級編まで12項目を出してみましたが、面白そうな感じは伝わったでしょうか?

私も最初は面白くなりそうだけと微妙だなーと思ってましたが、徐々に改善されて正式版になり、これはかなり面白い仕上がりになったのでは?と思い始めて楽しくなりました。特に6人全員が上手い時の面白さが半端じゃないです!

ということで一人でも多くレギオンサバイヴをプレイしてくれると私が楽しめるので、プレイ人口を増やしたい!という思いの記事でした!


レギオンサバイヴの戦略(中級編)

前回は初級編について紹介しました。前回の記事を読みたい方は以下のリンクからご確認下さい。



では中級編の内容に入っていきましょう。


1.ユニットは少数精鋭を鍛えよ

レギオンサバイヴにおいては満遍なく24体のキャラクターを鍛えるよりも1キャラクターをすごく強く鍛えた方が有利にバトルを進めることができるようになります。これは初級編の1番目で説明した通り高レベル拠点を取れればかなり有利になるからです(何ならレベル4拠点の奪い合いだから)。強ユニット1つを上手く運用できればかなりポイントを効率良く稼ぐことができます。



2.隣接する敵の数を意識せよ

これは極端に考えると分かりやすいですが、拠点を取りまくって5人の相手と隣接すると、多くのユニットに攻撃されて数多くの拠点を守る必要性が出ます。行動力が限られており、防衛する回数にも限界がありますのですぐに拠点を取られてしまう羽目になります。隣接する相手の数を減らせば1人が同時に出撃できるユニット数は限られていますので、守りやすくなります。



3.足場を出来るだけ長く維持せよ

これは上記2の隣接する敵の数を相手に減らさせない戦略となります。自分の拠点群のど真ん中に敵の拠点が残っていたら四方に攻撃されてしまうので相当厄介だと皆さん感じるでしょう。相手も同じことを思います。



4.複数拠点を同時に攻めよ

これは初級編の2で説明しましたが、防衛が移動時間ほぼ無しで移動できることに由来します。例えば3ユニットを10分おきに同じ相手の別拠点に攻撃したとしても、防衛側は行動力さえあれば同じ強ユニットをそれぞれの拠点の防衛に使うことができます。相手に防衛の移動の隙間を与えないように、攻撃の際は極力複数拠点を同時に攻撃しましょう。どんな戦いも戦力の逐次投入は下策となることが多いです。攻撃の際は一気呵成に攻め立てましょう。


まとめ

中級編は以上ですが、いかがだったでしょうか?ここまで意識して行動している相手を見たら、「あ、慣れてるな」という印象でかなり手強い相手です。中級編までは表面的に見えやすい部分を扱いましたが、上級編ではなかなか見えづらい部分について扱っていきます。上級編は以下のリンクよりご覧下さい。


レギオンサバイヴの戦略(初級編)

レギオンサバイヴ正式版が始まり、もうすぐ第1シーズンが終わろうとしていますが、まだまだ盛り上がりには欠ける気がします。個人的にはファンキルオルタナの中ではレギオンサバイヴが一番好きなコンテンツなので、今後もさらに盛り上がってほしいと思っております。軽く検索しても戦略的な記事が見つからなかったので、今後の盛り上がりに寄与できるよう私の個人的な戦略の考え方をまとめて、レギオンサバイヴって面白そう!と思ってもらえたら幸いです。

一応プラチナランクには行けたので、私はそんなに下手な方じゃないのではなかろうかと思って書いてます。

この記事はレギオンサバイヴのルールは把握している方向けの内容になります。基本的なルール等は以下のリンク先よりご確認下さい。



今回はまずは初級編の内容になります。


1.高レベル拠点の重要性を認識せよ

まず各拠点のポイント数を覚えていない方は再度確認してください。1つ上のレベルの拠点はポイント数が約倍になります。レベル5拠点は1つしかありませんのでほぼ取れないとしても、レベル4拠点はほぼ人数分ありますのでレベル4拠点の確保具合で勝敗が決まると言っても過言ではありません。レギオンサバイヴはレベル4拠点の奪い合いゲームだと認識しましょう。

そしてレベル5拠点を守りきれる力がある人は1位になれる可能性が高いです。レベル4拠点約2個分のパワーですからね。相当有利だと認識しましょう。


2.攻撃のメリットと防衛のメリットを認識せよ

攻撃と防衛のメリットとデメリットは表裏一体ですが、これはかなり重要なので改めて認識しておきましょう。

  • 攻撃のメリット:相手からユニットが見えない
  • 防衛のメリット:移動時間がほぼ無い


3.戦ってはいけない相手を見極めよ

これはレギオンサバイヴ開始直後に必ず確認しましょう。ログやランキングなどからマスターランクを確認できますので、それでおおよその強さを確認します。ランクが5離れるごとに通常武具の強さが変わるので、5刻みのランクは意識しましょう。

今で言うと金獅子武器が装備できるかどうかのランク90に到達してるか否かは大きな差です。銀獅子武器装備可能ランク65も境目の一つです。


4.何位を目指すかを決めよ

戦ってはいけないかどうかを見極めることができれば、上からランク順で考えて自身は何位くらいに到達できそうかどうかが分かると思います。何位を目指すかで確保していく拠点が変わって行くと思いますので、これは最初に確実に決めましょう。



まとめ

初級編はここまでです。これらを考えておけば強い相手ばかりの時に全く楽しめないようなことが無くなると思います。次回は中級編ということで少し具体的な内容が多めになります。

中級編は以下のリンクよりご覧下さい。


Google Pixel 9 発表まとめ

本日Google keynoteの発表がYouTubeにて公開されましたので、スゴかったポイントなどを簡単にまとめます。


Gemini Live

遂にGeminiと音声で会話できるようになるようです。Open AIのChatGPTに対抗する機能となりそうです。最初はGemini Advancedプランのうち英語圏のユーザが対象となるようです。Gemini Liveは全てのAndroidスマートフォンへ順次展開予定とのことです。

GeminiLive1


GeminiLive2


気になる会話のレスポンス速度ですが、動画のデモを見る感じだとかなりすぐに答えてくれるようです。3秒は無いくらいでしょうか。ほぼストレス無く会話できそうで凄いです。


googlepixelbudspro1

ちなみにGemini Liveは今回新登場するGoogle Pixel Buds Pro 2でも使えるようで、イヤホン付けてGeminiと会話するデモがありました。これはかなり便利になりそうな予感がしますね。



Google Pixel

次はいよいよ新登場のGoogle Pixelシリーズの紹介です。

googlepixel1

左から順に「Google Pixel 9 Pro Fold」、「Google Pixel 9 Pro XL」、「Google Pixel 9 Pro」、「Google Pixel 9」です。

見た目はこれまで通りあまり変わらずです。色が子供が好きそうなピンク色?が出てきました。個人的にはけっこう好きな色合いですね。


googlepxel2

Google Pixel 9はPixel 8と比較してバッテリーが20%程度長持ちになったようです。



Pixel Call Assist

Pixel 9では通話の概要がメモとして通話記録に残るようになるそうです。これいろいろ電話する人にとってはかなり便利な機能になるのではないでしょうか。

googleCallAssist1

これいろいろ電話する人にとってはかなり便利な機能になるのではないでしょうか。


googleCallAssist2


その上、会話の全てのトランスクリプトも表示できるようです。これ地味に欲しい機能ですね。



Pixel Screenshots

Pixel Screenshotでは、スクリーンショットした内容がGeminiで抽出できるようになり、いろいろできるようになるようです。

PixelScreenshots1

「bikes」って検索したら自転車の画像が出てきたり、(Google フォトでもできたような)


PixelScreenshots2

「T-shirt price」って入力したらTシャツの値段が出てきたり、(あんまり使わなさそう…)


PixelScreenshots3

まああんまり使わないかもな…と思っていたら最後にスクリーンショットの詳細画面みたいなところからChromeマークを押すと…


PixelScreenshots4

んん!?!?スクリーンショット撮ったサイトに飛んだだと~~~!?これはけっこう凄い!

どうやらスクリーンショット撮ったサイトを覚えているとのこと。これはけっこう役に立ちそう!



Pixel Studio

こちらは画像編集機能のようですが、今までのGeminiみたいに文字を入力して画像生成ができるようです。デモを見ると画像生成なのにレスポンスがかなり速い!会話と同じく3秒くらいでしょうか。まあここまでは今までと同じなのであまり驚きもない感じ。

PixelStudio1

そして生成された画像の下に何と「Add caption」と「Add sticker」というボタンがある!!


PixelStudio2

このボタンからステッカーを入れたり、文字を入れたりできるようで、下のような画像が作れるそうです。めっちゃ簡単w



Pixel Camera

カメラもけっこう性能が上がったようです。

PixelCamera1

Pixel 9 Pro XLはiPhone 15 Pro Maxよりすごいぞってアピールしてましたが、ホンマかいな?って思うような比較画像が以下の通り。カメラは素人にはもうわからない次元なので、とりあえずスルー。



Pixel Camera Add me

そして今回一番驚いたのが次の「Add me」という機能からです。「Pixel 8で写っている人のベストテイクを撮れるようになりましたが、最大の問題点はグループ全員をどのように写すかということです。」という導入に始まり、「場合によっては自分も写りたい写真を撮ることもあります。」……… ま、まさか!?

まずはAdd meモードで普通に写真を撮って、

PixelCameraAddme1

まずはAdd meモードで普通に写真を撮って、


PixelCameraAddme2

次は撮影者を入れ替えてオーバーレイのガイドに従って写真を撮ると…


PixelCameraAddme3

写真を処理して、はい、この通り!って凄すぎーーーー!!なかなかの衝撃映像な気がするんですけど!w

これ考えたやつ天才過ぎでしょw


Google Photos Magic Editor

そして次は消しゴムマジックに次ぐマジックエディターなる機能です。遂に消すだけでなく編集ができるようになるそうです。

MagicEditor1

こんな感じで傾いた画像をオートフレームすると、


MagicEditor2

こんな感じに生成AIが再構成してくれるとのこと。まあまあすごい。


MagicEditor3

「でもまだこれでは物足りないので、こうして…」

(え…?)


MagicEditor4


「こう入力すると…」

(えええ……!?)


MagicEditor5

「oh yah.」

(えええええええええええーーーーー!?!?!)

これもなかなか衝撃映像だと思いますが、マジですか!?これできるんですか!


MagicEditor6

「まだまだ調理中です。ここにズームして。」


MagicEditor7

「こうやって…」


MagicEditor8

「熱気球って入力すると……」


MagicEditor9

「Wow…」

(やべーーーー!w)


これは凄すぎますね。Pixel 9を持っていれば誰でも画像加工職人になれますね。


販売価格

では気になる価格ですが、日本での販売価格は以下の通りのようです。


Google Pixel 9 128GB 128,900 円

Google Pixel 9 256GB 143,900 円

Google Pixel 9 Pro 128GB 159,900 円

Google Pixel 9 Pro 256GB 174,900 円

Google Pixel 9 Pro 512GB 194,900 円

Google Pixel 9 Pro XL 128GB 177,900 円

Google Pixel 9 Pro XL 256GB 192,900 円

Google Pixel 9 Pro XL 512GB 212,900 円

Google Pixel 9 Pro Fold 256GB 257,500 円

Google Pixel 9 Pro Fold 512GB 277,500 円


また、特典については以下の通りのようです。


対象製品

通常価格

セール内容や​特典

Google Pixel 9 Pro

159,900 円より

実質割引額:​最大 120,100円 - 32,100 円分の​ストアクレジット - 対象スマホの​下取りで​払い​戻し​(Google Pixel 8 Pro の​場合、​最大 88,000 円)
Google Pixel 9 Pro XL

177,900 円より

実質割引額:​最大 138,100 円 - 50,100 円分の​ストアクレジット - 対象スマホの​下取りで​払い​戻し​(Google Pixel 8 Pro の​場合、​最大 88,000 円)
Google Pixel 9

128,900 円より

実質割引額:​最大 89,100 円 - 24,200 円分の​ストアクレジット - 対象スマホの​下取りで​払い​戻し​(Google Pixel 8の​場合、​最大 64,900 円)
Google Pixel 9 Pro Fold

257,500 円より

実質割引額:​最大 157,700 円 - 57,700 円分の​ストアクレジット - 対象スマホの​下取りで​払い​戻し​(Google Pixel Fold の​場合、​最大 100,000 円)
Google Pixel Watch 3 (45 mm)

59,800 円より

12,800 円分の​ストアクレジット

Google Pixel Watch 3 (41 mm)

52,800 円より

10,800 円分の​ストアクレジット

Google Pixel Buds Pro 2

36,800 円より

6,800 円分の​ストアクレジット


まとめ

geminiが発表されてからGoogle Pixelの発表会は非常にワクワクしますね。生成AIを活用した機能が年々進化していって、また来年も楽しみですね。

記事を見て気になった方は以下の発表会動画も見てみてくださいね。



ヘルプデスクGPTの作り方と実践ガイド - 自動応答とFAQ管理

 今回はヘルプデスクのチャットボットのように自動で回答してくれるチャットボットをGPTsで作成してみました。自動応答だけだとよく見かけるチャットボットと大差ないので、FAQなどを自動でメンテナンスしてくれるFAQ管理機能も付けてみました。

実際作ってみると、これがちゃんとできてしまえばもうヘルプデスクに人要らないのでは?と思うくらい凄いと思いましたので参考になると幸いです。



ヘルプデスクGPTsの全体概要

まず、利用者側のチャットボットと管理者側のチャットボットとでIFを分けてみました。

全部をまとめようとするとGPTsを作るのが大変なので、それぞれ別にすることでシンプルにしようとしています。ヘルプデスクGPTの概要図は以下のようなイメージ図です。


全体概要

大抵のヘルプデスクなどでは、お問い合わせの一覧的なものがあるかと思いますので、QA一覧はそういった管理表を想定しています。

FAQ一覧はよくある質問などのFAQの一覧等を想定しています。

Googleスプレッドシートへの操作はチャットボット側から操作する際は、Google Apps Scriptを利用して操作します。

管理者側から操作する際もGoogle Apps Scriptを利用して操作しています。これは後程紹介しますが、ChatGPTのAIの能力を活かすためです。普通にスプレッドシートを開いてそのまま利用してもいいと思います。

今回はGPTsの構成の部分の知識にファイルをアップロードするのではなく、何故Googleスプレッドシートを使うのかと言うと、知識の部分は人の手でメンテナンスする必要があるからです。また、知識の部分にアップロードできるファイルの数にも上限があったりするようです。この2つの部分を効率化するためにも今回はGPTs内部ではなく、外部にデータを持たせることにしています。



GPTsの動作

では、早速に作ってみたGPTsの動作から見ていきましょう。今回はIT関係のヘルプデスクを想定してQAやFAQのサンプルデータを作成してます。


利用者側GPTsの動作

利用者が任意の質問をすると、FAQやこれまでのQAから回答を探して、回答してくれます。ここまでだとこれまでの古のチャットボットと変わらないじゃないかと思うかもしれませんが、GPTsはインターネット検索することができますので、FAQやQAに回答が無ければ、インターネット上の一般的な知識を利用しても回答をしてくれます。やっぱりChatGPT使うならChatGPT自体の能力も活用したいですよね!

以下のような感じで動きます。

まずはユーザが問い合わせるところからです。まずは以下のような感じで問い合わせせるとします。すると、スプレッドシートのQA一覧にお問い合わせ内容を記録するとともに、回答をFAQや過去のQAの履歴から検索します。今回はFAQにもQAにも回答が無かったので、ChatGPT側の知識でいくつか追加情報をユーザに求めてヒアリングしようとしています。今回はインターネット検索していないですが、たまにインターネット検索することもあります。

利用者側GPTs動作1

ちなみに、この時のFAQ一覧は以下の状態です。

FAQ一覧1


QA一覧は以下の状態です。No.12に今回のユーザの問い合わせ内容が記録されていますね。


利用者側GPTs動作2

では、言われたとおりに追加情報を伝えてみましょう。実はFAQにブラウザに関する情報を入れていたので、今回は聞いてくれなかったですが、ブラウザの情報も提供してみることにします。すると、FAQには具体的な回答は無いとは言うものの、Firefoxは動作保証がされていない旨の情報を提供してくれました!GPTさんやりますね!!

利用者側GPTs動作3

このチャットのタイミングでApps Scriptへ通信しようとしてますが、これはユーザから提供された内容を基に質問に追加情報を付加してQA一覧のお問い合わせ内容を更新する処理です。この処理後、スプレッドシートのNo.12のお問い合わせ内容は以下の通りFirefoxを利用している旨の情報が付加されています。こうやってユーザに自動でヒアリングしてくれてどんどん情報を付加していってくれるととっても便利ですよね!


利用者側GPTs動作4


では、解決したことにしてみましょう。すると以下のように今後のためにどうやって解決したのかを聞いてくれます。


そして、解決方法を教えてもらったらQA一覧へ回答内容を入力してくれます。解決後のQA一覧の内容は以下の状態です。

利用者側GPTs動作6

ここまで自動でやってくれると相当便利ではないでしょうか!私も探り探りできるのかな?と思ってやってみてましたが、かなりChatGPTが賢くてあっさりとやりたいところまで全てできてしまいました。

回答を自動で記録してくれるということは、同様の質問が別のユーザから来たとしても次回は回答できてしまうということです!

では、別のユーザが問い合わせしてきたとして、新しいチャットを開いて似たような問い合わせをしてみましょう。

利用者側GPTs動作7
はい、もうGPT先生天才過ぎます。次からすぐ回答してくれるようになりました。これはヘルプデスクの回答精度がみるみる上がっていきそうですね!

こちらの問い合わせも上手に回答してくれたので、解決したことにしましょう。

利用者側GPTs動作8

「ありがとうございました。」などの不要な言葉が入っていると、ChatGPT側で判断して、以下のように不要なワードは削除してQA一覧に記録してくれます。賢いですね。

利用者側GPTs動作9


利用者側のGPTsの動作は以上です。ここまででかなりChatGPTの凄さを体感しましたが、本当に恐ろしく凄いのはここからです。では、次は管理者側のGPTsの動作を見ていきましょう!



管理者側GPTsの動作

管理者側のGPTsの機能としては、FAQ一覧のメンテナンス機能と未回答一覧の取得機能を作ってみました。

人間がやるには非常に面倒なFAQのメンテナンスですが、GPT先生はそんな簡単にやってしまうのでしょうか!?やってみましょう。

まずは、更新前のFAQ一覧は先ほどと同様に以下の通りです。


このFAQ一覧を先ほどのQA一覧の内容を基に更新をかけます。チャットはおまじないのように「FAQメンテナンス」と唱えればいいだけにしました。「FAQメンテナンス」と伝えるとFAQ一覧が以下のような感じに更新されます。凄い!!!!

管理者側GPTs動作2

このFAQ一覧のメンテナンスですが、QA一覧にあってFAQ一覧に足りない分を補っているだけかと思ったら大間違い。GPTs側に追加で指示を記述したのですが、個別対応となるような問い合わせは除外するように指定してます。そのため、FAQにあるべきではないシステム復旧依頼のような問い合わせについては、FAQに追加されてません。けっこう何度もこのメンテナンス処理をしてみましたが、復旧依頼の問い合わせについては、一度も追加されたケースは見かけませんでした。

もちろんChatGPT側で判断していい具合に更新をかけるので、毎回若干更新内容は異なります。ここは上手く指示するなり人間のチェックで補正をかけるなりで対応が必要なところでしょう。

しかし、FAQ更新のドラフトがこんなに簡単に出来上がり、かつ利用者側のチャットボットに即時反映されるのであれば、今までの手間は何だったんだというくらいに圧倒的に楽になるのではないでしょうか!

ただ、こちらのFAQメンテナンスボットですが、なぜかユーザへの回答では以下のようにエラーと返してしまいます。私の実装が若干不足しているだけだと思いますが、メインとなるスプレッドシートの更新まできちんと意図した通りできているので、時間ももったいないしここで諦めました。

管理者側GPTs動作1


また、おまけで管理者っぽく未回答の状況を確認できる機能の方も作ってます。こちらは「未回答一覧取得」と唱えると動作してくれます。


こうして全体の状況などを参照できると管理者としてはやりやすいかと思います。これができればChatGPTと一緒に回答を検討して回答案を作成したりできるようになると思うので、いろいろ活用の幅が広がりそうですよね。



利用者側GPTsの作り方

では本題のGPTsの作り方を説明します。利用者側のGPTsの構成(GPTの設定)は以下の通りです。

利用者側GPTs構成1
利用者側GPTs構成2


指示の内容

  1. 以下の入力パターンに応じて回答するボットです。
  2. #対応パターン1
  3. 以下の回答段取りでユーザへ回答してください。
  4. ##回答段取り
  5. 1.ユーザから問い合わせがあったら、ユーザの質問内容をスプレッドシートに追加してください。
  6. 2.以下の「回答検索段取り」で回答を取得してください。
  7. 3.回答を見つけたら'回答更新'のリクエストをスプレッドシートへ送信してください。
  8. 4.ユーザへ回答してください。「回答検索段取り」で回答を取得し、質問にマッチする回答案が無い場合は、スプレッドシートに追加した結果の氏名、カテゴリ、お問い合わせ内容、回答希望日を回答の上、ユーザに少々お時間を頂くように連絡してください。また、他に提供可能な情報が無いかいくつか選択肢を提示してヒアリングを行ってください。
  9. 5.ユーザが情報提供してくれた場合には、ヒアリング内容を基に'質問更新'を実施してスプレッドシートに提供された情報を付加して問い合わせ内容を更新してください。
  10. ##回答検索段取り
  11. 1.スプレッドシートのFAQ一覧から、回答案を取得してください。
  12. 2.FAQ一覧内に質問にマッチする回答案が無い場合は、スプレッドシートのQA一覧を検索し、最も近い回答案を取得してください。
  13. 3.QA一覧内に質問にマッチする回答案が無い場合は、ウェブ参照にてインターネットを検索し、最も近い回答案を取得してください。
  14. #対応パターン2
  15. ユーザから別の回答案、もしくは別の調査項目が欲しい旨の要望があった場合は、以下の「追加対応手順」に従い対応してください。
  16. ##追加対応手順
  17. 1.ヒアリング内容を基に'質問更新'を実施してスプレッドシートに追加した問い合わせ内容を更新してください。
  18. 2.ヒアリングを行って質問へマッチする回答案が見つかった場合は回答案をスプレッドシートへ'回答更新'します。
  19. 3.回答更新後、更新した回答案をユーザへ回答をしてください。
  20. #対応パターン3
  21. 対応パターン1の対応後、別の質問をされたら、再度対応パターン1の通りに対応してくしてください。
  22. #対応パターン4
  23. ユーザが問題を自己解決した場合は、以下の「解決方法確認手順」に従い対応してください。
  24. ##解決方法確認手順
  25. 1.ユーザにどうのようにしたら解決したかを確認する。今後のサービス向上のために必要である旨を添えてください。
  26. 2.ユーザに確認した解決方法を'回答更新'を実行することで、回答を更新する。

ChatGPTへの指示は、パターンの考え方や手順のみとし、それぞれの手順の詳細等は考え方だけを指示することで、ChatGPTに考えさせます。いろいろな詳細事項を考えるところが一番面倒なところなので、その一番面倒な部分をAIに考えさせるように作りこむことで全体を設計します。


利用者側GPTsのActionのスキーマは以下の通りです。

  1. {
  2.   "openapi": "3.1.0",
  3.   "info": {
  4.     "title": "スプレッドシートデータの検索・更新・追加",
  5.     "description": "ユーザの問い合わせの状況に応じてスプレッドシートデータの追加・回答更新・取得・質問更新を行います。",
  6.     "version": "v1.0.0"
  7.   },
  8.   "servers": [
  9.     {
  10.       "url": "https://script.google.com"
  11.     }
  12.   ],
  13.   "paths": {
  14.     "/macros/s/AKfycbwSacdMPMciSGO0J85mPwJ43j-9iamODzPGQyWkYaB7T9_QipCPi-eRFm_SP8KYenVB/exec": {
  15.       "get": {
  16.         "description": "ユーザから問い合わせがあった場合は、問い合わせ内容の'追加'のリクエストを送信します。回答案を取得する場合は、'取得'のリクエストを送信します。ユーザへの回答後に回答をスプレッドシートへ更新する場合は、'回答更新'のリクエストを送信します。質問の更新をする場合は、'質問更新'のリクエストを送信します。",
  17.         "operationId": "operation sheet",
  18.         "parameters": [
  19.           {
  20.             "name": "action",
  21.             "in": "query",
  22.             "description": "'追加' or '取得' or '回答更新' or '質問更新'",
  23.             "required": true,
  24.             "schema": {
  25.               "type": "string"
  26.             }
  27.           },
  28.           {
  29.             "name": "sheet",
  30.             "in": "query",
  31.             "description": "回答案を取得するシート名で、'FAQ' or 'QA'",
  32.             "required": true,
  33.             "schema": {
  34.               "type": "string"
  35.             }
  36.           },
  37.           {
  38.             "name": "timestamp",
  39.             "in": "query",
  40.             "description": "タイムスタンプがこのスプレッドシートのIDの代わりになります。'追加'時は空文字を送信します。タイムスタンプの形式は、'yyyy/MM/dd hh:mm:ss'で、ユーザが入力した時のJSTです。'取得'、'回答更新'、'質問更新'時は最初に質問を追加した時にgoogle apps scriptから返却されたレスポンスのタイムスタンプを送信します。",
  41.             "required": true,
  42.             "schema": {
  43.               "type": "string"
  44.             }
  45.           },
  46.           {
  47.             "name": "name",
  48.             "in": "query",
  49.             "description": "氏名",
  50.             "required": true,
  51.             "schema": {
  52.               "type": "string"
  53.             }
  54.           },
  55.           {
  56.             "name": "category",
  57.             "in": "query",
  58.             "description": "主にシステム名になるカテゴリです。",
  59.             "required": true,
  60.             "schema": {
  61.               "type": "string"
  62.             }
  63.           },
  64.           {
  65.             "name": "content",
  66.             "in": "query",
  67.             "description": "お問い合わせ内容。対応パターン2の場合は、最初の質問内容にユーザからヒアリングした内容を付加して値をお問い合わせ内容をセットします。",
  68.             "required": true,
  69.             "schema": {
  70.               "type": "string"
  71.             }
  72.           },
  73.           {
  74.             "name": "deadline",
  75.             "in": "query",
  76.             "description": "回答希望日。フォーマットは、'yyyy/MM/dd'",
  77.             "required": true,
  78.             "schema": {
  79.               "type": "string"
  80.             }
  81.           },
  82.           {
  83.             "name": "answer",
  84.             "in": "query",
  85.             "description": "ユーザへ回答した内容",
  86.             "required": true,
  87.             "schema": {
  88.               "type": "string"
  89.             }
  90.           }
  91.         ],
  92.         "deprecated": false
  93.       }
  94.     }
  95.   },
  96.   "components": {
  97.     "schemas": {
  98.       "NameResponse": {
  99.         "type": "object",
  100.         "properties": {
  101.           "name": {
  102.             "type": "string"
  103.           },
  104.           "category": {
  105.             "type": "string"
  106.           },
  107.           "content": {
  108.             "type": "string"
  109.           },
  110.           "deadline": {
  111.             "type": "string"
  112.           },
  113.           "answer": {
  114.             "type": "string"
  115.           },
  116.           "QAdata": {
  117.             "type": "string"
  118.           }
  119.         }
  120.       }
  121.     }
  122.   }
  123. }

こちらは、APIのパラメータ値でApps Script側で処理を分岐をさせるので、それぞれのパラメータの役割をきちんと教える必要があります。ここは確実に間違えないように具体的な値を指示する必要があります。

また、データ型も間違えるとApps Script側でエラーになるので、データフォーマットも具体的に指定します。ChatGPTへの指示も、何をする部分なのかに応じて指示の抽象度を変えて指示していくことがコツになると思っています。


Actionには一番最初の全体概要に示した通り、google Apps Scriptを使っています。Apps Scriptの利用者側のコードは以下の通りです。

QABot.gs

  1. function doGet(e) {
  2.   var action = e.parameter.action;
  3.   if (action == '追加') {
  4.     return addAction(e);
  5.   } else if (action == '取得') {
  6.     return getData(e);
  7.   } else if (action == '回答更新') {
  8.     return updateAnswer(e);
  9.   } else if (action == '質問更新') {
  10.     return updateQuestion(e);
  11.   } else {
  12.     return ContentService.createTextOutput(
  13.       JSON.stringify({ "error": "Invalid Action." })
  14.     ).setMimeType(ContentService.MimeType.JSON);
  15.   }
  16. }
  17. function addAction(e) {
  18.   // リクエストからパラメータを取得
  19.   var addtimestamp = Utilities.formatDate( new Date(), 'Asia/Tokyo', 'yyyy/MM/dd hh:mm:ss');
  20.   var addname = e.parameter.name;
  21.   var addcategory = e.parameter.category;
  22.   var addcontent = e.parameter.content;
  23.   var adddeadline = e.parameter.deadline;
  24.   // スプレッドシートデータの取得
  25.   var sheet = SpreadsheetApp.openById('{スプレッドシートID}').getSheetByName('QA');
  26.   var lastRow = sheet.getLastRow();
  27.   if (lastRow > 1) { // データがある場合
  28.     var lastIdCell = sheet.getRange(lastRow, 1).getValue(); // 最後の行のIDを取得
  29.     lastId = parseInt(lastIdCell, 10);
  30.   }
  31.   sheet.appendRow([addtimestamp, addname, addcategory, addcontent, adddeadline]);
  32.   return ContentService.createTextOutput(
  33.     JSON.stringify({ "timestamp": addtimestamp, "name": addname, "category": addcategory, "content": addcontent, "deadline": adddeadline, "answer": "", "QAdata": "" })
  34.   ).setMimeType(ContentService.MimeType.JSON);
  35. }
  36. function getData(e) {
  37.   // リクエストからパラメータを取得
  38.   var targetsheet = e.parameter.sheet;
  39.   // スプレッドシートの準備
  40.   var sheet = SpreadsheetApp.openById('{スプレッドシートID}').getSheetByName(targetsheet);
  41.   var data = sheet.getDataRange().getValues();
  42.   // スプレッドシートのデータを返却
  43.   return ContentService.createTextOutput(
  44.     JSON.stringify({ "timestamp": "", "name": "", "category": "", "content": "", "deadline": "", "answer": "", "QAdata": data })
  45.   ).setMimeType(ContentService.MimeType.JSON);
  46. }
  47. function updateAnswer(e) {
  48.   // リクエストからパラメータを取得
  49.   var searchtimestamp = e.parameter.timestamp;
  50.   var updateanswer = e.parameter.answer;
  51.   // スプレッドシートデータの取得
  52.   var sheet = SpreadsheetApp.openById('{スプレッドシートID}').getSheetByName('QA');
  53.   var data = sheet.getDataRange().getValues();
  54.   // スプレッドシートを検索
  55.   for (var i = 1; i < data.length; i++) { // 2行目から開始
  56.     if (Utilities.formatDate(data[i][0], "JST", "yyyy/MM/dd HH:mm:ss") === searchtimestamp.toString()) {
  57.       sheet.getRange(i + 1, 6).setValue(updateanswer);
  58.       var responseanswer = sheet.getRange(i + 1, 6).getValues();
  59.       // 見つかった場合、JSONとして返す
  60.       return ContentService.createTextOutput(
  61.         JSON.stringify({ "timestamp": "", "name": "", "category": "", "content": "", "deadline": "", "answer": responseanswer, "QAdata": "" })
  62.       ).setMimeType(ContentService.MimeType.JSON);
  63.     }
  64.   }
  65.   
  66.   // 一致するデータが見つからない場合
  67.   return ContentService.createTextOutput(
  68.     JSON.stringify({ "error": "回答を記録する対象の質問が見つかりませんでした。" })
  69.   ).setMimeType(ContentService.MimeType.JSON);
  70. }
  71. function updateQuestion(e) {
  72.   // リクエストからパラメータを取得
  73.   var searchtimestamp = e.parameter.timestamp;
  74.   var updatecontent = e.parameter.content;
  75.   // スプレッドシートデータの取得
  76.   var sheet = SpreadsheetApp.openById('{スプレッドシートID}').getSheetByName('QA');
  77.   var data = sheet.getDataRange().getValues();
  78.   // スプレッドシートを検索
  79.   for (var i = 1; i < data.length; i++) { // 2行目から開始
  80.     if (Utilities.formatDate(data[i][0], "JST", "yyyy/MM/dd HH:mm:ss") === searchtimestamp.toString()) {
  81.       sheet.getRange(i + 1, 4).setValue(updatecontent);
  82.       var responseContent = sheet.getRange(i + 1, 4).getValues();
  83.       // 見つかった場合、JSONとして返す
  84.       return ContentService.createTextOutput(
  85.         JSON.stringify({ "timestamp": "", "name": "", "category": "", "content": responseContent, "deadline": "", "answer": "", "QAdata": "" })
  86.       ).setMimeType(ContentService.MimeType.JSON);
  87.     }
  88.   }
  89.   
  90.   // 一致するデータが見つからない場合
  91.   return ContentService.createTextOutput(
  92.     JSON.stringify({ "error": "更新対象の質問が見つかりませんでした。" })
  93.   ).setMimeType(ContentService.MimeType.JSON);
  94. }


管理者側GPTsの作り方

管理者側のGPTsの構成は以下の通りです。

管理者側GPTs構成1
管理者側GPTs構成2


指示の内容

  1. 以下の入力パターンに応じて対応するボットです。
  2. #対応パターン1
  3. 「FAQメンテナンス」とユーザが指示したら、以下の「メンテナンス段取り」でFAQデータをメンテナンスします。
  4. ##「メンテナンス段取り」
  5. 1.FAQ一覧を取得します。取得した内容は表示不要です。
  6. 2.QA一覧を取得します。取得した内容は表示不要です。
  7. 3.「FAQ一覧の更新の考え方」に基づいてFAQ一覧を更新し、'FAQ更新'処理を実行する。FAQのデータ形式は変更しない。FAQの更新内容はユーザへ確認及び表示せずに'FAQ更新'処理を実行します。
  8. ###FAQ一覧の更新の考え方
  9. 1.FAQ一覧に掲載されておらず、QA一覧にて頻出の質問は追加する。
  10. 2.FAQ一覧に掲載されている回答よりも、もっと良い回答がQA一覧にあれば、FAQの回答をその回答に更新する。
  11. 3.個別の質問・対応内容と考えられるQAについては掲載しない。
  12. #対応パターン2
  13. 「未回答一覧取得」とユーザが指示したら、QA一覧を取得し、未回答分の質問を出力します。
  14. 未回答件数が10件未満の場合は、すべての質問の一覧を出力します。
  15. 未回答件数が10件以上ある場合は、未回答の件数を出力し、任意の10件の質問の一覧を出力します。

一番メインとなるFAQ一覧の更新のやり方については、考え方のレベルまでを指示します。具体的にきちんと指示するのであれば、それは通常のプログラム等にて処理すればよいことになります。あくまでAIにやってほしいことは、抽象的な内容から具体的な操作に落とし込む面倒なところをやってほしいので、その面倒な部分をChatGPTに考えさせます。


管理者側GPTsのActionのスキーマは以下の通りです。

  1. {
  2.   "openapi": "3.1.0",
  3.   "info": {
  4.     "title": "QA管理者用機能",
  5.     "description": "FAQもしくはQAの'取得'、'FAQ更新'、'未回答一覧取得'を行う",
  6.     "version": "v1.0.0"
  7.   },
  8.   "servers": [
  9.     {
  10.       "url": "https://script.google.com"
  11.     }
  12.   ],
  13.   "paths": {
  14.     "/macros/s/AKfycbwSacdMPMciSGO0J85mPwJ43j-9iamODzPGQyWkYaB7T9_QipCPi-eRFm_SP8KYenVB/exec": {
  15.       "post": {
  16.         "description": "FAQかQAの一覧取得の場合は、'取得'のリクエストを送信します。FAQ一覧を更新する場合は、'FAQ更新'のリクエストを送信します。未回答一覧を取得する場合は、'未回答一覧取得'のリクエストを送信します。",
  17.         "operationId": "operation sheet",
  18.         "parameters": [
  19.           {
  20.             "name": "action",
  21.             "in": "query",
  22.             "description": "'取得' or 'FAQ更新' or '未回答一覧取得'",
  23.             "required": true,
  24.             "schema": {
  25.               "type": "string"
  26.             }
  27.           },
  28.           {
  29.             "name": "sheet",
  30.             "in": "query",
  31.             "description": "FAQ一覧を取得する場合は、'FAQ'をセットし、QA一覧を取得する場合は'QA'をセットする。",
  32.             "required": true,
  33.             "schema": {
  34.               "type": "string"
  35.             }
  36.           },
  37.           {
  38.             "name": "faqdata",
  39.             "in": "query",
  40.             "description": "FAQ一覧を更新するための更新後のFAQ一覧の文字列型の配列データをセットする。セットするFAQデータのサンプルは、次のフォーマットで、各要素全て文字列です。#フォーマット:[[\"a\",\"b\",\"c\",\"d\"],[\"e\",\"f\",\"g\",\"h\"]]。ヘッダ行は削除する。",
  41.             "required": false,
  42.             "schema": {
  43.               "type": "string"
  44.             }
  45.           }
  46.         ],
  47.         "deprecated": false
  48.       }
  49.     }
  50.   },
  51.   "components": {
  52.     "schemas": {
  53.       "NameResponse": {
  54.         "type": "object",
  55.         "properties": {
  56.           "responsedata": {
  57.             "type": "string"
  58.           }
  59.         }
  60.       }
  61.     }
  62.   }
  63. }

こちらは、APIのパラメータで分岐をさせるので、それぞれのパラメータの役割をきちんと教える必要があります。特に、今回のApps Scriptは文字列で分岐する設計になっているので、ここは確実に間違えないように具体的な値を指示する必要があります。


Apps Scriptの管理者側のコードは以下の通りです。

QAmaintenance.gs

  1. function doPost(e) {
  2.   var action = e.parameter.action;
  3.   if (action == '取得') {
  4.     return getDataForMaintenance(e);
  5.   } else if (action == 'FAQ更新') {
  6.     return updateFaq(e);
  7.   } else if (action == '未回答一覧取得') {
  8.     return getUnAnsweredData(e);
  9.   } else {
  10.     return ContentService.createTextOutput(
  11.       JSON.stringify({ "error": "Invalid Action." })
  12.     ).setMimeType(ContentService.MimeType.JSON);
  13.   }
  14. }
  15. function getDataForMaintenance(e) {
  16.   // リクエストからパラメータを取得
  17.   var targetsheet = e.parameter.sheet;
  18.   // スプレッドシートデータの取得
  19.   var sheet = SpreadsheetApp.openById('{スプレッドシートID}').getSheetByName(targetsheet);
  20.   var data = sheet.getDataRange().getValues();
  21.   // スプレッドシートのデータを返却
  22.   return ContentService.createTextOutput(
  23.     JSON.stringify({"responsedata": data })
  24.   ).setMimeType(ContentService.MimeType.JSON);
  25. }
  26. function updateFaq(e) {
  27.   // リクエストからパラメータを取得
  28.   var faqStringData = e.parameter.faqdata;
  29.   var faqArray = JSON.parse(faqStringData);
  30.   // スプレッドシートの準備
  31.   var sheet = SpreadsheetApp.openById('{スプレッドシートID}').getSheetByName('FAQ');
  32.   // 既存のデータを取得
  33.   var existingData = sheet.getDataRange().getValues();
  34.   // Noをキーにしてデータをマップに変換
  35.   var dataMap = {};
  36.   for (var i = 0; i < faqArray.length; i++) {
  37.     dataMap[faqArray[i][0]] = faqArray[i];
  38.   }
  39.   // 既存データを更新
  40.   for (var j = 0; j < existingData.length; j++) {
  41.     var no = existingData[j][0];
  42.     if (dataMap[no]) {
  43.       existingData[j] = dataMap[no];
  44.       delete dataMap[no]; // 更新済みのデータは削除
  45.     }
  46.   }
  47.   // 新しいデータを追加
  48.   for (var key in dataMap) {
  49.     existingData.push(dataMap[key]);
  50.   }
  51.   // 更新したデータをスプレッドシートに反映
  52.   sheet.getRange(1, 1, existingData.length, 4).setValues(existingData);
  53.   return ContentService.createTextOutput(
  54.     JSON.stringify({"responsedata": "FAQ一覧のメンテナンスが完了しました。" })
  55.   ).setMimeType(ContentService.MimeType.JSON);
  56. }
  57. function getUnAnsweredData(e) {
  58.   // スプレッドシートデータの取得
  59.   var sheet = SpreadsheetApp.openById('{スプレッドシートID}').getSheetByName('QA');
  60.   var data = sheet.getDataRange().getValues();
  61.   // 未回答のデータを格納する配列
  62.   var unansweredFAQs = [];
  63.   
  64.   // 未回答のデータを抽出
  65.   for (var i = 0; i < data.length; i++) {
  66.     var row = data[i];
  67.     if (row[5] === "") { // 回答列が空の場合
  68.       unansweredFAQs.push(row);
  69.     }
  70.   }
  71.   // スプレッドシートのデータを返却
  72.   return ContentService.createTextOutput(
  73.     JSON.stringify({"responsedata": unansweredFAQs })
  74.   ).setMimeType(ContentService.MimeType.JSON);
  75. }


※利用者側のApps Scriptは管理者側のApps Scriptと同一プロジェクトにしてます。そのため、こちらはPostメソッドでリクエストを受けるAPIになっておりますが、単なるPostの実験ですので、気にしないでください。



まとめ

やれるかな~と思って実際作ってみると、思った以上にChatGPTが賢く、これはさらに活用の幅が広がるなと思いました。チャットなどでやり取りできる仕事などはどんどんAIに置き換えられていきそうな感じですね。

作成にあたってのコツも私なりにいくつか記載してみましたので、参考となりましたら幸いです。

ポイントとしては、以下の通りと考えます。

  • 考え方だけを指示するだけでChatGPTは概ね上手く考えて動いてくれる。
  • AIにどこの部分をを考えてほしいのかを明確にして設計する。

また何か面白そうな使い方を思いついたら記事を書きたいと思いますので、次回お会いしましょう。

今回の記事については、一回くらいはGPTsを作ったことがある人向けの内容になっていると思いますので、そもそもGPTsってどう作るんだっけ?という人は、以下のサイトなどを参考に一度ご自身のGPTを作成してみてください。


参考サイト






レギオンサバイヴの戦略(上級編)

今回はレギオンサバイヴの戦略シリーズ最後の上級編の内容となります。前回の中級編の記事を読みたい方は以下のリンクよりご確認下さい。 では、上級編の内容にいってみましょう! 1.対人戦は心理戦だと心得よ 何と言っても対人戦の醍醐味はこれでしょう!もちろん戦闘力はかなり勝敗に影響を及ぼ...