
AI 에이전트, 데모는 작동하는데 실전에서 가끔 실패하는 이유 — '출력'이 아닌 '경로'를 측정하는 에이전트 평가 실전 가이드
요약
AI 에이전트가 데모와 달리 실전에서 실패하는 이유를 분석하고, 최종 답변뿐만 아니라 실행 과정인 '트래젝토리(trajectory)'를 측정하는 평가 가이드를 제공합니다. LLM-as-judge와 CI 통합을 통해 에이전트의 신뢰성을 확보하는 구체적인 방법을 다룹니다.
핵심 포인트
- 최종 응답만으로는 에이전트의 중간 과정 오류를 잡아낼 수 없음
- 에이전트의 실행 경로인 '트래젝토리'를 측정하는 것이 핵심
- 결정적 체크는 기계로, 품질 평가는 LLM-as-judge로 이원화하여 채점
- 성공 여부를 인간이 미리 정의하고 반복 가능성을 검증해야 함
AI 에이전트는 만드는 것까지는 의외로 금방 도달할 수 있습니다.
LangChain이나 MCP, 혹은 직접 만든 루프(loop)를 사용해 도구(tool)를 몇 가지 건네주고 "부탁해"라고 말하면, 그럴싸하게 조사하고 그럴싸하게 움직이며 제대로 된 답을 돌려줍니다. 데모에서는 기분 좋을 정도로 잘 작동하죠.
하지만 솔직히 말씀드리면. "데모에서 작동하는 것"과 "실전에서 믿어도 되는 것"은 완전히 별개의 이야기입니다.
실전에 투입하는 순간, 이런 일이 일어납니다. 10번 중 9번은 제대로 해주는데, 10번째에는 왠지 모르게 상관없는 주문을 취소하고 있었다. 최종 답변은 올바르게 보이지만, 중간에 몰래 과금 API를 두 번이나 호출했다. 지난주까지는 완벽했는데, 모델을 새로 바꿨더니 3수째의 판단이 어긋나서 그 이후의 수들이 전부 이상해졌다….
게다가 까다로운 점은, 이런 어긋남이 "최종적인 답"만 보고 있다면 전혀 보이지 않는다는 것입니다.
여기서 많은 사람이 저지르기 쉬운 실수가, "그럼 테스트를 쓰자"라고 말하며 평소의 유닛 테스트(unit test) 감각으로 assert를 나열해 보는 것입니다. 하지만 실제로 해보면 금방 벽에 부딪힙니다. 에이전트는 매번 조금씩 다른 말투로 답변하기 때문에, 출력 문자열 일치로는 측정할 수 없습니다. 그렇다고 "왠지 괜찮아 보이네" 식의 육안 체크로는 실전에 내보낼 판단 근거가 되지 못합니다.
이 기사는 그 벽을 넘는 방법에 대한 이야기입니다. 결론부터 말씀드리면 다음과 같습니다.
만들기 전에 "무엇을 성공으로 볼 것인가"를 인간이 정의한다 -
내보내기 전에 "최종적인 답"뿐만 아니라 "그곳에 이르는 경로"를 측정한다 - OX로 측정할 수 있는 것은 기계(결정적 체크)에게, 서술형 같은 품질은 별도의 AI(LLM-as-judge)에게 채점시킨다
- "한 번 성공했다"가 아니라 "매번 할 수 있는가"까지 측정하고 나서야 비로소 실전에 내보낸다
오늘은 이것을 그대로 CI에 올릴 수 있는 코드와 프롬프트와 함께 처음부터 구축해 나가겠습니다. AI 에이전트를 이제 막 실전에 내보내고 싶은 분, 만들기는 했지만 테스트 작성법을 모르는 분들을 위해 용어도 하나씩 풀어서 설명하며 진행하겠습니다.
이 기사의 코드나 도메인(주문, 취소 등)은 모두 설명용 더미(dummy)입니다. 실존하는 인물, 조직, 데이터와는 관계가 없습니다.
먼저 용어를 통일해 두겠습니다. 이 기사에서 말하는 **에이전트(Agent)**는 대략 "혼자서 순서를 짜고 움직여 주는 신입 사원" 같은 것이라고 생각하시면 됩니다.
일반적인 LLM 호출은 "질문하면 답이 돌아오는" 단판 승부입니다. 하지만 에이전트는 다릅니다. 다음과 같은 루프를 스스로 여러 번 반복합니다.
생각하기 (다음에 무엇을 해야 할지 계획한다) -
도구 사용하기 (검색하기, DB 보기, API 호출하기. 이 "도구"를 tool 또는 function calling이라고 부릅니다) -
결과 보기 (도구가 돌려준 것을 관찰한다) -
다시 생각하기 (그것을 바탕으로 다음 수를 결정한다)
이 "생각하기 $\rightarrow$ 도구 $\rightarrow$ 관찰 $\rightarrow$ 다시 생각하기"로 이어지는 일련의 발자취를 이 기사에서는 트래젝토리 (trajectory, 경로)라고 부릅니다. 이 부분이 매우 중요하므로 꼭 기억해 두세요.
즉, 에이전트의 평가에는 보아야 할 곳이 두 군데 있습니다.
최종 응답 (final response)… 마지막에 돌아온 답. = "제출물"만 보는 채점 -
트래젝토리 (trajectory)… 그곳에 이르는 경로. = "작업 과정"도 보는 채점
많은 사람은 처음에 전자인 최종 응답만 봅니다. 하지만 실전에서 사고가 나는 것은 대개 후자의 부분입니다.
조금 더 "왜 최종 응답만으로는 안 되는지"를 깊이 파고들어 보겠습니다. 이 부분이 납득이 되면 이후의 구현이 모두 연결됩니다.
단판 승부인 LLM이라면 입력에 대해 출력을 대조하면 됐습니다. 입구와 출구만 확인하는 심플한 채점입니다.
하지만 에이전트는 입구와 출구 사이에 스스로 결정한 분기점이 여러 개 존재합니다. 3수째에 다른 도구를 선택하면 4수째 이후의 풍경은 전부 바뀝니다. 따라서 최종적인 답이 우연히 맞았더라도, 그 과정 중에 "정말로 해서는 안 될 조작"을 거쳤을지도 모릅니다.
평가 관련 실무에서 자주 쓰이는 표현을 빌리자면 이렇습니다.
올바른 최종 답이 망가진 과정을 숨겨버린다.
구체적인 사례를 들어보겠습니다. "주문 A-1001을 취소하고 환불 금액을 알려줘"라는 태스크가 있다고 가정합시다. 에이전트는 "환불 금액은 3,200엔입니다"라고 올바르게 답했습니다. 최종 응답만 본다면 만점입니다.
하지만 경로를 열어보니...
- 취소 대상을 확인해야 하는데, 갑자기
cancel_order를 호출했다 (확인 단계를 건너뜀) - 게다가
refundAPI를 왠지 모르게 2번 호출했다 (하마터면 이중 환불이 될 뻔함) - 환불 금액은 어쩌다 보니 맞았을 뿐이다 (다른 주문의 금액을 가져왔음)
이런 것들은 최종 응답의 문자열만 봐서는 절대로 잡아낼 수 없습니다. 실제로 최종 출력만으로 평가하면, 궤적(Trajectory)까지 살펴보는 평가에 비해 꽤 많은 케이스가 '겉보기에는 통과(Pass)'해 버린다는 지적도 있습니다. 관대하게 평가하게 되는 것이죠.
그래서 순서대로 이렇게 생각해 나갈 것입니다. 먼저 '무엇을 볼 것인가'의 지도를 가진다. 다음으로 '어떻게 측정할 것인가'의 도구를 선택한다. 마지막으로 '매번 수행 가능한가'까지 다듬는다. 지금부터 그 지도와 도구를 하나씩 만들어 봅시다.
에이전트 평가의 세계에서는, 보는 곳을 3가지 레이어(Layer)로 나누어 생각하면 머릿속이 정리됩니다. 이는 LangChain(LangSmith)이 정리한 분류가 이해하기 쉬우므로 그것을 기반으로 합니다.
| 레이어 | 무엇을 보는가 | 비유 | 적합한 측정 방식 |
|---|---|---|---|
| 최종 응답 (Final Response) | 마지막 답만 | 제출물만 채점 | 최종 상태 체크 / LLM-as-judge |
| ... |
중요한 것은, 이 3가지는 '하나만 선택하는' 것이 아니라는 점입니다. 2026년의 에이전트 평가에서는 3개 레이어 모두를 일급 시민으로 취급하는 것이 당연해지고 있습니다. 최종 응답만으로 만족하지 않는 것이 출발점입니다.
그리고 각 레이어에 따라 측정 방식이 달라집니다. 다음은 그 '측정 방식'에 대한 이야기입니다.
측정 방식에는 크게 두 가지가 있습니다. 이 부분을 혼동하면 비용과 정밀도 모두 문제가 생기므로 확실히 구분합시다.
**결정적 체크 (deterministic check)**는 'O/X로 채점할 수 있는 문제'입니다. 올바른 도구를 선택했는가, 인자(Argument)의 타입은 맞는가, 마지막에 DB 상태가 이렇게 되어 있는가. 답이 일의적으로 결정되므로 기계로 대조할 수 있습니다. 빠르고, 몇 번을 반복해도 같은 결과가 나옵니다 (재현성).
LLM-as-judge는 '다른 선생님에게 서술형을 채점받는' 이미지입니다. 답이 일의적으로 결정되지 않는 개방적인 품질—예를 들어 '사용자의 의도에 제대로 부합했는가', '설명이 이해하기 쉬웠는가'—을, 채점용 LLM에게 루브릭(Rubric, 채점 기준)을 전달하여 평가하게 하는 방법입니다.
구분 원칙은 심플하게 이렇습니다.
| 측정 대상 | 결정적 체크 | LLM-as-judge |
|---|---|---|
| 도구 선택 (올바른 도구인가) | ◎ 특기 | 사용하지 않음 |
| ... |
O/X로 측정할 수 있는 것을 굳이 LLM에게 채점하게 하지 마세요. 이것은 사소해 보이지만 매우 중요합니다. LLM에게 채점을 맡기면 느리고, 비용이 들며, 무엇보다 채점 자체가 흔들립니다. 결정적으로 측정할 수 있는 것은 결정적으로 측정합니다. 판단이 필요한 부분에만 LLM의 도움을 받습니다. 이도류(Two-track) 전략인 셈입니다.
그럼 이제부터 실제로 손을 움직여 봅시다.
무엇을 측정하든, 우선 경로(Trajectory)가 기록되어 있지 않으면 시작할 수 없습니다. 에이전트가 '생각했다·도구를 사용했다·결과를 보았다'를 나중에 채점할 수 있는 형태로 남겨두어야 합니다. 이것이 토대입니다.
TypeScript로 최소한의 기록 타입을 만들어 보겠습니다.
type ToolCall = {
name: string; // 사용한 도구의 이름
args: Record<string, unknown>; // 해당 도구에 전달한 인자
...
};
하고 있는 일은 단순합니다. 한 수 움직일 때마다 그 수의 내용을 배열에 추가할 뿐입니다. 하지만 이 steps의 나열이야말로 나중에 보물창고가 됩니다.
한 가지만 미리 주의를 드리겠습니다. 이 발자취에 비밀번호나 API 키, 개인정보를 섞지 마세요. 트레이스(Trace)는 로그에 남고, 채점을 위해 AI에게 전달할 수도 있습니다. 'AI에게 전달한다 = 외부로 내보낸다'라는 마음가짐으로, 기록하기 전에 마스킹(Masking)해 두어야 합니다. 이 부분은 나중에 안전(Security) 장에서 다시 다루겠습니다.
발자취가 남았다면, 우선 결정적 체크부터 시작합니다. 가장 효과적인 것은 도구 호출(Tool Call) 대조입니다.
도구의 정확성은 엄격함의 단계에 따라 3가지로 나누어 살펴봅니다.
- 도구 선택 (Tool Selection)… 애초에 올바른 도구를 선택했는가 -
- 인자 구축 (Argument Construction)… 도구는 맞더라도, 인자가 망가져 있지 않은가 (타입이 다르거나, 필수 값이 누락되었거나, 어디에도 없는 값을 지어내고 있는가) -
- 파라미터/출력 검증 (Parameter/Output Verification)… 인자와 반환값을 통틀어 타당한지 확인
특히 세 번째인 '날조(捏造)'——사용자의 요청에도, 문맥에도 없는 값을 그럴싸하게 임의로 만들어 인자에 넣어버리는 것——은 에이전트가 자주 저지르는 사고입니다. 이 부분을 기계적으로 잡아냅니다.
Python으로, 기대했던 도구의 순서와 실제 트래젝토리 (Trajectory)를 대조해 봅시다.
from dataclasses import dataclass, field
@dataclass
class ExpectedCall:
...
포인트는, 이 과정에서 LLM을 전혀 사용하지 않는다는 점입니다. 집합과 딕셔너리의 비교일 뿐입니다. 그래서 순식간에 끝나고, CI에서 수백 번 돌려도 결과가 흔들리지 않습니다. "올바른 도구를, 올바른 인자로 호출했는가"는 바로 이런 결정적 체크 (Deterministic Check)의 독무대입니다.
다음도 결정적 체크입니다. 트래젝토리의 "순서"를 살펴보는 것만으로도 구조적인 이상함을 발견할 수 있습니다. 무한 루프, 동일한 도구의 연타 (헛바퀴 돌기), 불필요한 되돌아오기. 이런 것들은 내용을 깊이 읽지 않아도, 순서를 보는 것만으로 알 수 있습니다.
def detect_path_anomalies(trajectory: list[dict], max_steps: int = 20) -> list[str]:
"""실행 경로의 "구조적인" 이상함을 LLM 없이 찾아낸다.
루프, 헛바퀴 돌기, 연타는 순서를 보는 것만으로 검출할 수 있다."""
...
이러한 "경로의 타당성" 체크는 트레이스 (Trace)를 분석하는 것만으로 계산할 수 있으며, LLM의 판단이 필요하지 않습니다. 게다가 사실 실제 환경에서의 사고 상당 부분은, 이 "스텝이 너무 많아짐", "같은 일을 반복함"과 같은 구조적인 미아 상태인 경우가 많습니다. 저렴하고 빠르게 효과를 볼 수 있는, 가성비 좋은 체크입니다.
자, 여기서 발상을 한 단계 점프시켜 보겠습니다.
최종 응답을 채점한다고 하면, 자기도 모르게 "돌아온 문장"을 보고 싶어집니다. 하지만 문장은 매번 표현이 달라지기 때문에 문자열 일치로는 측정할 수 없습니다. 그럼 어떻게 해야 할까요?
답변 문장이 아니라, "세계가 제대로 변했는가"로 합격 여부를 결정한다.
이는 코딩 에이전트 평가에 사용되는 SWE-bench Verified의 발상과 같습니다. 그것은 "AI가 답변한 설명문"을 채점하는 것이 아닙니다. "AI가 작성한 코드로 테스트가 통과(Green)되었는가"라는 결과의 상태만으로 기계 판정을 내립니다. 답의 겉모습이 아니라 사실로 측정하는 것입니다.
아까의 "주문을 취소해줘"라는 태스크를 샌드박스 (Sandbox) 상의 DB 상태로 판정해 보겠습니다.
def evaluate_final_state(sandbox_db: dict, task: dict) -> bool:
"""출력의 문자열 일치가 아니라, "세계가 올바르게 변했는가"로 합격 여부를 결정한다.
예: "주문 A-1001을 취소해줘"라면,
...
주목해야 할 부분은 no_side_effect 부분입니다. "대상 주문이 취소되었는가"뿐만 아니라, "관련 없는 주문까지 휘말리게 하지 않았는가"도 함께 확인합니다. 목표 달성과 불필요한 일을 하지 않았다는 것. 이 두 가지를 세트로 측정하는 것이 최종 상태 체크의 핵심입니다.
참고로, 여기서 사용하고 있는 것이 샌드박스 (격리된, 일회용 가상 환경)라는 점도 중요합니다. 실제 운영 DB에서 평가를 돌리면 말 그대로 진짜 주문이 삭제되어 버립니다. 평가는 반드시 망가뜨려도 괜찮은 상자 안에서 진행해야 합니다. 이 내용은 뒤의 안전(Safety) 장에서 다시 다루겠습니다.
여기까지는 전부 결정적으로 측정할 수 있는 것들이었습니다. 하지만 아무리 해도 기계로는 측정할 수 없는 것이 있습니다. "사용자의 의도에 제대로 부응했는가", "설명이 무리가 없었는가". 이런 서술형 채점은 LLM-as-judge의 차례입니다.
채점용 LLM에게 루브릭 (Rubric, 채점 기준) + 태스크 + 트래젝토리 (+ 있다면 참조 해답)를 전달하여 점수와 이유를 받습니다.
import json
JUDGE_RUBRIC = """당신은 AI 에이전트의 채점자입니다. 다음 3개 축을 1~5점으로 채점하고, 이유를 짧게 서술하세요.
- goal_alignment: 사용자의 목표에 부합했는가
...
다만, 여기서 솔직하게 말씀드려야 할 것이 있습니다. LLM-as-judge는 만능이 아닙니다.
몇몇 연구에서는 일반적인 LLM-as-judge의 채점이 인간의 판단과 일치하는 정도가 67할에 불과하다는 보고가 있습니다. 즉, 34번 중 1번은 인간과 다른 점수를 매기고 있다는 뜻입니다. 최근에는 채점 측에서도 트레이스(trace)를 따라가며 근거를 확인하는 "Agent-as-a-Judge"라는 기법을 통해 인간과의 일치율을 9할 가까이 끌어올렸다는 이야기도 나오고 있지만, 그럼에도 "판정을 전부 LLM에 통째로 맡기고 끝내도 될" 정도의 정밀도는 아닙니다.
따라서 다음과 같이 활용해야 합니다.
O/X로 측정 가능한 것(도구 이름, 인자(argument), 최종 상태)은 결정적인 체크로 돌린다. judge에게 넘기지 않는다. judge는 "개방적인 품질"에만 사용한다.
낮은 점수가 나온 것은 인간이 다시 확인한다. judge의 점수는 "인간이 봐야 할 것을 좁혀주는 탐지기"이지, 최종 판정이 아니다.
judge를 "또 한 명의 완벽한 심판"이라고 생각하면 사고가 납니다. "봐야 할 곳을 알려주는 조수" 정도로 생각하는 것이 딱 적당합니다.
이 부분이 이 글에서 가장 전달하고 싶은 핵심입니다.
에이전트를 한 번 실행해서 성공했다고 가정해 봅시다. "좋아, 작동한다". …하지만 그것이 정말 신뢰해도 좋은 성공일까요?
에이전트는 동일한 태스크라도 매번 완전히 똑같이 움직이지 않습니다 (이 부분은 비결정성 (non-determinism)에 관한 이야기입니다). 따라서 "한 번 성공했다"는 것이 "매번 성공한다"를 전혀 보장해주지 않습니다.
여기서 등장하는 것이 pass^k라는 지표입니다. $\tau$-bench (Princeton과 Sierra가 만든, 에이전트 평가의 대표적인 벤치마크)에서 사용되는 개념으로, 다음과 같이 대비하면 이해하기 쉽습니다.
pass@k… k번 중 한 번이라도 성공하면 OK (관대한 기준)
pass^k… k번 전부 성공했는가 (엄격한 기준. 신뢰성의 지표)
pass^k = p^k
로, 성공률을 계속 곱해나가기 때문에 지수적으로 수치가 줄어듭니다.
def pass_hat_k(run_task, task, k: int = 8) -> dict:
"""동일한 태스크를 k번 실행하여, k번 전부 성공했는지 확인한다.
pass@k(한 번이라도 성공하면 OK)보다 훨씬 엄격한 신뢰성 지표."""
...
주석의 계산을 자세히 살펴보세요. 한 번당 90%나 성공하는 꽤 우수한 에이전트라도, 8회 연속으로 전부 성공할 확률은 **대략 43%**까지 떨어집니다 (이 수치는 독립 시행을 가정한 독자적인 계산입니다).
"9할 정도 움직이면 충분하겠지"라고 생각했던 것이 "8번 중 1번은 어디선가 실패한다"라는 현실로 변합니다. 실전(production)은 바로 이 "매번"의 세계입니다. 단 한 번의 데모로 작동한 것에 안심하지 말고, 동일한 태스크를 여러 번 실행하여 "매번 할 수 있는지"까지 확인해야 합니다. 이것이 신뢰할 수 있는 서비스를 내놓기 위한 마지막 점검입니다.
참고로 $\tau$-bench의 발전형인 $\tau^2$-bench에서는 에이전트와 사용자가 각각 별도의 도구를 갖는 "dual-control"이라는 설정도 등장하며, "사용자가 없는 상태(추론력만 있는 상태)"와 "협업이 필요한 상태"를 나누어 진단할 수 있습니다. 자신의 에이전트가 어느 쪽에서 취약한지 구분하고 싶을 때 유용한 힌트가 됩니다.
평가 스위트(evaluation suite)가 완성되었다면, 마지막은 시스템으로 돌릴 차례입니다. 손으로 생각날 때마다 실행하는 것이 아니라, Pull Request가 발생할 때마다 자동으로 실행하여 품질이 떨어지면 멈추게 합니다. 이 "멈추는 검문소"를 회귀 게이트(regression gate)라고 부릅니다.
# .github/workflows/agent-eval-gate.yml
name: agent-eval-gate
on: [pull_request]
...
여기서 의식해야 할 것은 게이트의 철학입니다. "절대값이 낮다"는 이유로 책망하지 마세요. "이전보다 떨어졌다"는 사실만으로 멈추세요. 처음부터 만점인 에이전트는 없으므로, 갑자기 높은 기준을 적용해 차단하면 아무도 평가를 돌리지 않게 됩니다. 베이스라인(baseline)을 설정하고, 거기서 떨어졌을 때만 검문소를 닫으세요. 올라갔다면 베이스라인을 업데이트합니다. 이 "떨어졌을 때만 멈추는" 설계라면 무리 없이 지속할 수 있습니다.
과거의 낮은 점수를 비난하기 위한 게이트가 아닙니다. 미래의 내가 조용히 성능 저하(degradation)를 겪지 않도록 돕는, 다정한 감시자인 것입니다.
여기까지 왔으니, 다시 한번 역할 분담을 정리하겠습니다. 에이전트 평가에서도 축은 언제나와 같습니다. "무엇을·왜"는 인간이, "어떻게"는 AI가 담당합니다.
| 공정 | 인간 (What / Why) | AI (How) |
|---|---|---|
| 성공의 정의 | 「무엇을 성공으로 볼 것인가」, 「허용되는 경로와 허용되지 않는 경로」를 결정함 | — |
| ... |
채점 작업 그 자체는 점점 더 AI에게 맡겨도 좋습니다. 하지만 「무엇을 성공이라 부를 것인가」, 「어디서부터 인간이 승인할 것인가」와 같은 가치 판단만은 AI에게 넘겨주어서는 안 됩니다. 이것이 바로 평가를 설계하는 인간의 업무입니다.
그 「인간의 업무」를 돕기 위해, 그대로 사용할 수 있는 프롬프트 3개를 놓아두겠습니다.
당신은 AI 에이전트의 테스트 설계를 돕는 파트너입니다. 최종 판단은 저(인간)가 합니다.
다음 에이전트의 「대표적인 태스크」 10개를 골든 태스크 (Golden Task)로 설계해 주세요.
각 태스크에 반드시 포함할 것:
...
다음 LLM-as-judge용 루브릭 (Rubric)을 리뷰해 주세요. 최종 판단은 제가 합니다.
관점:
- OX로 측정 가능한 것(도구 이름, 인자, 최종 상태)이 섞여 있지 않은가
...
다음 실패한 트래젝토리 (Trajectory, 발자취)를 읽고, 원인을 분류해 주세요. 최종 판단은 제가 합니다.
분류 후보:
- 도구 선택 실수 / 인자 실수 (타입, 필수 항목 누락, 값의 날조)
...
3개 모두 머리에 「최종 판단은 제가 합니다」라고 적혀 있는 것이 핵심입니다. AI를 조사 담당자·준비 담당자로 고정하고, 판단은 수중에 남겨둡니다. 이것만으로도 사고는 크게 줄어듭니다.
평가는 「안전한 곳에서 수행한다」는 것이 대전제입니다. 이 원칙을 벗어나면, 품질을 측정해야 할 작업이 가장 큰 사고 원인이 됩니다.
운영 환경의 연결을 평가에 사용하지 마세요. 평가는 반드시 샌드박스 (Sandbox, 일회용 가상 환경)에서 수행해야 합니다. 실제 DB를 대상으로 취소나 삭제 태스크를 실행하면 진짜 데이터가 날아갑니다.
- 비가역적인 조작 (전송, 결제, 삭제, 공개, 배포)은 평가에서도 모크 (Mock) 또는 샌드박스를 사용하세요. 그리고 운영 환경에서 이를 허용할지 여부는 마지막에 인간이 승인하도록 설계해야 합니다.
- 트레이스 (Trace)나 평가 데이터에 개인정보·비밀정보를 포함하지 마세요. 트레이스는 로그에 남으며, LLM-as-judge에 전달하면 외부 모델로도 전달됩니다. 「AI에게 전달한다 = 외부로 유출한다」는 뜻입니다. 테스트 데이터는 전부 더미 (Dummy)로 만드세요.
- 외부에서 들어온 텍스트 (웹, 파일, 사용자 입력)는 명령이 아닌 데이터로 취급하세요. 평가 태스크 안에 섞여 들어온 「지금까지의 지시를 무시해」라는 문구에 대해, 채점 측도 에이전트 측도 따르지 않도록 해야 합니다.
평가는 에이전트를 구속하기 위한 것이 아닙니다. 안심하고 고삐를 늦추기 위한 장치입니다. 안전한 상자가 있기 때문에 과감하게 맡길 수 있는 것입니다. 순서가 뒤바뀌지 않도록 주의해야 합니다.
마지막으로, 빠지기 쉬운 함정과 철수 라인을 정리하겠습니다.
| 함정 | 발생하는 현상 | 대책 |
|---|---|---|
| 최종 응답만 측정함 | 중간의 위험한 경로를 놓침 | 트래젝토리 (Trajectory)도 측정함 |
| ... |
그리고 세 가지 철수 라인입니다.
성공률이 더 이상 올라가지 않는다면 일단 멈추세요. 평가 스위트 (Evaluation Suite)를 늘리기보다, 실패 원인 분류 (프롬프트 3)에 시간을 쓰세요.
- Judge의 점수와 인간의 감각이 계속 어긋난다면 루브릭을 의심하세요. 모델을 수정하기 전에 기준을 먼저 바로잡아야 합니다.
- 비가역 조작이 포함된 태스크는 pass^k가 충분히 높아질 때까지 인간의 승인을 해제하지 마세요. 자동화 단계 직전에서 반드시 멈춰야 합니다.
길어졌습니다만, 마지막으로 한 가지만 말씀드리겠습니다.
에이전트 평가는 솔직히 지루합니다. 새로운 기능을 추가하는 것이 훨씬 즐겁고 눈에 보이는 성과로 이어집니다. 평가 스위트를 작성해도 누군가에게 「대단하다」는 말을 듣는 것도 아닙니다.
하지만 잠시 생각해 보셨으면 합니다.
오늘 당신이 작성한 10개의 골든 태스크는, 반년 후 모델을 교체했을 때 조용히 성능 저하 (Degradation)를 알려줄 것입니다. 오늘 설치한 회귀 게이트 (Regression Gate)는, 심야의 릴리스 상황에서 당신이 미처 알아차리지 못한 차이를 릴리스 전에 막아줄 것입니다.
평가 스위트는 미래의 자신에게 남기는 쪽지입니다. 게다가 비난하기 위한 쪽지가 아닙니다. 「여기 위험하니까 조심해」라고 살며시 건네주는 배려 섞인 쪽지입니다. 과거의 낮은 점수를 탓하기 위한 도구도 아닙니다. 그저 내일의 내가 「고마워요」라고 말할 수 있는 상태를 오늘 만들어 두는 것, 그것뿐입니다.
그리고 이 평가 스위트는 한 번 작성하면 사라지지 않습니다. 태스크가 늘어날수록, 실패 패턴을 알게 될수록 점점 더 똑똑하게 쌓여갑니다. 일회성 수작업과는 달리, 당신의 자산 (Code as Capital)이 되어 갑니다. 무엇을 성공이라 부를지 결정하는 눈—그것은 AI에게 넘겨줄 수 없는, 당신의 업무입니다.
마지막으로, 내일부터 실천할 수 있는 4단계만 남겨두겠습니다.
발자국을 남기세요. 에이전트의 도구 호출 (Tool Call)을 우선 하나의 태스크에 대해 기록해 보세요 (구현 ①) -
단 하나의 결정적 체크를 작성하세요. "이 도구를, 이 인자 (Argument)로 호출했는가"를 대조합니다 (구현 ②) -
최종 상태로 합격 여부를 결정하세요. 출력 문자열이 아니라, "세계가 어떻게 변했는가"로 판정합니다 (구현 ④) -
동일한 태스크를 8번 반복하세요. pass^k를 통해 "매번 수행할 수 있는가"를 확인한 뒤 실전에 투입합니다 (구현 ⑥)
만드는 과정은 이제 AI가 충분히 도와줄 것입니다. 그렇기에 더욱 "믿어도 되는지를 측정하는 눈"을 우리가 직접 길러나가야 합니다. 오늘의 작은 평가가 분명 내일의 당신을 도와줄 것입니다.
- LangChain / LangSmith Docs — Application-specific evaluation approaches (최종 응답 / 단일 단계 / 트래젝토리 (Trajectory)의 3가지 분류)
- LangChain — "LLM Evaluation Framework: Trajectories vs. Outputs"
- Braintrust — "AI agent evaluation: A practical framework for testing multi-step agents" (결정적 체크 vs LLM-as-judge, 도구 호출의 정확성, 실행 경로의 타당성)
- sierra-research/tau2-bench (GitHub) / τ²-Bench: Evaluating Conversational Agents in a Dual-Control Environment (arXiv:2506.07982)
- "Towards a Science of AI Agent Reliability" (arXiv:2602.16666) — pass^k와 신뢰성
- "Agent-as-a-Judge" (arXiv:2601.05111) — LLM-as-judge의 인간 일치율과 개선
- Confident AI / MLflow — 2026년의 에이전트 평가 메트릭 (Metrics), LLM-as-a-Judge의 실무
- SWE-bench Verified — 코딩 에이전트를 "테스트를 통과하는가"라는 최종 상태로 기계 판정
(본 기사의 코드와 도메인은 모두 설명을 위한 더미(Dummy)입니다. 실존하는 인물, 조직, 데이터와는 관계가 없습니다.)
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기