에이전트 평가하기

에이전트 평가하기 (Agent Evals)

평가(evals)는 에이전트가 만들어내는 실행 궤적(trajectory), 즉 메시지와 도구 호출의 연속을 평가해서 에이전트가 얼마나 잘 동작하는지 측정해요. 기본적인 정확성을 검증하는 통합 테스트와 달리, evals는 참조값이나 루브릭(rubric)에 비추어 에이전트의 동작에 점수를 매깁니다. 그래서 프롬프트·도구·모델을 바꿨을 때 회귀(regression)가 생기는지 잡아내는 데 특히 유용해요.

출처: LangChain 공식 문서 — Agent Evals

이밸류에이터란 무엇인가 (What is an evaluator)

이밸류에이터는 에이전트의 출력(그리고 선택적으로 참조 출력)을 받아 점수를 반환하는 함수예요.

def evaluator(*, outputs: dict, reference_outputs: dict):
    output_messages = outputs["messages"]
    reference_messages = reference_outputs["messages"]
    score = compare_messages(output_messages, reference_messages)
    return {"key": "evaluator_score", "score": score}

agentevals 패키지는 에이전트 궤적을 위한 사전 구축 이밸류에이터를 제공합니다. 궤적 매칭(trajectory match)(결정적 비교)이나 LLM 판정(LLM judge)(정성적 평가) 방식으로 평가할 수 있어요.

접근법 언제 사용할까
궤적 매칭 (Trajectory match) 기대하는 도구 호출을 아는 상황. 빠르고 결정적이며 비용이 들지 않는 검사가 필요할 때
LLM-as-judge 엄격한 기대 없이 전반적인 품질과 추론을 평가하고 싶을 때

AgentEvals 설치하기 (Install AgentEvals)

pip install -U agentevals
uv add agentevals

아니면 AgentEvals 저장소를 직접 클론해도 됩니다.

궤적 매칭 이밸류에이터 (Trajectory match evaluator)

AgentEvals는 에이전트의 궤적을 참조값과 대조하는 create_trajectory_match_evaluator 함수를 제공해요. 네 가지 모드가 있습니다.

모드 설명 사용 사례
strict 메시지 구조와 도구 호출이 같은 순서로 정확히 일치(메시지 내용은 달라도 됨) 특정 순서를 강제할 때 (예: 권한 부여 전 정책 조회)
unordered 참조값과 같은 메시지 구조·도구 호출이지만, 도구 호출 순서는 무관 순서가 중요하지 않은 정보 검색 검증
subset 에이전트가 참조값의 도구만 호출(추가 없음) 에이전트가 기대 범위를 벗어나지 않게 확인
superset 에이전트가 참조값의 도구를 최소한 호출(추가 허용) 최소한의 필수 동작이 수행됐는지 확인

아래 예시들은 공통 설정을 공유해요. get_weather 도구를 가진 에이전트입니다.

from langchain.agents import create_agent
from langchain.tools import tool
from langchain.messages import HumanMessage, AIMessage, ToolMessage
from agentevals.trajectory.match import create_trajectory_match_evaluator

@tool
def get_weather(city: str):
    """Get weather information for a city."""
    return f"It's 75 degrees and sunny in {city}."

agent = create_agent("claude-sonnet-4-6", tools=[get_weather])

Strict 매칭

strict 모드는 궤적이 같은 도구 호출로 같은 순서의 동일한 메시지를 포함하도록 보장해요. 단, 메시지 내용의 차이는 허용됩니다. 정책 조회를 거친 뒤에만 권한 부여를 요구하는 것처럼 특정 동작 순서를 강제할 때 유용하죠.

evaluator = create_trajectory_match_evaluator(
    trajectory_match_mode="strict",
)

def test_weather_tool_called_strict():
    result = agent.invoke({
        "messages": [HumanMessage(content="What's the weather in San Francisco?")]
    })

    reference_trajectory = [
        HumanMessage(content="What's the weather in San Francisco?"),
        AIMessage(content="", tool_calls=[
            {"id": "call_1", "name": "get_weather", "args": {"city": "San Francisco"}}
        ]),
        ToolMessage(content="It's 75 degrees and sunny in San Francisco.", tool_call_id="call_1"),
        AIMessage(content="The weather in San Francisco is 75 degrees and sunny."),
    ]

    evaluation = evaluator(
        outputs=result["messages"],
        reference_outputs=reference_trajectory
    )
    # {
    #     'key': 'trajectory_strict_match',
    #     'score': True,
    #     'comment': None,
    # }
    assert evaluation["score"] is True

Unordered 매칭

unordered 모드는 같은 도구 호출을 어떤 순서로든 허용합니다. 특정 정보를 검색했는지는 확인하되 순서는 신경 쓰지 않을 때 유용해요. 예를 들어 어떤 도시의 날씨와 이벤트를 서로 다른 도구 호출로 확인하는 에이전트가 있죠.

@tool
def get_events(city: str):
    """Get events happening in a city."""
    return f"Concert at the park in {city} tonight."

agent = create_agent("claude-sonnet-4-6", tools=[get_weather, get_events])

evaluator = create_trajectory_match_evaluator(
    trajectory_match_mode="unordered",
)

def test_multiple_tools_any_order():
    result = agent.invoke({
        "messages": [HumanMessage(content="What's happening in SF today?")]
    })

    reference_trajectory = [
        HumanMessage(content="What's happening in SF today?"),
        AIMessage(content="", tool_calls=[
            {"id": "call_1", "name": "get_events", "args": {"city": "SF"}},
            {"id": "call_2", "name": "get_weather", "args": {"city": "SF"}},
        ]),
        ToolMessage(content="Concert at the park in SF tonight.", tool_call_id="call_1"),
        ToolMessage(content="It's 75 degrees and sunny in SF.", tool_call_id="call_2"),
        AIMessage(content="Today in SF: 75 degrees and sunny with a concert at the park tonight."),
    ]

    evaluation = evaluator(
        outputs=result["messages"],
        reference_outputs=reference_trajectory,
    )
    assert evaluation["score"] is True

Subset과 Superset 매칭

supersetsubset 모드는 부분 궤적을 매칭합니다. superset 모드는 에이전트가 참조 궤적의 도구를 최소한 호출했는지 검증하며 추가 호출을 허용해요. subset 모드는 에이전트가 참조값에 없는 도구를 호출하지 않았는지 보장합니다.

@tool
def get_detailed_forecast(city: str):
    """Get detailed weather forecast for a city."""
    return f"Detailed forecast for {city}: sunny all week."

agent = create_agent("claude-sonnet-4-6", tools=[get_weather, get_detailed_forecast])

evaluator = create_trajectory_match_evaluator(
    trajectory_match_mode="superset",
)

def test_agent_calls_required_tools_plus_extra():
    result = agent.invoke({
        "messages": [HumanMessage(content="What's the weather in Boston?")]
    })

    # Reference only requires get_weather, but agent may call additional tools
    reference_trajectory = [
        HumanMessage(content="What's the weather in Boston?"),
        AIMessage(content="", tool_calls=[
            {"id": "call_1", "name": "get_weather", "args": {"city": "Boston"}},
        ]),
        ToolMessage(content="It's 75 degrees and sunny in Boston.", tool_call_id="call_1"),
        AIMessage(content="The weather in Boston is 75 degrees and sunny."),
    ]

    evaluation = evaluator(
        outputs=result["messages"],
        reference_outputs=reference_trajectory,
    )
    assert evaluation["score"] is True

또한 tool_args_match_mode 속성과/또는 tool_args_match_overrides를 설정해서, 실제 궤적과 참조값 사이에서 도구 호출의 동등성을 어떻게 판단할지 커스터마이즈할 수 있어요. 기본적으로는 같은 도구에 같은 인자를 넘긴 호출만 동등한 것으로 간주합니다. 자세한 내용은 저장소를 참고하세요.

LLM-as-judge 이밸류에이터 (LLM-as-judge evaluator)

create_trajectory_llm_as_judge 함수로 LLM이 에이전트의 실행 경로를 평가하게 할 수 있어요. 궤적 매칭 이밸류에이터와 달리 참조 궤적이 필수는 아니지만, 있으면 제공할 수 있습니다.

참조 궤적 없이

from agentevals.trajectory.llm import create_trajectory_llm_as_judge, TRAJECTORY_ACCURACY_PROMPT

evaluator = create_trajectory_llm_as_judge(
    model="openai:o3-mini",
    prompt=TRAJECTORY_ACCURACY_PROMPT,
)

def test_trajectory_quality():
    result = agent.invoke({
        "messages": [HumanMessage(content="What's the weather in Seattle?")]
    })

    evaluation = evaluator(
        outputs=result["messages"],
    )
    assert evaluation["score"] is True

참조 궤적과 함께

참조 궤적이 있다면 사전 구축된 TRAJECTORY_ACCURACY_PROMPT_WITH_REFERENCE 프롬프트를 사용하세요.

from agentevals.trajectory.llm import create_trajectory_llm_as_judge, TRAJECTORY_ACCURACY_PROMPT_WITH_REFERENCE

evaluator = create_trajectory_llm_as_judge(
    model="openai:o3-mini",
    prompt=TRAJECTORY_ACCURACY_PROMPT_WITH_REFERENCE,
)
evaluation = evaluator(
    outputs=result["messages"],
    reference_outputs=reference_trajectory,
)

LLM이 궤적을 평가하는 방식을 더 세밀하게 제어하고 싶다면 저장소를 참고하세요.

비동기 지원 (Async support)

모든 agentevals 이밸류에이터는 Python asyncio를 지원해요. 함수 이름에서 create_ 다음에 async를 붙이면 비동기 버전을 쓸 수 있습니다.

from agentevals.trajectory.llm import create_async_trajectory_llm_as_judge, TRAJECTORY_ACCURACY_PROMPT
from agentevals.trajectory.match import create_async_trajectory_match_evaluator

async_judge = create_async_trajectory_llm_as_judge(
    model="openai:o3-mini",
    prompt=TRAJECTORY_ACCURACY_PROMPT,
)

async_evaluator = create_async_trajectory_match_evaluator(
    trajectory_match_mode="strict",
)

async def test_async_evaluation():
    result = await agent.ainvoke({
        "messages": [HumanMessage(content="What's the weather?")]
    })

    evaluation = await async_judge(outputs=result["messages"])
    assert evaluation["score"] is True

LangSmith에서 Evals 실행하기 (Run evals in LangSmith)

시간에 따른 실험을 추적하려면 이밸류에이터 결과를 LangSmith에 기록하면 됩니다. 먼저 필요한 환경 변수를 설정하세요.

export LANGSMITH_API_KEY="your_langsmith_api_key"
export LANGSMITH_TRACING="true"

LangSmith는 평가를 실행하는 두 가지 주요 방법을 제공합니다: pytest 통합과 evaluate 함수예요.

pytest 통합 사용하기

import pytest
from langsmith import testing as t
from agentevals.trajectory.llm import create_trajectory_llm_as_judge, TRAJECTORY_ACCURACY_PROMPT

trajectory_evaluator = create_trajectory_llm_as_judge(
    model="openai:o3-mini",
    prompt=TRAJECTORY_ACCURACY_PROMPT,
)

@pytest.mark.langsmith
def test_trajectory_accuracy():
    result = agent.invoke({
        "messages": [HumanMessage(content="What's the weather in SF?")]
    })

    reference_trajectory = [
        HumanMessage(content="What's the weather in SF?"),
        AIMessage(content="", tool_calls=[
            {"id": "call_1", "name": "get_weather", "args": {"city": "SF"}},
        ]),
        ToolMessage(content="It's 75 degrees and sunny in SF.", tool_call_id="call_1"),
        AIMessage(content="The weather in SF is 75 degrees and sunny."),
    ]

    t.log_inputs({})
    t.log_outputs({"messages": result["messages"]})
    t.log_reference_outputs({"messages": reference_trajectory})

    trajectory_evaluator(
        outputs=result["messages"],
        reference_outputs=reference_trajectory
    )

pytest로 평가를 실행합니다.

pytest test_trajectory.py --langsmith-output

evaluate 함수 사용하기

LangSmith 데이터셋을 만들고 evaluate 함수를 사용하세요. 데이터셋은 다음 스키마를 가져야 해요.

  • input: {"messages": [...]} – 에이전트를 호출할 입력 메시지들.
  • output: {"messages": [...]} – 에이전트 출력에서 기대하는 메시지 기록. 궤적 평가에서는 어시스턴트 메시지만 남겨도 됩니다.
from langsmith import Client
from agentevals.trajectory.llm import create_trajectory_llm_as_judge, TRAJECTORY_ACCURACY_PROMPT

client = Client()

trajectory_evaluator = create_trajectory_llm_as_judge(
    model="openai:o3-mini",
    prompt=TRAJECTORY_ACCURACY_PROMPT,
)

def run_agent(inputs):
    return agent.invoke(inputs)["messages"]

experiment_results = client.evaluate(
    run_agent,
    data="your_dataset_name",
    evaluators=[trajectory_evaluator]
)

에이전트 평가에 대해 더 알고 싶다면 LangSmith 문서를 참고하세요.

더 알아보기 (Learn more)