GenAI Agentを簡易評価しよう on Databricks/MLflow

# 導入

最近、GenAI Agent作成のチュートリアルをウォークスルーしてますが、作るだけでなく評価もやるべきだと思ったので以下の記事で作成したエージェントをLLM as a Judgeで評価する処理を書いて実行してみます。

https://qiita.com/isanakamishiro2/items/802b2f31b207dbc04255

LLM as a Judgeによる評価はDatabricksとMLflowを使って行います。
※ [以前にも](https://qiita.com/isanakamishiro2/items/8b6214b8dde113cee389)評価に関する記事は書いていたので合わせて参考になれば。

# LLM as a Judgeでクエリと回答の指標を評価

上記記事で作成したエージェントをまずロードします。

まず、Databricks上でノートブックを作成して必要なパッケージをインストール。

```python
%pip install -q -U langchain-core==0.3.13 langchain-databricks==0.1.1 langchain_community==0.3.3 langgraph==0.2.39
%pip install -q -U typing-extensions
%pip install -q -U "mlflow-skinny[databricks]>=2.17.1"

dbutils.library.restartPython()
```

読み込むモデルのパスは以下の場所にあるものとします。

```python
# 読み込むモデルのパス(Unity Catalog上に登録されている前提)
registered_model_name = "training.llm.customer_support_agent"
```

MLflow(Databrikcs上のモデルレジストリ)からエージェントモデルをロード。

```python
import pandas as pd
import mlflow
from mlflow import MlflowClient
from mlflow.deployments import set_deployments_target

# Databricksを利用
set_deployments_target("databricks")

# 評価対象のエージェントをロード
client = MlflowClient()
versions = [
    mv.version for mv in client.search_model_versions(f"name='{registered_model_name}'")
]
agent = mlflow.pyfunc.load_model(f"models:/{registered_model_name}/{versions[0]}")
```

<br>

では、ここからロードしたモデルを評価していきます。
LLM as a Judgeでは評価のためにLLMを使いますが、今回はエージェント内で利用しているモデルのエンドポイント(Llama 3.2 3B Instruct)を利用しています。

:::note warn
実際にはLLM as a Judgeで利用するLLMはなるべく性能の高いモデルを利用ください。
今回、評価結果は出ていますが正しく採点はできていません。
GPT-4以上の性能を持つモデルが一般的には利用されています。
(過去からの評価と一貫性のためにGPT-4を用いるケースが多いようです)
:::

処理としては、最初にPandasデータフレームで問い合わせと正しい回答(Ground Truth)のペアを作成し、エージェントの推論結果列を追加します。

作成したデータフレームに対して```mlflow.evaluate```を使って評価指標を計算しています。

評価指標は、MLFlow標準で提供している回答の類似度、正当性、関連性をLLM as a Judgeを使って計算するように設定しています。
(レイテンシ評価も入れていますが、オマケです)

```python

# LLM as a Judgeに使うモデルのエンドポイント
# 簡易評価用にLlama 3.2 3B Instructを使って行う
judge_endpoint = "endpoints:/llama_v3_2_3b_instruct_endpoint"

# 予測を行う関数を定義
def predict(inputs: pd.Series) -> pd.Series:
    answer = []
    for row in inputs:
        result = agent.predict(
            {
                "messages": [{"role": "user", "content": row}],
                "temerature": 0.0,
                "max_tokens": 1000,
            }
        )
        answer.append(result["choices"][0]["message"]["content"])

    return pd.Series(answer)


# 入力と正解、予測のデータフレームを作成
eval_df = pd.DataFrame(
    {
        "inputs": [
            "My internet connection keeps dropping. Can you help?",
            "I need help talking to ChatGPT as technical",
            "where can i find my receipt as billing case?",
            "What are your business hours?",
        ],
        "ground_truth": [
            "This query has been escalated to a human agent due to its negative sentiment.",
            "Here's a technical support response. If you're experiencing issues with ChatGPT not responding as expected, try clearing the cache and restarting the chat interface. To do this, simply close and reopen the ChatGPT application or browser tab.",
            "Thank you for reaching out to our billing support team. We apologize for any inconvenience you're experiencing trying to locate your receipt.",
            "we're available 24/7 to respond to your query, and our support team will be happy to assist you as soon as possible. Please allow [insert response time, e.g. 1-2 business days] for us to respond to your question or concern.",
        ],
    }
)
eval_df["predictions"] = predict(eval_df["inputs"])

# LLM as a Judgeとしての評価指標の作成
answer_similarity = mlflow.metrics.genai.answer_similarity(model=judge_endpoint)
answer_correctness = mlflow.metrics.genai.answer_correctness(model=judge_endpoint)
answer_relevance = mlflow.metrics.genai.answer_relevance(model=judge_endpoint)

# 評価の実行・記録
with mlflow.start_run() as run:
    results = mlflow.evaluate(
        data=eval_df,
        targets="ground_truth",
        predictions="predictions",
        evaluators="default",
        extra_metrics=[
            mlflow.metrics.latency(), # レイテンシ評価
            answer_similarity,
            answer_correctness,
            answer_relevance,
        ],
    )
results.metrics
```

```python:出力
{'latency/mean': 0.0,
 'latency/variance': 0.0,
 'latency/p90': 0.0,
 'answer_similarity/v1/mean': 3.0,
 'answer_similarity/v1/variance': 0.5,
 'answer_similarity/v1/p90': 3.7,
 'answer_correctness/v1/mean': 3.25,
 'answer_correctness/v1/variance': 0.1875,
 'answer_correctness/v1/p90': 3.7,
 'answer_relevance/v1/mean': 3.75,
 'answer_relevance/v1/variance': 0.1875,
 'answer_relevance/v1/p90': 4.0}
```

評価結果はMLflowに記録されます。
また、以下のコードを実行することで、ノートブック内で詳細結果をDatabricksのノートブックUIを使って確認できます。

```python
pdf = results.tables["eval_results_table"]
df = spark.createDataFrame(pdf)

df.display()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168727/7d9bdc0e-ad99-a1ea-8ce1-dce59c9dafc4.png)

評価結果の統計値はMLflow上のモデルメトリクスとして確認可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168727/a8e415f5-a233-45bd-443c-c54fb4ed2af5.png)

今回の記事では行っていませんが、カスタムメトリクスも作成可能なのでエージェントの内容に合わせた評価指標を準備して自動評価を行うこともできそうです。

# まとめ

簡単ですが、MLflowを使ったLLM as a Judgeでエージェントの評価を行ってみました。

LLM as a Judgeで使う評価用エンドポイントはDatabricks Mosaic AI Model Servingを使うと手軽に様々なものが準備しやすいなと改めて感じました。
また、Mosaic AI Model Servingの「プロビジョニングされた基盤モデル」に対応していないモデルであっても、[MLflow Custom ChatModel](https://mlflow.org/docs/latest/llms/chat-model-guide/index.html)をエンドポイントとしてデプロイすればMLflowのLLM as a Judgeで利用することもできます。
例えばQwen2.5とかGemmaのモデルをJudge側にして利用することも可能。

使いやすく小回りが利く機能だと思うので、評価の自動化や管理に有効活用していければと思います。