every Tech Blog

株式会社エブリーのTech Blogです。

デリッシュAIのアーキテクチャ

この記事は every Tech Blog Advent Calendar 2024 の 14 日目の記事です。

はじめに

こんにちは。
開発本部のデータ&AIチームでデータサイエンティストをしている古濵です。
直近開発に取り組んでいるデリッシュAIのアーキテクチャについてご紹介します。

DELISH KITCHENでは 「作りたい!が見つかる」をサービスのコンセプトとして、様々な機能を提供してきました。
一方、ユーザーひとりひとりの多様なニーズに合わせたレシピを提案していくには既存機能だけでは難しい部分があります。 そこで、AIによる料理アシスタントとして「デリッシュAI」ベータ版を一部ユーザーから提供し始めています。

アーキテクチャ

AI Server

  1. あらかじめレシピのタイトル、手順、タグ情報などを持ったデータをEmbedding化するバッチを組んでおく
  2. そのレシピEmbedding情報をベクトル検索できるようにMosaic AI Vector Searchを用いたベクトルストアを用意
  3. RAGの仕組みを活用して、ユーザのクエリをベクトル検索し、検索して得られたレシピのコンテキストをプロンプトに入れ込んで、LLMにリクエスト
  4. LLMはOpenAI APIを利用し、Structured Outputs機能を活用して、レシピの提案コメントとレシピIDを返す
  5. これらのコードをmlflow.pyfunc.PythonModelを継承したクラスとして定義し、mlflow.pyfunc.log_modelでモデルを保存(後述する今度の課題>コード管理参照)
  6. Mosaic AI Foundation Model Servingの機能で、保存したモデルをデプロイし、Seving EndpointsのURLを取得

     例)
     https://{サブドメイン}.cloud.databricks.com/serving-endpoints/{モデル}/invocations
    
  7. API GatewayにSeving EndpointsのURLを登録しておく

Delish Server

  1. ユーザからのリクエストを受け取る
  2. 過去の会話履歴に基づいてレシピ提案するため、レシピデータと会話履歴を取得し、レシピのメタデータを持った会話履歴データを作成
  3. API Gatewayにユーザのクエリと会話履歴データをリクエスト
  4. API Gatewayのマッピングテンプレート機能を使って、リクエストのフォーマットを変換し、DatabricksのModel Servingにリクエスト
  5. Model Serving Endpointからのレスポンス(AI検索結果)を受け取り、レシピIDがハルシネーションしていないかなど、レスポンスが適切かをチェック
  6. ユーザにレシピを提案するレスポンスを返す
  7. 同時に会話履歴を踏まえたレシピの提案をするために会話履歴を保存
  8. 同様に蓄積したデータを分析するために、event logを保存

意図

職責による実装の分離

弊社では、データ&AIチームがデータの収集・分析基盤の開発・運用を担当し、事業部のサーバーサイドエンジニアがユーザに提供するためのAPIやバックエンドシステムの開発・運用を担当していることが多いです。
今回のデリッシュAIの開発も同様に、データ&AIチームが機械学習モデルを開発し、事業部のサーバーサイドエンジニアがこれらのモデルをサービスに統合するという役割分担をしました。
これにより、それぞれのチームが普段使い慣れている技術をベースに開発を進めることができ、短期間でデリッシュAIを提供することができています。

API Gatewayの活用

Delish ServerとDatabricksの繋ぎ込み部分は、Amazon API Gateway マッピングテンプレートと Amazon SageMaker を使用して機械学習を搭載した REST API の作成を参考に、API Gatewayを採用しています。これは、以前のPoC実績をベースにしています。

API Gatewayを利用することで、以下の2点の恩恵を受けられると考えています。

1. ログやメトリクスの拡充

ログやメトリクスは、DatabricksのMosaic AI Foundation Model Servingでも確認できるのですが、詳細を見ることができる情報量がAPI GatewayからCloundWatchに送られるログの方が多いです。実際に開発時にも、CloundWatchのログからエラーの原因を特定することができた実績もあります。

2. リクエスト/レスポンスの柔軟なフォーマット変換

「モデルのI/O」と「APIのリクエスト/レスポンス」が必ずしも一致しない場合があります。 また、何らかの理由でフォーマットを変更したい可能性もあります。 そのとき、マッピングテンプレートを変更することで、「モデルのI/O」と「APIのリクエスト/レスポンス」のどちらかを変更したい場合でも、柔軟な対応が可能になります。

今後の課題

コード管理

デリッシュAIのロジックであるRAGの実装部分は以下のようなコード構成になっています。 これまでは、これをDatabricksの1ノートブック内に全てコーディングする管理をしていました。 しかし、これから開発がさらに進むことを想定して、ある程度コードを共通化したいニーズが生まれて来ており、新たなコード管理の整備が必要だと感じています。

import mlflow
from mlflow.models import infer_signature

def generate_answer(query, conversation_history):
    # ここでRAGの仕組みを活用してレシピの提案コメントを生成
    return response

class DelishAI(mlflow.pyfunc.PythonModel):  
    def predict(self, context, input):
        query = str(input['query'][0])
        conversation_history = list(input['conversationHistory'][0])
        return generate_answer(query, conversation_history)

# サンプルデータ
query_sample = "もっとスパイシーなカレーのレシピ教えて"
conversation_history_sample = [...]
input_sample = {
    "query": query_sample,
    "conversationHistory": conversation_history_sample
}
output_sample = generate_answer(query_sample, seed_sample, conversation_history_sample)

# シグネチャの作成(モデルのI/Oを保存時に付与)
signature_sample = infer_signature(input_sample, output_sample)

# Unity Catalogで登録するモデル名
model_name = f"{catalog}.{schema}.{model}"

# モデルの保存
with mlflow.start_run():
    model_info = mlflow.pyfunc.log_model(
        artifact_path="model",
        python_model=DelishAI(),
        signature = signature_sample,
        registered_model_name=model_name,
    )

評価データの収集

Databricksでは、Mosaic AI Agent Frameworkを使用してAIエージェントを作ることができます。
AIエージェントをデプロイすると、Databricks上で簡単に自分たちが作成したモデルをレビューアプリとして利用することができます。
レビューアプリはAIエージェントの品質に関するフィードバックを利用者から集めることができ、社内で公開することで、toC向けの機能だとしても品質向上に役立てることができると考えています。
デリッシュAIも、Slackbotで社内提供させるところから始まって改善を繰り返しており、実際にどんな使われ方をしているかを把握することは、開発する上で重要だと感じています。

ただ、AIエージェントの利用には入出力のスキーマが特定のフォーマットである必要があります。ここでも柔軟にフォーマット変更できるマッピングテンプレートが活きてくると考えています。

まとめ

デリッシュAIのアーキテクチャについてご紹介しました。 今後も、ユーザのニーズに合わせた提案していくために、デリッシュAIの機能を拡充していく予定です。