LLM-as-a-judge 평가자 정의하는 방법

LLM-as-a-judge 평가자 정의하는 방법

LLM 애플리케이션은 대화형 텍스트를 생성하며 단일 정답이 없는 경우가 많아 평가하기 어려울 수 있어요.

이 가이드에서는 LangSmith SDK를 사용해 오프라인 평가를 위한 LLM-as-a-judge 평가자를 정의하는 방법을 보여드립니다.

팁: 빠른 시작을 위해서는 바로 사용할 수 있는 LLM-as-a-judge 평가자를 제공하는 openevals를 사용하세요.

출처: 문서

본문

나만의 LLM-as-a-judge 평가자 만들기

평가자 로직을 완전히 제어하려면 나만의 LLM-as-a-judge 평가자를 만들고 LangSmith SDK(Python / TypeScript)로 실행하세요.

langsmith>=0.2.0 필요

LLM-as-a-judge 평가자는 세 가지 핵심 구성요소로 이루어집니다:

  1. 평가자 함수(Evaluator function): 예제 입력과 애플리케이션 출력을 받아 LLM으로 품질을 채점하는 함수. boolean, number, string, 또는 점수 정보가 있는 dictionary를 반환해야 합니다.
  2. 대상 함수(Target function): 평가되는 애플리케이션 로직 (관측성을 위해 @traceable로 래핑).
  3. 데이터셋과 평가: 테스트 예제의 데이터셋과, 각 예제에서 대상 함수를 실행하고 평가자를 적용하는 evaluate() 함수.

예제

from langsmith import evaluate, traceable, wrappers, Client
from openai import OpenAI
from pydantic import BaseModel

# Wrap the OpenAI client to automatically trace all LLM calls
oai_client = wrappers.wrap_openai(OpenAI())

# 1. Define your evaluator function
# This function receives the inputs and outputs from each test example
def valid_reasoning(inputs: dict, outputs: dict) -> bool:
    """Use an LLM to judge if the reasoning and the answer are consistent."""
    # Define the evaluation criteria
    instructions = """
Given the following question, answer, and reasoning, determine if the reasoning
for the answer is logically valid and consistent with the question and the answer."""

    # Use structured output to get a boolean score
    class Response(BaseModel):
        reasoning_is_valid: bool

    # Construct the prompt with the actual inputs and outputs
    msg = f"Question: {inputs['question']}\nAnswer: {outputs['answer']}\nReasoning: {outputs['reasoning']}"

    # Call the LLM to judge the output
    response = oai_client.beta.chat.completions.parse(
        model="gpt-4o",
        messages=[{"role": "system", "content": instructions}, {"role": "user", "content": msg}],
        response_format=Response
    )

    # Return the boolean score
    return response.choices[0].message.parsed.reasoning_is_valid

# 2. Define your target function (the application being evaluated)
# The @traceable decorator logs traces to LangSmith for debugging
@traceable
def dummy_app(inputs: dict) -> dict:
    return {"answer": "hmm i'm not sure", "reasoning": "i didn't understand the question"}

# 3. Create a dataset with test examples
ls_client = Client()
dataset = ls_client.create_dataset("big questions")
examples = [
    {"inputs": {"question": "how will the universe end"}},
    {"inputs": {"question": "are we alone"}},
]
ls_client.create_examples(dataset_id=dataset.id, examples=examples)

# 4. Run the evaluation
# This runs dummy_app on each example and applies the valid_reasoning evaluator
results = evaluate(
    dummy_app,              # Your application function
    data=dataset,           # Dataset to evaluate on
    evaluators=[valid_reasoning]  # List of evaluator functions
)

참조 출력 사용하기

데이터셋 예제에 참조 출력(예상 답변)이 포함되면 평가자 함수의 파라미터로 reference_outputs를 전달할 수 있습니다. LangSmith는 이 파라미터를 선언하는 모든 평가자에게 예제의 참조 출력을 자동으로 제공합니다.

from langsmith import evaluate, traceable, wrappers, Client
from openai import OpenAI
from pydantic import BaseModel

# Wrap the OpenAI client to automatically trace all LLM calls
oai_client = wrappers.wrap_openai(OpenAI())

# Define an evaluator that checks the answer against a reference answer
def matches_expected(inputs: dict, outputs: dict, reference_outputs: dict) -> bool:
    """Use an LLM to judge if the actual answer matches the expected answer."""
    instructions = """
Given a question, an expected answer, and an actual answer, determine if the
actual answer is semantically equivalent to the expected answer."""

    class Response(BaseModel):
        answers_match: bool

    msg = (
        f"Question: {inputs['question']}\n"
        f"Expected answer: {reference_outputs['answer']}\n"
        f"Actual answer: {outputs['answer']}"
    )

    response = oai_client.beta.chat.completions.parse(
        model="gpt-4o",
        messages=[{"role": "system", "content": instructions}, {"role": "user", "content": msg}],
        response_format=Response,
    )

    return response.choices[0].message.parsed.answers_match

@traceable
def my_app(inputs: dict) -> dict:
    # Your application logic here
    return {"answer": "Paris"}

# Create a dataset with reference outputs (expected answers)
ls_client = Client()
dataset = ls_client.create_dataset("geography-qa")
examples = [
    {
        "inputs": {"question": "What is the capital of France?"},
        "outputs": {"answer": "Paris"},
    },
    {
        "inputs": {"question": "What is the capital of Germany?"},
        "outputs": {"answer": "Berlin"},
    },
]
ls_client.create_examples(dataset_id=dataset.id, examples=examples)

results = evaluate(
    my_app,
    data=dataset,
    evaluators=[matches_expected]
)
import { evaluate } from "langsmith/evaluation";
import { Client } from "langsmith";
import OpenAI from "openai";
import { zodResponseFormat } from "openai/helpers/zod";
import { z } from "zod";

const oaiClient = new OpenAI();

const matchesExpected = async ({
  inputs,
  outputs,
  referenceOutputs,
}: {
  inputs: Record<string, any>;
  outputs: Record<string, any>;
  referenceOutputs?: Record<string, any>;
}): Promise<boolean> => {
  const instructions = `Given a question, an expected answer, and an actual answer, determine if the
actual answer is semantically equivalent to the expected answer.`;

  const ResponseSchema = z.object({ answers_match: z.boolean() });

  const msg = `Question: ${inputs.question}\nExpected answer: ${referenceOutputs?.answer}\nActual answer: ${outputs.answer}`;

  const response = await oaiClient.beta.chat.completions.parse({
    model: "gpt-4o",
    messages: [
      { role: "system", content: instructions },
      { role: "user", content: msg },
    ],
    response_format: zodResponseFormat(ResponseSchema, "response"),
  });

  return response.choices[0].message.parsed?.answers_match ?? false;
};

const myApp = async (inputs: Record<string, any>): Promise<Record<string, any>> => {
  // Your application logic here
  return { answer: "Paris" };
};

// Create a dataset with reference outputs (expected answers)
const lsClient = new Client();
const dataset = await lsClient.createDataset("geography-qa-ts");
await lsClient.createExamples({
  inputs: [
    { question: "What is the capital of France?" },
    { question: "What is the capital of Germany?" },
  ],
  outputs: [{ answer: "Paris" }, { answer: "Berlin" }],
  datasetId: dataset.id,
});

await evaluate(myApp, {
  data: dataset.name,
  evaluators: [matchesExpected],
});

커스텀 평가자 작성에 대한 자세한 내용은 코드 평가자 정의 방법 (SDK)를 참고하세요.

더 알아보기