こんにちは!先日、オリジナルグッズ作成・販売サービスの「SUZURI」で『スリスリAIチャット(β)』を開発・リリースしました。LLMを活用してチャットベースのUIで好みのアイテムを探すことができる機能になります。また、LLMが自然な会話を得意としていることから、SUZURIキャラクターのスリスリくんと雑談をすることもできます。
今回は、開発の経緯や目指したユーザー体験、具体的な技術面に関してご紹介いたします!
開発経緯
お産合宿
SUZURIを運営するGMOペパボでは年に1度「お産合宿」という開発合宿があり、2日間の限られた時間内で集中して取り組む開発合宿があります。そこで、SUZURIのエンジニアである @hyuta555 @yukyu30 、プロダクト開発ディレクターの @__herman でチームを組み、参加しました。
課題・実現したかったこと
SUZURIではたくさんのクリエイターによって1,910万点以上のアイテムが出品・販売されています。購入者ユーザーの視点に立ったときに、その中から好みのアイテムを自力で見つけるのは難しいという課題がありました。
そこで、LLMを使って目的に沿ったセレンディピティを誘発するのはどうだろうか?がアイデアの起点になりました。具体的には、ユーザーが入力した文章から欲しいアイテムのキーワードを抽出し、LLMに他のキーワードへ連想してもらい、それらをクエリとして検索APIに投げます。
この仕組みにより、ユーザーが明確に認識していなかった(= クエリを想起できなかった)ニーズを深掘りし、より偶発性のある探索を促すことができます。
「忍者スリスリくん」という存在
SUZURIには公式忍者である通称「スリスリくん」と呼ばれるキャラクターが存在します。彼はかなり特徴的な口調であり、クセのある性格をしています。それ故に、ユーザーの皆さまからは長く愛されていて、そんな彼を直接会話をすることができるのが本機能の魅力の一つでもありました。
商品検索というECサービスの側面と同時に、エンターテインメントとしての「おもしろい価値」も提供する必要がありました。
どのように実現したか
システム構成と使用技術
バックエンドはPythonでFastAPIをベースに構築しています。Pythonである理由は後述するLangChainというライブラリがJavaScript版よりもPython版の方が機能が多く開発も活発なため、これを利用するために開発言語としてPythonを採用しました。
フロントエンドは業務でもReactを利用しており、ゼロコンフィグでビルドの最適化を行ってくれるNext.jsを採用しました。アプリケーション自体は1ページだけのシンプルな構成なので、SSRをせずにデプロイするときSSG(Static Site Generation)としてビルドしています。これをFastAPIのStaticFilesの機能を利用して静的ファイルとして配信しています。
デプロイは弊社が提供しているLOLIPOP!マネージドクラウドにデプロイしています。MySQLのデータベースやコンテナでのオートスケーリングが利用可能なのでとても便利です。
全体のシステム構成図は次のようになります。
LangChain
LangChainは言語モデルを利用したアプリケーション開発のためのフレームワークです。今回利用した主な機能はAgents、Tools、Chainsです。
Agentsは自然言語に対して、実行可能なアクションであるToolsから適切なToolを動的に選択し、実行します。Toolsには関数、Agents、後述するChainsなどを組み込むことができます。
Chainsは一連の処理をモジュール化し再利用できるようにしたものです。
今回実装したChainsについては次の章にて説明します。
全体構造のアップデート
お産合宿で急いで開発したのもあり、初回リリース後にうまくキーワード連想が働かなかったり、エラーがしばしば発生したり、スリスリくんの口調を守らなかったりと不安定な状態でした。
そこで、LangChain部分の全体の見直しをすることにしました。従来は「キーワード連想」「アイテム検索」をToolとして定義し、ユーザーの入力文から適切なToolをAgentに選択させていました。一見、LangChainを良い感じに使っていそうなのですがLLMに処理を任せすぎて思うように動いてくれない事象が発生していました。
そのため、ある程度システマチックにしようと考え、以下の構造で作り直しました。
まず、大きく「キーワード連想」と「会話(雑談)」に処理を切り分け、それぞれSequential Chainを使って細かく指示を出すようにしました。
具体的なイメージは「アイテム検索Sequential Chain」の中に
- 変換Chain(自然言語からキーワードに変換する)
- 分解Chain(検索クエリとカテゴリーを分解する)
- 連想Chain(検索クエリを連想する)
- ID変換Chain(カテゴリーをDB上のIDに変換させる)
を並べ、それぞれの処理を確実に実行させ、Outputを次のChainのInputにします。最終的にjson形式で検索クエリとカテゴリーIDが出力されるので、それを検索APIに渡すというのが全体像です。
また、会話を検索と切り分けることで、スリスリくんの情報を与えるプロンプトを削減することができました。
会話と検索のどちらを使用するかは、全体をラップしたAgentに判断させています。
最後に
少しずつ理想の体験に近づけるために機能追加や改善を行っていますが、まだ「(β)」の表記を取り外せていない状態です。
チャットベースのUIを採用しているからには、「サービスに関する詳しい情報」や「FAQ」の情報を持たせて、簡単なお問い合わせに答えられるようにしたいと思っています!忍者スリスリくんの今後の成長を見守っていてください。