1.導入
今回は、 AIを用いた共通点探しゲーム の記事になります。
当社の15周年記念イベントは、従業員間の団結、働く喜び、そして仲間、お客様、パートナーへの感謝、未来への挑戦意識の共有といった価値を強調する場として設けられました。このイベントを成功させるため、多くの仲間と共にプロジェクトチームを組み、特別な瞬間を創出することに努力を注ぎました。
その結果、事前に行われたワークショップ、当日の洗練されたプレゼンテーション、そして当社の歴史を振り返る映像など、多様なプログラムが実施され、参加者には忘れられない体験を提供することができました。
私が参加した懇親会プロジェクトチームでは、 AIを活用した「よりそう一枚岩 共通点探しゲーム」を通じて、参加者間のコミュニケーションを深め、皆が一枚岩となって楽しむことができる環境を作り出す ことをテーマにしました。
2.共通点探しゲームの紹介
このゲームは、各チームで5人以上が共有する共通点を探し、その中から最もユニークな共通点を一つ選び、共通点の素晴らしさをAIが採点するゲームとなります。
普段の仕事で接点がない人との会話を通じて、お互いの共通点を探していくイベント設計になります。
ゲームは雑談を交えつつ30分ほどで実施できるように調整しました。
投稿は手元のスマホからQRコードで投稿できるようにしました。
会社のミッションである「よりそう力で世界を変える」に因み、「世界を変えた偉人に扮したAI」に審査してもらいました。
※日本の芸能人で審査をしようと考えていましたが、肖像権、パブリシティ権、名誉毀損の法的リスクが発生する可能性を考慮しました。
3.AI実装の詳細
今回は、GPTのAPI(model:gpt-3.5-turbo)を使って実装することにしました。
gpt4を使いたかったのですが、審査に時間がかかったので、GPT3.5で実装することにしました。
4.Google Apps Scriptの使用
参加者からの共通点はQRコードからGoogleForm経由で投稿することでGoogleSpreadに書き出され、Google Apps Script(GAS)を活用してGPT(AI)のAPIを呼び出して実装する方式を取りました。
GASコード
/**
* すべてのチームの回答を取得する関数
*/
function getTeamResponses() {
// スプレッドシートを開く
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 「集計」シートから値を取得
const summarySheet = ss.getSheetByName('集計');
const data = summarySheet.getDataRange().getValues();
// 回答を格納する配列を初期化
let teamResponses = [];
for (let i = 1; i < data.length; i++) {
// チーム名と共通点がある場合に処理を行う
if (data[i][1] && data[i][3]) {
teamResponses.push(`チーム名:${data[i][1]}、共通点:${data[i][3]}`);
}
}
// すべてのチームの回答を連結して返却
return teamResponses.join('、');
}
/**
* プロンプトの内容を取得する関数
*/
function getPromptContent() {
// スプレッドシートを開く
const ss = SpreadsheetApp.getActiveSpreadsheet();
// プロンプトシートから値を取得
const promptSheet = ss.getSheetByName('プロンプト');
let content = promptSheet.getRange('A1').getValue();
// 各チームの回答を取得
const teamResponses = getTeamResponses();
// {{各チームの回答}}を置換
content = content.replace('{{各チームの回答}}', teamResponses);
console.log(content);
return content;
}
/**
* APIからデータを取得する関数
*/
function fetchApiData(content) {
// APIエンドポイント
const url = 'https://api.openai.com/v1/chat/completions';
const data = {
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": content}],
"temperature": 0.7
};
// スクリプトプロパティからAPIキーを取得
const scriptProperties = PropertiesService.getScriptProperties();
const options = {
method: 'post',
headers: {"Authorization": `Bearer ${scriptProperties.getProperty('GPT_API_KEY')}`},
contentType: 'application/json',
payload: JSON.stringify(data)
};
const response = UrlFetchApp.fetch(url, options);
return JSON.parse(response.getContentText());
}
/**
* 取得したデータをシートに書き込む関数
*/
function writeDataToSheet(jsonResponse) {
// スプレッドシートを開く
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('審査結果');
// シートの内容をクリア
sheet.getRange('A:K').clearContent();
let messageContent = JSON.parse(jsonResponse.choices[0].message.content);
console.log(messageContent);
// ヘッダーを作成し、シートに追加
const headers = createHeaders(messageContent);
sheet.appendRow(headers);
// 各チームのデータを準備
const allTeams = prepareDataForTeams(messageContent, headers.slice(6));
// データをシートに書き込み
writeTeamsToSheet(allTeams, sheet);
}
/**
* ヘッダーを作成する関数
*/
function createHeaders(messageContent) {
const headers = ["順位", "チーム名", "共通点", "平均点数", "点数を最も高くつけた人のコメント", "点数を最も低くつけた人のコメント"];
const firstTeam = Object.keys(messageContent)[0];
const judges = Object.keys(messageContent[firstTeam]["審査員ごとの点数"]);
return headers.concat(judges);
}
/**
* 各チームのデータを作成する関数
*/
function prepareDataForTeams(messageContent, judges) {
let allTeams = [];
for (let team in messageContent) {
// 各チームの情報を配列に格納
const row = createTeamRow(messageContent, team, judges);
allTeams.push({
"teamName": team,
"sharedPoint": messageContent[team]["共通点"],
"averageScore": messageContent[team]["平均点数"],
"highestComment": messageContent[team]["点数を最も高くつけた人のコメント"],
"lowestComment": messageContent[team]["点数を最も低くつけた人のコメント"],
"scores": row.slice(5),
"dataRow": row
});
}
// 平均点数で降順にソート
return allTeams.sort((a, b) => b.averageScore - a.averageScore);
}
/**
* チームの行を作成する関数
*/
function createTeamRow(messageContent, team, judges) {
let row = [];
row.push(team, messageContent[team]["共通点"], messageContent[team]["平均点数"],
messageContent[team]["点数を最も高くつけた人のコメント"], messageContent[team]["点数を最も低くつけた人のコメント"]);
// 審査員の点数を行に追加
row = row.concat(judges.map(judge => messageContent[team]["審査員ごとの点数"][judge]));
return row;
}
/**
* チームのデータをシートに書き込む関数
*/
function writeTeamsToSheet(allTeams, sheet) {
let rank = 1;
for (let i = 0; i < allTeams.length; i++) {
// 同じ点数の場合は順位を維持、異なる場合は順位をインクリメント
if (i > 0 && allTeams[i].averageScore < allTeams[i - 1].averageScore) {
rank++;
}
// ランキングとデータを連結して新しい行を作成
const row = [rank].concat(allTeams[i].dataRow);
// 行をシートに追加
sheet.appendRow(row);
}
}
/**
* APIからデータを取得し、それをスプレッドシートに書き込む主要な関数
*/
function fetchDataAndWriteToSpreadsheet() {
const content = getPromptContent();
const jsonResponse = fetchApiData(content);
writeDataToSheet(jsonResponse);
}
プロンプト
プロンプトは、バラエティ的に面白いコメントが返ってくるように調整をしました。
役割:あなたはイベントの審査員です。
目的:以下についてそれぞれ「共通点の希少性を100点満点で評価せよ」希少性が高いものを100点とし、低いものは0点とせよ。
前提:各審査員の評価点数の合計値は他の共通点と重複しないようにせよ。レスポンスはjson形式にせよ。チームごとに「チーム名、共通点、審査員名ごとの点数、平均点数、点数を最も高くつけた人のコメント、点数を最も低くつけた人のコメント」の配列を返却せよ。「審査員名ごとの点数」は、"審査員ごとの点数":{"ウォルトディズニー": 80,"ナイチンゲール": 80}
のように返却せよ。審査員は「ウォルトディズニー、レオナルドダヴィンチ、トーマスエジソン、アインシュタイン、ナイチンゲール」とし、それぞれの人物像をもとに本人が言いそうなコメントや、偉人の名言のような形式で評価せよ。ミッキーマウスならディズニーなど、関連性が高いものについては、贔屓の点数をつけて良い。審査員のコメントはバラエティー的に面白くしてほしい。「点数を最も高くつけた人のコメント」と「点数を最も低くつけた人のコメント」はそれぞれ誰がコメントしたかをコメントの後に括弧書きで表記せよ。
{{各チームの回答}}
実行結果
実行すると世界の偉人に扮したAIが、評価コメント付きで評価してくれます。
5.結果と反響
そして当日、結果発表...(ドキドキ)
3位はチームC
「靴下は左からはく」
「左右を区別することで、脳の活性化につながる」と、AIナイチンゲールから医療関係者らしいコメントをいただきました。
2位はチームL
「菩提寺がある」
「菩提寺」というよりそうらしいキーワード(笑)で、海外の偉人には難しそうですが、さすがAIトーマスエジソンは「自分自身と向き合うための大切な場所」と、素晴らしいコメントをいただきました。
そして栄えある優勝は、チームF
「MT免許を持っていて、えんがわが好き」
まさかの共通点の掛け合わせ!
会場からは「その手があったか!」「ずるい!笑」など、称賛?の声が上がっておりました。
点数を最も低くつけた人のコメントも紹介されていました。
「えんがわはあまり好きではありません。(アインシュタイン)」
「菩提寺にはあまり興味がありません。(アインシュタイン)」
自分の好みで辛口評価するAIアインシュタイン。笑
6.学びと次のステップ
今回はAIを用いたイベントに挑戦してみましたが、面白かったと好評の声を多数いただきました。
AIというと取っ付きにくいイメージがまだあるかもですが、イベントやゲームなど色々なシーンで活用できるイメージが湧きました。
よりそうでは、AIを積極活用していく方針にしていますので、AIに関する面白い事例があれば、今後も発信をしていきたいと思います。
最後に「参加者の笑顔を前に感極まる人事の嶋田さん」をぱしゃり📷