-
LLM 에이전트 평가(Evals) 가이드Knowledge Base/Foundations 2026. 1. 31. 22:32

https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents Author: Claude Code, mangowhoiscloud
Reference(26.01.19): Anthropic Engineering - Demystifying Evals for AI Agents
Purpose: 에이전트 개발을 위한 Knowledge Base
Date: 2026-01-31Executive Summary
에이전트 평가는 단순한 프롬프트-응답 테스트를 넘어, 다중 턴 상호작용, 도구 호출, 환경 상태 변화를 종합적으로 측정해야 합니다. 이 문서는 Anthropic의 에이전트 평가 방법론을 정리하고, 적용 가이드를 제시합니다.
핵심 공식:Eval = Input + Agent Execution + Grading Logic → Success Measurement1. 에이전트 평가의 기본 구조
1.1 핵심 용어 정의
Task 정의된 입력과 성공 기준을 가진 단일 테스트 "인증 우회 버그 수정" Trial 태스크에 대한 각 시도 (비결정성으로 여러 번 실행) 동일 태스크 5회 실행 Grader 에이전트 성능의 특정 측면을 채점하는 로직 단위 테스트, LLM 루브릭 Transcript 출력, 도구 호출, 추론, 중간 결과의 완전한 기록 JSON Lines 로그 Outcome 시도 종료 시 환경의 최종 상태 파일 변경, DB 레코드 Eval Harness 평가를 종합적으로 실행하는 인프라 pytest + 격리 환경 Agent Harness 모델이 에이전트로 작동하도록 하는 시스템 LangGraph Runner Eval Suite 특정 기능을 측정하도록 설계된 태스크 모음 "코드 수정 능력" 스위트 1.2 평가 구조 다이어그램
┌─────────────────────────────────────────────────────────────────────────────┐ │ Evaluation Pipeline │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Eval Suite │ │ Agent │ │ Graders │ │ │ │ (Tasks) │────▶│ Harness │────▶│ (Scoring) │ │ │ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Transcript │ │ Outcome │ │ │ │ (Logs) │ │ (State) │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ │ │ └─────────┬─────────┘ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Trial Result │ │ │ │ (pass/fail) │ │ │ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘2. 평가 유형 분류
2.1 Single-Turn vs Multi-Turn
구조 프롬프트 → 응답 프롬프트 → [도구 호출 → 상태 변화]* → 최종 응답 복잡도 낮음 높음 상태 무상태 환경 상태 추적 필요 평가 대상 응답 품질 응답 + 도구 사용 + 최종 상태 2.2 Capability vs Regression Evals
┌─────────────────────────────────────────────────────────────────────────────┐ │ Capability Evals │ ├─────────────────────────────────────────────────────────────────────────────┤ │ 질문: "이 에이전트가 무엇을 잘할 수 있는가?" │ │ │ │ 특징: │ │ • 낮은 통과율에서 시작 (도전적 태스크) │ │ • 팀이 목표를 향해 개선해나감 │ │ • 새로운 기능 개발 시 사용 │ │ │ │ 예: SWE-Bench (2024년 30% → 2025년 80%+) │ └─────────────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────────────┐ │ Regression Evals │ ├─────────────────────────────────────────────────────────────────────────────┤ │ 질문: "에이전트가 여전히 이전 기능을 처리하는가?" │ │ │ │ 특징: │ │ • 거의 100% 통과율 유지 │ │ • 성능 저하(regression) 즉시 감지 │ │ • 배포 전 게이트로 사용 │ │ │ │ 예: "이전에 성공한 버그 수정이 여전히 작동하는가?" │ └─────────────────────────────────────────────────────────────────────────────┘3. 에이전트 유형별 평가 전략
3.1 코딩 에이전트 (Coding Agents)
코드를 작성, 테스트, 디버그하는 에이전트.
평가 구성 예시:task: id: "fix-auth-bypass_1" description: "인증 우회 취약점 수정" graders: # 1. 결정론적 테스트 (필수) - type: deterministic_tests required: [test_empty_pw_rejected.py] # 2. LLM 루브릭 (코드 품질) - type: llm_rubric rubric: prompts/code_quality.md # 3. 정적 분석 - type: static_analysis commands: [ruff, mypy, bandit] # 4. 상태 확인 (부작용 검증) - type: state_check expect: security_logs: {event_type: "auth_blocked"} # 5. 도구 호출 확인 - type: tool_calls required: - {tool: read_file} - {tool: edit_file} - {tool: run_tests}핵심 원칙:
- 단위 테스트로 정확성 확인
- LLM 루브릭으로 코드 품질 평가
- 정적 분석으로 보안/타입 검증
- 상태 체크로 부작용 검증
3.2 대화형 에이전트 (Conversational Agents)
고객 지원, 판매, 코칭 도메인.
평가 구성 예시:task: id: "refund-complaint_1" description: "불만 고객 환불 처리" graders: # 1. LLM 루브릭 (상호작용 품질) - type: llm_rubric assertions: - "Agent showed empathy for customer's frustration" - "Resolution was clearly explained" - "Agent did not make promises beyond policy" # 2. 상태 확인 (작업 완료) - type: state_check expect: tickets: {status: resolved} refunds: {status: processed} # 3. 대화 길이 제한 - type: transcript max_turns: 10 # 4. 정책 준수 - type: policy_check violations: []핵심 원칙:
- 작업 완료와 상호작용 품질 모두 측정
- 사용자 페르소나를 시뮬레이션하는 두 번째 LLM 필요
- 정책 위반 여부 체크
3.3 연구 에이전트 (Research Agents)
정보 수집, 종합, 분석.
평가 유형:Groundedness 주장이 검색된 소스로 뒷받침되는지 NLI 모델로 entailment 확인 Coverage 핵심 사실이 포함되었는지 체크리스트 기반 평가 Source Quality 인용 소스의 신뢰성 도메인 화이트리스트 Citation Accuracy 인용이 정확한지 소스 원문과 대조 # Groundedness 체크 예시 def check_groundedness(claim: str, sources: list[str]) -> float: """주장이 소스에 근거하는지 확인""" from transformers import pipeline nli = pipeline("text-classification", model="facebook/bart-large-mnli") scores = [] for source in sources: result = nli(f"{source}</s></s>{claim}")[0] if result['label'] == 'entailment': scores.append(result['score']) return max(scores) if scores else 0.03.4 컴퓨터 사용 에이전트 (Computer Use Agents)
스크린샷, 마우스, 키보드로 소프트웨어와 상호작용.
평가 고려사항:네비게이션 URL 및 페이지 상태 확인 액션 완료 백엔드 상태 검증 (주문 실제 배치 등) 효율성 클릭/액션 수 오류 복구 실패 후 재시도 능력
트레이드오프:- DOM 기반: 빠르고 정확하지만, 실제 사용자 경험과 다름
- 스크린샷 기반: 실제적이지만, 느리고 불안정
작성자가 택한 개발 지향점:- GUI 기반 UI/UX는 사람을 위한 도구, 에이전트가 이를 수행할 시 컨택스트 유실과 레이턴시가 병목으로 잡힘
- GUI는 자연어 요청에 따라 에이전트가 수행한 작업 결과를 사람이 검수할 때 활용하는 대시보드의 역할을 해야한다고 판단
- 에이전트가 사용하는 툴은 파일시스템, 스크립트를 활용한 경량 tool_calling 혹은 bash로 진행하는 게 합리적
- 동일한 논리로 MCP의 사용은 최대한 배제, 외부 호출은 SDK Native Tool Calling + 스크립트 + API Key로 대체
4. 세 가지 Grader 유형
4.1 Code-Based Graders (결정론적)
from dataclasses import dataclass from typing import Callable import re import subprocess @dataclass class CodeGrader: """결정론적 코드 기반 그레이더""" @staticmethod def exact_match(expected: str, actual: str) -> bool: """정확한 문자열 일치""" return expected.strip() == actual.strip() @staticmethod def regex_match(pattern: str, text: str) -> bool: """정규식 매칭""" return bool(re.search(pattern, text)) @staticmethod def run_tests(test_file: str) -> dict: """단위 테스트 실행""" result = subprocess.run( ["pytest", test_file, "-v", "--tb=short"], capture_output=True, text=True ) return { "passed": result.returncode == 0, "output": result.stdout, "errors": result.stderr } @staticmethod def static_analysis(file_path: str) -> dict: """정적 분석 (ruff, mypy, bandit)""" results = {} for tool in ["ruff", "mypy", "bandit"]: result = subprocess.run( [tool, file_path], capture_output=True, text=True ) results[tool] = { "passed": result.returncode == 0, "issues": result.stdout } return results @staticmethod def state_check(expected_state: dict, actual_state: dict) -> bool: """환경 상태 확인""" for key, expected_value in expected_state.items(): if key not in actual_state: return False if actual_state[key] != expected_value: return False return True강점: 빠르고, 저렴하고, 객관적이며, 재현 가능하고, 디버그하기 쉬움
약점: 유효한 변형에 취약하고, 뉘앙스 부족, 주관적 작업에 제한적4.2 Model-Based Graders (LLM-as-Judge)
from anthropic import Anthropic from pydantic import BaseModel, Field from typing import Literal import json class RubricScore(BaseModel): """루브릭 기반 채점 결과""" score: int = Field(ge=1, le=10) reasoning: str passed_criteria: list[str] failed_criteria: list[str] class PairwiseResult(BaseModel): """쌍별 비교 결과""" winner: Literal["A", "B", "tie"] reasoning: str confidence: float = Field(ge=0, le=1) class LLMGrader: """LLM 기반 그레이더""" def __init__(self, model: str = "claude-sonnet-4-5"): self.client = Anthropic() self.model = model async def rubric_grade( self, response: str, rubric: str, context: str = None ) -> RubricScore: """루브릭 기반 채점""" prompt = f"""You are an expert evaluator. Grade the following response. Rubric: {rubric} {f'Context: {context}' if context else ''} Response to evaluate: {response} Provide your evaluation in JSON format: {{ "score": <1-10>, "reasoning": "<explanation>", "passed_criteria": ["criterion1", ...], "failed_criteria": ["criterion1", ...] }} IMPORTANT: If you cannot determine something, say "Unknown" rather than guessing.""" message = self.client.messages.create( model=self.model, max_tokens=1000, messages=[{"role": "user", "content": prompt}] ) return RubricScore(**json.loads(message.content[0].text)) async def pairwise_compare( self, response_a: str, response_b: str, criteria: str ) -> PairwiseResult: """쌍별 비교 (Position Bias 방지를 위해 순서 랜덤화 권장)""" prompt = f"""Compare these two responses based on the criteria. Criteria: {criteria} Response A: {response_a} Response B: {response_b} Which response is better? Answer in JSON: {{ "winner": "A" or "B" or "tie", "reasoning": "<explanation>", "confidence": <0-1> }}""" message = self.client.messages.create( model=self.model, max_tokens=500, messages=[{"role": "user", "content": prompt}] ) return PairwiseResult(**json.loads(message.content[0].text))강점: 유연하고, 확장 가능하며, 뉘앙스 포착, 개방형 작업 처리
약점: 비결정적, 비용 높음, 인간 평가자와 교정 필요4.3 Human Graders
전문가 검토 고위험 평가, LLM grader 교정 높음 크라우드소싱 대규모 주관적 평가 중간 샘플링 자동화된 평가 검증 낮음 A/B 테스트 실제 사용자 선호도 중간 강점: 금본위 품질, 전문가 판단 일치
약점: 비싸고, 느리며, 전문가 접근 필요4.4 Grader 선택 가이드
┌─────────────────────────────────────────────────────────────────────────────┐ │ Grader Selection Decision Tree │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 정답이 명확한가? │ │ │ │ │ ├── Yes ──▶ Code-Based Grader (단위 테스트, 정규식) │ │ │ │ │ └── No ──▶ 품질 기준이 정의 가능한가? │ │ │ │ │ ├── Yes ──▶ LLM Grader (루브릭 기반) │ │ │ │ │ └── No ──▶ Human Grader (전문가 검토) │ │ │ │ LLM Grader 사용 시: │ │ • 반드시 인간 전문가와 교정 (calibration) │ │ • "Unknown" 옵션 제공 (할루시네이션 방지) │ │ • Position Bias 방지를 위해 순서 랜덤화 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘5. 비결정성 처리: pass@k vs pass^k
모델 출력은 실행마다 다르므로, 적절한 메트릭 선택이 중요합니다.
5.1 pass@k (한 번의 성공)
정의: "k번의 시도에서 적어도 하나의 올바른 솔루션을 얻을 가능성"
def pass_at_k(n: int, c: int, k: int) -> float: """ pass@k 계산 (unbiased estimator) Args: n: 총 시도 횟수 c: 성공한 시도 횟수 k: 허용 시도 횟수 Returns: pass@k 확률 """ if n - c < k: return 1.0 return 1.0 - np.prod(1.0 - k / np.arange(n - c + 1, n + 1))특징:
- k 증가 시 pass@k 상승 (더 많은 시도 = 더 높은 성공 확률)
- 코딩 에이전트에서 주로 관심사 (pass@1)
- 사용 시점: 한 번의 성공이 중요한 도구
5.2 pass^k (일관된 성공)
정의: "모든 k번의 시도가 성공할 확률"
def pass_power_k(per_trial_success_rate: float, k: int) -> float: """ pass^k 계산 Args: per_trial_success_rate: 단일 시도 성공률 k: 시도 횟수 Returns: pass^k 확률 """ return per_trial_success_rate ** k # 예시: 75% 성공률, 3회 시도 # pass^3 = 0.75^3 ≈ 42%특징:
- k 증가 시 pass^k 하락 (일관성 요구가 더 어려움)
- 고객 대면 에이전트에 중요
- 사용 시점: 일관성이 중요한 에이전트
5.3 선택 기준
코딩 에이전트 pass@1 한 번 성공하면 됨 고객 지원 pass^k 일관된 품질 필요 연구 에이전트 pass@1 + 품질 점수 성공 + 품질 모두 중요 컴퓨터 사용 pass^k 신뢰성 필수 6. 평가 설계 8원칙
원칙 1: 작게 시작하라
20-50개 간단한 태스크로 시작 가능 초기에는 변화가 눈에 띄므로 작은 샘플 크기로도 충분시작점:
- 수동으로 테스트하던 기능 변환
- 버그 리포트를 테스트 케이스로
- 사용자 영향도로 우선순위 지정
원칙 2: 명확한 태스크 작성
"두 명의 도메인 전문가 혹은 특화 에이전트가 독립적으로 같은 합격/불합격 판정에 도달할 수 있어야"
체크리스트:- 모호성 없는 성공 기준
- 에이전트가 실제로 통과 가능
- 참고 솔루션 혹은 프로덕션 레벨 사례 존재 (해결 가능성 증명)
원칙 3: 균형잡힌 문제 세트
┌─────────────────────────────────────────────────────────────────────────────┐ │ Positive Cases │ Negative Cases │ │ (행동해야 할 때) │ (행동하지 말아야 할 때) │ ├─────────────────────────────────────────────────────────────────────────────┤ │ • 버그 수정 필요 │ • 코드가 이미 정상 │ │ • 환불 처리 필요 │ • 정책상 환불 불가 │ │ • 정보 검색 필요 │ • 이미 충분한 정보 제공됨 │ └─────────────────────────────────────────────────────────────────────────────┘ ⚠️ 한쪽으로 치우친 평가 = 한쪽으로 치우친 최적화원칙 4: 강건한 Eval Harness
class EvalHarness: """강건한 평가 하네스""" async def run_trial(self, task: Task, agent: Agent) -> TrialResult: # 1. 깨끗한 환경에서 시작 env = await self.create_isolated_environment() # 2. 이전 시도의 잔존 상태 제거 await env.reset() # 3. 에이전트 실행 transcript = await agent.run(task.input, env) # 4. 결과 수집 outcome = await env.get_state() # 5. 채점 scores = await self.grade(task, transcript, outcome) return TrialResult( task_id=task.id, transcript=transcript, outcome=outcome, scores=scores )핵심:
- 프로덕션 환경과 유사하게 동작
- 각 시도는 깨끗한 환경에서 시작
- 실행 간 불필요한 공유 상태 제거
원칙 5: 신중한 Grader 설계
중요 인사이트 (Anthropic):
"에이전트가 매우 구체적인 단계를 따랐는지 확인하는 경향이 있지만, 이 접근 방식이 너무 경직되어 있다는 것을 발견했습니다. 에이전트는 정기적으로 평가 설계자가 예상하지 못한 유효한 접근 방식을 찾습니다."
Best Practice:
# ❌ 너무 경직된 평가 grader: type: exact_steps steps: - "read auth.py" - "find validate_password function" - "add length check" # ✅ 결과 중심 평가 grader: type: outcome_based tests: - test_empty_password_rejected - test_short_password_rejected - test_valid_password_accepted부분 점수:
def calculate_partial_score(results: dict) -> float: """여러 구성 요소의 부분 점수 계산""" weights = { "tests_passed": 0.5, "code_quality": 0.2, "security_check": 0.2, "documentation": 0.1 } total = sum( results.get(key, 0) * weight for key, weight in weights.items() ) return total원칙 6: Transcript 검토
"좋은 평가를 아는 방법은 많은 시도의 transcript를 읽는 것"
체크리스트:- 실패가 공정하게 보이는가?
- 에이전트가 무엇을 놓쳤는지 명확한가?
- 유효한 솔루션이 처벌받지 않는가?
- Harness 제약이 공정한가?
원칙 7: Capability Eval 포화 모니터링
Eval Saturation: 에이전트가 모든 해결 가능한 태스크를 통과할 때 발생
SWE-Bench Verified: 2024년 초: 30% 2025년 현재: 80%+ ⚠️ 포화 접근 시: • 진행이 느려짐 • 가장 어려운 태스크만 남음 • 큰 성능 향상도 작은 점수 증가로 나타남대응 전략:
- 더 어려운 태스크 추가
- 새로운 기능 영역 평가
- 효율성 메트릭 추가 (속도, 비용)
원칙 8: 장기 유지 전략
┌─────────────────────────────────────────────────────────────────────────────┐ │ Eval Ownership Model │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ Evals Team (핵심 인프라) │ │ ├── Eval Harness 유지 │ │ ├── Grader 라이브러리 관리 │ │ └── 메트릭 대시보드 │ │ │ │ Domain Experts (태스크 기여) │ │ ├── 도메인별 태스크 작성 │ │ ├── 루브릭 정의 │ │ └── 실패 분석 │ │ │ │ Product Team (요구사항) │ │ ├── 사용자 시나리오 제공 │ │ ├── 우선순위 결정 │ │ └── 품질 기준 정의 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘Eval-Driven Development:
에이전트가 기능을 충족하기 전에 평가를 구축하여 계획된 기능 정의
7. 종합 평가 전략: Swiss Cheese Model
단일 평가 계층도 모든 문제를 잡지 못하므로 여러 방법을 결합합니다.
┌─────────────────────────────────────────────────────────────────────────────┐ │ Swiss Cheese Model │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Automated Evals │ │ │ │ 빠른 반복, 재현 가능, 사용자 영향 없음 │ │ │ │ [구멍: 현실 세계 복잡성 미반영] │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Production Monitoring │ │ │ │ 실제 사용자 행동, 진짜 문제 감지 │ │ │ │ [구멍: 반응적, 신호 잡음 많음] │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ A/B Testing │ │ │ │ 실제 사용자 결과 측정, 혼동 제어 │ │ │ │ [구멍: 느림, 테스트한 변화만 실행] │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ User Feedback │ │ │ │ 예상 못한 문제 표면, 실제 예시 │ │ │ │ [구멍: 드물고, 자기선택, 심각한 이슈 편향] │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Manual Transcript Review │ │ │ │ 실패 모드 직관 구축, 미묘한 문제 감지 │ │ │ │ [구멍: 시간 소비적, 확장 불가] │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Systematic Human Studies │ │ │ │ 금본위 판단, 주관적 작업 처리 │ │ │ │ [구멍: 비싸고, 느리고, 비용 높음] │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘8. 실전 구현 로드맵
Phase 1: 초기 데이터셋
□ 수동 테스트 케이스 20-50개 수집 □ 버그 리포트에서 실패 케이스 추출 □ 각 태스크에 참고 솔루션 작성 □ Positive/Negative 케이스 균형 확인Phase 2: Harness & Grader
□ 격리된 환경 구축 (Docker or K8s cluster) □ 결정론적 Grader 우선 구현 □ LLM Grader 루브릭 정의 □ 인간 전문가와 LLM Grader 교정Phase 3: 자동화
□ CI/CD 파이프라인 통합 □ Regression 테스트 게이트 설정 □ 메트릭 대시보드 구축 □ 알림 시스템 설정Phase 4: 장기 유지
□ 주간 Transcript 리뷰 세션 □ Eval Saturation 모니터링 □ 새로운 기능별 태스크 추가 □ Eval-Driven Development 도입9. Quick Reference
Grader 선택 매트릭스
정답이 명확 Code-Based 빠르고 정확 품질 평가 LLM + Human 교정 유연하지만 검증 필요 안전성 검사 Code + LLM 조합 다층 방어 A/B 비교 Pairwise LLM 상대 평가에 적합 최종 검증 Human 금본위 메트릭 선택 가이드
코딩 pass@1, 테스트 통과율 코드 품질, 속도 대화 pass^k, CSAT 대화 길이, 에스컬레이션 연구 Groundedness, Coverage 소스 품질, 응답 시간 컴퓨터 사용 pass^k, 성공률 효율성, 오류 복구 안티패턴
단일 메트릭 집착 다른 측면 무시 다중 메트릭 조합 작은 샘플 통계적 무의미 최소 20-50 태스크 경직된 단계 체크 유효한 대안 실패 결과 중심 평가 LLM Grader 무검증 할루시네이션 인간 교정 필수 Position Bias 순서에 따른 편향 랜덤화 References
'Knowledge Base > Foundations' 카테고리의 다른 글
In-Context Learning: Transformer는 어떻게 "학습 없이 학습"하는지, 메커니즘, 한계, Agent Memory로의 확 (0) 2026.02.08 Agent Memory Architecture: Retrieval System과 Knowledge Container 선택 가이드 (0) 2026.02.07 OpenClaw Gateway - Pi Agent 시스템 분석 리포트 (0) 2026.01.31 RAG 품질 평가 전략: LLM-as-a-Judge 설계 원칙 (1) 2026.01.15 Chain-of-Intent: Intent-Driven Multi-Turn Classification (0) 2026.01.15