Udostępnij za pośrednictwem


Metryki niestandardowe

Ważny

Ta funkcja jest dostępna w publicznej wersji testowej.

W tym przewodniku wyjaśniono, jak używać niestandardowych metryk do oceny aplikacji sztucznej inteligencji w ramach Mosaic AI Agent Framework. Metryki niestandardowe zapewniają elastyczność definiowania metryk oceny dostosowanych do konkretnego przypadku użycia biznesowego, niezależnie od tego, czy są oparte na prostych heurystyce, zaawansowanej logice czy ocenach programowych.

Przegląd

Wskaźniki niestandardowe są zapisywane w języku Python i dają deweloperom pełną kontrolę do oceny śladów przy użyciu aplikacji AI. Obsługiwane są następujące metryki:

Niestandardowych metryk można używać:

  • Dowolne pole w wierszu oceny.
  • Pole custom_expected dla dodatkowych oczekiwanych wartości.
  • Pełny dostęp do śledzenia MLflow, w tym zakresów, atrybutów i danych wyjściowych.

Użycie

Metryka niestandardowa jest przekazywana do struktury oceny przy użyciu pola extra_metrics w pliku mlflow.evaluate(). Przykład:

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

dekorator @metric

Dekorator @metric umożliwia użytkownikom definiowanie niestandardowych metryk oceny, które można przekazać do mlflow.evaluate() przy użyciu argumentu extra_metrics. Narzędzie oceny wywołuje funkcję metryczną z nazwanymi argumentami na podstawie poniższej sygnatury.

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

Wyjaśnienie argumentów

  • request: dane wejściowe dostarczone do agenta sformatowane jako obiekt ChatCompletionRequest OpenAI. Reprezentuje to zapytanie użytkownika lub monit.
  • response: Surowe dane wyjściowe z agenta, sformatowane jako opcjonalny format OpenAI ChatCompletionResponse. Zawiera on wygenerowaną odpowiedź agenta na potrzeby oceny.
  • retrieved_context: lista słowników zawierających kontekst pobrany podczas zadania. Ten kontekst może pochodzić z wejściowego zestawu danych oceny lub śledzenia, a użytkownicy mogą przesłonić lub dostosować wyodrębnianie za pomocą pola trace.
  • expected_response: ciąg reprezentujący poprawną lub żądaną odpowiedź dla zadania. Służy jako prawdziwa podstawa do porównania z odpowiedzią agenta.
  • expected_facts: lista faktów, które powinny pojawić się w odpowiedzi agenta, przydatne w przypadku zadań sprawdzania faktów.
  • expected_retrieved_context: lista słowników reprezentujących oczekiwany kontekst pobierania. Jest to niezbędne w przypadku zadań rozszerzonych pobierania, w których istotna jest poprawność pobranych danych.
  • trace: opcjonalny obiekt MLflow Trace zawierający zakresy, atrybuty i inne metadane dotyczące wykonywania agenta. Pozwala to na głęboką inspekcję wewnętrznych kroków podjętych przez agenta.
  • custom_expected: słownik do przekazywania wartości oczekiwanych zdefiniowanych przez użytkownika. To pole zapewnia elastyczność dołączania dodatkowych oczekiwań niestandardowych, które nie są objęte polami standardowymi.
  • tool_calls: Lista ToolCallInvocation, która opisuje, które narzędzia zostały wywołane i co zwróciły.

Wartość zwracana

Wartość zwracana metryki niestandardowej jest przypadająca na wiersz w schemacie Assessment. Jeśli zwracasz element pierwotny, jest on owinięty w Assessment z pustym uzasadnieniem.

  • float: w przypadku metryk liczbowych (np. wyników podobieństwa, wartości procentowe dokładności).
  • bool: w przypadku metryk binarnych.
  • Assessment lub list[Assessment]: bogatszy typ danych wyjściowych obsługujący dodawanie uzasadnienia. Jeśli zwrócisz listę ocen, ta sama funkcja metryki może zostać ponownie użyta do zwrócenia wielu ocen.
    • name: nazwa oceny.
    • value: wartość (liczba zmiennoprzecinkowa, liczba całkowita, boolean lub ciąg).
    • rationale: (Opcjonalnie) Uzasadnienie objaśniające sposób obliczania tej wartości. Może to być przydatne, aby pokazać dodatkowe rozumowanie w interfejsie użytkownika. To pole jest przydatne, na przykład podczas podawania rozumowania z modelu LLM, który wygenerował tę ocenę.

metryki zdane/oblane

Każda metryka ciągu zwracająca "yes" i "no" jest traktowana jako metryka pass/fail i ma specjalne traktowanie w interfejsie użytkownika.

Możesz również utworzyć metrykę zaliczenie/niezaliczenie za pomocą wywoływanego sędziego z zestawu SDK dla języka Python . To daje ci większą kontrolę nad tym, które części śladu oceniać i które oczekiwane pola stosować. Możesz użyć dowolnych wbudowanych sędziów oceny agentów AI Mozaiki. Zobacz wbudowaną AI oceniającą.

Przykład: niestandardowe metryki bezpieczeństwa zgodnie z wytycznymi

W tym przykładzie są tworzone dwa niestandardowe metryki bezpieczeństwa: wulgarność i niegrzeczność. Używa obiektu callable 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'])

metryki liczbowe

Metryki liczbowe oceniają wartości porządkowe, takie jak liczba zmiennoprzecinkowa lub liczba całkowita. Metryki liczbowe są wyświetlane w interfejsie użytkownika na wiersz wraz ze średnią wartością przebiegu oceny.

Przykład: podobieństwo odpowiedzi

Ta metryka mierzy podobieństwo między response a expected_response przy użyciu wbudowanej biblioteki języka 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'])

metryki boolowskie

Metryki logiczne przyjmują wartość True lub False. Są one przydatne w przypadku decyzji binarnych, takich jak sprawdzanie, czy odpowiedź spełnia prostą heurystyczną. Jeśli chcesz, aby metryka miała specjalne podejście "pass/fail" w interfejsie użytkownika, zobacz metryki "pass/fail".

Przykład: Odniesienie do samego siebie modelu językowego

Ta metryka sprawdza, czy odpowiedź wspomina "LLM" i zwraca True, jeśli tak.

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

Korzystanie z custom_expected

Pole custom_expected może być używane do przekazywania wszelkich innych oczekiwanych informacji do metryki niestandardowej.

Przykład: długość odpowiedzi ograniczona

W tym przykładzie pokazano, jak wymagać, aby długość odpowiedzi mieściła się w granicach (min_length, max_length) ustawionych dla każdego przykładu. Użyj custom_expected do przechowywania dowolnych informacji na poziomie wiersza, które mają być przekazywane do metryk niestandardowych podczas tworzenia oceny.

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

Potwierdzenia dotyczące śladów

Metryki niestandardowe mogą oceniać dowolną część śladu MLflow generowanego przez agenta, w tym rozpiętości, atrybuty i dane wyjściowe.

Przykład: routing & klasyfikacji żądań

W tym przykładzie utworzono agenta, który określa, czy zapytanie użytkownika jest pytaniem, czy instrukcją i zwraca je w języku angielskim dla użytkownika. W bardziej realistycznym scenariuszu można użyć tej techniki do kierowania różnych zapytań do różnych funkcji.

Zestaw oceny zapewnia, że klasyfikator typu zapytania generuje odpowiednie wyniki dla zestawu danych wejściowych przy użyciu metryk niestandardowych, które sprawdzają ślad MLFlow.

W tym przykładzie użyto MLflow Trace.search_spans do znalezienia zakresów typu KEYWORD, który jest niestandardowym typem zakresu zdefiniowanym dla tego agenta.


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

Korzystając z tych przykładów, można zaprojektować metryki niestandardowe w celu spełnienia unikatowych potrzeb oceny.

Ocenianie wywołań narzędzi

Metryki niestandardowe będą dostarczane z tool_calls, które stanowią listę wywołań ToolCallInvocation, dającą informacje o tym, które narzędzia zostały wywołane i co zwróciły.

Przykład: potwierdzanie właściwego narzędzia

Notatka

Ten przykład nie jest możliwy do skopiowania, ponieważ nie definiuje agenta LangGraph. Zapoznaj się z dołączonym notesem , aby uzyskać w pełni uruchamialny przykład.

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

Opracowywanie metryk niestandardowych

Podczas opracowywania metryk należy szybko iterować metrykę bez konieczności wykonywania agenta za każdym razem, gdy wprowadzisz zmianę. Aby to ułatwić, użyj następującej strategii:

  1. Wygeneruj arkusz odpowiedzi przy użyciu agenta zestawu danych ewaluacyjnych. Wykonuje agenta dla każdego wpisu w zestawie oceny, oraz generuje odpowiedzi i ślady, których można użyć do bezpośredniego wywołania metryki.
  2. Zdefiniuj metryki.
  3. Wywołaj metrykę dla każdej wartości w arkuszu odpowiedzi bezpośrednio i przejrzyj definicję metryki.
  4. Gdy metryka działa zgodnie z oczekiwaniami, uruchom mlflow.evaluate() w tym samym arkuszu odpowiedzi, aby sprawdzić, czy wyniki z uruchomienia Oceny Agenta są takie, jakich oczekujesz. Kod w tym przykładzie nie używa pola model=, więc ocena używa wstępnie obliczonych odpowiedzi.
  5. Jeśli wydajność metryki jest satysfakcjonująca, włącz pole model= w mlflow.evaluate(), aby wywołać agenta interaktywnie.
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'])

Przykładowy notatnik

Poniższy przykładowy notes ilustruje kilka różnych sposobów używania metryk niestandardowych w narzędziu Mosaic AI Agent Evaluation.

Przykładowy notatnik niestandardowych metryk oceny agenta

Zdobyć notes