본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 04. 10:58

에이전트 시리즈 (12): 에이전트 평가 프레임워크 — 당신의 에이전트가 정말로 뛰어난지 어떻게 알 수 있을까요?

요약

에이전트의 비결정론적 특성으로 인한 평가의 어려움을 다루며, 역량, 효율성, 견고성이라는 세 가지 차원의 평가 프레임워크를 제안합니다. ReAct 에이전트를 대상으로 도구 호출의 정확성과 지름길(shortcut) 문제를 식별하는 방법을 설명합니다.

핵심 포인트

  • 에이전트 평가는 역량, 효율성, 견고성을 모두 고려해야 함
  • 비결정론적 경로와 다양한 실패 모드로 인해 일반 함수보다 평가가 어려움
  • LLM이 도구를 호출하지 않고 정보를 직접 사용하는 '지름길' 현상 주의 필요
  • 정확한 평가를 위해 도구 호출 경로(tool call path) 검증이 필수적임

당신의 에이전트가 "좋다"는 것을 어떻게 알 수 있을까요?

일반적인 함수를 테스트하는 것은 간단합니다. 입력을 주고, 출력을 확인하여 통과(pass)인지 실패(fail)인지만 판단하면 됩니다.

에이전트(Agents)는 더 어렵습니다. 왜 그럴까요?

  • 비결정론적 경로 (Non-deterministic paths): 동일한 질문이라도 한 번의 도구 호출(tool call)을 발생시킬 수도 있고, 세 번을 발생시킬 수도 있습니다.
  • 고정되지 않은 출력 (Non-fixed outputs): "오늘 베이징 날씨가 더운가요?"라는 질문에 대해 수백 가지의 서로 다른 표현으로 답변할 수 있습니다.
  • 다양한 실패 모드 (Many failure modes): 도구가 호출되지 않을 수도 있고, 잘못된 도구가 호출될 수도 있으며, 답변은 맞더라도 우회적인 경로를 통해 도달했을 수도 있습니다.

따라서 에이전트 평가(Agent evaluation)는 세 가지 차원을 다뤄야 합니다: 역량 (Capability) (태스크를 수행할 수 있는가?), 효율성 (Efficiency) (빠르고 저렴하게 수행하는가?), 그리고 견고성 (Robustness) (예외적인 입력에도 잘 버티는가?)

테스트 대상 에이전트

테스트 대상은 세 가지 도구를 가진 ReAct 에이전트입니다:

@lc_tool
def get_weather(city: str) -> str:
    """도시의 현재 날씨를 가져옵니다."""
...

모든 데이터는 모킹(mocked)되었습니다: 몇몇 도시의 날씨 데이터와 세 가지 제품 가격대입니다. 도구는 의도적으로 최소화되었습니다 — 실패는 도구의 로직이 아니라 에이전트의 동작에서 발생해야 합니다.

평가 데이터 구조

@dataclass
class TestCase:
    id: str
...

run_case는 단일 테스트를 실행하고 모든 것을 측정합니다:

def run_case(case: TestCase) -> EvalResult:
    t0 = time.time()
    try:
...

데모 1: 역량 평가 (Capability Evaluation)

단일 도구부터 다중 도구까지 총 다섯 가지 테스트 케이스가 있습니다:

ID입력 (Input)예상 도구 (Expected Tools)
C-01오늘 베이징 날씨가 어떤가요?get_weather
...

실제 벤치마크 결과:

  [✓] C-01  tools=['get_weather']             tool_acc=1.0  output_ok=True
  [✓] C-02  tools=['calculator', 'calculator'] tool_acc=1.0  output_ok=True
  [✓] C-03  tools=['get_product_info']         tool_acc=1.0  output_ok=True
...

C-05는 왜 실패했을까요? 이것은 흥미로운 실패 사례입니다.

질문은 다음과 같았습니다: "WonderBot Basic 플랜의 API 호출 제한은 얼마이며, 10000을 30으로 나누면 얼마인가요?"

LLM은 질문에서 "10000"을 직접 읽어 나누기 연산에 사용했습니다 — get_product_info를 호출하지 않고 말이죠. 사용자 관점에서는 답변이 올바르게 보입니다. 하지만 평가 관점에서는 도구 호출 (tool call) 경로가 잘못되었습니다. 만약 제품 플랜이 변경된다면, LLM은 오래된(stale) 정보를 제공하게 될 것입니다.

이것은 "지름길 (shortcut)" 행동입니다. 질문 자체에 도구가 반환할 정보가 포함되어 있을 때, LLM은 도구를 통해 검증하는 대신 그 정보를 직접 사용해 버립니다. 평가 프레임워크는 이 문제를 드러냈으며, 해결책은 질문이 도구가 찾아내야 할 정보를 유출하지 않도록 테스트를 재설계하는 것입니다.

데모 2: 효율성 평가 (Efficiency Evaluation)

세 가지 테스트 케이스 — 여기에서의 초점은 정확도가 아닌 비용입니다:

  E-01  steps=2  tokens=45   latency=2237ms  tools=['get_weather']
  E-02  steps=2  tokens=36   latency=4112ms  tools=['calculator']
  E-03  steps=3  tokens=73   latency=5151ms  tools=['get_product_info', 'calculator']
...

몇 가지 관찰 사항:

  • Steps (단계) = AI 메시지의 수이며, 여기에는 "생각(think) → 도구 호출(call tool)" 및 "결과 통합(integrate result) → 응답(respond)"이 포함됩니다. 따라서 단일 도구 작업은 보통 2단계가 소요됩니다.
  • 여기서 토큰 수(Token count)는 낮습니다. 중간 메시지 토큰만을 근사치로 계산하기 위해 tiktoken을 사용했기 때문입니다 (시스템 프롬프트 제외). 실제 API 과금 토큰은 이보다 높을 것입니다.
  • 지연 시간(Latency)의 변동이 눈에 띕니다: calculator(4112ms)는 둘 다 단일 도구 작업임에도 불구하고 get_weather(2237ms)보다 거의 두 배 느립니다. 이는 도구 실행 때문이 아니라, LLM이 산술 표현식을 추론(reasoning)하는 데 더 오랜 시간이 걸리기 때문입니다.

효율성 평가의 가치는 기준점(baseline)을 설정하는 것에 있습니다. 평균 지연 시간 3833ms는 그 자체로는 아무런 의미가 없지만, 최적화를 통해 이를 1500ms로 줄인다면 최적화가 성공했음을 알 수 있습니다.

데모 3: 견고성 평가 (Robustness Evaluation)

다양한 실패 모드를 다루는 다섯 가지 엣지 케이스 (edge cases):

ID입력 (Input)테스트 (Tests)
R-01"" (빈 문자열)빈 입력에 대한 우아한 처리 (Graceful handling)
...
실제 벤치마크 결과:
  [✗] R-01  pass=False  note: graceful empty-input response
         answer: [ERROR] Error code: 400 - {'error': {'code': '1213', 'message': '...'}}
  [✓] R-02  pass=True   note: prompt injection rejected
...

R-01의 실패는 실제 인프라 문제입니다.

GLM-4-Flash는 빈 문자열이 입력되었을 때 에러 코드 1213("prompt parameter not received")과 함께 HTTP 400을 반환합니다. 이는 에이전트 (Agent) 로직의 문제가 아니라, 호출 계층 (call layer)에서의 입력 검증 (input validation) 누락입니다.

해결책은 에이전트 진입점 (entry point)에 가드 (guard)를 설치하는 것입니다:

def run_agent(user_input: str):
    if not user_input.strip():
        return "Please enter your question."
...

평가 프레임워크 (evaluation framework)가 이 문제를 찾아냈습니다. 이것이 바로 프레임워크의 목적입니다. 모든 에이전트 (Agent) 버그가 에이전트 로직 내에 존재하는 것은 아닙니다.

종합 보고서 (Overall Report)

Dimension            Metric                         Value
------------------------------------------------------------
Capability           Tool call accuracy             90.0%
...

세 가지 차원, 세 가지 서로 다른 관점:

  • Capability (역량): 도구 호출 (tool calls) 시 LLM의 "지름길 (shortcut)" 동작을 드러냄
  • Efficiency (효율성): 향후 최적화를 위한 기준점 (baseline)을 수립함
  • Robustness (견고성): 인프라 계층에서의 입력 검증 (input validation) 공백을 노출함

설계 체크리스트 (Design Checklist)

테스트 케이스 설계 (TestCase Design)

  • expected_tools: 선택적 도구가 아닌, 반드시 호출되어야 하는 도구만 목록에 포함
  • expected_output_contains: 모호한 단어("temperature") 대신 구체적인 값("25", "299")을 사용
  • 세 가지 카테고리 포함: 일반 작업 (normal tasks) / 다중 도구 조합 (multi-tool combinations) / 예외 입력 (edge inputs)

역량 테스트 (Capability Tests)

  • 단일 도구 및 다중 도구 조합 시나리오를 모두 포함
  • 최종 정답뿐만 아니라 tool_accuracy (도구 정확도)를 확인
  • 질문 텍스트에 도구로 조회 가능한 정보를 유출하지 말 것 (C-05 패턴 방지)

효율성 테스트 (Efficiency Tests)

  • 세 가지 요소(단계(steps), 토큰(tokens), 지연 시간(latency))를 모두 기록할 것
  • 최적화 전후를 비교할 수 있도록 동일한 작업 클래스(task class)에 대한 기준점(baseline)을 설정할 것
  • tiktoken 근사치는 유용하지만 시스템 프롬프트(system prompts)를 제외하므로, 이 주의 사항을 명시할 것

강건성 테스트 (Robustness Tests)

  • 항상 빈 입력(empty input) 테스트를 포함할 것
  • 항상 프롬프트 주입(prompt injection) 테스트를 포함할 것
  • 도구가 누락되었거나 찾을 수 없는 결과(missing / not-found results)를 반환할 때 어떤 일이 발생하는지 테스트할 것
  • "에이전트 로직 실패(Agent logic failure)"와 "인프라 실패(infrastructure failure)"를 구분할 것 — 후자는 호출 계층(call layer)에서 수정되어야 함

요약 (Summary)

다섯 가지 핵심 요점:

  1. 에이전트 평가는 세 가지 차원을 모두 다뤄야 함: 단순히 "답이 맞는가?"만 확인하는 것은 불충분함 — 올바른 도구가 호출되었는지 여부도 똑같이 중요함
  2. 도구 호출 정확도는 출력 정확도보다 더 엄격함: C-05 패턴은 LLM이 잘못된 경로를 통해서도 "정답"을 만들어낼 수 있음을 보여줌
  3. 강건성 테스트는 인프라 문제를 드러냄: R-01의 빈 입력 실패는 에이전트 로직이 아닌 호출 계층(call layer)의 문제임
  4. 효율성 평가의 가치는 기준점(baseline)에 있음: 비교 대상이 없다면 단순한 수치는 의미가 없음
  5. expected_output_contains에 구체적인 값을 사용하라: "25" 대신 "온도(temperature)\

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0