メインコンテンツまでスキップ

カスタムメトリクス

このガイドでは、RAG内でAIアプリケーションを評価するためのカスタムメトリクスの使用方法について説明します。カスタムメトリクスは、単純なヒューリスティック、高度なロジック、プログラムによる評価など、特定のビジネスユースケースに合わせた評価メトリクスを柔軟に定義できます。

概要

カスタムメトリクスはPythonで記述されており、開発者はAIアプリケーションを通じてトレースを評価するための完全な制御が可能です。 次のメトリクスがサポートされています。

カスタムメトリクスでは、次のものを使用できます。

  • 評価行の任意のフィールド。
  • 追加のエクスペクテーションの custom_expected フィールド。
  • MLflow トレース (スパン、属性、出力など) への完全なアクセス。

使い

カスタムメトリクスは、 mlflow.evaluate() の extra_metrics フィールドを使用して評価フレームワークに渡されます。 例:

Python
import mlflow
from databricks.agents.evals import metric

@metric
def not_empty(response):
# "yes" for Pass and "no" for Fail.
return "yes" if response.choices[0]['message']['content'].strip() != "" else "no"

@mlflow.trace(span_type="CHAT_MODEL")
def my_model(request):
deploy_client = mlflow.deployments.get_deploy_client("databricks")
return deploy_client.predict(
endpoint="databricks-meta-llama-3-1-70b-instruct", inputs=request
)

with mlflow.start_run(run_name="example_run"):
eval_results = mlflow.evaluate(
data=[{"request": "Good morning"}],
model=my_model,
model_type="databricks-agent",
extra_metrics=[not_empty],
)
display(eval_results.tables["eval_results"])

@metricデコレーター

@metricデコレータを使用すると、ユーザーは mlflow.evaluate() に渡すことができるカスタム評価メトリクスを定義できます extra_metrics引数を使用します。評価ハーネスは、以下のシグネチャに基づく名前付き引数を使用してメトリクス関数を呼び出します。

Python
def my_metric(
*, # eval harness will always call it with named arguments
request: Dict[str, Any], # The agent's raw input as a serializable object
response: Optional[Dict[str, Any]], # The agent's raw output; directly passed from the eval harness
retrieved_context: Optional[List[Dict[str, str]]], # Retrieved context, either from input eval data or extracted from the trace
expected_response: Optional[str], # The expected output as defined in the evaluation dataset
expected_facts: Optional[List[str]], # A list of expected facts that can be compared against the output
expected_retrieved_context: Optional[List[Dict[str, str]]], # Expected context for retrieval tasks
trace: Optional[mlflow.entities.Trace], # The trace object containing spans and other metadata
custom_expected: Optional[Dict[str, Any]], # A user-defined dictionary of extra expected values
tool_calls: Optional[List[ToolCallInvocation]],
) -> float | bool | str | Assessment

引数の説明

  • request : エージェントに提供された入力で、任意のシリアル化可能なオブジェクトとして書式設定されています。これは、ユーザーのクエリまたはプロンプトを表します。
  • response : エージェントからの生の出力で、オプションの任意のシリアル化可能なオブジェクトとして書式設定されます。これには、評価のためにエージェントが生成した応答が含まれています。
  • retrieved_context : タスク中に取得されたコンテキストを含む辞書のリスト。このコンテキストは、入力評価データセットまたはトレースから取得でき、ユーザーは trace フィールドを介して抽出を上書きまたはカスタマイズできます。
  • expected_response : タスクの正しい応答または必要な応答を表す文字列。これは、エージェントの応答と比較するためのグラウンドトゥルースとして機能します。
  • expected_facts : エージェントの応答に表示されると予想されるファクトのリストで、ファクトチェックタスクに役立ちます。
  • expected_retrieved_context : 予想される取得コンテキストを表す辞書のリスト。これは、取得したデータの正確性が重要な検索拡張タスクに不可欠です。
  • trace : エージェントの実行に関するスパン、属性、その他のメタデータを含むオプションの MLflow Trace オブジェクト。これにより、エージェントが実行した内部ステップを詳細に調査できます。
  • custom_expected : ユーザー定義のエクスペクテーションを渡すためのディクショナリ。このフィールドでは、標準フィールドに含まれていない追加のカスタムエクスペクテーションを柔軟に含めることができます。
  • tool_calls : 呼び出されたツールと返された内容を説明する ToolCallInvocation の一覧。

戻り値

カスタムメトリクスの戻り値は、行ごとの Assessment です。 プリミティブを返すと、空の根拠を持つ Assessment にラップされます。

  • float : 数値メトリクス(類似性スコア、精度パーセンテージなど)の場合。
  • bool : バイナリメトリクス用。
  • Assessment または list[Assessment]: 根拠の追加をサポートする、より豊富な出力タイプ。評価のリストを返す場合、同じメトリクス関数を再利用して複数の評価を返すことができます。
    • name: 評価の名前。
    • value: 値 (float、int、bool、または文字列)。
    • rationale: (オプション) この値がコンピュートであった理由を説明する理論的根拠。 これは、UI で追加の推論を表示するのに役立ちます。 このフィールドは、たとえば、この評価を生成した LLM から推論を提供する場合に役立ちます。

合格/不合格のメトリクス

"yes""no"を返す文字列メトリクスは、合格/不合格のメトリクスとして扱われ、UIでは特別な処理が行われます。

また、呼び出し可能な judge Python SDK を使用して合格/不合格のメトリクスを作成することもできます。これにより、トレースのどの部分を評価し、どのフィールドを使用するかをより詳細に制御できます。 組み込みの Mosaic AI エージェント評価ジャッジのいずれかを使用できます。 「組み込みの AI ジャッジ」を参照してください

取得したコンテキストにPIIがないことを確認します

この例では、 guideline_adherence ジャッジを呼び出して、取得したコンテキストに PII がないことを確認します。

Python
import mlflow
import pandas as pd
from databricks.agents.evals import metric
from databricks.agents.evals import judges

evals = [
{
"request": "Good morning",
"response": "Good morning to you too!",
"retrieved_context": [{
"content": "The email address is noreply@databricks.com",
}],
}, {
"request": "Good afternoon",
"response": "This is actually the morning!",
"retrieved_context": [{
"content": "fake retrieved context",
}],
}
]

@metric
def retrieved_context_no_pii(request, response, retrieved_context):
retrieved_content = '\n'.join([c['content'] for c in retrieved_context])
return judges.guideline_adherence(
request=request,
response=retrieved_content,
guidelines=[
"The response must not contain personally identifiable information.",
]
)

with mlflow.start_run(run_name="safety"):
eval_results = mlflow.evaluate(
data=pd.DataFrame.from_records(evals),
model_type="databricks-agent",
extra_metrics=[retrieved_context_no_pii],
# Disable built-in judges.
evaluator_config={
'databricks-agent': {
"metrics": [],
}
}
)
display(eval_results.tables['eval_results'])

数値メトリクス

数値メトリクスは、浮動小数点数や整数などの序数を評価します。 UI には、数値メトリクスが行ごとに表示され、評価実行の平均値も表示されます。

例: 応答の類似性

このメトリクスは、組み込み Python ライブラリ SequenceMatcherを使用して、responseexpected_responseの類似性を測定します。

Python
import mlflow
import pandas as pd
from databricks.agents.evals import metric
from difflib import SequenceMatcher

evals = [
{
"request": "Good morning",
"response": "Good morning to you too!",
"expected_response": "Hello and good morning to you!"
}, {
"request": "Good afternoon",
"response": "I am an LLM and I cannot answer that question.",
"expected_response": "Good afternoon to you too!"
}
]

@metric
def response_similarity(response, expected_response):
s = SequenceMatcher(a=response, b=expected_response)
return s.ratio()

with mlflow.start_run(run_name="response_similarity"):
eval_results = mlflow.evaluate(
data=pd.DataFrame.from_records(evals),
model_type="databricks-agent",
extra_metrics=[response_similarity],
evaluator_config={
'databricks-agent': {
"metrics": [],
}
}
)
display(eval_results.tables['eval_results'])

Boolean メトリクス

Boolean メトリクスは True または False に評価されます。 これらは、応答が単純なヒューリスティックを満たしているかどうかを確認するなど、バイナリの決定に役立ちます。 UI でメトリクスに特別な pass/fail 処理を持たせる場合は、「 pass/fail メトリクス」を参照してください。

例: チェック入力要求は適切にフォーマットされています

このメトリクスは、任意の入力が期待どおりにフォーマットされているかどうかをチェックし、フォーマットされている場合は True を返します。

Python
import mlflow
import pandas as pd
from databricks.agents.evals import metric

evals = [
{
"request": {"messages": [{"role": "user", "content": "Good morning"}]},
}, {
"request": {"inputs": ["Good afternoon"]},
}, {
"request": {"inputs": [1, 2, 3, 4]},
}
]

@metric
def check_valid_format(request):
# Check that the request contains a top-level key called "inputs" with a value of a list
return "inputs" in request and isinstance(request.get("inputs"), list)

with mlflow.start_run(run_name="check_format"):
eval_results = mlflow.evaluate(
data=pd.DataFrame.from_records(evals),
model_type="databricks-agent",
extra_metrics=[check_valid_format],
# Disable built-in judges.
evaluator_config={
'databricks-agent': {
"metrics": [],
}
}
)
eval_results.tables['eval_results']

例: 言語モデルの自己参照

このメトリクスは、レスポンスに「LLM」と記載されているかどうかを確認し、言及されている場合はTrueを返します。

Python
import mlflow
import pandas as pd
from databricks.agents.evals import metric

evals = [
{
"request": "Good morning",
"response": "Good morning to you too!"
}, {
"request": "Good afternoon",
"response": "I am an LLM and I cannot answer that question."
}
]

@metric
def response_mentions_llm(response):
return "LLM" in response

with mlflow.start_run(run_name="response_mentions_llm"):
eval_results = mlflow.evaluate(
data=pd.DataFrame.from_records(evals),
model_type="databricks-agent",
extra_metrics=[response_mentions_llm],
evaluator_config={
'databricks-agent': {
"metrics": [],
}
}
)
display(eval_results.tables['eval_results'])

使用 custom_expected

custom_expected フィールドを使用して、その他の必要な情報をカスタムメトリクスに渡すことができます。

例: 応答の長さが制限されている

この例では、応答の長さが各例に設定された (min_length, max_length) 範囲内であることを要求する方法を示します。 custom_expected を使用して、評価の作成時にカスタム メトリクスに渡される行レベルの情報を格納します。

Python
import mlflow
import pandas as pd
from databricks.agents.evals import metric
from databricks.agents.evals import judges

evals = [
{
"request": "Good morning",
"response": "Good night.",
"custom_expected": {
"max_length": 100,
"min_length": 3
}
}, {
"request": "What is the date?",
"response": "12/19/2024",
"custom_expected": {
"min_length": 10,
"max_length": 20,
}
}
]

# The custom metric uses the "min_length" and "max_length" from the "custom_expected" field.
@metric
def response_len_bounds(
request,
response,
# This is the exact_expected_response from your eval dataframe.
custom_expected
):
return len(response) <= custom_expected["max_length"] and len(response) >= custom_expected["min_length"]

with mlflow.start_run(run_name="response_len_bounds"):
eval_results = mlflow.evaluate(
data=pd.DataFrame.from_records(evals),
model_type="databricks-agent",
extra_metrics=[response_len_bounds],
# Disable built-in judges.
evaluator_config={
'databricks-agent': {
"metrics": [],
}
}
)
display(eval_results.tables['eval_results'])

トレースに対するアサーション

カスタムメトリクスは、エージェントによって生成された MLflow トレース の任意の部分(スパン、属性、出力など)を評価できます。

例: リクエストの分類とルーティング

この例では、ユーザークエリが質問かステートメントかを判断し、それを平易な英語でユーザーに返すエージェントを構築します。 より現実的なシナリオでは、この手法を使用して、さまざまなクエリをさまざまな機能にルーティングできます。

評価セットにより、クエリの種類の分類子は、MLFlow トレースを検査するカスタム メトリクスを使用して、一連の入力に対して正しい結果を生成することが保証されます。

この例では、MLflow Trace.search_spans を使用して、このエージェントに対して定義したカスタム スパンの種類である type KEYWORDのスパンを検索します。

Python

import mlflow
import pandas as pd
from mlflow.types.llm import ChatCompletionResponse, ChatCompletionRequest
from databricks.agents.evals import metric
from databricks.agents.evals import judges
from mlflow.evaluation import Assessment
from mlflow.entities import Trace
from mlflow.deployments import get_deploy_client

# This agent is a toy example that returns simple statistics about the user's request.
# To get the stats about the request, the agent calls methods to compute stats before returning the stats in natural language.

deploy_client = get_deploy_client("databricks")
ENDPOINT_NAME="databricks-meta-llama-3-1-70b-instruct"

@mlflow.trace(name="classify_question_answer")
def classify_question_answer(request: str) -> str:
system_prompt = """
Return "question" if the request is formed as a question, even without correct punctuation.
Return "statement" if the request is a statement, even without correct punctuation.
Return "unknown" otherwise.

Do not return a preamble, only return a single word.
"""
request = {
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": request},
],
"temperature": .01,
"max_tokens": 1000
}

result = deploy_client.predict(endpoint=ENDPOINT_NAME, inputs=request)
return result.choices[0]['message']['content']

@mlflow.trace(name="agent", span_type="CHAIN")
def question_answer_agent(request: ChatCompletionRequest) -> ChatCompletionResponse:
user_query = request["messages"][-1]["content"]

request_type = classify_question_answer(user_query)
response = f"The request is a {request_type}."

return {
"messages": [
*request["messages"][:-1], # Keep the chat history.
{"role": "user", "content": response}
]
}

# Define the evaluation set with a set of requests and the expected request types for those requests.
evals = [
{
"request": "This is a question",
"custom_expected": {
"request_type": "statement"
}
}, {
"request": "What is the date?",
"custom_expected": {
"request_type": "question"
}
},
]

# The custom metric checks the expected request type against the actual request type produced by the agent trace.
@metric
def correct_request_type(request, trace, custom_expected):
classification_span = trace.search_spans(name="classify_question_answer")[0]
return classification_span.outputs == custom_expected['request_type']

with mlflow.start_run(run_name="multiple_assessments_single_metric"):
eval_results = mlflow.evaluate(
data=pd.DataFrame.from_records(evals),
model=question_answer_agent,
model_type="databricks-agent",
extra_metrics=[correct_request_type],
evaluator_config={
'databricks-agent': {
"metrics": [],
}
}
)
display(eval_results.tables['eval_results'])

これらの例を活用することで、独自の評価ニーズを満たすカスタムメトリクスを設計できます。

ツール呼び出しの評価

カスタムメトリクスには、呼び出されたツールとそれらが返した内容に関する情報を提供する ToolCallInvocation のリストである tool_calls が提供されます。

例: 適切なツールのアサートは

注記

この例は、LangGraph エージェントを定義していないため、コピー&ペーストできません。 完全に実行可能な例については、 添付のノートブック を参照してください。

Python
import mlflow
import pandas as pd
from databricks.agents.evals import metric
from databricks.agents.evals import judges

eval_data = pd.DataFrame(
[
{
"request": "what is 3 * 12?",
"expected_response": "36",
"custom_expected": {
"expected_tool_name": "multiply"
},
},
{
"request": "what is 3 + 12?",
"expected_response": "15",
"custom_expected": {
"expected_tool_name": "add"
},
},
]
)

@metric
def is_correct_tool(tool_calls, custom_expected):
# Metric to check whether the first tool call is the expected tool
return tool_calls[0].tool_name == custom_expected["expected_tool_name"]

@metric
def is_reasonable_tool(request, trace, tool_calls):
# Metric using the guideline adherence judge to determine whether the chosen tools are reasonable
# given the set of available tools. Note that `available_tools` requires `databricks-agents >= 0.16.0`
tool_call_choice = f"""
<available_tools>{tool_calls[0].available_tools}</available_tools>
<chosen_tools>{[tool_call.tool_name for tool_call in tool_calls]}</chosen_tools>
"""

return judges.guideline_adherence(
request=request["messages"][0]["content"],
response=tool_call_choice,
guidelines=[
"The selected tool must be a reasonable tool call with respect to the request and available tools.",
]
)

results = mlflow.evaluate(
data=eval_data,
model=tool_calling_agent,
model_type="databricks-agent",
extra_metrics=[is_correct_tool]
)
results.tables["eval_results"].display()

カスタムメトリクスの開発

メトリクスを開発する際には、変更を加えるたびにエージェントを実行することなく、メトリクスを迅速に反復処理する必要があります。 これを簡単にするには、次の戦略を使用します。

  1. eval データセット・エージェントから解答用紙を生成します。 これにより、評価セット内の各エントリに対してエージェントが実行され、メトリクスを直接呼び出すことができる応答とトレースが生成されます。
  2. メトリクスを定義します。
  3. 解答用紙の各値に対してメトリクスを直接呼び出し、メトリクスの定義を反復処理します。
  4. メトリクスが期待どおりに動作している場合は、同じ解答用紙で mlflow.evaluate() を実行して、Agent Evaluationの実行結果が期待どおりであることを確認します。 この例のコードでは model= フィールドを使用しないため、評価ではコンピュート前の応答が使用されます。
  5. メトリクスのパフォーマンスに問題がなければ、mlflow.evaluate()model=フィールドを有効にして、エージェントに対話形式で電話をかけます。
Py
import mlflow
import pandas as pd
from databricks.agents.evals import metric
from databricks.agents.evals import judges
from mlflow.evaluation import Assessment
from mlflow.entities import Trace

evals = [
{
"request": "What is Databricks?",
"custom_expected": {
"keywords": ["databricks"],
},
"expected_response": "Databricks is a cloud-based analytics platform.",
"expected_facts": ["Databricks is a cloud-based analytics platform."],
"expected_retrieved_context": [{"content": "Databricks is a cloud-based analytics platform.", "doc_uri": "https://databricks.com/doc_uri"}]
}, {
"request": "When was Databricks founded?",
"custom_expected": {
"keywords": ["when", "databricks", "founded"]
},
"expected_response": "Databricks was founded in 2012",
"expected_facts": ["Databricks was founded in 2012"],
"expected_retrieved_context": [{"content": "Databricks is a cloud-based analytics platform.", "doc_uri": "https://databricks.com/doc_uri"}]
}, {
"request": "How do I convert a timestamp_ms to a timestamp in dbsql?",
"custom_expected": {
"keywords": ["timestamp_ms", "timestamp", "dbsql"]
},
"expected_response": "You can convert a timestamp with...",
"expected_facts": ["You can convert a timestamp with..."],
"expected_retrieved_context": [{"content": "You can convert a timestamp with...", "doc_uri": "https://databricks.com/doc_uri"}]
}
]
## Step 1: Generate an answer sheet with all of the built-in judges turned off.
## This code calls the agent for all the rows in the evaluation set, which you can use to build the metric.
answer_sheet_df = mlflow.evaluate(
data=evals,
model=rag_agent,
model_type="databricks-agent",
# Turn off built-in judges to just build an answer sheet.
evaluator_config={"databricks-agent": {"metrics": []}
}
).tables['eval_results']
display(answer_sheet_df)

answer_sheet = answer_sheet_df.to_dict(orient='records')

## Step 2: Define the metric.
@metric
def custom_metric_consistency(
request,
response,
retrieved_context,
expected_response,
expected_facts,
expected_retrieved_context,
trace,
# This is the exact_expected_response from your eval dataframe.
custom_expected
):
print(f"[custom_metric] request: {request}")
print(f"[custom_metric] response: {response}")
print(f"[custom_metric] retrieved_context: {retrieved_context}")
print(f"[custom_metric] expected_response: {expected_response}")
print(f"[custom_metric] expected_facts: {expected_facts}")
print(f"[custom_metric] expected_retrieved_context: {expected_retrieved_context}")
print(f"[custom_metric] trace: {trace}")

return True

## Step 3: Call the metric directly before using the evaluation harness to iterate on the metric definition.
for row in answer_sheet:
custom_metric_consistency(
request=row['request'],
response=row['response'],
expected_response=row['expected_response'],
expected_facts=row['expected_facts'],
expected_retrieved_context=row['expected_retrieved_context'],
retrieved_context=row['retrieved_context'],
trace=Trace.from_json(row['trace']),
custom_expected=row['custom_expected']
)

## Step 4: After you are confident in the signature of the metric, you can run the harness with the answer sheet to trigger the output validation and make sure the UI reflects what you intended.
with mlflow.start_run(run_name="exact_expected_response"):
eval_results = mlflow.evaluate(
data=answer_sheet,
## Step 5: Re-enable the model here to call the agent when we are working on the agent definition.
# model=rag_agent,
model_type="databricks-agent",
extra_metrics=[custom_metric_consistency],
# Uncomment to turn off built-in judges.
# evaluator_config={
# 'databricks-agent': {
# "metrics": [],
# }
# }
)
display(eval_results.tables['eval_results'])

ノートブックの例

次のノートブックの例は、Mosaic AI Agent Evaluation でカスタムメトリクスを使用するさまざまな方法を示しています。

エージェント評価カスタムメトリクスサンプルノートブック

Open notebook in new tab