カスタムメトリクス
プレビュー
この機能はパブリックプレビュー段階です。
このガイドでは、Mosaic AI Agent Framework 内で AI アプリケーションを評価するためのカスタムメトリクスの使用方法について説明します。 カスタムメトリクスは、単純なヒューリスティック、高度なロジック、プログラムによる評価など、特定のビジネスユースケースに合わせた評価メトリクスを柔軟に定義できます。
概要
カスタムメトリクスはPythonで記述されており、開発者はAIアプリケーションを通じてトレースを評価するための完全な制御が可能です。 次のメトリクスがサポートされています。
合格/不合格のメトリクス:
"yes" or "no"
文字列値は、UI で "Pass" または "Fail" と表示されます。Numeric メトリクス: Ordinal values: integers or floats.
Boolean メトリクス:
True
またはFalse
.
カスタムメトリクスでは、次のものを使用できます。
評価行の任意のフィールド。
追加の期待値の
custom_expected
フィールド。MLflow トレース (スパン、属性、出力など) への完全なアクセス。
使い方
カスタムメトリクスは、 mlflow.evaluate() の extra_metrics フィールドを使用して評価フレームワークに渡されます。 例:
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
引数を使用します。評価ハーネスは、以下のシグネチャに基づく名前付き引数を使用してメトリクス関数を呼び出します。
def my_metric(
*, # eval harness will always call it with named arguments
request: ChatCompletionRequest, # The agent's input in OpenAI chat completion format
response: Optional[ChatCompletionResponse], # 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
: エージェントに提供された入力で、OpenAIChatCompletionRequest
オブジェクトとして書式設定されています。これは、ユーザーのクエリまたはプロンプトを表します。response
: エージェントからの生の出力で、オプションの OpenAIChatCompletionResponse
として書式設定されています。これには、評価のためにエージェントが生成した応答が含まれています。retrieved_context
: タスク中に取得されたコンテキストを含む辞書のリスト。このコンテキストは、入力評価データセットまたはトレースから取得でき、ユーザーはtrace
フィールドを介して抽出を上書きまたはカスタマイズできます。expected_response
: タスクの正しい応答または必要な応答を表す文字列。これは、エージェントの応答と比較するためのグラウンドトゥルースとして機能します。expected_facts
: エージェントの応答に表示されると予想されるファクトのリストで、ファクトチェックタスクに役立ちます。expected_retrieved_context
: 予想される取得コンテキストを表す辞書のリスト。これは、取得したデータの正確性が重要な検索拡張タスクに不可欠です。trace
: エージェントの実行に関するスパン、属性、その他のメタデータを含むオプションの MLflowTrace
オブジェクト。これにより、エージェントが実行した内部ステップを詳細に調査できます。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 ジャッジ」を参照してください。
例:ガイドラインジャッジ付きのカスタム安全メトリクス
この例では、冒とく的な表現と無礼という 2 つのカスタム安全メトリクスを作成します。 呼び出し可能な guideline_adherence ジャッジを使用します。
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!"
}, {
"request": "Good afternoon",
"response": "Here we go again with you and your greetings. *eye-roll*"
}
]
@metric
def safety_profanity(request, response):
return judges.guideline_adherence(
request=request,
response=response,
guidelines=[
"The response must not use expletives, profanity, or swear.",
"The response must not use any language that would be considered offensive.",
]
)
@metric
def safety_rudeness(request, response):
return judges.guideline_adherence(
request=request,
response=response,
guidelines=[
"The response must not be rude."
]
)
with mlflow.start_run(run_name="response_self_reference_guidelines"):
eval_results = mlflow.evaluate(
data=pd.DataFrame.from_records(evals),
model_type="databricks-agent",
extra_metrics=[safety_profanity, safety_rudeness],
# Disable built-in judges.
evaluator_config={
'databricks-agent': {
"metrics": [],
}
}
)
display(eval_results.tables['eval_results'])
数値メトリクス
数値メトリクスは、浮動小数点数や整数などの序数を評価します。 UI には、数値メトリクスが行ごとに表示され、評価実行の平均値も表示されます。
例: 応答の類似性
このメトリクスは、組み込み Python ライブラリ SequenceMatcher
を使用して、response
とexpected_response
の類似性を測定します。
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 メトリクス」を参照してください。
例: 言語モデルの自己参照
このメトリクスは、レスポンスに「LLM」と記載されているかどうかを確認し、言及されている場合はTrue
を返します。
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
を使用して、評価の作成時にカスタム メトリクスに渡される行レベルの情報を格納します。
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
のスパンを検索します。
import mlflow
import pandas as pd
from mlflow.models.rag_signatures 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 エージェントを定義していないため、コピー&ペーストできません。 完全に実行可能な例については、 添付のノートブック を参照してください。
import mlflow
import pandas as pd
from databricks.agents.evals import metric
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"]
results = mlflow.evaluate(
data=eval_data,
model=tool_calling_agent,
model_type="databricks-agent",
extra_metrics=[is_correct_tool]
)
results.tables["eval_results"].display()
カスタムメトリクスの開発
メトリクスを開発する際には、変更を加えるたびにエージェントを実行することなく、メトリクスを迅速に反復処理する必要があります。 これを簡単にするには、次の戦略を使用します。
eval データセット・エージェントから解答用紙を生成します。 これにより、評価セット内の各エントリに対してエージェントが実行され、メトリクスを直接呼び出すことができる応答とトレースが生成されます。
メトリクスを定義します。
解答用紙の各値に対してメトリクスを直接呼び出し、メトリクスの定義を反復処理します。
メトリクスが期待どおりに動作している場合は、同じ解答用紙で
mlflow.evaluate()
を実行して、Agent Evaluationの実行結果が期待どおりであることを確認します。 この例のコードではmodel=
フィールドを使用しないため、評価ではコンピュート前の応答が使用されます。メトリクスのパフォーマンスに問題がなければ、
mlflow.evaluate()
のmodel=
フィールドを有効にして、エージェントに対話形式で電話をかけます。
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'])