DatabricksにおけるRAGのデプロイとレビュー

こちらの続編です。

https://qiita.com/taka_yayoi/items/1210d88ff570627e4c5d

前回は、RAGを構成する機能をツールとして実装し、AI Playgroundで動作を確認しました。プロダクションに投入できる見通しが立ったらデプロイしましょう。ここでは、[Mosaic AI Agent Framework](https://docs.databricks.com/ja/generative-ai/agent-framework/build-genai-apps.html)と[Agent Evaluation](https://docs.databricks.com/ja/generative-ai/agent-evaluation/index.html)を活用します。

![Screenshot 2024-11-10 at 20.33.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/d53637e6-1d59-5f9f-076a-13015f0b2c12.png)

Agent Framework + Agent Evaluationと連携する機能がすでに用意されています。AI Playgroundの画面の上部に**エクスポート**ボタンがあります。こちらをクリックすることで、選択されている言語モデル、ツール、AI Playground上でのリクエストやレスポンスを取り込んで、Pythonの実装ノートブックが生成されます。

![Screenshot 2024-11-10 at 20.07.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/6bdd5fb1-1182-897f-8262-2bd3f2093ab1.png)

以下のようなドライバーノートブックに遷移します。コメントを翻訳しながら実行していきます。

# ドライバーノートブック

これはAIプレイグラウンドエクスポートによって自動生成されたノートブックです。同じフォルダーに3つのノートブックを生成しました：
- agent: エージェントを構築するコードが含まれています。
- config.yml: 設定が含まれています。
- **driver**: エージェントのログ、評価、登録、およびデプロイを行います。

このノートブックは、agentノートブックで定義されたエージェントをデプロイするためにMosaic AIエージェントフレームワーク（[AWS](https://docs.databricks.com/ja/generative-ai/retrieval-augmented-generation.html) | [Azure](https://learn.microsoft.com/ja-jp/azure/databricks/generative-ai/retrieval-augmented-generation)）を使用します。このノートブックは以下を行います：
1. エージェントをMLflowにログします
2. エージェントをUnity Catalogに登録します
3. エージェントをモデルサービングエンドポイントにデプロイします

## 前提条件

- このノートブックのすべての`TODO`を解決してください。
- [config.yml]($./config.yml)の内容を確認してください。これはエージェントが利用できるツールとLLMエンドポイントを定義しています。
- このフォルダーの[agent]($./agent)ノートブックを確認し、エージェントのコードを反復し、出力をテストしてください。

## config.yml

このエージェントでは、独自のエンべディングモデルとベクトル検索インデックスを使用しています。これをエージェントの設定に含めるように、以下のconfig.ymlでは`vector_search_index`と`embedding_endpoint`のエントリーを追加しています。

```yaml:config.yml
agent_prompt: "あなたはDatabricksに関する質問に回答するためにツールを活用する有能なアシスタントです。提供されているツールを活用してください: Databricksに関する質問に回答するために、vector_searchとgenerate_responseを活用してください。"
llm_endpoint: "taka-openai-gpt-4o-mini"
vector_search_index: "takaakiyayoi_catalog.qiita_2023.qiita_2023_vs"
embedding_endpoint: "taka-japanese-embedding"
tools:
  uc_functions:
    - "takaakiyayoi_catalog.qiita_2023.generate_response"
    - "takaakiyayoi_catalog.qiita_2023.vector_search"
```


## 次のステップ

エージェントがデプロイされた後、AIプレイグラウンドでエージェントとチャットして追加のチェックを行ったり、組織内のSMEと共有してフィードバックを得たり、プロダクションアプリケーションに埋め込んだりすることができます。詳細はドキュメント（[AWS](https://docs.databricks.com/en/generative-ai/deploy-agent.html) | [Azure](https://learn.microsoft.com/en-us/azure/databricks/generative-ai/deploy-agent)）を参照してください。

```py
%pip install -U -qqqq mlflow databricks-agents
dbutils.library.restartPython()
```

## `agent`をMLflowモデルとしてログする
agentノートブックのコードをエージェントとしてログします。詳細は[MLflow - Models from Code](https://mlflow.org/docs/latest/models.html#models-from-code)を参照してください。

:::note info
**注意**
config.ymlで定義したカスタムのエンべディングモデルサービングエンドポイントとベクトル検索インデックスを`resources`に追加しています。これらの設定を行わないと、デプロイされたエージェントからそれらのリソースにアクセスする際に、`[REMOTE_FUNCTION_HTTP_FAILED_ERROR] The remote HTTP request failed with code 403, and error message 'HTTP request failed with status: {"error_code":"PERMISSION_DENIED","message":"Insufficient permissions for UC entity `と言うような権限不足のエラーが発生します。
:::


```py
# モデルをMLflowにログする
import os
import mlflow

from mlflow.models import ModelConfig
from mlflow.models.signature import ModelSignature
from mlflow.models.rag_signatures import (
    ChatCompletionRequest,
    ChatCompletionResponse,
)
from mlflow.models.resources import (
    DatabricksFunction,
    DatabricksServingEndpoint,
    DatabricksVectorSearchIndex
)
from databricks.sdk import WorkspaceClient

w = WorkspaceClient()
config = ModelConfig(development_config="config.yml")
resources = [
    DatabricksServingEndpoint(endpoint_name=config.get("llm_endpoint")),
    DatabricksServingEndpoint(endpoint_name=config.get("embedding_endpoint")),
    DatabricksVectorSearchIndex(index_name=config.get("vector_search_index")),
]
uc_functions_to_expand = config.get("tools").get("uc_functions")
for func in uc_functions_to_expand:
    # 関数名が正規表現の場合、スキーマ内のすべての関数を取得
    if func.endswith("*"):
        catalog, schema, _ = func.split(".")
        expanded_functions = list(
            w.functions.list(catalog_name=catalog, schema_name=schema)
        )
        for expanded_function in expanded_functions:
            resources.append(
                DatabricksFunction(function_name=expanded_function.full_name)
            )
    # それ以外の場合は関数を追加
    else:
        resources.append(DatabricksFunction(function_name=func))

signature = ModelSignature(ChatCompletionRequest(), ChatCompletionResponse())

input_example = {
    "messages": [{"role": "user", "content": "find me franchises in the US"}]
}

with mlflow.start_run():
    logged_agent_info = mlflow.pyfunc.log_model(
        "agent",
        python_model=os.path.join(
            os.getcwd(),
            "agent",
        ),
        signature=signature,
        input_example=input_example,
        model_config="config.yml",
        resources=resources,
    )
```

## [エージェント評価](https://docs.databricks.com/ja/generative-ai/agent-evaluation/index.html)を使用してエージェントを評価する

評価データセットのリクエストや期待される応答を編集し、エージェントを反復しながら評価を実行できます。mlflowを活用して計算された品質指標を追跡します。

```py
import pandas as pd

eval_examples = [
    {
        "request": {
            "messages": [
                {
                    "role": "user",
                    "content": "DeltaとParquetの違い"
                }
            ]
        },
        "expected_response": None
    },
    {
        "request": {
            "messages": [
                {
                    "role": "user",
                    "content": "DeltaとParquetの違い"
                },
                {
                    "role": "assistant",
                    "content": "DeltaとParquetの違いについての情報を探すために、関連する文書を検索します。\n\n<uc_function_call>{\"id\":\"mFQnD8oosIYhAQNd60sr\",\"name\":\"takaakiyayoi_catalog.qiita_2023.vector_search\",\"arguments\":\"{\\\"query_string\\\":\\\"Delta Parquet 違い\\\"}\"}</uc_function_call>\n\n<uc_function_result>{\"id\":\"mFQnD8oosIYhAQNd60sr\",\"content\":\"{\\\"is_truncated\\\":false,\\\"columns\\\":[\\\"context\\\",\\\"url\\\"],\\\"rows\\\":[[\\\"パーティションは特に非常に大きなテーブルにおいては有効です。パーティショニングに関する数多くのパフォーマンス強化は、非常に大きなテーブル(数百TB以上)にフォーカスしています。  \\\\n多くのお客様は、ParquetベースのデータレイクからDelta Lakeに移行しています。CONVERT TO DELTA文によって、既存のデータを再度書き込むことなしに、ParquetベースのテーブルをDeltaテーブルに変換することができます。このようにして、多くのお客様は以前のパーティション戦略を継承する大規模なテーブルを手にすることができます。Databricksによって開発されるいくつかの最適化処理は、可能な限りこれらのパーティションを活用しようとし、Delta Lake向けに最適化されていないパーティション戦略のいくかの副作用を軽減します。  \\\\nDelta LakeとApache Sparkはオープンソーステクノロジーです。Databricksでは、パーティションへの依存度を削減する機能の導入を進めますが、オープンソースコミュニティでは、複雑性を増加させるような新機能を構築し続けるかもしれません。\\\",\\\"https://qiita.com/taka_yayoi/items/b50beddb8a687cd243c6\\\"]]}\"}</uc_function_result>\n\nDeltaとParquetの違いについて詳しく説明するために、関連する文書を参照して具体的な情報を提供します。\n\n<uc_function_call>{\"id\":\"NQc7v7niAToHXDWA05jN\",\"name\":\"takaakiyayoi_catalog.qiita_2023.generate_response\",\"arguments\":\"{\\\"query_string\\\":\\\"DeltaとParquetの違い\\\",\\\"context\\\":\\\"パーティションは特に非常に大きなテーブルにおいては有効です。パーティショニングに関する数多くのパフォーマンス強化は、非常に大きなテーブル(数百TB以上)にフォーカスしています。  \\\\n多くのお客様は、ParquetベースのデータレイクからDelta Lakeに移行しています。CONVERT TO DELTA文によって、既存のデータを再度書き込むことなしに、ParquetベースのテーブルをDeltaテーブルに変換することができます。このようにして、多くのお客様は以前のパーティション戦略を継承する大規模なテーブルを手にすることができます。Databricksによって開発されるいくつかの最適化処理は、可能な限りこれらのパーティションを活用しようとし、Delta Lake向けに最適化されていないパーティション戦略のいくかの副作用を軽減します。  \\\\nDelta LakeとApache Sparkはオープンソーステクノロジーです。Databricksでは、パーティションへの依存度を削減する機能の導入を進めますが、オープンソースコミュニティでは、複雑性を増加させるような新機能を構築し続けるかもしれません。\\\",\\\"url\\\":\\\"https://qiita.com/taka_yayoi/items/b50beddb8a687cd243c6\\\"}\"}</uc_function_call>\n\n<uc_function_result>{\"id\":\"NQc7v7niAToHXDWA05jN\",\"content\":\"{\\\"is_truncated\\\":false,\\\"columns\\\":[\\\"response\\\"],\\\"rows\\\":[[\\\"DeltaとParquetの主な違いは、Delta Lakeがトランザクション管理やスキーマの進化、データのバージョニングをサポートしている点です。特に、Delta Lakeはパーティショニングを活用してパフォーマンスを向上させる機能があり、非常に大きなテーブルにおいては特に効果的です。多くの顧客は、既存のParquetベースのデータレイクからDelta Lakeに移行する際、`CONVERT TO DELTA`文を使用して、再書き込みなしでテーブルを変換し、以前のパーティション戦略を維持しています。Databricksは、これらのパーティションを最大限に活用するための最適化を進めていますが、オープンソースコミュニティでは新たな機能が複雑性を増す可能性もあります。\\\"]]}\"}</uc_function_result>\n\nDeltaとParquetの主な違いは以下の通りです。\n\n1. **トランザクション管理**: Delta Lakeはトランザクション管理をサポートしており、データの整合性を保ちながら複数のユーザーが同時にデータにアクセスできるようにします。一方、Parquetはトランザクション管理の機能を持っていません。\n\n2. **スキーマの進化**: Delta Lakeはスキーマの進化をサポートしており、データのスキーマを変更する際に柔軟性があります。Parquetでは、スキーマの変更が難しい場合があります。\n\n3. **データのバージョニング**: Delta Lakeはデータのバージョニングを提供し、過去のデータに簡単にアクセスできるようにします。これにより、データの変更履歴を追跡することが可能です。Parquetはこの機能を持っていません。\n\n4. **パフォーマンスの最適化**: Delta Lakeはパーティショニングを活用してパフォーマンスを向上させる機能があり、特に非常に大きなテーブルにおいて効果的です。多くの顧客は、既存のParquetベースのデータレイクからDelta Lakeに移行する際、`CONVERT TO DELTA`文を使用して再書き込みなしでテーブルを変換し、以前のパーティション戦略を維持しています。\n\nこれらの違いにより、Delta Lakeはデータの管理や分析においてより高度な機能を提供します。詳しくは[こちらのリンク](https://qiita.com/taka_yayoi/items/b50beddb8a687cd243c6)をご覧ください。"
                },
                {
                    "role": "user",
                    "content": "Sparkの特徴を5つ挙げてください"
                }
            ]
        },
        "expected_response": None
    }
]

eval_dataset = pd.DataFrame(eval_examples)
display(eval_dataset)
```

```py
import mlflow
import pandas as pd

# 評価を開始するためにMLflowのランを開始
with mlflow.start_run(run_id=logged_agent_info.run_id):
    eval_results = mlflow.evaluate(
        f"runs:/{logged_agent_info.run_id}/agent",  # log_modelを呼び出す際に使用したartifact_pathに`chain`を置き換えます。
        data=eval_dataset,  # 評価データセット
        model_type="databricks-agent",  # Mosaic AI Agent Evaluationを有効にする
    )

# MLflow UIで評価結果を確認するか、以下のようにアクセスします:
display(eval_results.tables['eval_results'])
```

[こちらでウォークスルー](https://qiita.com/taka_yayoi/items/eb17f38b97892e7ab8ce)したように、AI Playgroundでのやり取りを評価データセットとして評価します。これが、エージェントシステムを開発する際に最初から評価の仕組みを組み込んでおくという[評価ドリブン開発ワークフロー](https://qiita.com/taka_yayoi/items/e70843b6795e010f5425#%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%AF%E8%87%AA%E5%8B%95%E5%8C%96%E3%82%92%E3%82%AC%E3%82%A4%E3%83%89%E3%81%99%E3%81%B9%E3%81%8D%E3%81%A7%E3%81%82%E3%82%8B)です。

![Screenshot 2024-11-10 at 20.25.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/ecb1a8c2-cf52-50a6-6a2f-7a68638d9e39.png)

## モデルをUnity Catalogに登録

以下の`catalog`、`schema`、`model_name`を更新して、MLflowモデルをUnity Catalogに登録します。

```py
mlflow.set_registry_uri("databricks-uc")

# TODO: UCモデルのカタログ、スキーマ、およびモデル名を定義します
catalog = "takaakiyayoi_catalog"
schema = "qiita_2023"
model_name = "databricks_rag"
UC_MODEL_NAME = f"{catalog}.{schema}.{model_name}"

# モデルをUCに登録します
uc_registered_model_info = mlflow.register_model(model_uri=logged_agent_info.model_uri, name=UC_MODEL_NAME)
```

## エージェントをデプロイ

```py
from databricks import agents

# モデルをレビューアプリとモデルサービングエンドポイントにデプロイします
agents.deploy(UC_MODEL_NAME, uc_registered_model_info.version)
```

```
Successfully registered model 'takaakiyayoi_catalog.qiita_2023.feedback'.
Created version '1' of model 'takaakiyayoi_catalog.qiita_2023.feedback'.
2024/11/10 11:12:04 INFO mlflow.tracking._tracking_service.client: 🏃 View run feedback-model at: https://xxxxxx.databricks.com/ml/experiments/117686861228987/runs/8e00f1a5b8874c26a64369f0b1071151.
2024/11/10 11:12:04 INFO mlflow.tracking._tracking_service.client: 🧪 View experiment at: https://xxxxxx.databricks.com/ml/experiments/117686861228987.

    Deployment of takaakiyayoi_catalog.qiita_2023.databricks_rag version 1 initiated.  This can take up to 15 minutes and the Review App & Query Endpoint will not work until this deployment finishes.

    View status: https://xxxxxx.databricks.com/ml/endpoints/agents_takaakiyayoi_catalog-qiita_2023-databricks_rag
    Review App: https://xxxxxx.databricks.com/ml/review/takaakiyayoi_catalog.qiita_2023.databricks_rag/1?o=1444828305810485
```

これでRAGエージェントとレビューアプリがデプロイされます。

![Screenshot 2024-11-10 at 20.30.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7d42452b-eff7-a12c-b041-c64435951bf7.png)

# RAGアプリのレビュー

上の**Review App**のリンクにアクセスすることで、レビュー用アプリにアクセスできます。

![Screenshot 2024-11-10 at 20.31.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/c33a2ebb-e9df-d35b-4901-e0964f194ca0.png)

レビュー用アプリではRAGアプリとインタラクションできるだけではなく、そのレスポンスに対する評価を入力することができます。

![Screenshot 2024-11-10 at 20.32.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/08a63ad3-3804-d350-5846-43eb0d293506.png)

![Screenshot 2024-11-10 at 20.33.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/716bbaa9-d609-09aa-e615-ae4b2c42ae84.png)

RAGアプリへのリクエストやレビューの結果はDeltaテーブルに格納されます。[次回](https://qiita.com/taka_yayoi/items/44c9f4e7d6cf7b88cfdf)ではレビュー結果の可視化、監視を行います。

![Screenshot 2024-11-10 at 20.40.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/a2f89dd1-5dc7-3333-cd3e-a296b691f00f.png)


### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)