본문 바로가기

AI Agent

AI 에이전트 성능을 어떻게 측정하나 — Evals와 평가 방법론 완전 정리

반응형

 

AI 에이전트를 만들고 나면 이런 질문이 생겨요.

"이 에이전트가 잘 동작하는 건지 어떻게 알지? 그냥 써보는 것 말고 제대로 측정하는 방법이 있나?"

일반 소프트웨어는 테스트가 간단해요. 같은 입력에 같은 출력이 나오면 패스, 다르면 실패. 근데 AI 에이전트는 비결정적이고, 여러 단계를 거치고, 툴을 호출하고, 컨텍스트를 누적해요. 전통적인 테스트 방식이 안 통해요.

이번 글에서는 에이전트 평가가 왜 어려운지, 무엇을 측정해야 하는지, 어떤 방법으로 측정하는지 정리해 드릴게요.


왜 에이전트 평가는 어려운가

일반 LLM 평가와 에이전트 평가의 차이는 이거예요.

일반 LLM 평가

프롬프트 입력 → 답변 출력 → 정답과 비교

에이전트 평가

목표 입력
  → 툴 호출 결정 (올바른 툴인가?)
  → 툴 실행 (파라미터가 맞는가?)
  → 결과 해석 (제대로 이해했나?)
  → 다음 행동 결정 (방향이 맞나?)
  → ... 반복 ...
  → 최종 결과 (목표를 달성했나?)

평가해야 할 지점이 훨씬 많고, 각 단계가 다음 단계에 영향을 줘요. 한 곳에서 틀리면 에러가 전파돼요.

에이전트 평가를 어렵게 만드는 요인이 세 가지예요.

비결정성 — 같은 질문에도 매번 다른 결과가 나올 수 있어요. 정답이 하나가 아니에요.

다단계 의존성 — 10단계짜리 작업에서 3단계가 틀리면 이후 7단계가 전부 영향을 받아요. 어디서 실패했는지 추적하기 어려워요.

목표의 다차원성 — "고객 문제를 해결했는가"와 "10턴 이내에 해결했는가"와 "적절한 톤으로 대응했는가"를 동시에 평가해야 해요. 하나의 점수로 표현하기 어렵습니다.


무엇을 측정해야 하나 — 5가지 평가 축

축 1: 태스크 완료율 (Task Completion Rate)

가장 기본적인 지표예요. 에이전트가 주어진 목표를 달성했는가를 봐요.

def evaluate_task_completion(agent, test_cases):
    results = []
    for test in test_cases:
        output = agent.run(test["input"])
        # 성공 여부 판단
        success = check_success(output, test["expected_outcome"])
        results.append({
            "task": test["input"],
            "success": success,
            "output": output
        })

    completion_rate = sum(r["success"] for r in results) / len(results)
    return completion_rate

단순히 성공/실패 바이너리로 보는 게 아니라 부분 완료도 측정해야 해요. "목표의 70%를 달성했다"도 의미 있는 정보예요.

축 2: 툴 사용 정확도 (Tool Use Accuracy)

에이전트가 올바른 툴을 올바른 파라미터로 호출했는가를 봐요. 두 가지를 측정해요.

툴 선택 정확도 — 이 상황에서 올바른 툴을 골랐는가.

def evaluate_tool_selection(agent_trace, expected_tools):
    selected_tools = [step["tool"] for step in agent_trace if step["type"] == "tool_call"]
    correct = sum(t in expected_tools for t in selected_tools)
    return correct / len(expected_tools)

파라미터 정확도 — 툴을 올바르게 골랐더라도 파라미터를 잘못 넘기면 실패해요.

def evaluate_tool_arguments(agent_trace, expected_calls):
    correct = 0
    for actual, expected in zip(agent_trace, expected_calls):
        if actual["tool"] == expected["tool"]:
            if actual["args"] == expected["args"]:
                correct += 1
    return correct / len(expected_calls)

Amazon이 수천 개의 에이전트를 구축하면서 발견한 것처럼, 잘못 정의된 툴 스키마가 잘못된 파라미터 전달로 이어지고, 이게 컨텍스트 창을 불필요하게 확장해서 성능을 떨어뜨려요.

축 3: 효율성 (Efficiency)

같은 목표를 달성하더라도 적은 턴, 적은 토큰, 적은 시간으로 달성하는 게 좋아요.

측정 지표는 이렇게 잡아요.

efficiency_metrics = {
    "avg_turns": [],         # 평균 턴 수
    "avg_tokens": [],        # 평균 토큰 사용량
    "avg_latency_ms": [],    # 평균 응답 시간
    "avg_cost_usd": [],      # 평균 비용
    "tool_call_count": []    # 툴 호출 횟수
}

10턴으로 해결할 수 있는 문제를 50턴으로 해결하는 에이전트는 "성공"이지만 프로덕션에서 쓰기 어려워요.

축 4: 신뢰성 (Reliability)

같은 입력에 일관된 결과가 나오는가를 봐요. 비결정적인 에이전트를 여러 번 실행해서 성공률의 분산을 측정해요.

def evaluate_reliability(agent, test_case, n_runs=10):
    results = []
    for _ in range(n_runs):
        output = agent.run(test_case["input"])
        success = check_success(output, test_case["expected_outcome"])
        results.append(success)

    success_rate = sum(results) / n_runs
    variance = calculate_variance(results)

    return {
        "success_rate": success_rate,
        "variance": variance,
        "consistent": variance < 0.1  # 10% 이하 분산이면 일관성 있음
    }

에러 복구도 신뢰성의 일부예요. 툴 호출이 실패했을 때 에이전트가 스스로 재시도하거나 대안을 찾는지 측정해요.

축 5: 안전성 (Safety)

에이전트가 해서는 안 될 행동을 하는지 봐요. 특히 툴을 통해 실제 세계에 영향을 미치는 에이전트에서 중요해요.

safety_checks = [
    "위험한 툴을 승인 없이 호출하는가",       # DB 삭제, 이메일 발송 등
    "민감한 정보를 외부로 유출하는가",
    "프롬프트 인젝션 공격에 취약한가",
    "허용되지 않은 범위의 작업을 시도하는가"
]

어떻게 측정하나 — 3가지 평가 방법

방법 1: 자동화 Evals

코드로 자동 실행하는 평가예요. 빠르고 반복 가능해서 개발 중에 CI/CD 파이프라인에 넣어서 쓰는 게 좋아요.

단위 평가 (Component-level Eval)

에이전트의 각 구성 요소를 개별적으로 테스트해요.

# 툴 선택만 테스트
def test_tool_selection():
    agent = MyAgent()
    result = agent.select_tool("서울 날씨 알려줘")
    assert result.tool_name == "get_weather"
    assert result.args["city"] == "서울"

# 추론만 테스트
def test_reasoning():
    agent = MyAgent()
    context = "검색 결과: 서울 15도 맑음"
    answer = agent.generate_answer("날씨 어때?", context)
    assert "15도" in answer or "맑음" in answer

엔드투엔드 평가 (E2E Eval)

전체 작업 흐름을 테스트해요.

test_cases = [
    {
        "input": "내일 서울 날씨 알려주고 옷차림 추천해줘",
        "expected_tools": ["get_weather"],
        "success_criteria": lambda output: "도" in output and ("가볍" in output or "따뜻" in output)
    },
    {
        "input": "애플 주가 지금 얼마야",
        "expected_tools": ["get_stock_price"],
        "success_criteria": lambda output: "$" in output or "달러" in output
    }
]

for test in test_cases:
    result = agent.run(test["input"])
    assert test["success_criteria"](result)

방법 2: LLM-as-Judge (LLM 심판)

다른 LLM이 에이전트의 출력을 평가해요. 정답이 하나가 아닌 주관적인 평가에 유용해요.

def llm_judge(question, agent_output, rubric):
    judge_prompt = f"""
    다음 에이전트 출력을 평가해줘.
    
    질문: {question}
    에이전트 출력: {agent_output}
    
    평가 기준:
    {rubric}
    
    1~5점으로 평가하고 이유를 설명해줘.
    JSON으로만 답해: {{"score": 점수, "reason": "이유"}}
    """
    result = judge_llm.invoke(judge_prompt)
    return json.loads(result.content)

# 사용 예시
rubric = """
- 5점: 질문에 완전히 답하고, 정확하고, 도움이 됨
- 3점: 부분적으로 답하거나 약간 부정확함
- 1점: 질문과 관련 없거나 틀린 답변
"""

score = llm_judge(
    question="서울 내일 날씨 알려줘",
    agent_output="내일 서울은 맑고 기온 18도입니다. 가벼운 겉옷 챙기세요.",
    rubric=rubric
)
# {"score": 5, "reason": "날씨 정보가 정확하고 추가 팁도 있음"}

멀티턴 평가에서는 시뮬레이션 유저를 써요. 한 LLM이 에이전트, 다른 LLM이 유저를 연기해서 대화를 시뮬레이션해요.

def simulate_conversation(agent, user_persona, goal, max_turns=10):
    messages = []
    user_llm = create_user_simulator(persona=user_persona, goal=goal)

    for turn in range(max_turns):
        # 유저 메시지 생성
        user_msg = user_llm.generate_message(messages)
        messages.append({"role": "user", "content": user_msg})

        # 에이전트 응답
        agent_response = agent.respond(messages)
        messages.append({"role": "assistant", "content": agent_response})

        # 목표 달성 확인
        if goal_achieved(messages, goal):
            return {"success": True, "turns": turn + 1, "conversation": messages}

    return {"success": False, "turns": max_turns, "conversation": messages}

방법 3: 휴먼 평가 (Human Evaluation)

자동화 평가가 못 잡는 뉘앙스, 톤, 사용자 경험을 사람이 직접 평가해요.

평가 항목:
□ 응답이 자연스럽고 이해하기 쉬운가?
□ 사용자의 의도를 제대로 파악했는가?
□ 불필요하게 길거나 짧지 않은가?
□ 오해를 일으킬 수 있는 표현이 없는가?
□ 전체적으로 도움이 됐는가?

자동화 평가와 휴먼 평가를 어떻게 조합하느냐가 중요해요.

개발 단계: 자동화 Evals 위주 (빠른 피드백)
출시 전:   자동화 + LLM 심판 + 휴먼 평가 병행
프로덕션:  지속적 자동 모니터링 + 샘플 휴먼 리뷰

주요 벤치마크

실제로 많이 쓰이는 에이전트 벤치마크들이에요.

SWE-bench — GitHub 실제 이슈를 에이전트가 해결하게 해요. 코딩 에이전트 평가의 표준이에요. 초기 GPT-4 기반 에이전트는 14%만 해결했는데, 2025년에는 60%까지 올라왔어요.

τ-bench / τ2-bench — 멀티턴 대화 에이전트를 평가해요. 한 LLM이 유저를 연기하면서 항공 예약, 쇼핑몰 고객 서비스 같은 시나리오를 시뮬레이션해요. 태스크 완료 + 대화 품질 + 턴 수 제한을 동시에 평가해요.

BrowseComp — 웹 브라우징 에이전트 평가예요. OpenAI Deep Research가 51.5%를 기록했어요.

AgentBench — 운영체제, DB, 웹 쇼핑, 지식 그래프 등 8개 환경에서 에이전트를 테스트해요.


평가 파이프라인 구축

에이전트를 개발할 때 평가를 처음부터 파이프라인에 넣어야 해요.

코드 변경
    │
    ▼
자동화 Evals 실행 (CI/CD)
    │
    ├─ 통과 → 스테이징 배포
    └─ 실패 → 알림 + 블록
           │
           ▼
      스테이징에서 심층 평가
      (LLM 심판 + 휴먼 샘플링)
           │
           ▼
      프로덕션 배포
           │
           ▼
      지속적 모니터링
      (성공률, 지연 시간, 비용 추적)

Anthropic이 강조하는 것처럼 **"eval 점수를 액면 그대로 믿지 말고, 직접 트랜스크립트를 읽어봐야 한다"**는 게 중요해요. 그레이딩이 불공정하거나 태스크가 모호하면 eval이 의미 없거든요.


실전 평가 도구

도구 특징 적합한 용도

LangSmith LangChain 네이티브, 트레이싱 강함 LangGraph 기반 에이전트
DeepEval 오픈소스, 메트릭 풍부 범용 LLM/에이전트 평가
Arize Phoenix 오픈소스, RAG 평가 강함 RAG 파이프라인
Galileo 툴 선택 품질, 세션 성공률 프로덕션 에이전트 모니터링

마무리

에이전트 평가의 핵심은 세 가지예요.

첫째, 단일 지표로 충분하지 않아요. 태스크 완료율, 툴 정확도, 효율성, 신뢰성, 안전성을 함께 봐야 해요.

둘째, 자동화와 휴먼 평가를 조합해야 해요. 자동화는 빠르고 일관성 있지만 뉘앙스를 못 잡아요. 휴먼은 느리지만 진짜 품질을 잡아요.

셋째, 평가는 개발 시작부터 파이프라인에 넣어야 해요. 출시 직전에 급하게 평가하면 이미 늦어요. 😄


 

반응형