Skip to main content
_복합 평가자_는 여러 평가자 점수를 단일 점수로 결합하는 방법입니다. 애플리케이션의 여러 측면을 평가하고 결과를 단일 결과로 결합하려는 경우 유용합니다.

UI를 사용하여 복합 평가자 생성

추적 프로젝트(온라인 평가용) 또는 데이터셋(오프라인 평가용)에서 복합 평가자를 생성할 수 있습니다. UI의 복합 평가자를 사용하면 구성 가능한 가중치로 여러 평가자 점수의 가중 평균 또는 가중 합계를 계산할 수 있습니다.
LangSmith UI showing an LLM call trace called ChatOpenAI with a system and human input followed by an AI Output.

1. 추적 프로젝트 또는 데이터셋으로 이동

복합 평가자 구성을 시작하려면 Tracing Projects 또는 Dataset & Experiments 탭으로 이동하여 프로젝트 또는 데이터셋을 선택합니다.
  • 추적 프로젝트 내에서: + New > Evaluator > Composite score
  • 데이터셋 내에서: + Evaluator > Composite score

2. 복합 평가자 구성

  1. 평가자 이름을 지정합니다.
  2. 집계 방법을 선택합니다. Average 또는 Sum 중 하나를 선택합니다.
    • Average: ∑(weight*score) / ∑(weight).
    • Sum: ∑(weight*score).
  3. 복합 점수에 포함하려는 피드백 키를 추가합니다.
  4. 피드백 키의 가중치를 추가합니다. 기본적으로 각 피드백 키의 가중치는 동일합니다. 가중치를 조정하여 최종 점수에서 특정 피드백 키의 중요도를 높이거나 낮춥니다.
  5. Create를 클릭하여 평가자를 저장합니다.
복합 점수의 가중치를 조정해야 하는 경우 평가자가 생성된 후에 업데이트할 수 있습니다. 결과 점수는 평가자가 구성된 모든 실행에 대해 업데이트됩니다.

3. 복합 평가자 결과 보기

복합 점수는 단일 평가자의 피드백과 유사하게 실행에 피드백으로 첨부됩니다. 보는 방법은 평가가 실행된 위치에 따라 다릅니다: 추적 프로젝트에서:
  • 복합 점수는 실행에 피드백으로 표시됩니다.
  • 복합 점수가 있거나 복합 점수가 특정 임계값을 충족하는 실행 필터링.
  • 시간 경과에 따른 복합 점수의 추세를 시각화하는 차트 생성.
데이터셋에서:
  • 실험 탭에서 복합 점수를 봅니다. 실행의 평균 복합 점수를 기준으로 실험을 필터링하고 정렬할 수도 있습니다.
  • 실험을 클릭하여 각 실행의 복합 점수를 봅니다.
구성 평가자 중 하나라도 실행에 구성되지 않은 경우 해당 실행에 대한 복합 점수는 계산되지 않습니다.

SDK로 복합 피드백 생성

이 가이드는 여러 평가자를 사용하고 사용자 정의 집계 함수로 점수를 결합하는 평가 설정에 대해 설명합니다.
langsmith>=0.4.29 필요

1. 데이터셋에서 평가자 구성

평가자를 구성하는 것부터 시작합니다. 이 예제에서 애플리케이션은 블로그 소개에서 트윗을 생성하고 세 가지 평가자(요약, 어조 및 형식)를 사용하여 출력을 평가합니다. 평가자가 구성된 자체 데이터셋이 이미 있는 경우 이 단계를 건너뛸 수 있습니다.
import os
from dotenv import load_dotenv
from openai import OpenAI
from langsmith import Client
from pydantic import BaseModel
import json

# Load environment variables from .env file
load_dotenv()

# Access environment variables
openai_api_key = os.getenv('OPENAI_API_KEY')
langsmith_api_key = os.getenv('LANGSMITH_API_KEY')
langsmith_project = os.getenv('LANGSMITH_PROJECT', 'default')


# Create a dataset. Only need to do this once.
client = Client()
oai_client = OpenAI()

examples = [
  {
    "inputs": {"blog_intro": "Today we're excited to announce the general availability of LangSmith — our purpose-built infrastructure and management layer for deploying and scaling long-running, stateful agents. Since our beta last June, nearly 400 companies have used LangSmith to deploy their agents into production. Agent deployment is the next hard hurdle for shipping reliable agents, and LangSmith dramatically lowers this barrier with: 1-click deployment to go live in minutes, 30 API endpoints for designing custom user experiences that fit any interaction pattern, Horizontal scaling to handle bursty, long-running traffic, A persistence layer to support memory, conversational history, and async collaboration with human-in-the-loop or multi-agent workflows, Native Studio, the agent IDE, for easy debugging, visibility, and iteration "},
  },
  {
    "inputs": {"blog_intro": "Klarna has reshaped global commerce with its consumer-centric, AI-powered payment and shopping solutions. With over 85 million active users and 2.5 million daily transactions on its platform, Klarna is a fintech leader that simplifies shopping while empowering consumers with smarter, more flexible financial solutions. Klarna's flagship AI Assistant is revolutionizing the shopping and payments experience. Built on LangGraph and powered by LangSmith, the AI Assistant handles tasks ranging from customer payments, to refunds, to other payment escalations. With 2.5 million conversations to date, the AI Assistant is more than just a chatbot; it's a transformative agent that performs the work equivalent of 700 full-time staff, delivering results quickly and improving company efficiency."},
  },
]

dataset = client.create_dataset(dataset_name="Blog Intros")

client.create_examples(
  dataset_id=dataset.id,
  examples=examples,
)

# Define a target function. In this case, we're using a simple function that generates a tweet from a blog intro.
def generate_tweet(inputs: dict) -> dict:
    instructions = (
      "Given the blog introduction, please generate a catchy yet professional tweet that can be used to promote the blog post on social media. Summarize the key point of the blog post in the tweet. Use emojis in a tasteful manner."
    )
    messages = [
        {"role": "system", "content": instructions},
        {"role": "user", "content": inputs["blog_intro"]},
    ]
    result = oai_client.responses.create(
        input=messages, model="gpt-5-nano"
    )
    return {"tweet": result.output_text}

# Define evaluators. In this case, we're using three evaluators: summary, formatting, and tone.
def summary(inputs: dict, outputs: dict) -> bool:
    """Judge whether the tweet is a good summary of the blog intro."""
    instructions = "Given the following text and summary, determine if the summary is a good summary of the text."

    class Response(BaseModel):
        summary: bool

    msg = f"Question: {inputs['blog_intro']}\nAnswer: {outputs['tweet']}"
    response = oai_client.responses.parse(
        model="gpt-5-nano",
        input=[{"role": "system", "content": instructions,}, {"role": "user", "content": msg}],
        text_format=Response
    )

    parsed_response = json.loads(response.output_text)
    return parsed_response["summary"]

def formatting(inputs: dict, outputs: dict) -> bool:
    """Judge whether the tweet is formatted for easy human readability."""
    instructions = "Given the following text, determine if it is formatted well so that a human can easily read it. Pay particular attention to spacing and punctuation."

    class Response(BaseModel):
        formatting: bool

    msg = f"{outputs['tweet']}"
    response = oai_client.responses.parse(
        model="gpt-5-nano",
        input=[{"role": "system", "content": instructions,}, {"role": "user", "content": msg}],
        text_format=Response
    )

    parsed_response = json.loads(response.output_text)
    return parsed_response["formatting"]

def tone(inputs: dict, outputs: dict) -> bool:
    """Judge whether the tweet's tone is informative, friendly, and engaging."""
    instructions = "Given the following text, determine if the tweet is informative, yet friendly and engaging."

    class Response(BaseModel):
        tone: bool

    msg = f"{outputs['tweet']}"
    response = oai_client.responses.parse(
        model="gpt-5-nano",
        input=[{"role": "system", "content": instructions,}, {"role": "user", "content": msg}],
        text_format=Response
    )
    parsed_response = json.loads(response.output_text)
    return parsed_response["tone"]

# Calling evaluate() with the dataset, target function, and evaluators.
results = client.evaluate(
    generate_tweet,
    data=dataset.name,
    evaluators=[summary, tone, formatting],
    experiment_prefix="gpt-5-nano",
)

# Get the experiment name to be used in client.get_experiment_results() in the next section
experiment_name = results.experiment_name

2. 복합 피드백 생성

사용자 정의 함수를 사용하여 개별 평가자 점수를 집계하는 복합 피드백을 생성합니다. 이 예제에서는 개별 평가자 점수의 가중 평균을 사용합니다.
from typing import Dict
import math
from langsmith import Client
from dotenv import load_dotenv

load_dotenv()

# TODO: Replace with your experiment name. Can be found in UI or from the above client.evaluate() result
YOUR_EXPERIMENT_NAME = "placeholder_experiment_name"

# Set weights for the individual evaluator scores
DEFAULT_WEIGHTS: Dict[str, float] = {
    "summary": 0.7,
    "tone": 0.2,
    "formatting": 0.1,
}
WEIGHTED_FEEDBACK_NAME = "weighted_summary"

# Pull experiment results
client = Client()
results = client.get_experiment_results(
    name=YOUR_EXPERIMENT_NAME,
)

# Calculate weighted score for each run
def calculate_weighted_score(feedback_stats: dict) -> float:
    if not feedback_stats:
        return float("nan")

    # Check if all required metrics are present and have data
    required_metrics = set(DEFAULT_WEIGHTS.keys())
    available_metrics = set(feedback_stats.keys())

    if not required_metrics.issubset(available_metrics):
        return float("nan")

    # Calculate weighted score
    total_score = 0.0
    for metric, weight in DEFAULT_WEIGHTS.items():
        metric_data = feedback_stats[metric]
        if metric_data.get("n", 0) > 0 and "avg" in metric_data:
            total_score += metric_data["avg"] * weight
        else:
            return float("nan")

    return total_score

# Process each run and write feedback
# Note that experiment results need to finish processing before this should be called.
for example_with_runs in results["examples_with_runs"]:
    for run in example_with_runs.runs:
        if run.feedback_stats:
            score = calculate_weighted_score(run.feedback_stats)
            if not math.isnan(score):
                client.create_feedback(
                    run_id=run.id,
                    key=WEIGHTED_FEEDBACK_NAME,
                    score=float(score)
                )

Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I