코드 평가기 정의하기

코드 평가기 정의하기

코드 평가기(evaluator)는 데이터셋 예제와 그에 대한 애플리케이션 출력을 받아 하나 이상의 메트릭을 반환하는 함수예요. 이 함수들은 evaluate() 또는 aevaluate() 함수에 직접 전달할 수 있답니다.

출처: 문서

팁: LangSmith UI에서 코드 평가기를 정의하려면 코드 평가기 정의 방법 (UI)을 참고하세요. 데이터셋 예제에 저장된 assertions에 대한 출력을 평가하려면 Assertions 사용을 참고하세요.

본문

코드 평가기는 데이터셋 예제와 그에 따른 애플리케이션 출력을 받아 하나 이상의 메트릭을 반환하는 함수입니다. 이 함수들은 evaluate() 또는 aevaluate() 함수에 직접 전달할 수 있습니다.

기본 예시

from langsmith import evaluate

def correct(outputs: dict, reference_outputs: dict) -> bool:
    """Check if the answer exactly matches the expected answer."""
    return outputs["answer"] == reference_outputs["answer"]

def dummy_app(inputs: dict) -> dict:
    return {"answer": "hmm i'm not sure", "reasoning": "i didn't understand the question"}

results = evaluate(
    dummy_app,
    data="dataset_name",
    evaluators=[correct]
)
import type { EvaluationResult } from "langsmith/evaluation";

const correct = async ({ outputs, referenceOutputs }: {
  outputs: Record<string, any>;
  referenceOutputs?: Record<string, any>;
}): Promise<EvaluationResult> => {
  const score = outputs?.answer === referenceOutputs?.answer;
  return { key: "correct", score };
}

평가기 인자

코드 평가기 함수는 특정 인자 이름을 가져야 합니다. 다음 인자 중 임의의 하위 집합을 받을 수 있습니다:

  • run: Run: 주어진 예제에서 애플리케이션이 생성한 전체 Run 객체.
  • example: Example: 예제 입력, 출력(있는 경우), 메타데이터(있는 경우)를 포함한 전체 데이터셋 Example.
  • inputs: dict: 데이터셋의 단일 예제에 해당하는 입력의 딕셔너리.
  • outputs: dict: 주어진 inputs에 대해 애플리케이션이 생성한 출력의 딕셔너리.
  • reference_outputs/referenceOutputs: dict: 가능한 경우 예제와 연결된 기준 출력의 딕셔너리.

대부분의 사용 사례에서는 inputs, outputs, reference_outputs만 필요합니다. runexample은 애플리케이션의 실제 입력/출력 외에 추가 트레이스 또는 예제 메타데이터가 필요할 때만 유용합니다.

JS/TS에서는 이 모든 것을 단일 객체 인자의 일부로 전달해야 합니다.

평가기 출력

코드 평가기는 다음 유형 중 하나를 반환해야 합니다:

Python 및 JS/TS

  • dict: {"score" | "value": ..., "key": ...} 형식의 dict는 메트릭 유형("score"는 숫자, "value"는 범주형)과 메트릭 이름을 커스터마이즈할 수 있게 합니다. 예를 들어 정수를 범주형 메트릭으로 기록하려면 유용합니다.

Python만

  • int | float | bool: 평균, 정렬 등이 가능한 연속 메트릭으로 해석됩니다. 함수 이름이 메트릭 이름으로 사용됩니다.
  • str: 범주형 메트릭으로 해석됩니다. 함수 이름이 메트릭 이름으로 사용됩니다.
  • list[dict]: 단일 함수로 여러 메트릭을 반환합니다.

추가 예시

langsmith>=0.2.0 필요

from langsmith import evaluate, wrappers
from langsmith.schemas import Run, Example
from openai import AsyncOpenAI
# Assumes you've installed pydantic.
from pydantic import BaseModel

# We can still pass in Run and Example objects if we'd like
def correct_old_signature(run: Run, example: Example) -> dict:
    """Check if the answer exactly matches the expected answer."""
    return {"key": "correct", "score": run.outputs["answer"] == example.outputs["answer"]}

# Just evaluate actual outputs
def concision(outputs: dict) -> int:
    """Score how concise the answer is. 1 is the most concise, 5 is the least concise."""
    return min(len(outputs["answer"]) // 1000, 4) + 1

# Use an LLM-as-a-judge
oai_client = wrappers.wrap_openai(AsyncOpenAI())

async def valid_reasoning(inputs: dict, outputs: dict) -> bool:
    """Use an LLM to judge if the reasoning and the answer are consistent."""
    instructions = """
Given the following question, answer, and reasoning, determine if the reasoning for the
answer is logically valid and consistent with question and the answer."""

    class Response(BaseModel):
        reasoning_is_valid: bool

    msg = f"Question: {inputs['question']}\nAnswer: {outputs['answer']}\nReasoning: {outputs['reasoning']}"
    response = await oai_client.beta.chat.completions.parse(
        model="gpt-5.4-mini",
        messages=[{"role": "system", "content": instructions,}, {"role": "user", "content": msg}],
        response_format=Response
    )
    return response.choices[0].message.parsed.reasoning_is_valid

def dummy_app(inputs: dict) -> dict:
    return {"answer": "hmm i'm not sure", "reasoning": "i didn't understand the question"}

results = evaluate(
    dummy_app,
    data="dataset_name",
    evaluators=[correct_old_signature, concision, valid_reasoning]
)
import { Client } from "langsmith";
import { evaluate } from "langsmith/evaluation";
import { Run, Example } from "langsmith/schemas";
import OpenAI from "openai";

// Type definitions
interface AppInputs {
    question: string;
}

interface AppOutputs {
    answer: string;
    reasoning: string;
}

interface Response {
    reasoning_is_valid: boolean;
}

// Old signature evaluator
function correctOldSignature(run: Run, example: Example) {
    return {
        key: "correct",
        score: run.outputs?.["answer"] === example.outputs?.["answer"],
    };
}

// Output-only evaluator
function concision({ outputs }: { outputs: AppOutputs }) {
    return {
        key: "concision",
        score: Math.min(Math.floor(outputs.answer.length / 1000), 4) + 1,
    };
}

// LLM-as-judge evaluator
const openai = new OpenAI();

async function validReasoning({
    inputs,
    outputs
}: {
    inputs: AppInputs;
    outputs: AppOutputs;
}) {
    const instructions = `\
  Given the following question, answer, and reasoning, determine if the reasoning for the \
  answer is logically valid and consistent with question and the answer.`;

    const msg = `Question: ${inputs.question}
Answer: ${outputs.answer}
Reasoning: ${outputs.reasoning}`;

    const response = await openai.chat.completions.create({
        model: "gpt-4",
        messages: [
            { role: "system", content: instructions },
            { role: "user", content: msg }
        ],
        response_format: { type: "json_object" },
        functions: [{
            name: "parse_response",
            parameters: {
                type: "object",
                properties: {
                    reasoning_is_valid: {
                        type: "boolean",
                        description: "Whether the reasoning is valid"
                    }
                },
                required: ["reasoning_is_valid"]
            }
        }]
    });

    const parsed = JSON.parse(response.choices[0].message.content ?? "{}") as Response;
    return {
        key: "valid_reasoning",
        score: parsed.reasoning_is_valid ? 1 : 0
    };
}

// Example application
function dummyApp(inputs: AppInputs): AppOutputs {
    return {
        answer: "hmm i'm not sure",
        reasoning: "i didn't understand the question"
    };
}

const results = await evaluate(dummyApp, {
    data: "dataset_name",
    evaluators: [correctOldSignature, concision, validReasoning],
    client: new Client()
});

관련 문서

더 알아보기