🔞

【個人開発】FANZA x ChatGPT インテリジェンス!?なおかず検索アプリが爆誕!

2023/03/14に公開
2

何これ?

FANZA の動画が見たいとチャット形式で質問すると、人格付きのコンセルジュが要求に沿った商品紹介をしてくれるというウェブアプリです。
思いつきで週末で個人開発しました。何周遅れか、何番煎じかわかりませんが FANZA API と先日公開された ChatGPT API を使用しています。
初めて zenn の記事を書きます。

こんな感じ(パッケージ画像はぼかしてあります)
https://fanza-man.com/

使ったもの

  • ChatGPT API と ChatGPT 自体
  • FANZA API
  • React 18
  • Vite
  • Supabse Edge Functions
  • Firebase Hosting
  • TypeScript(ベターJSとして)
  • Ptera(deno 対応日付モジュール)
  • kuromoji-es(形態素解析)
  • react-textarea-autosize(テキストエリアタグの便利モジュール)
  • emotion(CSS in JS)
  • normalize.css
  • react-ga4
  • DALL-E(アイコン作成)
  • お名前.com(ドメインレジストラ)
  • figma(モック作成、絵素材修正等)

チャットのコア部分

それらしいチャットを行わせるために ChatGPT を使うわけですが、ChatGPT が膨大な FANZA の動画情報を全て学習しているわけではありません(それでも驚くほど学習しているw)ので In context Learning(ICL) としていくつか情報を与えるようにし、FANZA で商品検索した結果と共に表示するという形で実現しています。

プロンプトエンジニアリング

In context Learning(ICL) の詳しい説明はこちらを参考にさせていただきました。
https://zenn.dev/noritamarino/articles/a2321a65fe2be8
https://zenn.dev/offers/articles/20230309-chatgpt-prompt-engineering

OpenAPI のキーの取得から使い方

こちらを参考にさせていただきました。
https://zenn.dev/umi_mori/articles/chatgpt-api-python

大まかな処理フロー

  1. FANZA 商品検索(バックエンド)
  2. ICL 情報の付与(バックエンド)
  3. 深津式プロンプトで人格付与(バックエンド)
  4. ChatGPT のレスポンスと FANZA 商品の表示(フロントエンド)

FANZA 商品検索

まず入力された文字列を kuromoji で形態素解析を行い名詞を抽出し、この名詞をキーワードとして FANZA API で商品検索を行います。
次に得られたレスポンスには複数の商品情報がありますので、単体作品>ジャンルにキーワードが含まれる>商品タイトルにキーワードが含まれる、の順で検索しヒットしたものを採用します。
何もヒットしなければランダムで選んでいます。

またバックエンドとして Supabase Edge Function を使いました。これは node ではなく deno なので kuromoji-es を使わせていただきました。
https://code4fukui.github.io/kuromoji-es

ICL 情報の付与

以下の形式は後述する人格付与フォーマットの一部で、得られた FANZA の商品情報のタイトルやジャンルの文字列を title、genre 部分に埋め込むことで学習情報として付与しています。

制約条件:
* 質問者は「${title}」という作品に興味があります
* この作品は「${genre}」というジャンルです

深津式プロンプトで人格付与

https://note.com/fladdict/n/neff2e9d52224
こちらの記事を参考にし FANZA マンとなる人格を付与しました。
FANZA マンは時々間違うが憎めないキャラという設定で制約条件は15個ほどです。

ChatGPT のレスポンスと FANZA 商品の表示

フロント側では ChatGPT API のレスポンスと FANZA API の商品情報をセリフとして表示しています。
他には FANZA マンの検索待ちや、検索結果を伝える場合の間の繋ぎなどのために 「ちょっと待って」「商品見つけたぜ」のようなセリフを挿入しています。
これが無いと話者どうしの発話と発話の間が持たない、対話になりずらい問題が発生しました。
セリフは ChatGPT 自体を使って事前に100個ほど生成しておき、プログラムから定数文字列として参照しています。ただこのセリフが逆にシーマンのような人工無能感が出てしまってますので今後の課題の一つではあります。

その他

バックエンドとして Supabase Edge Functions を使っているためこれまで常用していた moment.js が使えませんでした。そこで Ptera と言う非常に使いやすい軽量モジュールを使わせていただきました。
https://zenn.dev/tak_iwamoto/articles/8b32b27bd577b1

Firebase Functions を使うとバックエンドは Firebase だけで完結するのですが、個人開発のため極力追加コストなしで実現するために Supabase Edge Functions を採用しました。

ほか FANZA マンとなるアイコンはブラウザから気軽に使える DALL-E を使って10パターンほど生成し、Firebase Storage に配置し参照しています。
https://labs.openai.com/

ドメインはお名前.comで取得し、Firebase Hosting の設定はこちらを参考にしました。
https://lpeg.info/webworks/firebase/firebase_domain.html
ドメイン設定から反映までは6時間ほどでした。

OGP対応をしてあるのですが注意点として Firebase Hostring のリライトの順番があります。
デフォルトだとバンドルされている画像のパスにアクセスする前に index.html が優先されてしまいますので firebase.json を以下のように index.html より手前に ogp-cover.png を記述してあります。robots.txt も同様です。

firebase.json
{
  "hosting": {
    "public": "dist",
    "trailingSlash": false,
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "ogp-cover.png",
        "destination": "/ogp-cover.png"
      },
      {
        "source": "robots.txt",
        "destination": "/robots.txt"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

他、Google サーチコンソールの登録、 Google アナリティクスの登録など
https://search.google.com/search-console/
https://analytics.google.com/analytics/web/
Firebase Hosting を設定した際に自動作成される G-XXXXX 識別子がありますのでこれをそのままアナリティクスに設定しました。
どちらも1日ほどで計測が開始されました。

まとめ

もっと賢いコンセルジュ(チャットbot)として実装するためには LangChain を使うか、ベクトル検索対応すべきなんでしょうが、週末にサクッとやるには重いので見送りました。
https://qiita.com/sakasegawa/items/d01dafdf0c77da133f24
https://zenn.dev/tfutada/articles/a90e2c83b50356

そうこうしてるうちに ChatGPT-4 が出るとか出ないとかの噂がありますので 4 になればよりフレキシブルなカスタムができる様になるのかもしれませんね。

FANZA マンは時々間違ったりしますが夜のお供にぜひ!
https://fanza-man.com/

Discussion

さっずんさっずん

このアプリすごく面白くて興味深かったです!

ペーペーのエンジニアの自分でも作ってみたいと思ったのですが、コードもしくは作成手順など販売予定などありませんか?