
形態素解析
イベント
該当するコンテンツが見つかりませんでした
マガジン

技術ブログ
Elastic Inference Service (EIS) を使った「ベクトル検索」と「生成AIによる回答(RAG)」について、全2回にわたって解説します。 第2回となる今回は「実践編」として、EIS を通じてモデルを呼び出し、「ベクトル検索」と「生成AIによる回答(RAG)」を実際に動かしてみます。 目次 前提条件 テストデータ、各種スクリプト 検索データのアップロード インデックスとパイプラインの作成 1. インデックスの作成 2. マッピングの定義 3. エイリアスの作成 4. インジェストパイプラインの作成 5. データの Reindex(ベクトル化の実行) 各種検索 キーワード検索(全文検索) ベクトル検索 (kNN) Reciprocal Rank Fusion (RRF) によるハイブリッド検索 セマンティックリランク 生成AIによる回答 技術的な補足 モデル名の指定 RRF とセマンティックリランクの役割 waganeko_tmp インデックス 参考リンク まとめ 前提条件 前回の「 準備編 」での設定が完了していることを前提とします。 テストデータ、各種スクリプト このサンプルで使用するテストデータおよび各種スクリプトは、下記の GitHub リポジトリで公開しています。 elastic-blogs/2026-03-eis at main · SIOS-Technology-Inc/elastic-blogs A sample code for blogs about Elastic. Contribute to SIOS-Technology-Inc/elastic-blogs development by creating an accoun... github.com 検索データのアップロード 今回のデモデータには、夏目漱石の『吾輩は猫である』を使用します。 青空文庫のデータ を元に、ルビを削除して NDJSON 形式に加工したファイルを用意しました。 データファイル: no_ruby_wagahai_wa_neko_dearu.ndjson アップロード手順: README.md を参照し、Self-Managed の Elasticsearch 上の waganeko_tmp インデックスへアップロードしてください。 インデックスとパイプラインの作成 1. インデックスの作成 まずは、形態素解析(icu/kuromoji)の設定を施した waganeko_2026_03 インデックスを作成します。 a2_create_index.md のスクリプトを Dev Tools の Console から実行してください。 2. マッピングの定義 a3_create_index_mapping.md を Self-Managed の Dev Tool の Console から実行し、waganeko_2026_03 インデックスへフィールドを作成します。 「吾輩は猫である」の本文を content フィールドに、本文から生成される密ベクトルを content_embedding フィールドへ格納するようにしています。 密ベクトルの type には、bbq_disk を指定しています。今回のデータは少量なので bbq_disk を使わなくてもよいのですが、bbq_disk の検証も兼ねて bbq_disk を使用しています。 3. エイリアスの作成 運用の利便性を高めるため、waganeko_2026_03 に対して waganeko というエイリアスを付与します。 a4_create_alias.md を実行してください。 4. インジェストパイプラインの作成 ここが EIS の真骨頂です。 a5_create_ingest_pipeline.md を実行します。 パイプライン内で指定している .jina-embeddings-v5-text-nano モデルは、Self-Managed 側にはインストールされていません。 EIS を経由することで、外部モデルをあたかもローカルモデルのように利用できます。 このパイプラインをデータ取り込み時に通過させることで、content フィールドの内容に応じた密ベクトルを生成し、content_embedding フィールドへ格納できるようになります。 5. データの Reindex(ベクトル化の実行) a6_reindex.md を実行し、waganeko_tmp から waganeko へデータをコピーします。 この際、前述のパイプラインにより自動的にベクトル化が行われます。 各種検索 ここからは、ES|QL を用いて異なる検索手法を試していきます。 キーワード検索(全文検索) まずは、従来の全部検索です。スクリプトは下記にも掲載しています。 a7_keyword_search.md POST /_query { "query": """ FROM waganeko METADATA _score, _id, _index | WHERE MATCH(content, ?query) | KEEP chunk_no, content, _score | SORT _score DESC | LIMIT 20 """, "params": [ { "query": "吾輩が生まれた場所は?" } ] } 検索結果:「場所」という単語に引っ張られ、必ずしも意図した回答(冒頭の一節)が上位に来るとは限りません。 ... "values": [ [ 1217, "しばらくは爺さんの方へ気を取られて他の化物の事は全く忘れていたのみならず、苦しそうにすくんでいた主人さえ記憶の中から消え去った時突然流しと板の間の中間で大きな声を出すものがある。見ると紛れもなき苦沙弥先生である。主人の声の図抜けて大いなるのと、その濁って聴き苦しいのは今日に始まった事ではないが場所が場所だけに吾輩は少からず驚ろいた。", 8.456548690795898 ], [ 213, "「なるほど仲居は茶屋に隷属するもので、遣手は娼家に起臥する者ですね。次に見番と云うのは人間ですかまたは一定の場所を指すのですか、もし人間とすれば男ですか女ですか」「見番は何でも男の人間だと思います」「何を司どっているんですかな」「さあそこまではまだ調べが届いておりません。その内調べて見ましょう」これで懸合をやった日には頓珍漢なものが出来るだろうと吾輩は主人の顔をちょっと見上げた。", 6.545511245727539 ], ... ] ... ベクトル検索 (kNN) 次にベクトル検索(kNN)を行ってみます。スクリプトは下記にも掲載しています。 a8_vector_search.md POST /_query { "query": """ FROM waganeko METADATA _score, _id, _index | WHERE KNN(content_embedding, TEXT_EMBEDDING(?query, ".jina-embeddings-v5-text-nano")) | KEEP chunk_no, content, _score | SORT _score DESC | LIMIT 20 """, "params": [ { "query": "吾輩が生まれた場所は?" } ] } クエリーから密ベクトルを生成するモデルには、”.jina-embeddings-v5-text-nano” を指定します。 検索結果:「どこで生れたかとんと見当がつかぬ…」という有名な冒頭部分が 1 位にランクインしました。 ... "values": [ [ 2, "どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。", 0.7278214693069458 ], [ 1039, "ちょうど三日目の暁方に、隣の家で赤ん坊がおぎゃあと泣いた声を聞いて、うんそうだと豁然大悟して、それから早速長い髪を切って男の着物をきて Hierophilus の講義をききに行った。首尾よく講義をきき終せて、もう大丈夫と云うところでもって、いよいよ産婆を開業した。ところが、奥さん流行りましたね。あちらでもおぎゃあと生れるこちらでもおぎゃあと生れる。", 0.7182090282440186 ], ... ] ... Reciprocal Rank Fusion (RRF) によるハイブリッド検索 さきほどのキーワード検索結果とベクトル検索結果を RRF により融合してみます。スクリプトは下記にも掲載しています。 a9_rrf.md POST /_query { "query": """ FROM waganeko METADATA _score, _id, _index | FORK (WHERE KNN(content_embedding, TEXT_EMBEDDING(?query, ".jina-embeddings-v5-text-nano")) | SORT _score DESC | LIMIT 20) (WHERE MATCH(content, ?query) | SORT _score DESC | LIMIT 20) | DROP content_embedding | FUSE | KEEP chunk_no, content, _score | SORT _score DESC | LIMIT 10 """, "params": [ { "query": "吾輩が生まれた場所は?" } ] } ES|QL の FUSE を使って RRF によるランキング融合を行っています。 ES|QL FUSE command | Elasticsearch Reference www.elastic.co 検索結果:今回は、欲しかったドキュメントのキーワード検索での順位が低かったために、RRF での結果では欲しかったドキュメントが第2位になっています。 ... "values": [ [ 1462, "今日何人あばたに出逢って、その主は男か女か、その場所は小川町の勧工場であるか、上野の公園であるか、ことごとく彼の日記につけ込んである。彼はあばたに関する智識においては決して誰にも譲るまいと確信している。せんだってある洋行帰りの友人が来た折なぞは、「君西洋人にはあばたがあるかな」と聞いたくらいだ。", 0.028958333333333336 ], [ 2, "どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。", 0.01639344262295082 ], ... ] ... セマンティックリランク さきほどの RRF により候補を絞り込んだ後に、セマンティックリランクを行ってみます。 セマンティックリランクに利用するモデルは、”.jina-reranker-v3″ です。 スクリプトは下記にも掲載しています。 a10_rrf_semantic_rerank.md POST /_query { "query": """ FROM waganeko METADATA _score, _id, _index | FORK (WHERE MATCH(content, ?query) | SORT _score DESC | LIMIT 20) (WHERE KNN(content_embedding, TEXT_EMBEDDING(?query, ".jina-embeddings-v5-text-nano")) | SORT _score DESC | LIMIT 20) | DROP content_embedding | FUSE | SORT _score DESC | LIMIT 10 | RERANK ?query ON content WITH { "inference_id" : ".jina-reranker-v3" } | KEEP chunk_no, content, _score | SORT _score DESC """, "params": [ { "query": "吾輩が生まれた場所は?" } ] } ES|QL の RERANK コマンドを使ってセマンティックリランクを行います。 ES|QL RERANK command | Elasticsearch Reference www.elastic.co 注目してほしいのは、Self-Managed の Elasticsearch には .jina-reranker-v3 をインストールしていないにもかかわらず、利用できる点です。 検索結果:欲しかったドキュメントが第1位になりました。 ... "values": [ [ 2, "どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。", 0.33165714144706726 ], [ 1217, "しばらくは爺さんの方へ気を取られて他の化物の事は全く忘れていたのみならず、苦しそうにすくんでいた主人さえ記憶の中から消え去った時突然流しと板の間の中間で大きな声を出すものがある。見ると紛れもなき苦沙弥先生である。主人の声の図抜けて大いなるのと、その濁って聴き苦しいのは今日に始まった事ではないが場所が場所だけに吾輩は少からず驚ろいた。", 0.08227790892124176 ], ... ] ... 生成AIによる回答 最後に、検索結果のコンテキストを LLM に渡し、自然言語で回答を生成させます。 やや乱暴ですが、セマンティックリランクの結果の第1位のドキュメントの内容を元にして、生成AI に質問に回答するよう依頼してみます。 回答に使用するモデルは、.openai-gpt-oss-120b-completion です。 スクリプトは下記にも掲載しています。 a11_completion.md POST /_query { "query": """ FROM waganeko METADATA _score, _id, _index | FORK (WHERE MATCH(content, ?query) | SORT _score DESC | LIMIT 20) (WHERE KNN(content_embedding, TEXT_EMBEDDING(?query, ".jina-embeddings-v5-text-nano")) | SORT _score DESC | LIMIT 20) | DROP content_embedding | FUSE | SORT _score DESC | LIMIT 10 | RERANK ?query ON content WITH { "inference_id" : ".jina-reranker-v3" } | SORT _score DESC | KEEP content | LIMIT 1 | COMPLETION CONCAT("Answer in Japanese the following question ", ?query, " based on:\n", content) WITH { "inference_id" : ".openai-gpt-oss-120b-completion" } """, "params": [ { "query": "吾輩が生まれた場所は?" } ] } ES|QL の COMPLETION コマンドを利用しています。 ES|QL COMPLETION command | Elasticsearch Reference www.elastic.co 注目してほしいのは、Self-Managed の Elasticsearch には .openai-gpt-oss-120b-completion をインストールしていないにもかかわらず、利用できる点です。 回答結果の例 吾輩は「薄暗く湿った所」、すなわち暗くてじめじめした場所で生まれました。 (「どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。」という記述に基づく。) 合っているようです。 技術的な補足 モデル名の指定 EIS で利用するモデル名は、Elastic Cloud の Relevance > Inference endpoints 画面に表示される Endpoint を使用します。 RRF とセマンティックリランクの役割 本サンプルでは、RRF とセマンティックリランクを併用しています。 Reciprocal Rank Fusion (RRF): キーワードとベクトルの異なる検索手法を統合し、候補を漏れなく抽出する「絞り込み」のフェーズ。 セマンティックリランク: 絞り込まれた上位ドキュメントに対し、LLM 的な文脈理解で「真の回答」を最上位に持ってくる「仕上げ」のフェーズ。 waganeko_tmp インデックス waganeko_tmp インデックスの内容を waganeko インデックスへ reindex した後は、waganeko_tmp インデックスは不要となります。 必要なければ、削除してかまいません。 参考リンク Cloud Connect を利用した場合の追加費用については、下記を参照してください。 https://cloud.elastic.co/cloud-pricing-table?productType=cloud_connect まとめ Self-Managed の Elasticsearch であっても、Elastic Inference Service (EIS)を活用することで、 重い推論モデルを自前で管理・運用することなく、ベクトル検索やセマンティックリランク、生成AIによる回答を極めてシンプルに実装できました。 ぜひ、皆さんの環境でも EIS を活用した高度な検索体験を試してみてください。 The post Elastic Inference Service (EIS) を使った「ベクトル検索」および「生成AIによる回答(RAG)」(実践編) first appeared on Elastic Portal .
株式会社リクルートは、日本国内で HR・販促事業を行う事業会社です。リクルートでは、満足度No1(*1)を誇る飲食店予約・グルメ情報サイト『ホットペッパーグルメ』を運営しています。 『ホットペッパーグルメ』では、ユーザーが飲食店を検索する際の「0件ヒット」問題を解決するため、 Amazon OpenSearch Service (以下、OpenSearch Service)を採用し、Hybrid Search 機能を実現しました。約6ヶ月の取り組みにより、検索における0件ヒットを90%削減し、検索経由の予約数を10%向上させることに成功しています。 本ブログでは、『ホットペッパーグルメ』における検索改善の取り組みについて、チャレンジから導入効果までをご紹介します。 課題 『ホットペッパーグルメ』では、ユーザーが飲食店を探そうと検索しても、検索結果が0件になるケースがありました。行きたいお店があるのに見つけられず、ユーザーと飲食店のマッチングの機会を逃してしまう状況です。これはユーザー体験の低下だけでなく、検索から予約への転換機会の損失にもつながる重要なビジネス課題でした。 0件ヒットが発生する背景として従来の検索技術である Lexical Search には限界があり、ユーザーの意図を正しく理解できないケースがありました。 Lexical Search について : Lexical Search(字句検索)は、入力されたキーワードと完全に一致する文字列を検索する手法です。しかし入力ミスに弱いという課題がありました。ユーザーが「寿司」と入力しようとして「寿し」や「すそ」と入力してしまうと、検索結果を取得できません。また、日本語はスペースで単語を区切らない言語のため、セグメンテーションエラーが発生しやすいという日本語特有の課題もありました。 Vector Search の限界 : Vector Search(ベクトル検索)は、テキストを数値ベクトルに変換し、意味的な類似度で検索する手法です。タイポや言い換えに強いという特徴がありますが、固有名詞の検索で精度が低下するという課題がありました。店舗名のような固有名詞を検索する際、意味的に近い別の店舗が上位に表示されてしまいます。また、位置情報などのノイズにより、検索意図とは異なる結果が返される意味的なずれも発生していました。 検索手法 Lexical Search Vector Search 完全一致キーワード ○ × タイポ耐性 × ○ 言い換え対応 × ○ 固有名詞の精度 ○ × 日本語セグメンテーション × ○ 一般的に、ユーザーは検索結果の上位に高い関心があるため(*2)、 特にTop-1の検索結果を改善する方針 としました。 OpenSearch Service を活用した Hybrid Search の実現 これらの課題を解決するため、『ホットペッパーグルメ』では Lexical Search と Vector Search を組み合わせた Hybrid Search を採用しました。またそれを支える基盤として、OpenSearch Service を採用しました。 OpenSearch Service を選定した最大の理由は、Full-text Search と Vector Search を単一のサービスで実現できる Hybrid Search 機能の統合です。また、フルマネージド型サービスとしてインフラ管理の負荷を軽減できる開発・運用の容易さも重要な選定ポイントでした。さらに、OpenSearch Ingestion による Managed ETL や言語別テキスト解析プラグインなど AWS がサポートするプラグインが充実しており、豊富な周辺機能を活用できます。Blue/Green デプロイによる無停止でのスケーリングも、運用の安心感を確保する上で大きなメリットでした。 アーキテクチャと実装 Two-tower Model Architecture 『ホットペッパーグルメ』では、検索クエリと店舗情報をそれぞれ別のエンコーダーで処理する Two-tower Model Architecture を採用しました。 Two-tower Model は、Query Encoder と Document Encoder という2つの独立したエンコーダーで構成されています。Query Encoder はユーザーが入力した検索クエリ(例:「東京 寿司」)をベクトルに変換します。一方、Document Encoder は店舗情報をベクトルに変換します。店舗情報には、店舗名、住所、メニュー、説明文、キャッチコピー、レビューなど、検索に関連する情報が含まれます。埋め込みのスコープについても検証を行いました。店舗名や住所といった基本情報のみを使用するパターンと、メニューや説明文、キャッチコピーといったコンテンツフィールドを含めるパターンを比較し、検索精度への影響を評価しています。 モデルの学習には対照学習を採用しました。学習データとして、ユーザーの検索ログと LLM で生成した合成ペアを組み合わせて使用しています。ユーザーログからは実際の検索行動に基づくクエリと店舗のペアを抽出し、LLM 生成の合成ペアにより学習データのカバレッジを拡大しました。ベースモデルには日本語 BERT を採用し、『ホットペッパーグルメ』のドメインに特化したファインチューニングを実施しています。汎用的な埋め込みモデルと比較して、店舗名のようなドメイン固有の用語に対する検索精度が向上しました。埋め込みの次元数は512次元を採用しています。 日本語テキストの形態素解析には Sudachi を使用し、split_mode: A(最小単位での分割)を設定しています。これにより、日本語特有の複合語や固有名詞を適切にトークン化し、検索精度の向上に寄与しています。 導入プロセス Built-in Hybrid Search(第1フェーズ) 最初のステップとして、AOS の標準機能である Built-in Hybrid Search を導入しました。Built-in Hybrid Search は実装が容易であり、検索基盤の構築よりもモデル改善に集中できるというメリットがありました。 Built-in Hybrid Search では、OpenSearch が Lexical と Vector の検索結果を取得し、それぞれのスコアを正規化した後、単一のスコアにマージしてから Reranking を行います。この仕組みにより、短期間で Hybrid Search を導入し、効果を検証することができました。 一方で、運用を通じて課題も見えてきました。一度スコアがマージされると、スコアの差異が何に起因するのかを把握できなくなります。Lexical Score が高く Vector Score が低い場合、それがタイポによるものなのか、言い換えによるものなのか、あるいはレアな固有名詞によるものなのか、判断できません。どちらのシグナルを重視すべきかは検索意図に依存するため、固定のマージ重みでは対応しきれないケースがありました。この学びから、Reranker には統合されたスコアではなく、Lexical Score と Vector Score を個別に渡す必要があるという結論に至りました。 Custom Hybrid Search(第2フェーズ) Built-in Hybrid Search の課題を解決するため、Custom Hybrid Search を開発しました。 Custom Hybrid Search では、Lexical Score と Vector Score を別々に保持し、マージ前の個別スコアを Reranker に渡すことで、より精緻なランキングが可能になりました。また、ユーザーログや検索意図などの追加シグナルを Reranking に活用しています。スコアの統合には LightGBM による適応的なスコア統合とランキングを採用し、機械学習モデルによる動的なスコア統合を実現しています。アーキテクチャは Planner → Executor → Merger & Reranker の構成となっており、検索意図に応じた柔軟なランキングを実現しています。Custom Hybrid Search の導入により、Top-1 検索精度が最大2倍改善しました。 段階的なリリース戦略 新しい検索システムの導入にあたり、A/B Test Router を用いたトラフィック制御を実施しました。当初は新規システムに10%、既存システムに90%のトラフィックを振り分け、この比率を段階的に調整しながら A/B テストにより効果を検証しました。A slot / B slot による並行検証を行い、新システムの安定性と効果を確認した上で、トラフィック比率を徐々に増加させていきました。 成果 ビジネス成果 : Custom Hybrid Search を本番環境に導入した結果、検索経由の予約数が10%改善し、0件ヒット検索が90%削減されました。これらの改善により、ユーザーと飲食店のマッチング機会が大幅に増加し、サービス全体の価値向上につながっています。 技術的成果 : 運用面でも多くのメリットが得られました。Blue/Green デプロイによる無停止でのスケーリングで運用負荷が軽減され、Plugins や OpenSearch Ingestion を活用することでランキングモデルの改善サイクルを加速できています。フルマネージド型サービスとしてインフラ管理の負荷が軽減されたことで、運用の安心感も確保できました。 まとめ 本ブログでは、『ホットペッパーグルメ』における OpenSearch Service で実現された Hybrid Search を活用した検索改善の取り組みをご紹介しました。 Lexical Search と Vector Search を組み合わせた Hybrid Search により、それぞれの検索手法の強みを活かしながら弱点を補完し、0件ヒット問題を大幅に改善することができました。また、Built-in Hybrid Search から Custom Hybrid Search への段階的な進化により、Top-1 検索精度を最大2倍改善し、検索経由の予約数10%向上という成果を達成しています。 OpenSearch Service の採用により、Lexical Search と Vector Search を組み合わせた Custom Hybrid Search の構築、開発・運用の容易さ、Blue/Green Deployment による安定した運用を実現できました。 こうした検索基盤の進化は、新たなユーザー体験の創出にもつながっています。2026年1月22日にリリースした 「席押さえ」機能 は、現在地周辺のお店をマップから探し、「今すぐ席を押さえる」ボタンをタップするだけで、予約なしで席を確保できるアプリ限定機能です。リアルタイムな「空席情報」と「位置情報」を地図 UI 上でマッチングするこの検索体験も、OpenSearch Service で実現しています。 Reference (*1)2025年6月時点 株式会社東京商工リサーチ調べ (*2) Google検索のClick Trough Rateは、Top1が27.6% backlinko 社調べ AWS re:Invent 2025 – Build Advanced Search with Vector, Hybrid, and AI Techniques (ANT314) Amazon OpenSearch Service BlackBelt 『ホットペッパーグルメ』「席押さえ」機能を全国で提供開始
こんにちは、AIテクノロ ジー グループのエンジニアの吉田です。 本記事は Enigmo Advent Calendar 2025 の 18日目の記事です。 普段は検索システム全般、 機械学習 システムのMLOps、AI関連の機能開発を担当しております。 この記事では「AIでさがす」サービスのリニューアルについて紹介します。 「AIでさがす」サービスとは 「AIでさがす」サービスは、 BUYMA のWebサイトおよびアプリで提供している、AIを活用した商品提案サービスです。 実際の機能は以下からご利用頂けます。( BUYMA アカウントでのログインが必要となります。) 「AIでさがす」サービス ユーザーが文章で質問すると、AIが質問内容を理解し、おすすめの商品を提案します。例えば「春のデートにぴったりなワンピースを教えて」といった質問に対して、AIが回答文とともに具体的な商品を紹介します。 従来のキーワード検索では見つけにくかった商品や、ユーザー自身が気づいていなかった新しい商品との出会いを提供することで、 BUYMA でのショッピング体験をより豊かにすることを目指しています。 ※商品画像はモザイク加工しております。 リニューアルの背景 旧システムは、ChatGPT API を活用した商品提案サービスでしたが、主な課題が3点ありました。 BUYMA の知識不足 ChatGPT が一般的な知識で回答を生成するため、 BUYMA ならではのトレンドや商品特性を反映できない。 根拠の不明確さ ChatGPT の回答に 参照元 がない。 検索キーワード生成の精度 形態素解析 ツールの MeCab を併用していましたが、文脈や意味を理解した検索キーワード生成ができない。 また、リリースから2年が経過し、本格的にバージョンアップが必要なタイミングでもありました。 ※旧システムの詳細は こちらの記事 で紹介しております。 ちょうどチームメンバーが社内ドキュメントのAI検索システムを開発しており、この仕組みを BUYMA の多数の記事コンテンツに適用すれば、より BUYMA らしい商品提案が可能になると考えました。 そこで、今回のリニューアルでは、 BUYMA 内の記事コンテンツ群をベースに会話するエージェントを作成しました。これにより、 BUYMA ならではの知識を持ったAIが、より BUYMA でおすすめしたい商品を提案できるようになりました。 システム変更前後の比較 旧システムと新システムの違いは以下の通りです。 旧システムでは、ChatGPT が一般的な知識で回答を生成し、 MeCab による単純な 形態素解析 で検索キーワードを生成していました。そのため、 BUYMA ならではの文脈を理解した商品提案が難しい状況でした。 新システムでは、 BUYMA 内記事コンテンツを参照した Vertex AI Search が回答文を生成し、Gemini が文脈を理解した検索キーワードを生成します。その結果、より BUYMA らしい商品提案が可能になりました。 それぞれの処理フローは以下の通りです。 旧システム処理フロー BUYMA 基幹システムから「AIでさがす」 API にリクエスト 「AIでさがす」 API がユーザーの質問を ChatGPT API に送信 ChatGPT が回答文とおすすめアイテムリストを生成 アイテム名を MeCab ( 形態素解析 )で解析し、検索キーワードを生成 検索 API で商品情報を取得し、ユーザーに表示 新システム処理フロー BUYMA 基幹システムから「AIでさがす」 API にリクエスト 「AIでさがす」 API がユーザーの質問を Vertex AI Search に送信 Vertex AI Search (事前に BUYMA 内記事コンテンツをインポート済み)が回答文を生成 質問文と回答文を Gemini に送信し、検索キーワードを生成 検索 API で商品情報を取得し、ユーザーに表示 アーキテクチャ ー特徴 1. Vertex AI Search Vertex AI Search を利用して、インポートした BUYMA 内記事コンテンツをベースに会話を行うエージェントを構築しました。 BUYMA 内記事コンテンツのインポート 約4000件の記事をデータストアにインポート プロンプト設計 「ファッション ECサイト BUYMA のショッピングアドバイザー」として定義し、ユーザーの質問に対して最適な商品を提案する形で回答を生成 2. Gemini Gemini を活用する事により、会話内容から商品検索キーワードを生成する機能を作成しました。 プロンプト設計 「 ECサイト の検索キーワードを生成する専門家」として定義し、会話の文脈を理解して検索キーワードを生成 MeCab との違い MeCab は単語の分解のみだが、Gemini は文脈を理解してブランド名・カテゴリ名・モデル名を組み合わせた検索キーワードを生成 実装時の課題・解決策・工夫した点 Vertex AI Search の幻覚への対応 初回質問時に Vertex AI Search が過去から質問が続いているような幻覚を見る場合がありました。当初は初回と2回目以降の会話を共通のプロンプトで行っており、「ユーザーの過去の質問履歴」の項目に入っている文言の有無から初回なのか、2回目以降の会話なのかを判断する指示を出していました。ところが、「過去」という文言に引きずられてなのか、初回なのに過去の質問をAI側が捏造して、その続きとして回答する場合が稀にありました。 プロンプトテンプレートを初回用と2回目以降用の2種類に分け、初回用のプロンプトからは「ユーザーの過去の質問履歴」の文言自体を削除する事によって対応しました。 敵対的クエリへの対応 敵対的クエリ(不適切な質問)の場合、Vertex AI Search の API からのレスポンスフォーマットが通常とは異なるものになり、要約が生成できないにもかかわらず、無理やり商品紹介を行ってしまいました。 敵対的クエリーのフォーマットを検知した場合は、要約失敗として扱い、商品紹介を行わないように修正しました。 この場合以外でも稀に異なるフォーマットのレスポンスになる場合があり、サービス継続に支障が出ないように都度改善を行いました。 Gemini のライブラリ移行 もともと使用していたライブラリがサポート終了を迎えるため、社内では実績がない新しいライブラリに移行する必要がありました。移行後、従来使用していた Gemini モデルが初期設定では使用できず、次世代のモデルを試したところレスポンスタイムが大幅に遅くなってしまいました。新しいライブラリという事もあり、AIツールではなかなか解決できず、最終的には Google サポートに問い合わせして解決に至りました。 得られた学びとノウハウ AIツールの活用と限界 「AIでさがす」のバックエンド API の リポジトリ は、ほぼ全部作り直したのですが、AIツールを活用する事によって、 工数 を節約する事ができました。Terraform 関連のリソース修正、テストケース作成やMOCK用のフロントエンド実装等においてもAIツールにより大幅な 工数 削減ができました。 一方で、Gemini のライブラリ移行など、ドキュメントの記載やインターネット上での知見が少ない領域ではAIツールでは解決できず、結果的に公式サポートへの問い合わせが必要でした。 AIの不確定な挙動への対応 Vertex AI Search の幻覚や部分的な失敗など、AIサービス特有の不確実性に対して、初回用と2回目以降用でテンプレートを分けるなど、細かな調整が重要でした。また、プロンプトだけではどうする事もできない場合があり、そのような場合は後処理でルールベースのロジックを追加する必要がありました。 効果測定 リニューアル後、以下のような指標が上昇しました。 1スレッドあたりの質問数の平均 会話の継続性が向上し、ユーザーが複数回質問を続けるようになりました。 1ユーザー1日あたりの質問数 利用頻度が向上し、ユーザーがより積極的に機能を活用するようになりました。 検索URLに遷移された回数 商品検索への誘導効果が向上し、実際の商品閲覧につながるケースが増加しました。 これらの結果から、 BUYMA 内記事コンテンツを根拠とした回答の提供と、文脈を理解した検索キーワード生成により、ユーザーの満足度と利用価値が向上したと考えられます。 直近の対応/今後の展開・課題 金額絞り込み機能の追加(今月対応) ユーザーからの要望が多い金額絞り込み機能の対応をしました。価格帯に関する質問に対して適切な商品提案ができていない課題があったため、Gemini で検索 API 用の金額フィルタークエリを生成することで対応しました。 コンテンツの拡充 現在は BUYMA 内記事コンテンツのみを Vertex AI Search にインポートしていますが、今後は YouTube での発信内容も追加する予定です。記事以外のコンテンツも活用することで、より幅広い情報をユーザーに提供できるようになります。 継続的なメンテナンス AIのライブラリやモデルは随時更新されていくため、継続的なメンテナンスが課題となります。特に Gemini や Vertex AI Search などのサービスは進化が早く、新しいモデルへの対応や非推奨ライブラリ/バージョンから移行など、定期的な見直しが必要です。 まとめ 本記事では、「AIでさがす」サービスのリニューアルについて紹介しました。 旧システムでは ChatGPT と MeCab を使用していましたが、 BUYMA 特有の知識不足や根拠の不明確さなどの課題がありました。リニューアルでは Vertex AI Search と Gemini を採用し、 BUYMA 内記事コンテンツを根拠とした回答生成と文脈を理解した検索キーワード生成を実現しました。 実装時には敵対的クエリへの対応やAIサービス特有の不確実性への対処など様々な課題に直面しましたが、ロジックでの細かい制御やAIツールの活用により解決できました。リニューアル後は会話継続性や利用頻度、商品検索への誘導効果が明らかに向上しています。 明日の記事は同じAIテクノロ ジー グループの髙橋さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
動画
該当するコンテンツが見つかりませんでした












