
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、昨今よく言われるのが「人手不足」ですよね。
我々IT
業界もその例にもれず慢性的な人手不足となっている職場も多かったりするんじゃないでしょうか。
ただ、その裏で聞かれるのが・・・・・・
リクルート(採用)が大変
これは日本の労働環境の影響もあるんでしょうが、「雇ったら解雇しにくい」システムになっているので、とくに経営者や人事の方は神経をすり減らしていることと思います。
で、そう考えていたら、ある1つのアイデアが浮かびました。
それは・・・・・・
マウスや、キーボードの動きで性格を大まかに把握する
という機能です。
つまり、エントリーシートで志望動機や経歴などを入力するときに以下のようなデータもとっておき一緒にサーバーへ送信するという流れです。
- キーボードのタイピング速度
- バックスペースや Delete キーの使用頻度(間違いを修正する回数)
- 各種ボックスに入力した時間
※ ちなみにこういったデータを取得する場合は事前に「操作データを収集しています」などの注意書きや同意は必要だと思います。念のため)
そこで
今回は「Laravel + Vue 3」を使って送信者の性格を大まかに把握する機能をつくってみたいと思います。
※ なお、当初はマウスの移動速度なども考えていたのですが、複雑になりすぎるのでキーボード操作のみにしました。
ぜひ何かの参考になりましたら嬉しいです。
「リステリン使ったら
胃の調子まで良くなった!
年末は暴飲暴食するぞ(笑)」
開発環境: Laravel 10.x、Vue 3(options api)
目次 [非表示]
ChatGPT の API が使えるようにする
まずは、ChatGPT
がLaravel
から使えるようにします。
以下のコマンドでパッケージをインストールしてください。
composer require openai-php/laravel
パッケージをインストールしたら「API キー」を取得して.env
へセットします。手順は過去記事を参考にしてみてください。
.env
OPENAI_API_KEY=*********************************
※ ハイフンなどの文字が入っている場合はクォートで囲んだほうがいいかもしれません。
コントローラーをつくる
では、以下のコマンドでコントローラーをつくりましょう。
php artisan make:controller ApplicationFormController
そして、app/Http/Controllers
に作成されたファイルを以下のようにします。
app/Http/Controllers/ApplicationFormController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use OpenAI\Laravel\Facades\OpenAI;
class ApplicationFormController extends Controller
{
public function create()
{
return view('application_form.create');
}
public function store(Request $request)
{
// バリデーションは省略しています
// 本来はここでデータを保存(今回は省略)
$typing_data = $request->typing_data;
$prompt = view('application_form.prompt', [
'typing_data' => $typing_data,
])->render();
$chatgpt_response = '';
// ご注意: 時間がかかる場合があるので PHP のタイムアウト設定(max_execution_time)を無効にするか長めに設定しておいてください
try {
$result = OpenAI::chat()->create([
'model' => 'gpt-4',
'messages' => [
['role' => 'user', 'content' => $prompt],
],
]);
$chatgpt_response = Arr::get($result, 'choices.0.message.content');
} catch (\Exception $e) {
throw $e;
}
return [
'status' => 'success',
'chatgpt_response' => $chatgpt_response, // 今回はテストなので、chatgpt_response をブラウザ側へ返しています
];
}
}
ちなみにコードの中でも書いていますが、タイムアウトする可能性があるのでphp
の実行期間(max_execution_time)は無効にするか、長めに設定しておく方がいいと思います。
ビュー(フォーム用)をつくる
では、コントローラーの中にセットしたビューをつくります。
以下のコマンドを実行してください。
php artisan make:view application_form.create
すると、resources/views/application_form
にファイルが作成されるので中身を以下のようにします。
resources/views/application_form/create.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>エントリーシート</title>
</head>
<body id="app" class="bg-gray-100 p-8">
<div class="max-w-md mx-auto">
<h1 class="text-3xl font-bold mb-8">エントリーシート</h1>
<div class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700">お名前:</label>
<input
id="name"
type="text"
class="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
v-model="form.name"
@keydown="onKeydown"
@keyup="onKeyup"
@focus="onFocus">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">メールアドレス:</label>
<input
id="email"
type="text"
class="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
v-model="form.email"
@keydown="onKeydown"
@keyup="onKeyup"
@focus="onFocus">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">志望動機:</label>
<textarea
id="motivation"
class="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
rows="5"
v-model="form.motivation"
@keydown="onKeydown"
@keyup="onKeyup"
@focus="onFocus"></textarea>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">職務経歴:</label>
<textarea
id="career"
class="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
rows="5"
v-model="form.career"
@keydown="onKeydown"
@keyup="onKeyup"
@focus="onFocus"></textarea>
</div>
<button
type="button"
class="w-full flex justify-center py-3 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
@click="onSubmit">
送信
</button>
</div>
</div>
<script src="https://cdn.tailwindcss.com/3.3.5"></script>
<script src="https://unpkg.com/vue@3.3.11/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script>
Vue.createApp({
data() {
return {
form: {
name: '',
email: '',
motivation: '',
career: ''
},
typingData: {
durations: [],
backspaceCount: 0,
inputStartTimes: {},
inputEndTimes: {},
startTime: 0,
},
lastKeyUpTime: 0,
};
},
methods: {
onKeydown(e) {
if(this.lastKeyUpTime > 0) {
const duration = Date.now() - this.lastKeyUpTime;
this.typingData.durations.push(duration); // キーボードのタイピング速度
}
if (e.key === 'Backspace') { // バックスペース
this.typingData.backspaceCount++;
}
const inputId = e.target.id;
if (typeof this.typingData.inputStartTimes[inputId] === 'undefined') {
this.typingData.inputStartTimes[inputId] = Date.now();
}
},
onKeyup(e) {
if(e.key === 'Tab') { // タブのときは無視
return;
}
this.lastKeyUpTime = Date.now();
const inputId = e.target.id;
this.typingData.inputEndTimes[inputId] = Date.now();
},
onFocus() {
// フォーカスしたときは、直前のキー入力からの経過時間をリセット
this.lastKeyUpTime = 0;
},
onSubmit() {
if(confirm('送信しますか?') === false) {
return;
}
// タイピング速度
const averageKeystrokeDuration = _.mean(this.typingData.durations);
// バックスペース比率
const inputTextCount =
this.form.name.length +
this.form.email.length +
this.form.motivation.length +
this.form.career.length;
const backspaceRatio = this.typingData.backspaceCount / inputTextCount || 0;
// 各項目の入力時間
let entryTimes = {};
for (const key in this.typingData.inputStartTimes) {
const startTime = this.typingData.inputStartTimes[key];
const endTime = this.typingData.inputEndTimes[key];
entryTimes[key] = endTime - startTime;
}
// ページが表示されてから送信するまでの経過時間
const totalTime = Date.now() - this.typingData.startTime;
const url = '/application_form';
const params = {
form: this.form,
typing_data: {
average_keystroke_duration: averageKeystrokeDuration,
backspace_ratio: backspaceRatio,
entry_times: entryTimes,
total_time: totalTime,
}
};
axios.post(url, params)
.then((response) => {
if(response.data.status === 'success') {
alert(response.data.chatgpt_response);
}
});
}
},
mounted() {
this.typingData.startTime = Date.now();
},
})
.mount('#app');
</script>
</body>
</html>
すこしコードが長いですが、やっていることは冒頭でも紹介した以下の項目と「ページが表示されてから送信するまでの時間」を送信しているだけです
- キーボードのタイピング速度
- バックスペースや Delete キーの使用頻度(間違いを修正する回数)
- 各種ボックスに入力した時間
また、キーボードのタイピング速度は以下の流れで取得しています。
- 最後に「キーアップ」された時間をとっておく
- キーダウンされた時間との差を計算(これをタイプ速度とする)
- ただし、Tabで移動されるとうまくいかないのでタブは無効にする
ビュー(プロンプト用)をつくる
では、もう1つコントローラーの中で使っている「プロンプト用」のビューをつくります。
以下のコマンドを実行してください。
php artisan make:view application_form.prompt
すると、同じくresources/views/application_form
にファイルが作成されるので中身を以下のようにします。
resources/views/application_form/prompt.blade.php
あなたは、行動的生体認証(Behavioral Biometrics)の優秀なスペシャリストです。
以下は入社希望者が応募フォーム入力した際の各種タイピングデータです。
これらを使って入社希望をしている人の性格を分析してください。
# キーボードのタイピング速度(平均)
{{ $typing_data['average_keystroke_duration'] }}ミリ秒
# 入力されたテキスト文字数に対するバックスペースの使用回数(比率)
{{ $typing_data['backspace_ratio'] }}回
# 各項目の入力時間(平均)
@foreach($typing_data['entry_times'] as $key => $entry_time)
{{ $key }}: {{ $entry_time }}ミリ秒
@endforeach
# ページが表示されてから送信するまでの経過時間
{{ $typing_data['total_time'] }}ミリ秒
ルートをつくる
では、最後にルートです。
以下のようにコードを追加してください。
routes/web.php
use App\Http\Controllers\ApplicationFormController;
// 省略
Route::prefix('application_form')->controller(ApplicationFormController::class)->group(function(){
Route::get('create', 'create')->name('application_form.create');
Route::post('/', 'store')->name('application_form.store');
});
これで作業は完了です。
お疲れ様でした。
テストしてみる
では実際にテストしてみましょう。
まず、ブラウザで「https://******/application_form/create」へアクセスします。
すると、エントリーシートのフォームが表示されるので、(わざと間違えて修正したりして、リアルなカンジで)以下のように入力&送信してみます。
すると、どうなるでしょうか・・・・・・
はい
ChatGPT
(Open AI
)から取得された内容が表示されました。
では、その内容を見てみましょう。
(ChatGPT による性格判断)
タイピング速度は平均的な値であり、早すぎず遅すぎず、計画的で安定した作業ができる性格を示している可能性があります。また、バックスペースの使用回数が少ないことから、自信があるか、または事前に考えをまとめてからタイピングするような行動特性が見られます。
各項目の入力時間を見ると、名前とメールアドレスは速めで、特に職歴の入力時間が長い事から、細部に配慮する側面や徹底的に情報をチェックする慎重さを感じます。
全体の送信までの時間は約46秒と、一般的なタイムフレーム内に収まっており、効率的であると言えます。これは注意深く、具体的かつ直接的な作業を行う能力を示しています。
これらのデータから、入社希望者は計画的であると同時に注意深く、また自身の作業に自信と責任感を持って取り組むことができると考えられます。このことから、この人物は組織に対して意義深かつ生産的な貢献をすることが期待できます。
はい
(内容がどうかはさておき)うまく性格判断が表示されました。
すべて成功です
企業様へのご提案
もちろん今回の判別が絶対的に正しいかと言えばそうではありませんが、1つの判断材料にはなるのではないでしょうか。
また、今回はテストなのでデータは多くありませんが、冒頭でも言及した「マウスの移動速度」などその他のデータも組み合わせるとより精度が高い性格判断ができると考えています。
もしそういったシステムをご用意されたい方はぜひお問い合わせからご相談ください。
お待ちしております。
おわりに
ということで今回は「ChatGPT」で採用に関連する性格判断をしてみました。
なお、はじめは「gpt-3.5-turbo」でやってみたところ、『申し訳ないですけどできません』」と言われうまくいかず、最新版の「gpt-4-1106-preview
(gpt-4
より高性能なのに安い)」を使ってみたら、cURL error 28: Operation timed out
となりうまくいきませんでした。
そこで、「gpt-4」モデルを使うとうまくいきましたので最終的にはこれにしました。(新しいやつはアクセスが多すぎるんでしょうか…)
なんにせよ、ChatGPT
はこういう使い方をすると完璧ではないにせよ、ちょっとした専門家の力を借りることができるようになるので便利ですね。
ぜひ皆さんもやってみてくださいね。
ではでは〜
「エガちゃんねるで見た
横浜あんかけラーメン(冷凍食品)、
ガチでうまかったです」