自定义指标
重要
此功能目前以公共预览版提供。
本指南介绍如何使用自定义指标在马赛克 AI 代理框架中评估 AI 应用程序。 自定义指标可以灵活地定义针对特定业务用例定制的评估指标,无论是基于简单的启发式、高级逻辑还是编程评估。
概述
自定义指标以 Python 编写,使开发人员能够完全控制通过 AI 应用程序评估跟踪。 支持以下指标:
自定义指标可以使用:
- 评估行中的任何字段。
- 其他预期值的
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 修饰器允许用户定义自定义评估指标,并通过 extra_metrics
参数传递到 mlflow.evaluate()。 评估工具基于以下签名调用具有命名参数的指标函数:
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
中,并附带一个空理由。
float
:对于数值指标(例如相似性分数、准确性百分比)。bool
:用于二进制指标。Assessment
或list[Assessment]
:支持添加理由的更丰富的输出类型。 如果返回评估列表,则可以重新使用同一指标函数返回多个评估。name
:评估的名称。value
:值(浮点、int、bool 或字符串)。rationale
:(可选)解释如何计算此值的理由。 这可用于在 UI 中显示额外的推理。 例如,从生成此评估的 LLM 提供推理时,此字段非常有用。
通过/失败指标
返回 "yes"
和 "no"
的任何字符串指标都被视为传递/失败指标,并在 UI 中具有特殊处理。
还可以使用可调用的评判 Python SDK 创建通过/失败指标。 这使你能够更好地控制要评估的跟踪部分以及要使用的预期字段。 可以使用任何内置的 Mosaic AI 代理评估判定工具。 请参阅内置的 AI 判定工具。
示例:使用判定准则的自定义安全指标
此示例创建两个自定义安全指标:亵渎和粗鲁。 它使用可调用的 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'])
数值指标
数值度量评估序号值,例如浮点数或整数。 数值指标在用户界面的每一行中显示,并显示评估运行的平均值。
示例:响应相似性
此指标使用内置 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'])
布尔指标
布尔指标的计算结果为 True
或 False
。 这对于二进制决策非常有用,例如检查响应是否符合简单的启发式。 如果希望指标在 UI 上具有特殊的通过/不通过处理,请参阅 通过/不通过指标。
示例:语言模型自我引用
此指标检查响应是否提到“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 查找类型为 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'])
利用这些示例,可以设计自定义指标以满足独特的评估需求。
评估工具调用
自定义指标将随 tool_calls
一起提供,这是 ToolCallInvocation 的列表,可让你了解调用了哪些工具及其返回的内容。
示例:断言调用了正确的工具
注意
此示例不可复制粘贴,因为它未定义 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()
,以验证运行代理评估的结果是否为预期结果。 此示例中的代码不使用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'])
示例笔记本
以下示例笔记本演示了在马赛克 AI 代理评估中使用自定义指标的一些不同方法。