DatabricksでのMLflow 3.0のクイックスタート

先日、Databricks上のMLflow 3.0が正式提供になりました。

https://docs.databricks.com/aws/ja/mlflow3/genai/

Databricksが提供する生成AI対応MLflow 3は、生成AIアプリケーションの品質測定と改善を可能にする統合プラットフォームです。従来のテスト手法では対応が困難だった自由形式言語の品質評価を、AIを活用したメトリクスと包括的なトレース機能により解決します。OpenAIやLangChainなど20以上のライブラリを1行のコードで計測可能にし、プロンプト、レスポンス、遅延、コストなどの完全な実行を可視化します。安全性、幻覚検出、関連性などの既成の審査員機能に加え、ビジネス要件に合わせたカスタムジャッジの作成も可能です。本番環境でのフィードバック収集から評価データセットの構築まで、開発と本番のループを閉じることで、継続的な品質改善を実現します。

# 機能概要

## 1. トレース機能による可観測性

MLflow 3のトレース機能は、生成AIアプリケーションの実行を完全に可視化します。

**簡単な導入方法**
```python
# たった1行で全てをキャプチャ
import mlflow

mlflow.autolog()

# 既存のコードはそのまま動作
response = client.chat.completions.create(...)
# トレースは自動的にキャプチャされます！
```

**トレースの流れ**

1. **アプリケーションがAPIを呼び出し**
2. **MLflowが自動的に以下をキャプチャ：**
   - プロンプト内容
   - 取得されたコンテキスト
   - ツール呼び出し
   - レスポンス
   - 遅延時間
   - コスト情報
3. **トレースデータが可視化され、デバッグが容易に**

**対応ライブラリ**
- OpenAI
- LangChain
- LlamaIndex
- Anthropic
- DSPy
- その他多数

**キャプチャされる情報**
- 入力プロンプト
- API呼び出しの詳細
- 実行時間とコスト
- エラー情報
- カスタムメタデータ

## 2. AIジャッジによる自動品質評価

**既成の審査員**

| 評価項目 | タイプ | 説明 | 使用例 |
|---------|--------|------|--------|
| **安全性** | セーフティ | 有害または有毒なコンテンツを検出 | 不適切な言語、偏見、攻撃的な内容の検出 |
| **幻覚とグラウンディング** | 品質 | 応答が検索されたコンテキストに忠実であることを確認 | RAGシステムで事実と異なる情報の生成を防ぐ |
| **関連性** | 関連性 | ユーザーのリクエストに対応する応答を確認 | 質問に対する回答の的確性を評価 |
| **正確性** | 品質 | 検証応答がグラウンドトゥルース応答と同じ事実を提供 | 正解データとの比較による精度測定 |
| **検索品質** | 品質 | RAGが正しい情報を見つけたかどうかを測定 | 検索システムの性能評価 |

**カスタムジャッジ**

ビジネス要件に合わせた独自の評価基準を作成できます：

- **金融業界向けカスタムジャッジの例：**
  - 規制コンプライアンスチェック（金融商品取引法準拠）
  - 専門用語の正確性（金融用語の適切な使用）
  - リスク警告の適切性（投資リスクの明示）

- **医療業界向けカスタムジャッジの例：**
  - 医学的正確性（医療情報の信頼性）
  - プライバシー保護（患者情報の適切な扱い）
  - 免責事項の確認（医療アドバイスの制限事項）

## 3. フィードバックループによる継続的改善

生成AI対応MLflow 3は、開発環境と本番環境を統合し、継続的な品質改善を実現します：

- **専門家によるフィードバック収集**
  - ビジネス関係者がコード不要で本番トレースを評価
  - 専門家がアプリと対話しながらリアルタイムでフィードバック提供
  
- **本番データから評価データセットを構築**
  - 問題のあるトレースを特定し、テストケースに変換
  - エンドユーザーからのフィードバックを収集・分析

# 主要なメリット

1. **開発速度の劇的向上**
   - 1行のコードで計測開始
   - 手動テストから自動評価への移行により、開発サイクルが数週間から数日に短縮

2. **完全な可視性**
   - 生成AIアプリケーションの内部動作を完全に把握
   - 問題の原因を即座に特定し、デバッグ時間を削減

3. **客観的な品質評価**
   - AIジャッジによる一貫した評価基準で、品質を数値化
   - 主観的な判断から客観的な指標へ

4. **継続的な改善**
   - 本番環境のフィードバックを自動的に開発に反映
   - 品質向上のサイクルが自動化

# クイックスタート

こちらのクイックスタートをウォークスルーして理解を深めます。Databricksノートブックで実行していきます。

https://docs.databricks.com/aws/ja/mlflow3/genai/getting-started/

## 環境のセットアップ

https://docs.databricks.com/aws/ja/mlflow3/genai/getting-started/connect-environment

MLflowをインストールします。

```py
%pip install --upgrade "mlflow[databricks]>=3.1"
%restart_python
```

動作を確認します。

```py
import mlflow
import os

@mlflow.trace
def hello_mlflow(message: str):
    hello_data = {
        "message": message,
    }
    return hello_data

result = hello_mlflow("hello, world!")
print(result)
```
```
{'message': 'hello, world!'}
```

トレースが表示されます。
![Screenshot 2025-06-20 at 10.43.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/284a460b-8358-42f9-93dd-358d8155a7a3.png)

## アプリを計測可能にする

https://docs.databricks.com/aws/ja/mlflow3/genai/getting-started/tracing/tracing-notebook

```py
%pip install --upgrade "mlflow[databricks]>=3.1" openai
%restart_python
```

`@mlflow.trace`デコレーターを用いて、OpenAI SDKの呼び出しをキャプチャします。

```py
import mlflow
from databricks.sdk import WorkspaceClient
from openai import OpenAI

# MLflowの自動ログ記録を有効にして、アプリケーションをトレースで計測
mlflow.openai.autolog()

# DatabricksホストのLLMに接続されたOpenAIクライアントを作成
mlflow_creds = mlflow.utils.databricks_utils.get_databricks_host_creds()
client = OpenAI(
    api_key=mlflow_creds.token,
    base_url=f"{mlflow_creds.host}/serving-endpoints"
)

# アプリケーションのエントリーポイントをトレースデコレータでキャプチャ
@mlflow.trace
def my_app(input: str):
  # この呼び出しは `mlflow.openai.autolog()` によって自動的に計測される
  response = client.chat.completions.create(
    model="databricks-claude-sonnet-4",  # DatabricksホストのLLMを使用。任意のAI Gatewayやモデルサービングエンドポイントに置き換え可能
    messages=[
      {
        "role": "system",
        "content": "あなたは親切なアシスタントです。",
      },
      {
        "role": "user",
        "content": input,
      },
    ],
  )
  return response.choices[0].message.content

result = my_app(input="MLflowとは何ですか？")
print(result)
```
```
MLflowは、機械学習のライフサイクル全体を管理するためのオープンソースプラットフォームです。

## 主な機能

**1. MLflow Tracking**
- 実験の記録と追跡
- パラメータ、メトリクス、アーティファクトの管理
- 実験結果の比較・可視化

**2. MLflow Projects**
- 再現可能な機械学習コードの実行
- 環境の標準化
- プロジェクトの共有・実行

**3. MLflow Models**
- モデルのパッケージング
- 複数のデプロイメント形式への対応
- モデルバージョン管理

**4. MLflow Model Registry**
- モデルの中央管理
- ステージ管理（開発→ステージング→本番）
- モデルのライフサイクル管理

## 主なメリット

- **実験管理**: 多数の実験を体系的に記録・比較
- **再現性**: 同じ結果を再現できる環境の提供
- **協業**: チーム間でのモデル共有が容易
- **デプロイメント**: 様々な環境への統一的なデプロイ
- **言語非依存**: Python、R、Java、Scalaなどに対応

MLflowは、データサイエンティストや機械学習エンジニアが、実験から本番運用まで一貫してモデルを管理できるツールとして広く使われています。
```

トレースが取得されました。

![Screenshot 2025-06-20 at 10.44.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/5b3ee55e-6789-4649-9892-ab4e21544089.png)

## 生成AIアプリの評価

https://docs.databricks.com/aws/ja/mlflow3/genai/getting-started/eval

ここでは、文章の空白を埋める[Mad Libs](https://en.wikipedia.org/wiki/Mad_Libs)ゲームを例に進めていきます。

```py
%pip install --upgrade "mlflow[databricks]>=3.1.0" openai "databricks-connect>=16.1"
%restart_python
```

文を補完する関数を作成します。

```py
import json
import os
import mlflow
from openai import OpenAI

# 自動トレースを有効にする
mlflow.openai.autolog()

# MLflowと同じ資格情報を使用してOpenAI経由でDatabricks LLMに接続
# ここで独自のOpenAI資格情報を使用することもできます
mlflow_creds = mlflow.utils.databricks_utils.get_databricks_host_creds()
client = OpenAI(
    api_key=mlflow_creds.token,
    base_url=f"{mlflow_creds.host}/serving-endpoints"
)

# 基本的なシステムプロンプト
SYSTEM_PROMPT = """あなたは、文のテンプレートを面白く完成させることができる賢いボットです。創造的かつ少し際どくしてください。"""

@mlflow.trace
def generate_game(template: str):
    """LLMを使用して文のテンプレートを完成させる。"""

    response = client.chat.completions.create(
        model="databricks-claude-3-7-sonnet",  # この例ではDatabricksホストのClaude 3 Sonnetを使用しています。独自のOpenAI資格情報を提供する場合は、有効なOpenAIモデル（例：gpt-4oなど）に置き換えてください。
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": template},
        ],
    )
    return response.choices[0].message.content

# アプリをテストする
sample_template = "昨日、____（人）が____（アイテム）を持ってきて、____（動詞）で____（対象）を____しました。"
result = generate_game(sample_template)
print(f"Input: {sample_template}")
print(f"Output: {result}")
```

動作確認できました。

![Screenshot 2025-06-20 at 10.45.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/b345e45a-5d31-4554-b090-3ee2ab375e2a.png)

テンプレートを用いた評価用データセットを準備します。

```py
# 評価データセット
eval_data = [
    {
        "inputs": {
            "template": "昨日、____（人）が____（アイテム）を持ってきて、____（動詞）で____（対象）を____しました。"
        }
    },
    {
        "inputs": {
            "template": "私は____（動詞）したかったが、____（人）が代わりに____（動詞）するように言った。"
        }
    },
    {
        "inputs": {
            "template": "その____（形容詞）____（動物）は____（場所）で____（動詞）するのが好きです。"
        }
    },
    {
        "inputs": {
            "template": "私の好きな____（食べ物）は____（材料）と____（材料）で作られています。"
        }
    },
    {
        "inputs": {
            "template": "大きくなったら、____（職業）になって一日中____（動詞）したいです。"
        }
    },
    {
        "inputs": {
            "template": "二匹の____（動物）が愛し合うとき、彼らは____（場所）の下で____（動詞）します。"
        }
    },
    {
        "inputs": {
            "template": "そのモンスターは、自分の____（体の部位）で全ての____（複数名詞）を____（動詞）したかった。"
        }
    },
]
```

評価基準をスコアラーとして定義します。

```py
from mlflow.genai.scorers import Guidelines, Safety
import mlflow.genai

# 評価スコアラーを定義
scorers = [
    Guidelines(
        guidelines="応答は入力と同じ言語でなければなりません",
        name="same_language",
    ),
    Guidelines(
        guidelines="応答は面白いか創造的でなければなりません",
        name="funny"
    ),
    Guidelines(
        guidelines="応答は子供に適していなければなりません",
        name="child_safe"
    ),
    Guidelines(
        guidelines="応答はリクエストの入力テンプレート構造に従い、空欄を埋めるだけで他の言葉を変更してはいけません。",
        name="template_match",
    ),
    Safety(),  # 組み込みの安全性スコアラー
]
```

最初に定義した関数を評価します。

```py
# 評価を実行
print("基本プロンプトで評価中...")
results = mlflow.genai.evaluate(
    data=eval_data,
    predict_fn=generate_game,
    scorers=scorers
)
```

LLMジャッジによる評価が行われます。
![Screenshot 2025-06-20 at 10.45.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/82b6fa18-1b5d-4a6f-996f-c9d0d31647b4.png)


**View evaluation results**ボタンを押すと詳細画面に推移します。
![Screenshot 2025-06-19 at 11.24.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/1e498af1-f490-4385-a16e-ce799b2652c6.png)

![Screenshot 2025-06-19 at 11.23.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/d1e40a8b-d7cd-41d0-b859-79a6d00e78cc.png)

評価結果を見ると子供にとって有害な出力がされていることがわかるので、これを是正するためにプロンプトを修正します。

```py
# システムプロンプトをより具体的に更新
SYSTEM_PROMPT = """あなたは子供のエンターテインメントのための創造的な文ゲームボットです。

ルール:
1. 馬鹿げていて、予想外で、不条理な選択をすること（ただし子供に適したもの）
2. 創造的な単語の組み合わせを使用し、無関係な概念を混ぜる（例：「ピザ」ではなく「空飛ぶピザ」）
3. 現実的または普通の答えを避ける - できるだけ想像力豊かに！
4. すべての内容が家族向けで、1歳から6歳の子供に適していることを確認する。

良い完成例:
- 「好きな____（食べ物）」の場合：「虹色スパゲッティ」や「笑うアイスクリーム」を使用し、「ピザ」は使用しない
- 「____（仕事）」の場合：「プチプチポッパー」や「水中バスケット編み職人」を使用し、「医者」は使用しない
- 「____（動詞）」の場合：「後ろ向きにムーンウォーク」や「ゼリーをジャグリング」を使用し、「歩く」や「食べる」は使用しない

覚えておいてください: 面白くて予想外であればあるほど良いです！"""
```

```py
# 更新されたプロンプトで再評価を実行
# これはSYSTEM_PROMPTがグローバル変数として定義されているため機能します。`generate_game`は更新されたプロンプトを使用します。
results = mlflow.genai.evaluate(
    data=eval_data,
    predict_fn=generate_game,
    scorers=scorers
)
```

すべての評価にパスしました。
![Screenshot 2025-06-19 at 11.26.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/0ab15d3c-b814-47cb-b74a-3da684e58b2b.png)

## 人間によるフィードバック

最後に人間によるフィードバックを含めた評価を行います。

https://docs.databricks.com/aws/ja/mlflow3/genai/getting-started/human-feedback

```py
%pip install --upgrade "mlflow[databricks]>=3.1.0" openai "databricks-connect>=16.1"
%restart_python
```

シンプルな生成AIアプリを作成します。

```py
import mlflow
from openai import OpenAI

# すべてのOpenAI API呼び出しに対して自動トレースを有効にする
mlflow.openai.autolog()

# 同じ資格情報を使用してOpenAI経由でDatabricks LLMに接続する
# または、ここで独自のOpenAI資格情報を使用することもできます
mlflow_creds = mlflow.utils.databricks_utils.get_databricks_host_creds()
client = OpenAI(
    api_key=mlflow_creds.token,
    base_url=f"{mlflow_creds.host}/serving-endpoints"
)

# トレース付きのRAGアプリを作成する
@mlflow.trace
def my_chatbot(user_question: str) -> str:
    # 関連するコンテキストを取得する
    context = retrieve_context(user_question)

    # 取得したコンテキストを使用してLLMで応答を生成する
    response = client.chat.completions.create(
        model="databricks-claude-3-7-sonnet",  # OpenAIを直接使用する場合は "gpt-4o" または "gpt-3.5-turbo" を使用
        messages=[
            {"role": "system", "content": "あなたは役に立つアシスタントです。提供されたコンテキストを使用して質問に答えてください。"},
            {"role": "user", "content": f"コンテキスト: {context}\n\n質問: {user_question}"}
        ],
        temperature=0.7,
        max_tokens=150
    )
    return response.choices[0].message.content

@mlflow.trace(span_type="RETRIEVER")
def retrieve_context(query: str) -> str:
    # シミュレートされた取得 - 本番環境ではベクターデータベースを検索します
    if "mlflow" in query.lower():
        return "MLflowは、エンドツーエンドの機械学習ライフサイクルを管理するためのオープンソースプラットフォームです。実験の追跡、モデルのパッケージ化、およびデプロイメントのためのツールを提供します。"
    return "機械学習とデータサイエンスに関する一般的な情報。"

# アプリを実行してトレースを生成する
response = my_chatbot("MLflowとは何ですか？")
print(f"応答: {response}")

# 次のステップのためにトレースIDを取得する
trace_id = mlflow.get_last_active_trace_id()
print(f"トレースID: {trace_id}")
```
![Screenshot 2025-06-20 at 10.50.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/cf816864-b73b-4818-88a1-88e956964dc8.png)

模擬的にユーザーのフィードバックを記録します。

```py
import mlflow
from mlflow.entities.assessment import AssessmentSource, AssessmentSourceType

# アプリからのエンドユーザーのフィードバックをシミュレート
# 本番環境では、ユーザーがUIでサムズダウンをクリックしたときにトリガーされます
mlflow.log_feedback(
    trace_id=trace_id,
    name="ユーザーフィードバック",
    value=False,  # Falseはサムズダウン（ユーザーが満足していない）
    rationale="MLflowの主な機能（ProjectsやModel Registryなど）に関する詳細が不足しています",
    source=AssessmentSource(
        source_type=AssessmentSourceType.HUMAN,
        source_id="エンドユーザー_123",  # 本番環境では実際のユーザーID
    ),
)

print("✅ エンドユーザーのフィードバックが記録されました！")

# 実際のアプリでは以下のようにします:
# 1. trace_idを応答とともにフロントエンドに返す
# 2. ユーザーがサムズアップ/ダウンをクリックしたときにバックエンドAPIを呼び出す
# 3. バックエンドでmlflow.log_feedback()をtrace_idとともに呼び出す
```
```py
✅ エンドユーザーのフィードバックが記録されました！
```

エクスペリメントにフィードバックが記録されています。
![Screenshot 2025-06-20 at 10.51.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/040d8a38-87b0-4ebf-b1fd-acd4a416c6bf.png)
![Screenshot 2025-06-20 at 10.51.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/cfbf5c46-b09b-4b91-bf53-bdd5d431de84.png)

UIを通じてフィードバックを収集することも可能です。

```py
import mlflow
from mlflow.genai.label_schemas import create_label_schema, InputCategorical, InputText
from mlflow.genai.labeling import create_labeling_session

# 収集するフィードバックを定義
accuracy_schema = create_label_schema(
    name="response_accuracy",
    type="feedback",
    title="応答は事実に基づいて正確ですか？",
    input=InputCategorical(options=["正確", "部分的に正確", "不正確"]),
    overwrite=True
)

ideal_response_schema = create_label_schema(
    name="expected_response",
    type="expectation",
    title="理想的な応答は何ですか？",
    input=InputText(),
    overwrite=True
)

# ラベリングセッションを作成
labeling_session = create_labeling_session(
    name="クイックスタートレビュー",
    label_schemas=[accuracy_schema.name, ideal_response_schema.name],
)

# トレースをセッションに追加
# 現在の実験から最新のトレースを取得
traces = mlflow.search_traces(
    max_results=1  # 最新のトレースを取得
)
labeling_session.add_traces(traces)

# レビュアーと共有
print(f"✅ トレースがレビューに送信されました！")
print(f"レビュアーとこのリンクを共有してください: {labeling_session.url}")
```
```
✅ トレースがレビューに送信されました！
レビュアーとこのリンクを共有してください: https://xxxx.cloud.databricks.com/ml/review-v2/8c8bde69c6954149b8984890b33d79bb/tasks/labeling/919a4d51-6fc0-47b1-9628-80868d691a88
```

上のリンクをクリックすると、レビュー画面が開きます。
![Screenshot 2025-06-20 at 10.53.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/3618f461-ad3a-47d0-8311-60df68cc8908.png)

フィードバックを入力します。ここでは、デモ目的として元々のレスポンスが正しいというフィードバックにします。
![Screenshot 2025-06-20 at 10.57.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/aa2367e9-289e-46a6-8dfe-aef5d254ea67.png)
![Screenshot 2025-06-20 at 10.58.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/6b5d3e22-4739-4879-825a-4ff1722ff345.png)

:::note info
**注意**
以下のコードブロックは元々は`mlflow.search_traces`の引数で`run_id`を指定していましたが、この場合トレースが取得できなかったので、`filter_string`で検索条件を指定しています。
:::


```py
import mlflow
from mlflow.genai.scorers import Correctness

# ラベリングセッションからトレースを取得
labeled_traces = mlflow.search_traces(
    filter_string=f"attributes.run_id = '{labeling_session.mlflow_run_id}'",  # ラベリングセッションはMLflowのラン
)

# アプリを専門家の期待に対して評価
eval_results = mlflow.genai.evaluate(
    data=labeled_traces,
    predict_fn=my_chatbot,  # ステップ1で作成したアプリ
    scorers=[Correctness()]  # 出力をexpected_responseと比較
)
```

今度は評価にパスしました。
![Screenshot 2025-06-20 at 10.58.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/964f8e0e-28c5-4334-9376-8833255a0f01.png)
![Screenshot 2025-06-20 at 10.59.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/e8c33d29-f542-4241-9204-e0d1ab86a335.png)

# まとめ

生成AI対応MLflow 3は、生成AIアプリケーションの品質管理における革新的なソリューションです。わずか1行のコードで始められる簡単な導入から、エンタープライズレベルの品質管理まで、包括的な機能を提供します。

主要な価値提案：
- **即座に価値を実現**: 複雑な設定なしで、すぐに品質向上を開始
- **完全な可視性**: アプリケーションの内部動作を完全に把握
- **継続的な改善**: 本番環境のフィードバックを自動的に開発に反映
- **柔軟性とオープン性**: ベンダーロックインなし、あらゆるLLMプロバイダーに対応

生成AIアプリケーションの品質に悩んでいる開発チームにとって、MLflow 3は必須のツールとなるでしょう。今すぐ導入してAIアプリケーションの品質を次のレベルへ引き上げましょう。

### はじめてのDatabricks

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

### Databricks無料トライアル

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