LLM 앱이 "잘 돌아가고 있다"고 생각했던 시간이 3개월이었다. 사용자 불만이 없으니까 괜찮은 줄 알았다. 에러율 0.3%, 평균 응답 시간 2초대. 대시보드 지표만 보면 문제없었다. 그런데 LLM 옵저버빌리티 도구를 붙이고 나서 이 "괜찮음"이 착각이었다는 걸 깨달았다.

기존 모니터링으로는 뭘 놓치나

전통적인 APM 도구는 HTTP 상태 코드, 응답 시간, 에러율을 본다. 언어 모델 호출도 결국 외부 API 콜 취급이니까 200 OK가 떨어지면 성공이다. 근데 여기서 200 OK는 "모델이 뭔가를 뱉었다"일 뿐이지 "올바른 답을 줬다"가 아니다.

우리 팀이 3개월간 못 본 것들을 나열하면 이렇다:

  • 전체 응답의 12%가 할루시네이션이었다. 사용자가 의심 없이 그대로 활용하고 있었다.

  • 특정 시간대(오후 2-4시)에 레이턴시가 3배 뛰었다. API 제공사 쪽 서버 부하 패턴이었는데, 우리 지표에선 "평균" 속에 묻혀 있었다.

  • 동일 질문에 대해 매번 다른 답이 나오고 있었다. temperature 설정을 0.7로 둔 채 까먹은 거였다.

  • 전체 비용의 40%가 상위 5%의 긴 프롬프트에서 발생하고 있었다.

Datadog이나 Grafana만으로는 이런 건 절대 안 잡힌다. 200 OK 안에 숨어 있는 품질 문제니까.

트레이싱을 붙이면 뭐가 달라지나

올해 1월에 Langfuse를 도입했다. 셀프호스팅 방식이라 초기 비용 부담 없이 시작할 수 있었다. 바뀐 건 크게 세 가지다.

프롬프트 버전별 품질 비교가 가능해졌다. 이전에는 프롬프트를 바꾸면 "감"으로 판단했다. 트레이스에 프롬프트 버전 태그를 붙이고, 응답마다 품질 스코어를 매기기 시작하니까 v3이 v2보다 정확도가 23% 높다는 걸 숫자로 확인할 수 있었다. 감이 아니라 데이터로 프롬프트를 개선하는 루프가 생긴 셈이다.

비용 추적이 호출 단위로 된다. 월말에 OpenAI 청구서를 보고 "왜 이렇게 나왔지?" 하는 대신, 어떤 기능이 어떤 사용자 패턴에서 토큰을 잡아먹는지 실시간으로 보인다. 우리의 경우 "문서 요약" 기능 하나가 전체 토큰 사용량의 55%를 차지하고 있었다. 입력 문서 길이에 상한선 하나 걸었더니 월 비용이 30% 줄었다. 트레이싱 없었으면 이 원인을 찾는 데만 몇 주 걸렸을 거다.

체인과 에이전트의 중간 단계가 투명해진다. RAG 파이프라인에서 답변이 이상할 때, 검색은 제대로 됐는데 모델이 컨텍스트를 무시한 건지, 아니면 리트리버가 애초에 엉뚱한 문서를 가져온 건지 구분이 안 됐었다. 트레이싱을 붙이니 "리트리버가 상위 3개 청크 중 2개를 관련 없는 문서에서 가져왔네"라고 바로 짚을 수 있다. 블랙박스가 유리 상자로 바뀌는 느낌이다.

메트릭 과잉이라는 다른 종류의 지옥

한 가지 경고. 옵저버빌리티 달았다고 끝이 아니다. 처음에 모든 호출의 전체 입출력, 임베딩 벡터, 리랭킹 스코어까지 전부 저장하려고 했더니 일주일 만에 스토리지 200GB가 터졌다. 대시보드에 지표가 40개씩 깔려 있으니까 아무도 안 본다. 경보가 하루 50개 울려도 전부 무시.

지금은 핵심만 남겼다. 입력 프롬프트는 해시만 저장하고 전문은 디버그 모드에서만 확인한다. 대시보드 지표는 딱 세 개 — 응답 품질 스코어, p95 레이턴시, 일별 토큰 비용. 경보는 "품질 스코어 하락 + 호출량 증가"가 동시에 발생할 때만 울리도록 복합 조건을 걸었다.

도구보다 먼저 정해야 할 것

Langfuse, LangSmith, Braintrust, Datadog LLM Monitoring — 선택지가 넘쳐난다. 솔직히 어떤 걸 쓰든 기본적인 트레이싱 수준은 비슷하다.

진짜 차이를 만드는 건 "이 서비스에서 좋은 응답이란 무엇인가"에 대한 팀 합의다. 우리는 도구 세팅에 하루, 이 정의를 내리는 데 2주가 걸렸다. 정확성이 먼저인지, 응답 톤이 먼저인지, 할루시네이션 제로가 목표인지. 이걸 정하지 않으면 트레이싱은 그냥 비싼 로그 수집기로 전락한다.

그리고 이 기준은 계속 바뀐다. 출시 초기에는 "틀린 답 안 하기"가 최우선이었는데, 서비스 안정화 이후에는 "답변 톤이 브랜드와 맞는가"가 더 중요해졌다. 스코어링 함수를 쉽게 교체할 수 있는 구조로 만들어놔야 한다. 이걸 하드코딩하면 3개월 뒤에 또 갈아엎게 된다.