"Evaluation & Operations — 측정 가능해야 개선 가능하다 (하네스 시리즈 6/6)"
시리즈를 종합하는 마지막 글. 컨텍스트(2)·메모리(3)·도구(4)·라우팅(5)을 다 잘 만들었는데, 작동 중인지 어떻게 아는가? 답은 측정. 측정 못하면 개선도 못한다.
이 글은 LLM 에이전트의 Evaluation(품질 측정)과 Operations(운영) 영역을 다룬다. 시리즈 1~5에서 다룬 모든 컴포넌트를 지속적으로 검증하는 마지막 레이어.
시리즈 (6편 완결)
- 하네스 엔지니어링이란
- Context Engineering
- Memory Systems
- Tools & Sandboxing
- Multi-Provider Routing
- Evaluation & Operations ← 이 글 (최종)
1. 왜 Evaluation이 따로 필요한가
일반 소프트웨어와 다르다
- 단위 테스트 통과 ≠ 사용자가 만족
- 동일 입력 다른 출력 (비결정적)
- "정답"이 항상 명확하지 않음 (창작·요약·코드 리팩터)
- 모델 변경 시 모든 동작이 미세 변화
에이전트는 연속적으로 검증해야 한다
- 새 모델 출시 → 라우팅 정책 영향
- 시스템 프롬프트 수정 → 회귀 가능성
- 새 도구 추가 → 기존 작업 변화
- 메모리 누적 → 답변 변동
결론: Eval은 한 번 하는 것이 아니라 상시 돌리는 인프라.
2. Eval 종류
2-1. Automatic Metrics (정량)
- BLEU, ROUGE (번역·요약, 한계 많음)
- Code execution: 코드가 실행되는가, 테스트 통과하는가
- Schema validation: JSON 출력이 schema 맞는가
- Length, latency, cost (자동 추적)
2-2. LLM-as-Judge (모델로 평가)
- 더 큰 모델(Opus 4.7)이 작은 모델 출력을 평가
- Rubric 기반 채점 (0~10 점)
- 한계: 같은 모델이 평가자·생성자면 self-bias
2-3. Human Eval
- 가장 정확, 가장 비싸고 느림
- 50~200 샘플로 starting baseline 만들기
- LLM-as-Judge와 상관관계 검증 시 필수
2-4. Adversarial / Red Team
- 의도적으로 어려운 케이스 만들기
- prompt injection 테스트
- 보안 우회 시도
3. Eval Harness 구축
데이터셋 구성
- 100~1000개 대표 케이스
- 정답 또는 기준 출력 라벨링
- 카테고리별 분리 (chat / code / reasoning / agent)
Eval 파이프라인
results = []
for case in dataset:
output = await agent.run(case.input)
score = await evaluate(case, output) # automated or LLM-as-judge
results.append({
"case_id": case.id,
"model": agent.model,
"input_tokens": output.input_tokens,
"output_tokens": output.output_tokens,
"cost": output.cost,
"latency_ms": output.latency_ms,
"score": score,
"passed": score >= threshold,
})
summary = compute_metrics(results)
report.publish(summary)
CI 통합
- PR 머지 전 → eval 100케이스 실행
- 임계 미달 시 차단
- 비용·지연·정확도 회귀 감지
Continuous Eval
- Production 트래픽 일부(5~10%)를 eval로 샘플링
- 매일 합성 → 트렌드 감지
4. Observability — 무엇을 측정하나
핵심 지표
| 카테고리 | 지표 | 목표 |
|---|---|---|
| 비용 | Per-call cost | 예산 내 |
| 지연 | TTFT, TPOT, 총 latency | SLO 준수 |
| 품질 | Eval score, 사용자 평가 | 회귀 없음 |
| 신뢰성 | 성공률, 실패 분류 | 99%+ |
| 사용 패턴 | Per-user / per-task | 이상 감지 |
트레이스 (분산 추적)
하나의 사용자 요청이 여러 도구 호출 + 여러 모델 호출로 분기. 전체를 한 그래프로 본다.
User request
├─ Classifier (Haiku 4.5, 50ms)
├─ Tool: search_codebase (200ms)
├─ Tool: read_file × 3 (각 30ms)
└─ Generation (Kimi K2.6, 2.1s)
Total: 2.4s, $0.03
로그 vs 트레이스 vs 메트릭
- 로그: 텍스트 이벤트 (디버깅)
- 트레이스: 단일 요청 흐름 (성능 분석)
- 메트릭: 집계 (대시보드)
세 가지 모두 필요.
5. 도구 비교 (2026-04 기준)
LangSmith (managed, by LangChain)
- 트레이스 + eval + 데이터셋 통합
- LangChain 깊이 통합
- $39~$300/월 + 사용량
- 학습곡선 낮음
Langfuse (open source)
- 자체 호스팅 가능
- 트레이스 + eval + 사용자 피드백
- 대규모 사용 시 비용 우위
- LangChain·LiteLLM·OpenAI 통합
Helicone (managed proxy)
- 프록시 형식 (코드 변경 최소)
- 로그·메트릭·캐시 통합
- 비용 예측 + 알림
Phoenix (Arize)
- LLM 관측성 + ML 관측성 통합
- 오픈소스 + managed 옵션
자체 구축
- SQLite + Grafana
- 작은 규모면 충분
- 커스텀 정책
6. Long-Running Tasks — Operations의 가장 어려운 부분
문제
- 에이전트가 30분~수시간 걸리는 작업 시작
- 사용자가 닫으면? 시스템 재시작이면? 토큰 한도 도달이면?
- 진행 상태 어떻게 보존?
Runner 패턴 (CLAUDE.md에서 정의된 표준)
CLAUDE.md
runnerskill: "Long-running/background task control. Externalize task state to a durable ledger so compact, handoff, and session resume reconnect to the same work instead of relaunching it."
핵심 원칙: - 작업 상태를 외부 ledger에 저장 (SQLite, 파일) - 매 단계 진행 시 ledger 갱신 - 재시작·compact·handoff 시 ledger에서 복원
단계 분리
Step 1: research (5min) → save to ledger
Step 2: plan (3min) → save
Step 3: implement_a (10min) → save
Step 4: implement_b (8min) → save
Step 5: verify (5min) → save
Step 6: done
각 단계 독립 실행 가능. 5번에서 죽어도 4번 결과 활용해 재개.
Compact + Handoff
- 컨텍스트 압박 → 현재 진행 상태를 handoff 문서로 압축
- 새 세션은 handoff 문서 + ledger 상태로 어디서 멈췄는지 파악
- CLAUDE.md
tasks/handoffs/패턴
7. 비용 운영
예산 관리
- 월 예산 설정 → 실시간 추적
- 80% 도달 시 알림
- 100% 도달 시 어떻게 할 것인가 (서비스 중단? Degrade?)
Per-user limits
- Free tier: 일 100K 토큰
- Paid: 일 1M 토큰
- 한도 도달 → 다음 날 reset 또는 추가 결제
라우팅 + 캐시 효과 측정
- 라우팅 도입 전후 평균 비용 비교
- 캐시 적중률 일별 추이
- 모델별 비용 / 작업 종류별 비용
이상치 알림
- 한 사용자가 1시간에 평소 10× 토큰 → 알림 (계정 탈취? 무한 루프?)
- 비용 spike 자동 감지
8. Quality Gates
Pre-deploy
- 100케이스 eval → 80% 통과 필수
- Cost regression 5% 이하
- Latency regression 10% 이하
Production
- A/B 테스트: 5% 트래픽으로 새 정책 검증
- 자동 rollback: eval score 떨어지면 즉시 이전 버전으로
Post-deploy
- 24시간 후 메트릭 비교
- 사용자 피드백 수집
9. 사고 대응 (Incidents)
분류
- P0: 서비스 다운 → 즉시 fallback
- P1: 품질 회귀 → 24시간 내 수정
- P2: 비용 spike → 다음 sprint
- P3: 사용자 보고 1건 → 추적
Runbook
- 각 사고 유형별 대응 절차 문서화
- "Anthropic API 다운 시" → "OpenRouter로 fallback. 1시간 후 재시도"
- "비용 spike 감지" → "라우팅 정책 검토 + 사용자 한도 일시 적용"
Postmortem
- 사고 후 비난 없는 분석
- 무엇이 일어났나 / 왜 / 어떻게 막을까
- Action items → 다음 sprint
10. 시리즈 종합 — 하네스 엔지니어링 체크리스트
| 영역 | 핵심 질문 | 권장 도구 |
|---|---|---|
| 1. 정의 | 모델 vs 하네스 분리 명확? | (개념적) |
| 2. Context | 5K로 충분한 답이 가능? | CLAUDE.md, MCP 한도 |
| 3. Memory | 세션 간 상태 보존? | SQLite+FTS5 → Mem0 → Zep |
| 4. Tools | 권한·격리·실패 처리? | Hooks, worktree, VM |
| 5. Routing | 작업별 적절 모델? | OpenRouter → 자체 |
| 6. Eval/Ops | 측정·운영 시스템? | LangSmith / Langfuse |
이 6가지를 다 갖추면 진짜 하네스다. 하나라도 빠지면 그 영역에서 6배 격차가 생긴다.
11. 어디서부터 시작하나
작은 팀 (개인 ~ 5명)
- CLAUDE.md 작성 (Context 1+2+3 기본)
- Bash 권한 화이트리스트 (Tools 4)
- OpenRouter 가입 (Routing 5)
- SQLite + 기본 로깅 (Eval 6 시작)
- 측정 → 개선 → 측정
팀 (5~50명)
- 위 모든 것 + Langfuse 자체 호스팅
- CI에 eval 100케이스 통합
- Worktree 격리 표준화
- Per-user 비용 추적
엔터프라이즈
- 자체 라우터 (Router_Control 패턴)
- Mem0 / Zep 도입
- VM 격리 (Cursor 패턴)
- 24/7 oncall + runbook
12. 시리즈 마무리
이 6편으로 다룬 것:
| Part | 핵심 명제 |
|---|---|
| 1 | Agent = Model + Harness. 같은 모델 6× 차이. |
| 2 | 컨텍스트는 공유 예산. 작게·정확히. |
| 3 | 메모리는 컨텍스트의 연장 아니다. 별도 시스템. |
| 4 | 모델은 도구를 부르고 싶어한다. 하네스가 방법을 정한다. |
| 5 | 단일 모델 사용은 더 이상 합리적이지 않다. |
| 6 | 측정 못하면 개선 못한다. 상시 eval은 인프라. |
시리즈 한 줄 결론: "2026년에 LLM 에이전트의 차별은 모델이 아니라 그 위에 올라가는 모든 것이다. 그 모든 것을 잘 짜는 일이 하네스 엔지니어링이다."
다음
이 시리즈가 끝났지만 하네스 영역은 지금 막 형성되는 중이다. 6~12개월 내 변동 가능 사항: - AutoHarness 같은 메타 도구의 성숙 - 모델별 전용 하네스 패턴 표준화 - Eval 데이터셋의 공개·공유 - KG 메모리의 mainstream 도입 - Cursor 4 / Claude Code 4 (다음 분기 예상)
이 블로그에서 분기마다 업데이트 예정.
참고 자료 (1차 출처)
- LangSmith: docs.smith.langchain.com
- Langfuse: langfuse.com
- "Dive into Claude Code": arxiv.org/abs/2604.14228
- Martin Fowler harness: martinfowler.com/articles/harness-engineering.html
- AutoHarness: github.com/aiming-lab/AutoHarness
- Cursor 3 Cloud Agents: cursor.com/cloud
댓글
댓글 쓰기