どうも、20 代でアルツハイマー病になった疑惑ある Xu です。
ところで皆さん、急に固有名詞とかが思い出せない場面とかありませんか!?
「あれ、あれよ、あれあれ」(口パク)ってなるやつ。
調べてみたらアレアレ病というやつで、アルツハイマー病の初期症状の可能性もあります。
「アレアレ病」(アレアレ症候群)とは
色々と固有名詞を覚えることが多いエンジニアでカタカナ使うことが多いゲーマーだと特に発症しやすいのではないかと思います。
せや、あれって言ったら教えてくれるアプリ作ろ
文字を打ってるときとかはすぐに調べたり、それこそ今は AI が予測変換出してくれる時代なので、そんなに困らないかもしれませんが、話してる時だと一回調べる間を挟むのはだるいものです。
テンポ悪いですし、お客様の前で発症すると呆れられるかもしれない、想像しただけでも恥ずかしいですよね。
例えば会議中に適当に立ち上げておいて、「あれ」と言ったら反応して勝手に教えてくるものがあったらいいよね!
ってわけで音声認識で「あれ」とか「なんだっけ」に反応して、生成 AI が言いたいこと教えてくれるアプリを作りました。
■アレアレヘルパー
App URL: https://arearehelper.vercel.app/
まずは音声認識から
意外と音声関連やったことなかったので、以下の記事を参考に Speech Recognition を実装してみました。
Webページでブラウザの音声認識機能を使おう - Web Speech API Speech Recognition
var SpeechRecognition = window.webkitSpeechRecognition || window.SpeechRecognition;
var recognition = new SpeechRecognition();
recognition.lang = 'ja-JP';
recognition.interimResults = true;
recognition.continuous = true;
const startListen = () => {
recognition.start();
}
これでボタンが押されたときにブラウザーが音声の聞き取りを開始する最低限の機能が完成しました。
AI の選定
音声認識できたら今度はその聞いた内容を分析して回答してくれる AI を探します。
とにかくお金を払いたくないです。
言うて極たまにちょっと立ち上げて一瞬聞くだけなのに月額料金払ってたら個人プロジェクトとしてはたまったもんじゃない。
そんな保守的な考えでたどり着いたのが:
■Groq AI
https://console.groq.com/home
現在ほぼ完全無料で提供されていて、今後有料化の可能性があると言われ続けているが、しばらくは大丈夫そう?
とりあえず API キー発行して雑に日本語で質問投げてみたところ、まあまあちゃんと日本語認識して動いているよう。
呼び出しコードを作成:
async function api(Text: string) {
const Groq = require('groq-sdk');
const groq = new Groq({
apiKey: process.env.NEXT_PUBLIC_GROQ_CLOUD_API_KEY,
dangerouslyAllowBrowser: true
});
const chatCompletion = await groq.chat.completions.create({
"messages": [{role: 'user', content: Text}],
"model": "meta-llama/llama-4-scout-17b-16e-instruct",
"temperature": 1,
"max_completion_tokens": 1024,
"top_p": 1,
"stream": true,
"stop": null
});
}
Vercel でホスト
以前の記事でも度々登場している Vercel、自動的に GitHub からデプロイできて、お財布にも優しいので最近重宝してます。
今はほぼ一部 Lambda 関数使ってる以外は AWS を完全に脱しました。
メインで利用しているクラウドを AWS から Salesforce に切り替えた話
実は時系列的にはこのアプリを作った時が初めて Vercel 触りました。
「今話題の Vercel だが、フロントのみの静的サイトだけって聞いたけど、API とかコールできるかな??」
とか考えてましたが、普通に行けました。
ベストプラクティスとしてはフロントサイドと呼び出しを切り分けて別モジュールに置いたほうがいいけど今回は page.tsx で完結しました。
GitHub URL:https://github.com/pyxudev/arearehelper
苦労した点
- TypeScript、型の定義が必要
const SpeechRecognition = webkitSpeechRecognition || SpeechRecognition;
↑こいつの型どうすればいいねん!?と思ったら window オブジェクトだった。
そりゃーそうか、一応ブラウザーの機能を呼び出しているだけだもんな。
window 型を使うにはまずフロントで使ってるよってのを教えなければならない。
if (typeof window !== 'undefined') {
const { innerHeight: height, innerWidth: width } = window;
//処理を書く
}
- 環境変数、NEXT_PUBLIC_を付けないとブラウザー側で使用できない
apiKey: process.env.GROQ_CLOUD_API_KEY
-->apiKey: process.env.NEXT_PUBLIC_GROQ_CLOUD_API_KEY
- 「あれ」に対して反応してほしいけど、すぐに続くワードを分割するのは難しい、あと考えたりして間を置くと送信してしまって途切れてしまう
recognition.onresult = (event) => {
let interimTranscript = '';
for (let i = event.resultIndex; i < event.results.length; i++) {
let transcript = event.results[i][0].transcript;
if (event.results[i].isFinal) {
finalTranscript += transcript;
} else {
interimTranscript = transcript;
}
if (isAre && !transcript.includes('あれ') && !transcript.includes('なんだっけ') && !finalTranscript.includes('何だっけ')) {
const resp = api(interimTranscript).then((resp) => {
const res = document.querySelector('#res-text') || document.createElement('div');;
res.innerHTML = '<i>ああ、なんだ、君が言いたいのはこれね!<br><br><div>' + resp.replace(/undefined/g, "") + '</div><br><br>ほーら、俺のほうがお前をわかってるだろ(笑)(笑)</i>';
});
recognition.stop();
}
if (transcript.includes('あれ') || finalTranscript.includes('なんだっけ') || finalTranscript.includes('何だっけ')) {
isAre = true;
}
}
一回トリガーが発動してから次に来る音声を聞く
if (transcript.includes('あれ') || finalTranscript.includes('なんだっけ') || finalTranscript.includes('何だっけ')) {
isAre = true;
}
api()
内で都度 isAre = false;
を最後に入れることで一区切りを作れる。
プレビュー
「あれ?髪乾かすやつなんだっけ?」と聞いてみた↓
作っておいてだけど、チャラ男の画像と返信の口調がうざい、正直。
[ちょっと黙って] をクリックすると初期状態に戻ります。
あとがき
適当にお遊びアプリを作ってみましたが、正直2ヶ月たってもそんなに使わなかったです。
まあいいんじゃないかな、こういうのもたまにはいいかな : )