홈/AI 자동화/LLM 에이전트 심화 3편: 평가 프레임워크와 멀티 에이전트 운영
AI 자동화LLM 에이전트평가

LLM 에이전트 심화 3편: 평가 프레임워크와 멀티 에이전트 운영

에이전트가 실패하는 가장 흔한 이유 2편에서 도구 호출 오케스트레이션과 플래닝을 다뤘습니다. 프로덕션 에이전트의 실제 실패 원인은 대부분 평가 체계 부재와 디버깅 어려움에서 옵니다. 에이전트 평가 프레임워크 단순 출력 품질 평가로는 에이전트를 제대로 평가할 수 없습니다. 실행 경로·도구 선택·효율성을 함께 측정해야 합니다. 구조화된 추적 (Structu…

LLM 에이전트 심화 3편: 평가 프레임워크와 멀티 에이전트 운영
목차
  1. 에이전트가 실패하는 가장 흔한 이유
  2. 에이전트 평가 프레임워크
  3. 구조화된 추적 (Structured Tracing)
  4. 멀티 에이전트 오케스트레이터
  5. Human-in-the-Loop 패턴
  6. 에이전트 안정성 지표 (프로덕션 SLA)

에이전트가 실패하는 가장 흔한 이유

2편에서 도구 호출 오케스트레이션과 플래닝을 다뤘습니다. 프로덕션 에이전트의 실제 실패 원인은 대부분 평가 체계 부재와 디버깅 어려움에서 옵니다.

에이전트 평가 프레임워크

단순 출력 품질 평가로는 에이전트를 제대로 평가할 수 없습니다. 실행 경로·도구 선택·효율성을 함께 측정해야 합니다.

Python
from dataclasses import dataclass

@dataclass
class AgentTrace:
    task: str
    steps: list[dict]          # 각 단계: {thought, action, observation}
    final_answer: str
    total_tokens: int
    elapsed_ms: int
    tool_calls: list[str]
    success: bool

class AgentEvaluator:
    def evaluate(self, trace: AgentTrace, golden: dict) -> dict:
        return {
            "task_success": self._check_success(trace.final_answer, golden["answer"]),
            "tool_precision": self._tool_precision(trace.tool_calls, golden["expected_tools"]),
            "efficiency_score": self._efficiency(trace),
            "hallucination_rate": self._check_hallucination(trace),
            "step_count": len(trace.steps),
            "token_efficiency": golden.get("min_tokens", 500) / max(trace.total_tokens, 1),
        }

    def _tool_precision(self, used: list, expected: list) -> float:
        if not expected:
            return 1.0
        correct = sum(1 for t in used if t in expected)
        return correct / len(used) if used else 0.0

    def _efficiency(self, trace: AgentTrace) -> float:
        # 최적 경로 대비 실제 단계 수 비율
        optimal_steps = 3  # 도메인 지식으로 설정
        return min(1.0, optimal_steps / max(len(trace.steps), 1))

구조화된 추적 (Structured Tracing)

Python
import contextvars
import uuid
from datetime import datetime

trace_id_var = contextvars.ContextVar("trace_id")

class AgentTracer:
    def __init__(self, backend="langfuse"):
        self.backend = backend

    def start_trace(self, task: str) -> str:
        tid = str(uuid.uuid4())
        trace_id_var.set(tid)
        self._emit({
            "event": "trace_start",
            "trace_id": tid,
            "task": task,
            "timestamp": datetime.utcnow().isoformat(),
        })
        return tid

    def log_step(self, step_type: str, content: dict):
        self._emit({
            "event": "step",
            "trace_id": trace_id_var.get(),
            "step_type": step_type,  # thought | action | observation | answer
            "content": content,
            "timestamp": datetime.utcnow().isoformat(),
        })

    def end_trace(self, success: bool, final_answer: str):
        self._emit({
            "event": "trace_end",
            "trace_id": trace_id_var.get(),
            "success": success,
            "final_answer": final_answer,
            "timestamp": datetime.utcnow().isoformat(),
        })

멀티 에이전트 오케스트레이터

단일 에이전트의 한계를 넘어 전문 에이전트들이 협력하는 패턴:

Python
class OrchestratorAgent:
    """복잡한 태스크를 전문 에이전트에게 위임"""

    AGENTS = {
        "research": ResearchAgent(),     # 정보 수집·요약
        "code":     CodeAgent(),          # 코드 생성·실행
        "data":     DataAnalysisAgent(),  # 데이터 분석
        "writer":   WriterAgent(),        # 보고서 작성
    }

    async def execute(self, task: str) -> str:
        # 1. 태스크 분해
        subtasks = await self.decompose(task)

        # 2. 병렬 실행 (독립 태스크)
        results = {}
        independent = [s for s in subtasks if not s.dependencies]
        parallel_results = await asyncio.gather(*[
            self.AGENTS[s.agent_type].run(s.prompt)
            for s in independent
        ])
        for s, r in zip(independent, parallel_results):
            results[s.id] = r

        # 3. 순차 실행 (의존 태스크)
        dependent = [s for s in subtasks if s.dependencies]
        for s in dependent:
            context = {dep: results[dep] for dep in s.dependencies}
            results[s.id] = await self.AGENTS[s.agent_type].run(
                s.prompt, context=context
            )

        # 4. 결과 통합
        return await self.synthesize(task, results)

Human-in-the-Loop 패턴

자율 에이전트에 필요한 지점에만 사람이 개입하는 설계:

Python
class HumanInLoopAgent:
    HIGH_RISK_ACTIONS = {
        "delete_records", "send_email", "execute_payment", "deploy_code"
    }

    async def execute_action(self, action: str, params: dict) -> dict:
        if action in self.HIGH_RISK_ACTIONS:
            approved = await self.request_approval(
                action=action,
                params=params,
                context=self.current_trace(),
                timeout_seconds=300,
            )
            if not approved:
                return {"status": "rejected", "action": action}

        return await self.tool_registry.call(action, **params)

    async def request_approval(self, **kwargs) -> bool:
        # Slack / 웹훅 / 이메일로 승인 요청
        approval_id = await self.notify_reviewer(**kwargs)
        return await self.wait_for_approval(approval_id, kwargs["timeout_seconds"])

에이전트 안정성 지표 (프로덕션 SLA)

지표목표알람 기준
태스크 성공률≥ 90%< 85%
평균 단계 수≤ 8> 12
도구 정밀도≥ 85%< 75%
P95 지연≤ 30s> 45s
환각 감지율≤ 5%> 8%

이 3편 시리즈로 LLM 에이전트 심화 마스터 가이드(기초→도구 오케스트레이션→평가·운영)가 완성됩니다.

✦ ✦ ✦
출처·검증 안내

글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보

이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.

발행 ·

댓글

첫 번째 댓글을 남겨보세요.