AI 에이전트 모니터링·평가 체계 구축: 프로덕션에서 조용히 망가지는 품질을 DeepEval·Langfuse로 잡아내는 법
AI 에이전트를 로컬에서 잘 돌아가게 만드는 건 생각보다 어렵지 않습니다. 그런데 그걸 프로덕션에 올리고 나서 "이 에이전트가 지금 제대로 동작하고 있나?"라는 질문에 자신 있게 답할 수 있는 팀은 의외로 드뭅니다. 저도 처음 에이전트를 배포했을 때, 며칠이 지나서야 특정 케이스에서 에이전트가 전혀 엉뚱한 도구를 호출하고 있었다는 걸 뒤늦게 발견했던 기억이 있습니다. 로그도 없고, 추적도 없었으니까요.
LangChain의 2026년 보고서에 따르면 이미 57%의 조직이 에이전트를 프로덕션에 배포하고 있습니다. 동시에 응답자의 32%가 "출력 품질"을 배포 최대 장벽으로 꼽았습니다. 만드는 건 하는데 믿을 수 있는지를 모르겠다는 거죠.
이 글을 읽고 나면, 기존 에이전트에 트레이싱을 붙이고 PR마다 품질 게이트가 동작하는 CI 파이프라인을 구성하는 구체적인 방법을 손에 넣게 됩니다. 단순한 LLM 응답 품질 체크와는 다릅니다. 에이전트는 다단계 추론, 외부 도구 호출, 장기 맥락 유지, 자율적 의사결정을 하기 때문에 평가 체계도 그만큼 정교해야 합니다. 코드를 바로 복사해서 써볼 수 있는 수준으로 풀어드릴게요.
핵심 개념
평가 인프라를 쌓는 데는 순서가 있습니다. 먼저 무엇을 측정할지(평가 차원), 누가 평가할지(LLM-as-a-Judge), 어디서 추적할지(트레이싱), 어떤 종합 지표로 볼지(CLEAR 프레임워크) 순서로 살펴보겠습니다.
에이전트 평가는 왜 일반 LLM 평가와 다른가
일반 LLM 평가는 간단합니다. 입력 넣고 출력 보고, 좋으면 합격. 그런데 에이전트는 다릅니다. 에이전트는 목표를 받으면 스스로 계획을 세우고, 여러 도구를 순서대로 호출하고, 중간 결과를 보고 다음 행동을 결정합니다. 이 긴 실행 경로 어딘가에서 잘못되면, 최종 결과만 보고는 원인을 찾을 수 없습니다.
그래서 에이전트 평가는 크게 여섯 가지 차원을 봅니다.
| 평가 차원 | 설명 | 왜 중요한가 |
|---|---|---|
| 태스크 완료율 | 주어진 목표를 정확히 달성했는지 | 가장 기본적인 합격/불합격 기준 |
| 추론 경로 품질 | 결과뿐 아니라 올바른 경로로 도달했는지 | 운 좋게 맞은 것과 논리적으로 맞은 것을 구분 |
| 할루시네이션 비율 | 사실과 다른 내용을 생성하는 빈도 | 고객 응대·금융·의료 등 민감 도메인에서 치명적 |
| 도구 호출 정확도 | 적절한 도구를 적절한 인자로 호출하는지 | 잘못된 도구 호출은 연쇄 오류를 만들어냄 |
| 비용·지연 | 토큰 사용량, 응답 시간, 운영 비용 | 정확도만 보다가 인프라 비용 4배 나온 팀이 실제로 있습니다 |
| 안전성·컴플라이언스 | 편향, 유해 콘텐츠, 정책 준수 여부 | 규제 산업에서는 배포 전 필수 체크 |
오프라인 평가 vs 온라인 평가: 오프라인 평가는 미리 만들어둔 골든 데이터셋(예상 입력과 올바른 출력을 쌍으로 모아둔 기준 집합)으로 배포 전에 돌리는 것이고, 온라인 평가는 실제 프로덕션 트래픽을 실시간으로 추적하는 것입니다. 두 가지를 함께 운영하는 것이 이상적이지만, 현실적으로 옵저버빌리티(온라인)부터 먼저 정착시키는 팀이 훨씬 많습니다.
LLM-as-a-Judge: 사람 대신 LLM이 평가한다
솔직히 Human Eval은 느리고 비쌉니다. 매 배포마다 사람이 수백 개 응답을 검토하는 건 현실적이지 않죠. 그래서 지금 업계 표준처럼 자리잡은 게 LLM-as-a-Judge, 즉 강력한 LLM(GPT-4o, Claude Opus 등)을 평가자로 쓰는 방식입니다.
기본 아이디어는 단순합니다. 평가 LLM에게 "이 에이전트 응답이 다음 기준을 만족하는지 점수로 매겨줘"라고 시키는 겁니다. DeepEval, Langfuse, Arize Phoenix 같은 주요 플랫폼이 모두 이를 지원하고, 직접 구현하는 것도 어렵지 않습니다.
from deepeval import evaluate
from deepeval.metrics import (
AnswerRelevancyMetric,
HallucinationMetric,
ToolCorrectnessMetric,
)
from deepeval.test_case import LLMTestCase, ToolCall
test_case = LLMTestCase(
input="서울 날씨 알려줘",
actual_output="서울은 현재 맑고 기온은 22도입니다.",
context=["weather_api 호출 결과: {'city': 'Seoul', 'temp': 22, 'condition': 'clear'}"],
tools_called=[
ToolCall(name="weather_api", input_parameters={"city": "Seoul"})
],
expected_tools=[
ToolCall(name="weather_api", input_parameters={"city": "Seoul"})
],
)
metrics = [
AnswerRelevancyMetric(threshold=0.7, model="gpt-4o"),
HallucinationMetric(threshold=0.3, model="gpt-4o"),
ToolCorrectnessMetric(), # LLM API 비용 없이 결정론적으로 평가
]
evaluate(test_cases=[test_case], metrics=metrics)자기 일관성 편향(Self-Consistency Bias) 주의: GPT-4o로 만든 에이전트를 GPT-4o로 평가하면 점수가 부풀 수 있습니다. 같은 회사 모델이 자사 출력을 우호적으로 평가하는 경향이 있기 때문입니다. 가능하면 에이전트 모델과 다른 회사 모델을 평가자로 쓰는 것을 권장합니다. GPT-4o 에이전트라면 Claude Opus로, Claude 에이전트라면 GPT-4o로 평가하는 식입니다.
OpenTelemetry로 에이전트 실행 경로 추적하기
에이전트 디버깅에서 가장 중요한 건 "어떤 순서로 무엇을 했는가"를 볼 수 있는 트레이스(trace — 하나의 요청이 시작부터 끝까지 거쳐간 전체 실행 경로)입니다. Amazon이 프로덕션 에이전트 구축에서 얻은 교훈 중 하나가 바로 이겁니다. "단계별 추론 로그 없이는 디버깅이 불가능하다."
OpenTelemetry는 분산 시스템에서 트레이스를 수집하는 업계 표준입니다. 에이전트 추적에서는 트레이스를 세 계층의 스팬(span — 트레이스를 구성하는 개별 작업 단위)으로 나눠 구성합니다.
- 루트 스팬: 사용자 요청 전체 (에이전트 실행 시작~종료)
- 추론 스팬: LLM 호출 단위 (프롬프트, 토큰 수, 지연 시간 기록)
- 도구 스팬: 외부 API나 함수 호출 단위 (입력 파라미터, 반환값, 소요 시간)
Langfuse는 이 계층을 Python 데코레이터 하나로 바로 적용할 수 있습니다.
from langfuse.decorators import observe, langfuse_context
from langfuse.openai import openai # OpenAI 클라이언트 래핑
@observe() # 루트 스팬: 이 함수 전체가 하나의 트레이스로 기록됨
async def run_agent(user_query: str) -> str:
langfuse_context.update_current_trace(
name="customer-support-agent",
user_id="user-123",
tags=["production", "v2.1"],
)
# 도구 스팬: 외부 도구 호출을 별도로 기록
with langfuse_context.span("tool:search_kb"):
kb_results = await search_knowledge_base(user_query)
# 추론 스팬: LLM 호출 시 토큰·비용이 자동으로 기록됨
response = openai.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "당신은 고객 지원 에이전트입니다."},
{"role": "user", "content": user_query},
{"role": "assistant", "content": f"검색 결과: {kb_results}"},
],
)
return response.choices[0].message.content이렇게 설정하면 Langfuse 대시보드에서 각 사용자 요청에 대해 에이전트가 어떤 도구를 어떤 순서로 호출했는지, 각 단계에서 몇 토큰을 썼는지, 어디서 지연이 생겼는지 한눈에 볼 수 있습니다.
CLEAR 프레임워크: 정확도 하나만 보면 망한다
저도 처음엔 "에이전트가 맞는 답을 내면 됐지"라고 생각했는데, 실제로 운영하다 보면 정확도 외에도 신경 써야 할 게 훨씬 많습니다. arXiv 논문 "Beyond Accuracy: Multi-Dimensional Framework for Evaluating Enterprise Agentic AI"에 따르면, 정확도만 최적화한 에이전트는 비용 인식 대안보다 4.4~10.8배 비쌀 수 있습니다.
CLEAR 프레임워크는 이 논문에서 제안한 엔터프라이즈 환경의 다차원 평가 방식입니다.
| 항목 | 의미 | 측정 방법 |
|---|---|---|
| Cost | 토큰 비용, 인프라 비용 | 요청당 평균 비용, 월 총 비용 |
| Latency | 응답 시간 | P50/P95/P99 지연 분포 |
| Efficacy | 목표 달성률 | 태스크 완료율, 사용자 만족도 |
| Assurance | 안전성, 컴플라이언스 | 정책 위반 비율, 편향 점수 |
| Reliability | 안정성, 일관성 | 오류율, 동일 입력 응답 일관성 |
다섯 항목을 한 화면에서 같이 봐야 합니다. 그게 핵심입니다.
실전 적용
아래 세 예시는 동일한 고객 지원 에이전트를 기준으로 합니다. DeepEval로 CI 품질 게이트를 먼저 만들고, Langfuse로 프로덕션 추적을 붙이고, 마지막으로 도구 호출 정확도 평가를 추가하는 순서입니다. 이 흐름대로 따라가면 하나의 에이전트에 완성된 평가 체계가 갖춰집니다.
참고: 아래 예시는 모두 Python 기준입니다. JavaScript/TypeScript 에이전트를 운영 중이라면 각 도구의 JS SDK 공식 문서를 함께 참고하는 것을 권장합니다.
DeepEval로 CI/CD에 에이전트 품질 게이트 설정하기
"배포 전 검증"이 말처럼 쉽지 않은 이유 중 하나는 기존 테스트 인프라에 LLM 평가를 어떻게 끼워 넣느냐입니다. DeepEval은 "pytest for LLMs"를 표방하는데, 실제로 pytest 문법 그대로 씁니다. 기존 CI/CD에 붙이기 아주 좋습니다.
# test_agent_eval.py
import pytest
from deepeval import assert_test
from deepeval.metrics import (
AnswerRelevancyMetric,
HallucinationMetric,
GEval,
)
from deepeval.test_case import LLMTestCase, ToolCall
# --- 실제 에이전트 모듈로 교체하세요 ---
async def run_customer_support_agent(query: str) -> str:
return "30일 이내 구매 시 영수증 지참하면 전액 환불 가능합니다."
# ----------------------------------------
# 골든 데이터셋: 핵심 시나리오를 입력-컨텍스트-기대출력 쌍으로 정의
TEST_CASES = [
{
"input": "환불 정책이 어떻게 되나요?",
"context": ["30일 이내 영수증 지참 시 전액 환불 가능"],
"expected_output": "30일 이내 구매 시 환불 가능",
},
{
"input": "주문 취소는 어떻게 하나요?",
"context": ["배송 시작 전 앱에서 직접 취소 가능, 배송 후는 반품 절차 진행"],
"expected_output": "배송 시작 전 앱에서 취소 가능",
},
]
@pytest.mark.parametrize("case", TEST_CASES)
@pytest.mark.asyncio
async def test_customer_support_agent(case):
actual_output = await run_customer_support_agent(case["input"])
test_case = LLMTestCase(
input=case["input"],
actual_output=actual_output,
context=case["context"],
expected_output=case["expected_output"],
)
# 커스텀 평가 기준도 자연어로 정의할 수 있는데, 처음 짰을 때 꽤 놀랐습니다
policy_adherence = GEval(
name="PolicyAdherence",
criteria="응답이 제공된 컨텍스트의 정책을 정확히 반영하는가",
evaluation_params=["actual_output", "context"],
threshold=0.8,
)
assert_test(
test_case,
metrics=[
AnswerRelevancyMetric(threshold=0.7, model="gpt-4o-mini"),
HallucinationMetric(threshold=0.2, model="gpt-4o-mini"),
policy_adherence,
],
)이 파일을 GitHub Actions에 등록하면 PR마다 에이전트 품질이 자동 검증됩니다. 메트릭 점수가 임계값 아래로 떨어지면 배포가 막히는 구조입니다.
# .github/workflows/agent-eval.yml
name: Agent Evaluation
on: [pull_request]
jobs:
evaluate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: pip install deepeval langfuse openai pytest pytest-asyncio
- name: Run agent evaluation
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
DEEPEVAL_API_KEY: ${{ secrets.DEEPEVAL_API_KEY }}
run: deepeval test run test_agent_eval.pyLangfuse로 프로덕션 에이전트 실시간 추적하기
오프라인 평가로 배포를 통과했다고 끝이 아닙니다. 실제 사용자는 테스트셋에서 상상하지 못한 방식으로 에이전트를 씁니다. 프로덕션 트래픽을 실시간으로 추적하면서 품질 저하를 감지하는 게 온라인 평가의 역할입니다.
아래는 FastAPI나 Starlette 같은 비동기 웹 서버 환경 기준으로 작성된 코드입니다. asyncio.create_task()는 실행 중인 이벤트 루프가 있어야 동작하니, 동기 환경이라면 별도 비동기 처리 방식을 검토하는 것을 권장합니다.
from langfuse import Langfuse
from langfuse.decorators import observe, langfuse_context
import asyncio
import json
langfuse = Langfuse()
@observe()
async def run_agent_with_online_eval(user_id: str, query: str) -> str:
# --- 실제 에이전트 호출로 교체하세요 ---
result = f"'{query}'에 대한 고객 지원 응답입니다."
# ----------------------------------------
trace_id = langfuse_context.get_current_trace_id()
# 사용자 응답 지연 없이 백그라운드에서 품질 평가 실행
asyncio.create_task(
run_online_evaluation(trace_id, query, result)
)
return result
async def run_online_evaluation(trace_id: str, query: str, response: str):
"""백그라운드에서 LLM-as-a-Judge 평가 실행"""
from openai import AsyncOpenAI
client = AsyncOpenAI()
eval_prompt = f"""
사용자 질문: {query}
에이전트 응답: {response}
다음 기준으로 응답 품질을 0.0~1.0 사이 점수로만 답해주세요:
- 질문에 대한 관련성 (0.4)
- 정확성과 사실 기반 (0.4)
- 간결하고 명확한 설명 (0.2)
JSON 형식: {{"score": 0.85, "reason": "간략 이유"}}
"""
eval_response = await client.chat.completions.create(
model="gpt-4o-mini", # 비용 절감을 위해 미니 모델 활용
messages=[{"role": "user", "content": eval_prompt}],
response_format={"type": "json_object"},
)
result = json.loads(eval_response.choices[0].message.content)
langfuse.score(
trace_id=trace_id,
name="online-quality-score",
value=result["score"],
comment=result["reason"],
)
if result["score"] < 0.6:
await send_alert(trace_id, result["score"], result["reason"])행동 드리프트(Behavioral Drift): 에이전트가 처음 배포될 때와 달리 시간이 지나면서 응답 패턴이 조금씩 달라지는 현상입니다. 단순 오류가 아니라 미묘한 변화라 잡아내기 어렵습니다. 평균 품질 점수가 주 단위로 서서히 낮아지고 있다면 드리프트를 의심해볼 수 있습니다.
도구 호출 정확도 전용 테스트셋 만들기
에이전트에서 가장 디버깅하기 어려운 문제가 도구 호출 관련입니다. 에이전트가 search_products 대신 search_orders를 부르거나, 올바른 도구를 불렀지만 파라미터를 잘못 넘기는 경우, 최종 응답만 봐서는 원인을 알 수 없습니다. 첫 번째 예시에서 사용한 ToolCall을 그대로 활용해 도구 호출 전용 테스트셋을 만들 수 있습니다.
from deepeval.test_case import LLMTestCase, ToolCall
from deepeval.metrics import ToolCorrectnessMetric
# 고객 지원 에이전트의 도구 호출 시나리오
tool_test_cases = [
LLMTestCase(
input="지난달 주문 내역 조회해줘",
actual_output="지난달 주문 3건을 찾았습니다...",
tools_called=[
ToolCall(
name="get_orders",
input_parameters={
"user_id": "user-123",
"date_range": "last_month",
},
)
],
expected_tools=[
ToolCall(
name="get_orders",
input_parameters={
"user_id": "user-123",
"date_range": "last_month",
},
)
],
),
LLMTestCase(
input="이 제품 재고 있어?",
actual_output="현재 재고가 50개 있습니다.",
tools_called=[
ToolCall(
name="check_inventory",
input_parameters={"product_id": "PROD-456"},
)
],
expected_tools=[
ToolCall(
name="check_inventory",
input_parameters={"product_id": "PROD-456"},
)
],
),
]
# ToolCorrectnessMetric은 LLM API 비용 없이 결정론적으로 평가
metric = ToolCorrectnessMetric()
for tc in tool_test_cases:
metric.measure(tc)
print(f"입력: {tc.input}")
print(f"도구 정확도: {metric.score:.2f} - {metric.reason}\n")장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 품질 병목 조기 발견 | 오프라인 평가로 배포 전 회귀를 탐지해 프로덕션 장애를 예방합니다 |
| 비용 최적화 근거 확보 | 토큰 사용량·지연 데이터로 프롬프트 및 모델 선택의 의사결정 기반이 생깁니다 |
| 신뢰 구축과 설명 가능성 | 행동 추적 로그는 에이전트 결과에 대한 감사 증적이 되어 규제 대응에 활용됩니다 |
| 지속적 개선 루프 | 프로덕션 실패 사례를 수집해 테스트셋을 강화하는 플라이휠이 만들어집니다 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 비결정론적 특성 | 동일 입력에도 에이전트 출력이 달라져 단순 비교가 어렵습니다 | 확률적 평가 설계, 여러 번 실행 평균값 사용 |
| 평가 비용 | LLM-as-a-Judge도 API 비용 발생, 대규모 평가 시 상당한 금액 | gpt-4o-mini로 1차 필터링 후 대형 모델로 재검토 |
| 벤치마크-프로덕션 갭 | 벤치마크 고득점이 실제 배포 성공을 보장하지 않음 | 프로덕션 실패 사례를 지속적으로 테스트셋에 추가 |
| 다단계 추적 복잡성 | 서브에이전트가 얽힌 실행 경로 추적 인프라 구축이 복잡 | OpenTelemetry 표준 도입, 단계적으로 트레이싱 범위 확장 |
| 보안·프라이버시 | 트레이스에 사용자 입력이 포함되므로 개인정보 이슈 발생 | 민감 데이터 마스킹, 데이터 보존 정책 명문화 |
| 벤더 종속 | 특정 프레임워크 깊이 통합 시 마이그레이션 비용 발생 | OpenTelemetry 기반 표준 레이어 유지, 셀프호스팅 가능 도구 우선 |
생존 편향(Survivorship Bias): 온라인 평가는 실제로 에이전트를 사용한 사람들의 입력만 수집합니다. 어떤 유형의 사용자가 포기하고 떠났는지, 어떤 질문을 입력조차 하지 않았는지는 잡아낼 수 없습니다. 온라인 평가 데이터만 믿으면 코너케이스를 영원히 놓칠 수 있습니다.
실무에서 가장 흔한 실수
-
최종 출력만 평가하고 추론 경로를 무시합니다. 에이전트가 운 좋게 맞는 답을 냈다고 해서 올바른 추론을 한 것이 아닐 수 있습니다. 중간 스텝 품질을 함께 추적하지 않으면 재현 불가능한 성공만 쌓이게 됩니다.
-
골든 데이터셋을 한 번 만들고 업데이트하지 않습니다. 실제 사용자가 예상치 못한 방식으로 에이전트를 사용하면서 새로운 실패 패턴이 계속 생깁니다. 프로덕션 실패를 테스트셋에 추가하는 루프를 만들지 않으면 테스트셋은 빠르게 현실과 멀어집니다.
-
비용과 지연을 품질과 별도로 봅니다. 정확도만 모니터링하다가 어느 날 인프라 비용 청구서를 받고 당황하는 경우가 많습니다. CLEAR 프레임워크처럼 비용·지연을 처음부터 평가 메트릭에 포함시키는 것이 중요합니다.
마치며
에이전트를 프로덕션에서 신뢰할 수 있게 만드는 건 만드는 것만큼 중요한 엔지니어링 작업입니다. 좋은 소식은 Langfuse, DeepEval, Arize Phoenix 모두 오픈소스로 셀프호스팅이 가능해서 추가 비용 없이 시작해볼 수 있다는 점입니다. 상용 플랜으로 전환하지 않아도 기본 기능만으로 충분히 시작할 수 있습니다.
아래 세 단계는 순서 의존성이 있습니다. 트레이싱이 먼저 붙어야 온라인 평가 데이터가 쌓이고, 그 데이터가 쌓여야 CI 게이트의 골든 데이터셋이 현실적으로 개선됩니다. 이 순서대로 진행하는 것을 권장합니다.
-
트레이싱부터 붙여보는 것을 추천합니다.
pip install langfuse후 기존 에이전트 함수에@observe()데코레이터 하나를 추가하는 것으로 시작할 수 있습니다. 대시보드에서 실행 경로를 시각화하는 것만으로도 이미 보이지 않던 문제들이 눈에 들어오기 시작합니다. -
골든 데이터셋 10개를 만들어 CI에 연결해보는 것을 권장합니다. 핵심 시나리오 10개 정도로 시작해
deepeval test run을 PR마다 자동 실행되도록 설정하면 회귀 탐지 파이프라인이 생깁니다. 프로덕션 실패가 생길 때마다 케이스를 하나씩 추가하면서 점점 현실적인 데이터셋으로 만들어가는 방식이 효과적입니다. -
비용·지연 대시보드를 품질 메트릭과 같은 화면에서 보는 습관이 중요합니다. Langfuse나 Datadog LLM Observability를 활용하면 토큰 비용, P99 지연, 품질 점수를 한 화면에서 볼 수 있습니다. 세 숫자를 함께 보는 습관이 생기면, 어느 프롬프트 변경이 품질은 유지하면서 비용을 줄이는지 데이터 기반으로 판단할 수 있게 됩니다.
참고 자료
- LangChain State of Agent Engineering 2026 — 에이전트 프로덕션 도입 현황 및 배포 장벽 통계
- Evaluating AI Agents: Real-World Lessons from Amazon | AWS Blog — 단계별 추론 로그의 필요성 등 실전 구축 교훈
- Beyond Accuracy: Multi-Dimensional Framework for Evaluating Enterprise Agentic AI | arXiv — CLEAR 프레임워크 원문 논문, 비용 최적화 수치 출처
- AI Agent Evaluation Guide | DeepEval by Confident AI — DeepEval 에이전트 평가 공식 문서
- Agent Observability: LangSmith, Langfuse, Arize 2026 | Digital Applied — 주요 옵저버빌리티 플랫폼 비교
- Top 8 LLM Observability Tools | LangChain Articles — LLM 옵저버빌리티 도구 목록
- How Can We Best Evaluate Agentic AI? | Brookings — 에이전트 평가의 사회적·정책적 고려사항