Métricas personalizadas (MLflow 2)
A Databricks recomenda usar o MLflow 3 para a avaliação e monitoramento de aplicativos GenAI. Esta página descreve o MLflow 2 Agent Evaluation.
- Para uma introdução à avaliação e monitoramento no MLflow 3, consulte Avalie e monitore agentes de AI.
- Para obter informações sobre como migrar para o MLflow 3, consulte Migrar para o MLflow 3 a partir da Agent Evaluation.
- Para informação sobre o MLflow 3 neste tópico, consulte Avaliadores baseados em código.
Este guia explica como usar métricas personalizadas para avaliar aplicações de AI com Agentes Personalizados. As métricas personalizadas oferecem flexibilidade para definir métricas de avaliação adaptadas ao seu caso de uso de negócios específico. É possível baseá-los em heurísticas simples, lógica avançada ou avaliações programáticas.
Visão geral
Métricas personalizadas são escritas em Python e dão aos desenvolvedores controle total para avaliar rastreamentos através de um aplicativo de AI. As seguintes métricas são suportadas:
- Métricas de aprovação/reprovação:
"yes" or "no"strings são renderizadas como “Aprovado” ou “Reprovado” na IU. - Métricas numéricas: Valores ordinais: inteiros ou flutuantes.
- Métricas Boolean:
TrueouFalse.
Métricas personalizadas podem usar:
- Qualquer campo na linha de avaliação.
- O campo
custom_expectedpara valores esperados adicionais. - Acesso completo ao rastreamento MLflow, incluindo spans, atributos e saídas.
Uso
A métrica personalizada é passada para a estrutura de avaliação usando o campo extra_metrics em mlflow.evaluate(). Exemplo:
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-3-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"])
O decorador @metric
O decorator @metric permite que os usuários definam métricas de avaliação personalizadas que podem ser passadas para mlflow.evaluate() usando o argumento extra_metrics. A estrutura de avaliação invoca a função de métricas com argumentos nomeados com base na assinatura abaixo:
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
guidelines: Optional[Union[List[str], Dict[str, List[str]]]] # A list of guidelines or mapping a name of guideline to an array of guidelines for that name
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
Explicação dos argumentos
request: A entrada fornecida ao agente, formatada como um objeto serializável arbitrário. Isso representa a consulta ou o prompt do usuário.response: A saída bruta do agente, formatada como um objeto serializável arbitrário opcional. Ele contém a resposta gerada pelo agente para avaliação.retrieved_context** **: Uma lista de dicionários contendo o contexto recuperado durante a tarefa. Este contexto pode vir do dataset de avaliação de entrada ou do rastreamento, e os usuários podem substituir ou personalizar sua extração por meio do campotrace.expected_response: As strings que representam a resposta correta ou desejada para a tarefa. Ele atua como a verdade fundamental para comparação com a resposta do agente.expected_facts: Uma lista de fatos esperados para aparecer na resposta do agente, útil para tarefas de verificação de fatos.guidelines** **: Uma lista de diretrizes ou um mapeamento de um nome de diretriz para um array de diretrizes para esse nome. As diretrizes permitem que você forneça restrições em qualquer campo que possa ser avaliado pelo juiz de conformidade com as diretrizes.expected_retrieved_context: Uma lista de dicionários que representa o contexto de recuperação esperado. Isso é essencial para tarefas de recuperação aumentada onde a correção dos dados recuperados é importante.trace: Um objetoTraceopcional do MLflow contendo spans, atributos e outros metadados sobre a execução do agente. Isso permite uma inspeção aprofundada dos passos internos dados pelo agente.custom_expected: Um dicionário para passar valores esperados definidos pelo usuário. Este campo oferece flexibilidade para incluir expectativas personalizadas adicionais que não são cobertas pelos campos padrão.tool_calls: Uma lista de ToolCallInvocation que descreve quais ferramentas foram chamadas e o que elas retornaram.
Valor de retorno
O valor de retorno da métrica personalizada é uma Avaliação por linha. Se retornar uma primitiva, ela é encapsulada em um Assessment com uma justificativa vazia.
float: Para métricas numéricas (por exemplo, pontuações de similaridade, porcentagens de precisão).bool: Para métricas binárias.Assessmentoulist[Assessment]: Um tipo de saída mais rico que oferece suporte à adição de uma justificativa. Se você retornar uma lista de avaliações, a mesma função de métrica pode ser reutilizada para retornar múltiplas avaliações.name: O nome da avaliação.value: O valor (um float, int, bool, ou string).rationale: (Opcional) Uma justificativa explicando como este valor foi calculado. Isso pode ser útil para mostrar raciocínio adicional na interface do usuário. Este campo é útil, por exemplo, ao fornecer o raciocínio de um LLM que gerou esta Avaliação.
Métricas de aprovação/reprovação
Qualquer métrica de string que retorna "yes" e "no" é tratada como uma métrica de aprovação/reprovação e tem um tratamento especial na IU.
Você também pode criar uma métrica de aprovação/reprovação com o SDK Python de juiz invocável. Isso lhe dá mais controle sobre quais partes do rastreamento avaliar e quais campos esperados usar. Você pode usar qualquer um dos juízes integrados de Agent Evaluation. Consulte Juízes de AI integrados (MLflow 2).
Certifique-se de que o contexto recuperado não contenha PII
Este exemplo chama o juiz de aderência às diretrizes para garantir que o contexto recuperado não contenha PII.
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,
# You can also pass in per-row guidelines by adding `guidelines` to the signature of your metric
guidelines=[
"The retrieved context must not contain personally identifiable information.",
],
# `guidelines_context` requires `databricks-agents>=0.20.0`
guidelines_context={"retrieved_context": retrieved_content},
)
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'])
Métricas numéricas
As métricas numéricas avaliam valores ordinais, como floats ou inteiros. As métricas numéricas são exibidas na UI por linha, juntamente com o valor médio para a execução da avaliação.
Exemplo: similaridade de resposta
Esta métrica mede a similaridade entre response e expected_response usando a biblioteca Python integrada SequenceMatcher.
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'])
Métricas Boolean
Métricas Boolean avaliam para True ou False. Isto é útil para decisões binárias, como verificar se uma resposta atende a uma heurística simples. Caso queira que a métrica tenha um tratamento especial de aprovação/reprovação na IU, consulte métricas de aprovação/reprovação.
Exemplo: Verifique se as solicitações de entrada estão formatadas corretamente
Esta métrica verifica se a entrada arbitrária está formatada como esperado e retorna True se estiver.
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']
Exemplo: Autorreferência de modelo de linguagem
Esta métrica verifica se a resposta menciona “LLM” e retorna True se o fizer.
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'])
Usando custom_expected
O campo custom_expected pode ser usado para passar qualquer outra informação esperada para uma métrica personalizada.
Exemplo: Tamanho da resposta limitado
Este exemplo mostra como exigir que o comprimento da resposta esteja dentro dos limites (min_length, max_length) definidos para cada exemplo. Use custom_expected para armazenar qualquer informação em nível de linha a ser passada para métricas personalizadas ao criar uma avaliação.
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'])
Declarações sobre rastreamentos
Métricas personalizadas podem avaliar qualquer parte de um rastreamento do MLflow produzido pelo agente, incluindo spans, atributos e saídas.
Exemplo: Classificação e roteamento de solicitações
Este exemplo cria um agente que determina se a consulta do usuário é uma pergunta ou uma declaração e a retorna em inglês simples para o usuário. Em um cenário mais realista, esta técnica pode ser utilizada para direcionar diferentes consultas para diferentes funcionalidades.
O conjunto de avaliação garante que o classificador de tipo de consulta produza os resultados corretos para um conjunto de entradas usando métricas personalizadas que inspecionam o rastreamento MLFlow.
Este exemplo usa o MLflow Trace.search_spans para encontrar spans do tipo KEYWORD, que é um tipo de span personalizado que você definiu para este agente.
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-3-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'])
Ao aproveitar esses exemplos, você pode criar métricas personalizadas para atender às suas necessidades exclusivas de avaliação.
Avaliando chamadas de ferramenta
Métricas personalizadas serão fornecidas com tool_calls, que são uma lista de ToolCallInvocation que fornecem informações sobre quais ferramentas foram chamadas e o que elas retornaram.
Exemplo: atestando que a ferramenta correta é chamada
Este exemplo não pode ser copiado e colado, já que não define o agente LangGraph. Consulte o Notebook anexado para o exemplo totalmente executável.
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 `guidelines_context` requires `databricks-agents >= 0.20.0`
return judges.guideline_adherence(
request=request["messages"][0]["content"],
guidelines=[
"The selected tool must be a reasonable tool call with respect to the request and available tools.",
],
# `guidelines_context` requires `databricks-agents>=0.20.0`
guidelines_context={
"available_tools": str(tool_calls[0].available_tools),
"chosen_tools": str([tool_call.tool_name for tool_call in tool_calls]),
},
)
results = mlflow.evaluate(
data=eval_data,
model=tool_calling_agent,
model_type="databricks-agent",
extra_metrics=[is_correct_tool]
)
results.tables["eval_results"].display()
Desenvolver métricas personalizadas
À medida que você desenvolve métricas, precisa iterar rapidamente na métrica sem ter que executar o agente toda vez que faz uma alteração. Para simplificar, use a seguinte estratégia:
- Gere uma folha de respostas a partir do agente do conjunto de dados de avaliação. Isto executa o agente para cada uma das entradas no conjunto de avaliação, gerando respostas e rastreamentos que podem ser usados para chamar a métrica diretamente.
- Defina a métrica.
- Chame a métrica para cada valor na folha de respostas diretamente e itere na definição da métrica.
- Quando a métrica estiver se comportando conforme o esperado, execute
mlflow.evaluate()na mesma folha de respostas para verificar se os resultados da execução da Agent Evaluation correspondem ao esperado. O código neste exemplo não utiliza o campomodel=, portanto, a avaliação usa respostas pré-computadas. - Quando estiver satisfeito com o desempenho da métrica, habilite o campo
model=emmlflow.evaluate()para chamar o agente interativamente.
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'])
Notebook de exemplo
O exemplo de Notebook a seguir ilustra diferentes formas de usar métricas personalizadas em Agent Evaluation.