Compartir a través de


Métricas personalizadas (MLflow 2)

Importante

Databricks recomienda usar MLflow 3 para evaluar y supervisar aplicaciones de GenAI. En esta página se describe la evaluación del agente de MLflow 2.

En esta guía se explica cómo usar métricas personalizadas para evaluar aplicaciones de inteligencia artificial en Mosaic AI Agent Framework. Las métricas personalizadas proporcionan flexibilidad para definir métricas de evaluación adaptadas a su caso de uso empresarial específico, ya sea basadas en heurística simple, lógica avanzada o evaluaciones mediante programación.

Información general

Las métricas personalizadas se escriben en Python y proporcionan a los desarrolladores control total para evaluar los seguimientos a través de una aplicación de IA. Se admiten las siguientes métricas:

Las métricas personalizadas pueden usar:

  • Cualquier campo de la fila de evaluación.
  • El campo custom_expected para valores esperados adicionales.
  • Acceso completo a la traza de MLflow, incluidos segmentos, atributos y resultados.

Uso

La métrica personalizada se pasa al marco de evaluación mediante el campo extra_metrics en mlflow.evaluate(). Ejemplo:

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"])

@metric decorador

El decorador @metric permite a los usuarios definir métricas de evaluación personalizadas que se pueden pasar a mlflow.evaluate() mediante el extra_metrics argumento . La herramienta de evaluación invoca la función de métrica utilizando argumentos nombrados según la firma siguiente:

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

Explicación de los argumentos

  • request: la entrada proporcionada al agente, con el formato de un objeto serializable arbitrario. Esto representa la consulta o solicitud del usuario.
  • response: La salida sin procesar del agente, formateada como un objeto serializable arbitrario y opcional. Contiene la respuesta generada por el agente para su evaluación.
  • retrieved_context: lista de diccionarios que contienen contexto recuperado durante la tarea. Este contexto puede provenir del conjunto de datos de evaluación de entrada o del seguimiento, y los usuarios pueden invalidar o personalizar su extracción a través del campo trace.
  • expected_response: cadena que representa la respuesta correcta o deseada para la tarea. Actúa como la verdad básica para la comparación con la respuesta del agente.
  • expected_facts: una lista de hechos que se espera que aparezcan en la respuesta del agente, útil para las tareas de comprobación de hechos.
  • guidelines: una lista de instrucciones o una asignación de un nombre de guía a una matriz de directrices para ese nombre. Las directrices le permiten proporcionar restricciones en cualquier campo que el juez de cumplimiento de las directrices pueda evaluar.
  • expected_retrieved_context: lista de diccionarios que representan el contexto de recuperación esperado. Esto es esencial para las tareas aumentadas de recuperación en las que es importante la corrección de los datos recuperados.
  • trace: un objeto MLflow Trace opcional que contiene intervalos, atributos y otros metadatos sobre la ejecución del agente. Esto permite una inspección profunda de los pasos internos realizados por el agente.
  • custom_expected: diccionario para pasar valores esperados definidos por el usuario. Este campo proporciona flexibilidad para incluir expectativas personalizadas adicionales que no están cubiertas por los campos estándar.
  • tool_calls: una lista de ToolCallInvocation que describe qué herramientas fueron llamadas y qué devolvieron.

Valor devuelto

El valor devuelto de la métrica personalizada es unaEvaluación por fila. Si devuelve un valor primitivo, se encapsula en un Assessment con una racionalización vacía.

  • float: para métricas numéricas (por ejemplo, puntuaciones de similitud, porcentajes de precisión).
  • bool: para métricas binarias.
  • Assessment o list[Assessment]: un tipo de salida más completo que admite la adición de una lógica. Si devuelve una lista de evaluaciones, se puede volver a usar la misma función de métrica para devolver varias evaluaciones.
    • name: el nombre de la evaluación.
    • value: el valor (un valor float, int, bool o string).
    • rationale: (Opcional) Una justificación que explica cómo se calculó este valor. Esto puede ser útil para mostrar el razonamiento adicional en la interfaz de usuario. Este campo es útil, por ejemplo, al proporcionar el razonamiento de un LLM que generó esta Evaluación.

Métricas de aprobado/no aprobado

Cualquier métrica de cadena que devuelva "yes" y "no" se trata como una métrica de aprobado/no aprobado y tiene un tratamiento especial en la interfaz de usuario.

También puede realizar una métrica de paso o error con el evaluador invocable SDK de Python. Esto le proporciona más control sobre qué partes del seguimiento se van a evaluar y qué campos esperados se van a usar. Puede usar cualquiera de los jueces de evaluación incorporados del agente de IA de Mosaic. Consulte Jueces de IA integrados (MLflow 2).

Asegúrese de que el contexto recuperado no tiene PII

En este ejemplo se llama al juez guideline_adherence para asegurarse de que el contexto recuperado no tenga DCP.

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

Las métricas numéricas evalúan valores ordinales, como flotantes o enteros. Las métricas numéricas se muestran en la interfaz de usuario por fila, junto con el valor medio de la ejecución de evaluación.

Ejemplo: similitud de respuesta

Esta métrica mide la similitud entre response y expected_response mediante la biblioteca integrada de Python 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 booleanas

Las métricas booleanas se evalúan como True o False. Son útiles para las decisiones binarias, como comprobar si una respuesta cumple una heurística simple. Si desea que la métrica tenga un tratamiento especial de aprobación/fracaso en la interfaz de usuario, consulte Métricas de aprobación/fracaso.

Ejemplo: Comprobación de que las solicitudes de entrada tienen el formato correcto

Esta métrica comprueba si la entrada arbitraria tiene el formato esperado y devuelve True si es .

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']

Ejemplo: Autorreferencia del modelo de lenguaje

Esta métrica comprueba si la respuesta menciona "LLM" y devuelve True si lo hace.

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'])

Usar custom_expected

El campo custom_expected se puede usar para pasar cualquier otra información esperada a una métrica personalizada.

Ejemplo: Longitud de respuesta limitada

En este ejemplo se muestra cómo requerir que la longitud de la respuesta esté dentro de los límites (min_length, max_length) establecidos para cada ejemplo. Utiliza custom_expected para almacenar cualquier información a nivel de fila que se pase a métricas personalizadas al crear una evaluación.

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'])

Aserciones sobre rastros

Las métricas personalizadas pueden evaluar cualquier parte de una traza de MLflow generado por el agente, incluidos segmentos, atributos y salidas.

Ejemplo: Clasificación y enrutamiento de solicitudes

En este ejemplo se compila un agente que determina si la consulta del usuario es una pregunta o una instrucción y la devuelve en inglés sin formato al usuario. En un escenario más realista, puede usar esta técnica para enrutar diferentes consultas a distintas funcionalidades.

El conjunto de evaluación garantiza que el clasificador de tipo de consulta genere los resultados adecuados para un conjunto de entradas mediante métricas personalizadas que inspeccionan el seguimiento de MLFlow.

En este ejemplo se usa el Trace.search_spans MLflow para buscar intervalos con el tipo KEYWORD, que es un tipo de intervalo personalizado que definió 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'])

Al aprovechar estos ejemplos, puede diseñar métricas personalizadas para satisfacer sus necesidades de evaluación únicas.

Evaluación de invocaciones a herramientas

Se proporcionarán métricas personalizadas con tool_calls, las cuales son una lista de ToolCallInvocation que brindan información sobre qué herramientas se llamaron y lo que devolvieron.

Ejemplo: Se llama a la aserción de la herramienta correcta

Nota:

Este ejemplo no es copiable, ya que no define el agente de LangGraph. Consulte el cuaderno adjunto para ver el ejemplo totalmente ejecutable.

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()

Desarrollo de métricas personalizadas

A medida que desarrollas métricas, debes iterar rápidamente sobre la métrica sin tener que ejecutar el agente cada vez que realices un cambio. Para que sea más sencillo, use la siguiente estrategia:

  1. Genere una hoja de respuestas a partir del agente del conjunto de datos de evaluación. Esto ejecuta el agente para cada una de las entradas del conjunto de evaluación, generando respuestas y trazas que puede utilizar para llamar directamente a la métrica.
  2. Defina la métrica.
  3. Llame directamente a la métrica para cada valor de la hoja de respuestas e itera sobre la definición de métrica.
  4. Cuando la métrica se comporta según lo previsto, ejecute mlflow.evaluate() en la misma hoja de respuestas para comprobar que los resultados de la ejecución de la evaluación del agente son los que espera. El código de este ejemplo no usa el campo model=, por lo que la evaluación usa respuestas calculadas previamente.
  5. Cuando esté satisfecho con el rendimiento de la métrica, habilite el campo model= en mlflow.evaluate() para llamar al agente de forma interactiva.
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'])

Cuaderno de ejemplo

En el cuaderno de ejemplo siguiente se muestran algunas maneras diferentes de usar métricas personalizadas en la evaluación del agente de Mosaic AI.

Cuaderno de ejemplo de métricas personalizadas de evaluación del agente

Obtener el cuaderno