Grader

Grader (Grader)

Grader는 모델의 성능을 참조 답변과 비교해 평가하는 방법이에요. graders API를 사용해 grader를 테스트하고, 결과를 실험하며, 원하는 결과를 얻도록 fine-tuning이나 평가 프레임워크를 개선할 수 있어요.

출처: 문서

본문

참고로 OpenAI는 graders를 evals·fine-tuning 워크플로의 일부로 단계적으로 폐지하고 있어요. 현재 전환 일정은 폐지 페이지를 확인하세요.

개요

Grader를 사용하면 참조 답변을 해당 모델 생성 답변과 비교하고 0에서 1 범위의 점수를 반환할 수 있어요. 이진 0 또는 1 대신 부분 점수를 주는 것이 때로 유용해요.

Grader는 JSON 형식으로 지정하고 여러 유형이 있어요.

강화 fine-tuning에서는 multigrader 객체로 grader를 중첩·결합할 수 있어요.

이 가이드로 각 grader 유형을 배우고 시작 예시를 확인하세요. grader를 만들고 강화 fine-tuning을 시작하려면 RFT 가이드를, evals로 시작하려면 Evals 가이드를 참고하세요.

템플릿

특정 grader의 입력은 같은 구성으로 여러 예시를 채점하기 위해 템플릿 구문을 사용해요. {{ }} 이중 중괄호가 있는 문자열은 변수 값으로 치환돼요.

{{}} 안의 각 입력은 {{ namespace.variable }} 형식의 _namespace_과 _variable_을 포함해야 해요. 지원되는 유일한 namespace 값은 item과 sample이에요.

모든 중첩 변수는 JSON path와 유사한 구문으로 접근할 수 있어요.

Item namespace

item namespace는 evals에서는 입력 데이터 소스의 변수, fine-tuning에서는 각 데이터셋 항목의 변수로 채워져요. 예를 들어 행에 다음이 포함되면.

{
  "reference_answer": "..."
}

grader 안에서 {{ item.reference_answer }}로 쓸 수 있어요.

Sample namespace

sample namespace는 evals 중 모델 샘플링 단계나 fine-tuning 단계의 변수로 채워져요. 포함된 변수는:

  • output_text, 모델 출력 내용을 문자열로.
  • output_json, 모델 출력 내용을 JSON 객체로(샘플에 response_format이 포함된 경우에만).
  • output_tools, chat completions API의 출력 도구 호출과 같은 구조의 모델 출력 tool_calls.
  • choices, chat completions API의 출력 선택과 같은 구조의 출력 선택.
  • output_audio, Base64 인코딩 data와 transcript를 포함하는 모델 오디오 출력 객체.

예를 들어 모델 출력 내용을 문자열로 접근하려면 grader 안에서 {{ sample.output_text }}를 쓸 수 있어요.

도구 호출 채점 세부 사항

도구 호출 동작을 개선하도록 모델을 학습할 때는 sample.output_tools 변수로 작동하는 grader를 작성해야 해요. 이 변수의 내용은 response.choices[0].message.tool_calls(함수 호출 문서 참고)의 내용과 같을 거예요.

도구 호출을 채점하는 흔한 방법은 두 개의 grader를 쓰는 것이에요. 하나는 호출되는 도구의 이름을, 다른 하나는 호출된 함수의 인자를 검사해요. 이를 수행하는 grader 예시는 아래와 같아요.

{
  "type": "multi",
  "graders": {
    "function_name": {
      "name": "function_name",
      "type": "string_check",
      "input": "get_acceptors",
      "reference": "{{sample.output_tools[0].function.name}}",
      "operation": "eq"
    },
    "arguments": {
      "name": "arguments",
      "type": "string_check",
      "input": "{\"smiles\": \"{{item.smiles}}\"}",
      "reference": "{{sample.output_tools[0].function.arguments}}",
      "operation": "eq"
    }
  },
  "calculate_output": "0.5 * function_name + 0.5 * arguments"
}

이것은 두 개의 단순 string_check grader를 결합한 multi grader예요. 첫 번째는 sample.output_tools[0].function.name 변수로 호출된 도구의 이름을, 두 번째는 sample.output_tools[0].function.arguments 변수로 호출된 함수의 인자를 검사해요. calculate_output 필드는 두 점수를 단일 점수로 결합하는 데 쓰여요.

arguments grader는 함수 인자가 미묘하게 잘못된 경우(예: 부동소수점 1.0 대신 1 제출, 주(state) 이름을 풀어 쓰지 않고 약어로 제시) 모델을 과소 평가하기 쉬워요. 이를 피하려면 string_check 대신 text_similarity grader를, 또는 LLM이 의미 유사성을 검사하도록 score_model grader를 사용할 수 있어요.

String check grader

기본 문자열 연산으로 0 또는 1을 반환하는 데 쓰세요. String check grader는 명확한 pass/fail 답변(예: 올바른 도시 이름, 예/아니오 답변, 올바른 정보를 포함하거나 시작하는 답변)을 채점하는 데 좋아요.

{
    "type": "string_check",
    "name": string,
    "operation": "eq" | "ne" | "like" | "ilike",
    "input": string,
    "reference": string,
}

string-check-grader가 지원하는 연산:

  • eq: 입력이 참조와 일치하면(대소문자 구분) 1, 아니면 0 반환
  • neq: 입력이 참조와 일치하지 않으면(대소문자 구분) 1, 아니면 0 반환
  • like: 입력이 참조를 포함하면(대소문자 구분) 1, 아니면 0 반환
  • ilike: 입력이 참조를 포함하면(대소문자 구분 안 함) 1, 아니면 0 반환

Text similarity grader

모델 생성 출력이 참조에 얼마나 가까운지 다양한 평가 프레임워크로 채점할 때 text similarity grader를 쓰세요.

이것은 개방형 텍스트 응답에 유용해요. 예를 들어 데이터셋에 전문가의 문단 형태 참조 답변이 있으면, 모델 생성 답변이 그 내용에 수치적으로 얼마나 가까운지 보는 것이 도움이 돼요.

{
    "type": "text_similarity",
    "name": string,
    "input": string,
    "reference": string,
    "pass_threshold": number,
    "evaluation_metric": "fuzzy_match" | "bleu" | "gleu" | "meteor" | "cosine" | "rouge_1" | "rouge_2" | "rouge_3" | "rouge_4" | "rouge_5" | "rouge_l"
}

string-similarity-grader가 지원하는 연산:

  • fuzzy_match: rapidfuzz를 이용한 입력·참조 간 퍼지 문자열 매치
  • bleu: 입력·참조 간 BLEU 점수 계산
  • gleu: 입력·참조 간 Google BLEU 점수 계산
  • meteor: 입력·참조 간 METEOR 점수 계산
  • cosine: text-embedding-3-large를 사용한 임베디드 입력·참조 간 Cosine 유사성 계산. evals에만 사용 가능.
  • rouge-*: 입력·참조 간 ROUGE 점수 계산

모델 grader

일반적으로 모델 grader를 쓴다는 것은 fine-tuning하는 모델의 출력을 채점하도록 별도의 모델을 프롬프팅한다는 뜻이에요. 두 모델이 협력해 강화 fine-tuning을 수행해요. _grader 모델_이 _training 모델_을 평가해요.

Score model grader

Score model grader는 입력을 받아 프롬프트를 기반으로 주어진 범위 내의 숫자 점수를 반환해요.

{
    "type": "score_model",
    "name": string,
    "input": Message[],
    "model": string,
    "pass_threshold": number,
    "range": number[],
    "sampling_params": {
        "seed": number,
        "top_p": number,
        "temperature": number,
        "max_completions_tokens": number,
        "reasoning_effort": "minimal" | "low" | "medium" | "high"
    }
}

각 메시지는 다음 형태예요.

{
    "role": "system" | "developer" | "user" | "assistant",
    "content": str
}

Score model grader를 사용하려면 입력은 각각 role과 content를 가진 채팅 메시지 목록이에요. grader의 출력은 주어진 range로 잘리고, 숫자가 아닌 출력은 기본적으로 0이 돼요. 각 메시지 안에서 다른 일반 grader와 같은 템플릿을 사용해 ground truth나 모델 샘플을 참조할 수 있어요.

실행 가능한 전체 코드 샘플:

import os
import requests

# get the API key from environment
api_key = os.environ["OPENAI_API_KEY"]
headers = {"Authorization": f"Bearer {api_key}"}

# Define a score-model grader.
grader = {
    "type": "score_model",
    "name": "my_score_model",
    "input": [
        {
            "role": "system",
            "content": "You are an expert grader. If the reference and model answer are exact matches, output a score of 1. If they are somewhat similar in meaning, output a score in 0.5. Otherwise, give a score of 0.",
        },
        {
            "role": "user",
            "content": "Reference: {{ item.reference_answer }}. Model answer: {{ sample.output_text }}",
        },
    ],
    "pass_threshold": 0.5,
    "model": "o4-mini-2025-04-16",
    "range": [0, 1],
    "sampling_params": {
        "max_completions_tokens": 32768,
        "top_p": 1,
        "reasoning_effort": "medium",
    },
}

# validate the grader
payload = {"grader": grader}
response = requests.post(
    "https://api.openai.com/v1/fine_tuning/alpha/graders/validate",
    json=payload,
    headers=headers,
)
print("validate response:", response.text)

# run the grader with a test reference and sample
payload = {"grader": grader, "item": {"reference_answer": 1.0}, "model_sample": "0.9"}
response = requests.post(
    "https://api.openai.com/v1/fine_tuning/alpha/graders/run",
    json=payload,
    headers=headers,
)
print("run response:", response.text)

(Ruby 예시도 같은 score_model grader를 validate/run으로 실행하는 동일한 패턴입니다.)

Score model grader 출력

내부적으로 score_model grader는 제공된 프롬프트와 샘플링 파라미터로 요청한 모델을 쿼리하고 특정 응답 형식을 요청해요. 사용되는 응답 형식은 아래와 같아요.

{
  "result": float,
  "steps": ReasoningStep[],
}

각 추론 단계는 다음 형태예요.

{
    "description": string,
    "conclusion": string
}

이 형식은 모델에 숫자 result(쿼리에 대한 보상 값)뿐 아니라 점수 뒤의 추론을 생각할 공간을 제공해요. grader 프롬프트를 쓸 때 이 두 필드를 이름으로 명시적으로 참조하는 것이 유용할 수 있어요 (예: "분자의 화학 결합 유형에 대한 추론을 reasoning step의 conclusion에 포함하세요" 또는 "입력이 조건 X를 만족하지 않으면 result 필드에 −1.0을 반환하세요").

모델 grader 제약

  • model 파라미터에 다음 모델만 지원돼요
    • gpt-4o-2024-08-06
    • gpt-4o-mini-2024-07-18
    • gpt-4.1-2025-04-14
    • gpt-4.1-mini-2025-04-14
    • gpt-4.1-nano-2025-04-14
    • o1-2024-12-17
    • o3-mini-2025-01-31
    • o3-2025-04-16
    • o4-mini-2025-04-16
  • reasoning 모델에는 temperature 변경이 지원되지 않아요.
  • non-reasoning 모델에는 reasoning_effort가 지원되지 않아요.

Grader 프롬프트 작성법

grader 프롬프트 작성은 반복적인 과정이에요. 모델 grader 프롬프트를 반복하는 최선의 방법은 모델 grader eval을 만드는 것이에요. 이를 위해 필요한 것:

  1. 작업 프롬프트: 원하는 작업에 대한 매우 상세한 프롬프트를 단계별 지시와 많은 구체적 예시와 함께 작성.
  2. 모델이나 인간 전문가가 생성한 답변: 모델과 신뢰할 수 있는 인간 전문가의 고품질 답변 예시를 많이 제공.
  3. 그 답변에 대한 대응 ground truth 점수: 좋은 점수가 어떤 모습인지 확립. 예를 들어 인간 전문가 점수는 1이어야 함.

그런 다음 모델 grader가 다른 품질 수준의 답변을 얼마나 효과적으로 구분하는지 자동 평가할 수 있어요. 시간이 지나면서 발견하고 프롬프트 변경으로 패치하면서 모델 grader eval에 엣지 케이스를 추가하세요.

예를 들어 인간 전문가가 어떤 답변이 가장 좋은지 안다고 가정해요.

answer_1 > answer_2 > answer_3

모델 grader의 답변이 그와 일치하는지 검증하세요.

model_grader(answer_1, reference_answer) > model_grader(answer_2, reference_answer) > model_grader(answer_3, reference_answer)

Grader 해킹

학습되는 모델은 때로 모델 grader의 약점을 이용하는 법을 배우는데, "grader hacking" 또는 "reward hacking"라고도 불러요. 모델 grader eval과 전문가 인간 eval에서 모델 성능을 확인해 이를 감지할 수 있어요. grader를 해킹한 모델은 모델 grader eval에서 높게, 전문가 인간 평가에서는 낮게 점수를 받아요. 시간이 지나면서 학습 중 이를 더 쉽게 감지하도록 API의 관찰 가능성을 개선할 계획이에요.

Python grader

이 grader를 사용하면 임의의 Python 코드를 실행해 모델 출력을 채점할 수 있어요. grader는 두 인자를 받아 float 값을 출력하는 grade 함수가 있어야 해요. 다른 결과(예외, 유효하지 않은 float 값 등)는 유효하지 않은 것으로 표시되고 0 점수를 반환해요.

{
  "type": "python",
  "source": "def grade(sample, item):\n    return 1.0",
  "image_tag": "2025-05-08"
}

Python 소스 코드는 정확히 두 인자를 받아 float 값을 점수로 반환하는 grade 함수를 포함해야 해요.

from typing import Any


def grade(sample: dict[str, Any], item: dict[str, Any]) -> float:
    # your logic here
    return 1.0

채점 함수에 제공되는 첫 번째 인자는 학습 중 모델 출력으로 채워진 딕셔너리예요. output_json은 출력이 response_format을 사용할 때만 채워져요.

{
    "choices": [...],
    "output_text": "...",
    "output_json": {},
    "output_tools": [...],
    "output_audio": {}
}

제공되는 두 번째 인자는 입력 채점 컨텍스트로 채워진 딕셔너리예요. evals는 데이터 소스의 키를, fine-tuning은 각 학습 데이터 행의 키를 포함해요.

{
    "reference_answer": "...",
    "my_key": {...}
}

작동 예시:

import os
import requests

# get the API key from environment
api_key = os.environ["OPENAI_API_KEY"]
headers = {"Authorization": f"Bearer {api_key}"}

grading_function = """
from rapidfuzz import fuzz, utils

def grade(sample, item) -> float:
    output_text = sample["output_text"]
    reference_answer = item["reference_answer"]
    return fuzz.WRatio(output_text, reference_answer, processor=utils.default_process) / 100.0
"""

# Define a Python grader.
grader = {"type": "python", "source": grading_function}

# validate the grader
payload = {"grader": grader}
response = requests.post(
    "https://api.openai.com/v1/fine_tuning/alpha/graders/validate",
    json=payload,
    headers=headers,
)
print("validate request_id:", response.headers["x-request-id"])
print("validate response:", response.text)

# run the grader with a test reference and sample
payload = {
    "grader": grader,
    "item": {"reference_answer": "fuzzy wuzzy had no hair"},
    "model_sample": "fuzzy wuzzy was a bear",
}
response = requests.post(
    "https://api.openai.com/v1/fine_tuning/alpha/graders/run",
    json=payload,
    headers=headers,
)
print("run request_id:", response.headers["x-request-id"])
print("run response:", response.text)

(Ruby 예시는 grader.py 파일에서 grade 함수를 읽어 같은 validate/run 패턴을 수행합니다.)

팁: grade 함수를 문자열에 직접 넣고 싶지 않다면 importlib와 inspect로 Python 파일에서 로드할 수도 있어요. 예를 들어 grader 함수가 grader.py 파일에 있으면:

import importlib
import inspect

grader_module = importlib.import_module("grader")
grader = {"type": "python", "source": inspect.getsource(grader_module)}

이렇게 하면 grader.py 파일의 전체 소스 코드가 자동으로 grader로 사용돼요. 더 긴 grader에 유용해요.

기술적 제약

  • 업로드한 코드는 256kB 미만이어야 하고 네트워크 접근이 없어요.
  • 채점 실행 자체는 2분으로 제한돼요.
  • 런타임에 2Gb 메모리와 1Gb 디스크 공간 한도가 주어져요.
  • CPU 코어 2개 한도가 있고, 이 이상 사용하면 스로틀링이 돼요.

2025-05-08 이미지 태그 실행 시 다음 타사 패키지가 사용 가능해요.

numpy==2.2.4
scipy==1.15.2
sympy==1.13.3
pandas==2.2.3
rapidfuzz==3.10.1
scikit-learn==1.6.1
rouge-score==0.1.2
deepdiff==8.4.2
jsonschema==4.23.0
pydantic==2.10.6
pyyaml==6.0.2
nltk==3.9.1
sqlparse==0.5.3
rdkit==2024.9.6
scikit-bio==0.6.3
ast-grep-py==0.36.2

또한 다음 NLTK 말뭉치가 사용 가능해요.

punkt
stopwords
wordnet
omw-1.4
names

결합 Grader (Combined graders)

현재 이 grader는 Reinforcement fine-tuning에만 사용돼요.

multigrader 객체는 여러 grader의 출력을 결합해 단일 점수를 만들어요. 결합 grader는 다른 grader 객체의 필드에 걸쳐 점수를 계산하고 그 하위 점수를 전체 점수로 바꿔요. 이는 올바른 답이 여러 가지가 참이어야 할 때(예: 텍스트가 유사함 그리고 답변이 특정 문자열을 포함함) 유용해요.

예를 들어 모델이 다음 두 필드가 있는 JSON을 출력하길 원한다고 해요.

{
  "name": "John Doe",
  "email": "[email protected]"
}

grader가 두 필드를 비교한 뒤 그것들의 평균을 내길 원할 거예요.

여러 grader를 객체 grader로 결합하고 각 필드를 기반으로 출력 점수를 계산하는 공식을 정의해서 할 수 있어요.

{
  "type": "multi",
  "graders": {
    "name": {
      "name": "name_grader",
      "type": "text_similarity",
      "input": "{{sample.output_json.name}}",
      "reference": "{{item.name}}",
      "evaluation_metric": "fuzzy_match",
      "pass_threshold": 0.9
    },
    "email": {
      "name": "email_grader",
      "type": "string_check",
      "input": "{{sample.output_json.email}}",
      "reference": "{{item.email}}",
      "operation": "eq"
    }
  },
  "calculate_output": "(name + email) / 2"
}

이 예시에서 모델이 이메일을 정확히 맞추는 것이 중요하고(string_check는 0 또는 1을 반환), 이름의 일부 오타는 허용돼요(text_similarity는 0에서 1 범위를 반환). 이메일이 틀린 샘플은 0-0.5로, 이메일이 맞는 샘플은 0.5-1.0으로 점수가 나요.

multigrader 안에 다른 multigrader를 중첩할 수는 없어요.

calculate output 필드는 입력 graders의 키를 가능한 변수로 가지며 다음 기능이 지원돼요.

연산자

  • + (덧셈)
  • - (뺄셈)
  • * (곱셈)
  • / (나눗셈)
  • ^ (거듭제곱)

함수

  • min
  • max
  • abs
  • floor
  • ceil
  • exp
  • sqrt
  • log

제한 사항과 팁

Grader 설계·생성은 반복적인 과정이에요. 작게 시작하고, 실험하고, 더 나은 결과를 얻기 위해 계속 변경하세요.

설계 팁

Grader에서 최대 가치를 얻으려면 다음 설계 원칙을 사용하세요.

  • pass/fail 스탬프가 아니라 매끄러운 점수를 만드세요. 답변이 개선됨에 따라 점수가 점진적으로 움직이면 옵티마이저가 어떤 변경이 중요한지 보게 돼요.
  • 보상 해킹을 방어하세요. 모델이 실제 기술 없이 높은 점수를 얻는 지름길을 찾을 때 발생해요. 채점 시스템에 허점을 만들기 어렵게 하세요.
  • 편향된 데이터를 피하세요. 하나의 라벨이 대부분 나타나는 데이터셋은 모델이 그 라벨을 추측하게 유도해요. 집합을 균형 잡거나 희귀한 경우를 가중해 모델이 생각하게 하세요.
  • 코드가 부족할 때 LLM-as-a-judge를 쓰세요. 풍부한 개방형 답변에는 다른 언어 모델에 채점을 요청하세요. LLM grader를 만들 때는 여러 후보 응답과 ground truth를 LLM 판사에 돌려 채점이 안정적이고 선호와 일치하는지 확인하세요. 프롬프트에 훌륭한·공정한·나쁜 답변의 few-shot 예시를 제공하세요.

더 알아보기 (Learn more)

관련 문서: 강화 fine-tuning (RFT), Evals, 모델 최적화 가이드를 함께 보면 좋아요.