LLM 엔지니어링20장 · LLM 평가와 모니터링19 / 22← 이전목차다음 →MetaContents

20장. LLM 평가와 모니터링

실습 6 — 운영 지표와 알림

같은 운영 기록에서 날마다의 지표(질문 수, 지연 p95, 오류율, 모름·싫어요 비율)를 뽑고, 본문 "대시보드와 알림" 절의 문턱으로 알림을 겁니다. 이어진 날은 하나의 알림 사건으로 묶고, 네 지표를 문턱과 함께 그립니다.

1단계. 날마다의 지표

Python
import matplotlib.pyplot as plt
import pandas as pd

plt.rcParams["font.family"] = "Malgun Gothic"        # 한글 글꼴(4단계의 그래프)
plt.rcParams["axes.unicode_minus"] = False

log = pd.read_csv("chat_log.csv", parse_dates=["시각"])
log["날"] = log["시각"].dt.normalize()
daily = log.groupby("날").agg(
    질문=("질문", "size"),
    p95=("지연초", lambda s: s.quantile(0.95)),
    오류율=("오류", "mean"),
    모름=("모름", "mean"),
    싫어요=("피드백", lambda s: (s == "싫어요").mean()),
)
# 하루 값은 흔들리므로 모름·싫어요는 7일 이동 평균도 만든다(첫 6일은 있는 날만으로)
daily["모름7"] = daily["모름"].rolling(7, min_periods=1).mean()
daily["싫어요7"] = daily["싫어요"].rolling(7, min_periods=1).mean()
# 결과: 기준 기간(9월 7일~10월 4일)의 평소 값
print(daily.loc[:"2026-10-04"][["질문", "p95", "오류율", "모름", "싫어요"]].mean().round(3).to_dict())
출력
{'질문': 30.714, 'p95': 3.031, '오류율': 0.012, '모름': 0.05, '싫어요': 0.038}

2단계. 알림 규칙 걸기

Python
RULES = {"p95": 6.0, "오류율": 0.05, "모름7": 0.12, "싫어요7": 0.08}     # 이 값을 넘으면 알린다
over = pd.DataFrame({k: daily[k] > v for k, v in RULES.items()})
# 결과: 지표마다 문턱을 넘은 날의 수
print(over.sum().to_dict())
출력
{'p95': 3, '오류율': 3, '모름7': 15, '싫어요7': 14}

3단계. 이어진 날을 사건으로 묶기

Python
events = []
for metric in RULES:
    s = over[metric]
    group = (s != s.shift()).cumsum()               # 상태가 바뀔 때마다 번호가 하나 오른다
    for _, g in s[s].groupby(group[s]):
        peak = daily.loc[g.index, metric].max()
        events.append({"지표": metric, "시작": g.index[0].date(), "끝": g.index[-1].date(), "날수": len(g),
                       "가장 나쁜 값": round(peak, 3), "문턱": RULES[metric]})
events = pd.DataFrame(events).sort_values("시작")
# 결과: 알림 사건 목록
print(events.to_string(index=False))
출력
  지표         시작          끝  날수  가장 나쁜 값   문턱
싫어요7 2026-09-08 2026-09-08   1    0.081 0.08
 오류율 2026-09-13 2026-09-14   2    0.071 0.05
 모름7 2026-10-18 2026-11-01  15    0.287 0.12
 p95 2026-10-20 2026-10-22   3    8.922 6.00
싫어요7 2026-10-20 2026-11-01  13    0.147 0.08
 오류율 2026-10-21 2026-10-21   1    0.069 0.05

알림 사건은 여섯입니다. 앞의 둘은 헛알림입니다. 9월 8일의 싫어요 알림은 이동 평균의 첫 이틀이라 표본이 적어서 났고, 9월 13~14일의 오류율 알림은 하루 30개 남짓 가운데 2개가 오류여서 났습니다. 나머지 넷이 실제 사건입니다. 모름(10월 18일부터)과 싫어요(10월 20일부터)는 끝날까지 이어졌고, 지연 p95 는 10월 20~22일에 8.9초까지 올랐고, 오류율은 10월 21일에 0.069 였습니다. 헛알림을 줄이려면 하루 질문 수가 적은 날은 알리지 않거나, 이틀 이어질 때만 알리는 규칙을 더합니다.

4단계. 계기판 그리기

Python
fig, axes = plt.subplots(2, 2, figsize=(11, 6), sharex=True)
for ax, (col, title) in zip(axes.flat, [("p95", "지연 p95(초)"), ("오류율", "오류율"),
                                        ("모름7", "모름 비율(7일 이동)"), ("싫어요7", "싫어요 비율(7일 이동)")]):
    daily[col].plot(ax=ax, title=title)
    ax.axhline(RULES[col], linestyle="--", color="gray")      # 문턱
    ax.set_xlabel("")
plt.tight_layout()
plt.show()
날마다 지연 p95, 오류율, 7일 이동 모름 비율, 7일 이동 싫어요 비율을 문턱 점선과 함께 그린 네 칸 그래프

이 실습이 뜻하는 것

이 실습은 가게 계기판에 경보등을 다는 일과 같습니다. 날마다 손님 수, 기다린 시간, 실수, 불만을 적고(1단계), 칸마다 경보선을 그어 넘은 날을 세고(2단계), 며칠 이어서 울린 경보를 한 사건으로 묶은 뒤(3단계), 계기판 그림을 그립니다(4단계). 여기서 기다린 시간이 지연 p95(95번째 백분위수, 100번 가운데 느린 쪽 다섯 번을 뺀 가장 긴 값), 경보선이 문턱(RULES), 이어진 경보의 묶음이 알림 사건입니다. 비유와 다른 점은, 경보등은 그 순간만 보지만 여기의 모름·싫어요는 7일 이동 평균이라 지난 일주일을 함께 본다는 것입니다.

단계출력뜻
1. 평소 값질문 30.714, p95 3.031, 오류율 0.012, 모름 0.05, 싫어요 0.038기준 기간의 하루 평균입니다. 하루 질문이 31개쯤, 느린 쪽 답도 3초 남짓, 오류는 100번에 1번쯤입니다
2. 넘은 날p95 3, 오류율 3, 모름7 15, 싫어요7 14지표마다 문턱을 넘은 날의 수입니다. 모름과 싫어요는 두 주 안팎으로 넘었습니다
3. 알림 사건여섯 줄문턱을 넘은 날 35개가 사건 6건으로 묶였습니다. 날마다 울렸다면 35번 알렸을 것입니다
3. 헛알림9월 8일 싫어요7 0.081, 9월 13~14일 오류율 0.071이동 평균의 첫머리라 표본이 적었거나, 하루 30개 남짓 가운데 2개가 오류여서 난 헛알림입니다
3. 실제 사건모름7 10월 18일~11월 1일 최고 0.287, p95 10월 20~22일 최고 8.92210월 중순부터 모름과 싫어요가 끝날까지 이어졌고, 그 사이 지연이 9초 가까이 올랐습니다
4. 계기판네 칸 그래프네 지표를 날마다 선으로 그리고 문턱을 회색 점선으로 그었습니다. 선이 점선 위로 올라간 구간이 위의 사건입니다

한 줄로 줄이면, 날마다의 지표에 문턱을 걸고 이어진 날을 묶으면 35번 울릴 알림이 6건의 사건으로 줄어든다는 것을 보인 실습입니다. 그 가운데 2건은 헛알림이라, 하루 질문이 적은 날은 빼는 식으로 규칙을 더 다듬어야 합니다. 알림은 "언제 나빠졌나"를 알려 줄 뿐 "왜"는 알려 주지 않으니, 원인은 서버 기록과 맞춰 찾아야 합니다. 이 지표들을 주 단위로 묶어 화면에 띄우는 일은 프로젝트에서 합니다.

코드 설명

  • groupby("날").agg(이름=(칸, 함수)) 는 날마다 여러 지표를 한 번에 냅니다. p95 는 s.quantile(0.95) 입니다.
  • rolling(7, min_periods=1).mean() 은 7일 이동 평균입니다. 하루 표본이 30개 안팎이라 흔들리는 비율 지표를 고르게 합니다.
  • RULES 에 지표와 문턱을 모아 두면, 문턱을 고칠 때 한 곳만 바꿉니다.
  • (s != s.shift()).cumsum() 은 상태가 바뀔 때마다 번호를 올려, 이어진 날을 같은 묶음으로 만듭니다. 알림 피로를 줄이는 가장 쉬운 방법입니다.
← 이전 절20장 · 실습 5 — 드리프트 감지하기다음 절 →20장 · 프로젝트 1 — 챗봇 품질 대시보드