【怠惰】旅行先はランダムに決めて、ついでにChatGPTにオススメも教えてもらおうぜ！なサービスを作った

**この記事は[クラウドワークスAdvent Calendar 2023](https://qiita.com/advent-calendar/2023/crowdworks) シリーズ2の24日目の記事です。**

## はじめに
こんにちは。[crowdworks.jp](https://crowdworks.jp/)のエンジニアの @komaitaira です。

私事ですが、プライベートでよく遊んでいるエンジニアの友人たちと約2ヶ月間でサービス開発をし、その成果を発表する機会がありました。この記事では、その際に開発したWebサービスの概要についてと、得た学びについてお話ししようかと思います。

## 開発したWebサービスについて
<img width="50%" alt="都道府県ランダム決定アプリのデモのgif画像" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629581/f6b6e0c4-787f-f17d-19a4-edd6f6ec14b5.gif"><img width="50%" alt="都道府県ランダム決定アプリのデモの画像" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629581/ef402da1-5cf7-fe53-d0b9-3a8be9466bbd.png">

この記事のタイトルにもある通り、「旅行先をランダムに決定して、その都道府県のおすすめの観光スポットや食べ物などをChatGPTに紹介してもらう」という、旅行プランを考えるのが~~面倒~~ 苦手な方にぴったりのサービスを開発しました。
扱った技術については下記です。

### 開発技術
#### React
成果発表までの期限が決められているため、ベースとなるフレームワークは開発者がなるべく扱った経験があるものをと思い、たまたまReactはチーム全員が触れたことがあったため選択しました。

https://react.dev

#### ChatGPT API
せっかくなら普段あまり触ったことがない技術も取り入れたいということで、ランダムで決定した都道府県のおすすめの観光先や食べ物を紹介してもらうために、今や当たり前のように使っているChatGPTのAPIを使用しました。

https://openai.com/product

#### Mock Service Worker（MSW）
Service Workerのレイヤーでリクエストを受け取りモックを作成することができます。
開発環境で開発する際にChatGPTのAPIへ何度もリクエストすると、利用できるトークンが枯渇してしまうので、MSWを使用したモックサーバーを用意して本当に必要なとき以外はモックサーバーを叩くようにしました。

https://mswjs.io/

#### Vercel
ホスティング先はVercelを選択しました。featureブランチをpushするだけでそのブランチのプレビュー環境を作成してくれるため、複数人で動作確認する際にとても便利でした。

https://vercel.com/

## 開発して得た学び
### 他社の開発手法等を知ることができる
他社で働くエンジニアが普段どのような開発手法で開発をしているのかを知ることができました。

例：
- タスク管理の方法（GitHub Issues、Notion、Trello、Redmineなど）
- ブランチの切り方
- コミットの粒度

また、逆に自分が普段開発している中で使っている技術を導入することもできました。
開発環境では実際のAPIにリクエストするのではなく、MSWを使ったモックサーバーにリクエストするようにするというアイデアは、普段の開発で使っているからこそ採用できたので、本業でやっていることがこういった場面でも活きてよかったです。

### ChatGPTにリクエストする時のプロンプトは英語にするべし
ChatGPT APIの利用料金は、消費したトークンによって決まります。
トークンとはテキストを構成する最小単位のことで、単語や句などがその例です。入力されたテキストを意味のあるかたまりに分けることで、コンピュータが文章の意味を理解しやすくなるため、機械学習などで多く利用されるようです。
このトークンについて、日本語でプロンプトを入力する場合は英語に比べてトークンの消費量が多いため、トークンを節約するには英語でプロンプトを入力した方が良いのですが、調査段階ではそれを知らずに色々試してしまったため、お試しで利用できる無料枠の上限を一瞬で超えてしまいました。

後から知りましたが、OpenAIが提供しているツールで、ブラウザ上にテキストを入力すると、使用されているトークン数をカウントできるツールもあるのでそれを使用すると分かりやすいです。

https://platform.openai.com/tokenizer

このツールを利用して、英語と日本語でトークン数を比較してみると下記のようになります。
#### ◆ 英語で入力した場合
<img width="100%" alt="特定の都道府県のおすすめの観光スポットと食べ物について、箇条書きの形式で、言語を日本語で出力するプロンプトを英語で入力してトークン数や文字数を出力している" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629581/3040a297-fe7c-c325-c055-c53062ec4c1e.png">
Tokens（トークン）：27
Characters（文字数）：126

#### ◆ 日本語で入力した場合
<img width="100%" alt="特定の都道府県のおすすめの観光スポットと食べ物について、箇条書きの形式で、言語を日本語で出力するプロンプトを日本語で入力してトークン数や文字数を出力している" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629581/77491f78-064c-6553-33a3-581ebc100f88.png">
Tokens（トークン）：53
Characters（文字数）：50

英語で入力した場合はトークンが27であるのに対し、同じ意味の文章を日本語で入力した場合ではトークンが53になっており、倍近くトークンを消費してしまうことが分かります。なるべくコストは抑えたいのでこの辺りは意識したいですね。
余談ですが、プログラム的にトークン数を取得したい場合はtiktokenというトークナイザーがあるみたいなので、そちらも活用していきたいですね。

https://github.com/openai/tiktoken

https://www.npmjs.com/package/tiktoken

### ChatGPTからのレスポンスの精度が高くない時もある
今回使用したAPIのモデルは[GPT-3.5-turbo](https://platform.openai.com/docs/models/gpt-3-5)でした。
一定の精度は持っているとは思うものの、時折全く存在しない観光スポットを普通に紹介してくる時があります。
今回はそこまで精密な精度は求めていなかったのでよかったですが、精度を高くするためにはモデルを高いものに変更したり、より多くの学習対象・期間が必要だったりするのだと思います。
この辺りの精度を高めるという領域についてはまた違う機会に勉強してみたいと思っています。

### 

## おわりに
今回扱った技術は普段の開発業務では触れないようなものもありましたが、今後も幅広く技術に興味を持ち自身のキャリア領域を広げていきたいと思っています。

最後にクラウドワークスでは様々な仲間を募集しております。
ご興味のある方は以下のリンクからご応募ください！

https://crowdworks.co.jp/careers/

## おまけ
<img width="100%" alt="このサービスを使って実際に旅行しようと提案する人のslackのスクリーンショット" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629581/aa254197-ef15-0adb-6cde-bfd3c9306552.jpeg">

成果発表時に完成したサービスを使って旅行先を決定し、先日チームメンバーと実際に旅行に行ってきました。行き先は三重県です。
自分で作ったものをサービスとして実際に使うことができるのはエンジニアの面白みなので、こういう遊びはたまにやりたいですね。

<img width="50%" alt="赤福" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629581/5d0e9d22-145f-ab4b-1948-1d501696f0f6.jpeg"><img width="50%" alt="伊勢神宮内宮の写真" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629581/115da088-33cb-22a4-a3b3-22cb2225499b.jpeg">