1. 맥락
Polaris의 AI 기능은 실패해도 사용자에게 항상 5xx로 드러나지 않는다. 미션 생성과 별친구 대화는 fallback으로 흐름을 유지할 수 있기 때문이다.
이 구조는 사용자 경험을 보호하지만, 운영자 입장에서는 다른 문제가 생긴다.
- AI provider가 느리거나 실패해도 API는 성공처럼 보일 수 있다.
- fallback이 늘면 서비스는 살아 있지만 개인화 품질은 떨어질 수 있다.
- provider 호출량과 token 사용량이 늘면 비용이 증가한다.
- Redis rate limit 장애, provider 장애, prompt 품질 문제를 HTTP 5xx만으로 구분하기 어렵다.
따라서 AI 운영 지표는 "서비스가 죽었는가"가 아니라 "AI 품질이 degrade 되었는가"를 중심으로 본다.
2. 결정
2.1 AI 생성 결과는 DB 로그를 1차 관측 지점으로 삼는다
AI 요청 결과는 ai_mission_generations와 ai_usage_logs에 남긴다.
지표 근거 데이터 의미
| 전체 AI 요청 수 | ai_usage_logs, ai_mission_generations | AI 기능 사용량 |
| fallback rate | fallback_used, status | 사용자에게 fallback 결과가 제공된 비율 |
| error type 분포 | error_type | RATE_LIMIT, RATE_LIMIT_UNAVAILABLE, INVALID_OUTPUT, PROVIDER_ERROR 등 원인 |
| model별 latency | model, latency_ms | 모델별 응답 지연 |
| prompt version별 품질 | prompt_template_id, fallback_used, error_type | 프롬프트 변경 후 품질 변화 |
일 단위 AI 품질은 v_daily_ai_quality view로 요약한다.
CREATE OR REPLACE VIEW v_daily_ai_quality AS
SELECT
DATE(created_at) AS active_date,
COUNT(*) AS total_ai_requests,
COUNT(CASE WHEN fallback_used = true THEN 1 END) AS fallback_requests,
COUNT(CASE WHEN error_type = 'RATE_LIMIT_UNAVAILABLE' THEN 1 END) AS rate_limit_errors,
COUNT(CASE WHEN status = 'FAILED' THEN 1 END) AS failed_requests
FROM ai_mission_generations
GROUP BY DATE(created_at);
이 view는 AI 장애를 API 장애가 아니라 품질 저하 지표로 보기 위한 최소 집계다.
2.2 Token 사용량은 수집 가능 범위부터 신뢰한다
ai_usage_logs와 character_talk_sessions에는 token usage를 저장할 수 있는 필드가 있다. 다만 provider adapter나 streaming 응답에서 token metadata가 항상 안정적으로 들어오지 않을 수 있다.
따라서 초기 관측 우선순위는 다음과 같이 둔다.
우선순위 지표 이유
| 1 | fallback rate | 사용자 품질 저하를 가장 직접적으로 보여준다. |
| 2 | error type | Redis 장애, provider 장애, invalid output을 구분한다. |
| 3 | latency p95 | 사용자가 체감하는 느림과 provider timeout 위험을 보여준다. |
| 4 | model별 요청 수 | 비용 증가 방향을 파악한다. |
| 5 | token usage | provider metadata가 안정화된 뒤 비용 예측 정밀도를 높인다. |
token 값이 비어 있거나 0으로 들어오는 구간에서는 비용 분석을 중단하지 않고, 요청 수와 latency, fallback 비율을 먼저 본다. 모델별 누적 prompt/completion token과 별친구 세션별 token 누적량은 Grafana dashboard에서 함께 확인한다. 특정 경로의 token metadata가 0으로 보이면 비용 계산 실패가 아니라 "해당 provider 응답에서 usage metadata가 비어 있음"으로 분리해 판단한다.
2.3 Grafana/Loki/n8n은 장애 징후 감지와 알림 자동화에 사용한다
Grafana는 alert rule을 통해 장애 징후를 감지하고, n8n은 alert payload와 Loki 로그를 받아 Slack 알림으로 정리한다. Gemini는 장애 판단 주체가 아니라, 이미 감지된 alert와 sanitizing된 로그를 요약하는 보조 역할만 한다.
AI 관련 alert는 다음 흐름을 따른다.

이 구조에서 중요한 기준은 다음과 같다.
기준 결정
| 장애 판단 | Grafana alert rule이 담당한다. |
| 로그 요약 | Gemini가 sanitized evidence만 받아 수행한다. |
| 민감정보 | raw prompt, raw response, token, credential, 사용자 입력 전문은 보내지 않는다. |
| Gemini 실패 | 기본 Slack fallback 메시지를 보낸다. |
| 중복 알림 | fingerprint 기반 dedupe window를 둔다. |
2.4 알림 기준은 fallback과 rate limit을 중심으로 둔다
AI는 fallback으로 성공처럼 보일 수 있으므로, 5xx보다 fallback 증가를 먼저 본다.
Alert Rule 기준 의미
| AIFallbackRateHigh | 최근 10분 동안 FALLBACK, RATE_LIMITED, FAILED 비율이 10% 초과 | provider 장애, prompt 품질 저하, rate limit 증가 가능성 |
| AILatencyP95High | 최근 10분 기준 latency_ms p95가 5초 초과 | provider 지연, timeout 위험, 네트워크 지연 가능성 |
| AIRateLimited | 최근 5분 동안 RATE_LIMITED 1건 이상 | 사용자 반복 요청, traffic spike, 제한값 과소 설정 가능성 |
| AITokenMetadataMissing | 최근 1시간 성공 요청 중 total_tokens = 0 비율이 80% 초과 | provider/Spring AI usage metadata 수집 누락 가능성 |
초기 알림은 완벽한 원인 분석보다 "품질 저하를 놓치지 않는 것"을 목표로 둔다. 알림이 너무 자주 울리면 threshold를 올리고, 장애를 놓치면 window를 줄인다.
Alert Rule은 Grafana의 Polaris Production Alerts 폴더에서 관리하고, firing/resolved notification은 기존 n8n incident intake contact point로 전달한다. n8n의 Build Loki Query 노드는 alert name을 정규화해 AI 관련 키워드와 p5laris-ai container 로그를 조회한다.
3. 수치 결정 근거
3.1 Rate limit window 60초
항목 내용
| 선택값 | AI_RATE_LIMIT_WINDOW_SECONDS=60 |
| 결정 이유 | provider quota와 운영자가 이해하기 쉬운 단위가 분당 요청 수다. Redis fixed window counter 구현도 단순하다. |
| 기대 효과 | 분 단위 비용 폭주와 반복 클릭을 빠르게 차단한다. |
| trade-off | window 경계에서 순간 호출량이 몰릴 수 있다. |
| 재조정 기준 | 경계 burst가 문제가 되면 sliding window 또는 token bucket으로 바꾼다. |
3.2 Provider/user 분리 limit
항목 내용
| 선택값 | provider/model 전체 limit과 user별 limit을 분리 |
| 결정 이유 | 전체 비용 상한과 개별 사용자 반복 요청 방어는 목적이 다르다. 하나의 limit으로 둘 다 설명하기 어렵다. |
| 기대 효과 | 특정 사용자나 클라이언트 버그가 전체 provider 비용을 잠식하는 상황을 줄인다. |
| trade-off | 설정값이 늘어나 운영 복잡도가 증가한다. |
| 재조정 기준 | 정상 사용자가 제한을 자주 만나면 user limit을 올리고, 전체 비용이 높으면 provider limit을 낮춘다. |
3.3 AI fallback alert window
항목 내용
| 선택값 | 10-15분 단위 관측 window |
| 결정 이유 | AI fallback은 순간 spike보다 지속성이 중요하다. 너무 짧게 보면 일시적 provider 지연에도 과민하게 울린다. |
| 기대 효과 | 사용자 품질 저하가 지속되는 상황을 빠르게 확인한다. |
| trade-off | 1-2분짜리 짧은 장애는 알림 없이 지나갈 수 있다. |
| 재조정 기준 | 짧은 provider 장애가 사용자 불만으로 이어지면 window를 줄이고, noise가 많으면 threshold를 올린다. |
3.4 Provider latency p95
항목 내용
| 선택값 | p95 중심 관측 |
| 결정 이유 | 평균 latency는 일부 느린 요청을 가린다. AI provider는 tail latency가 사용자 체감과 timeout 비용에 더 직접적이다. |
| 기대 효과 | 일부 요청이 길게 늘어지는 현상을 빠르게 파악한다. |
| trade-off | 요청 수가 적은 구간에서는 p95가 흔들릴 수 있다. |
| 재조정 기준 | 요청 수가 적은 시간대에는 p95와 count를 함께 표시한다. |
4. 대시보드 구성 기준
운영 대시보드에는 다음 패널을 우선 둔다.
패널 목적
| AI 요청 수 | 전체 사용량과 비용 방향 확인 |
| fallback rate | AI 품질 저하 확인 |
| error type 분포 | provider, Redis, prompt 품질 문제 구분 |
| model별 latency p95 | 모델별 응답 지연 확인 |
| rate limit count | 비용 보호 장치 작동 여부 확인 |
| invalid output count | 프롬프트 안정성 확인 |
| prompt version별 fallback rate | 프롬프트 변경 영향 확인 |
| token usage | 미션 생성과 별친구 대화 token 비용 방향 확인 |
초기에는 SQL view와 Grafana query로 시작한다. 반복 조회가 필요한 지표는 view로 고정하고, 비용/지연 분석이 정교해지면 Prometheus metric 또는 별도 aggregate table로 분리한다.
현재 import 산출물은 루트의 polaris-ai-usage-dashboard.json이다. Grafana에서는 40_Business 폴더에 배치한다.
5. 결과
이 결정으로 Polaris는 AI 장애를 단순 서버 장애와 분리해서 볼 수 있다.
- API가 성공해도 AI fallback이 늘면 품질 저하로 감지한다.
- Redis rate limit 장애와 provider 장애를 error_type으로 구분한다.
- latency p95로 느린 provider 호출을 관측한다.
- Grafana Alert Rule을 기존 n8n incident intake에 연결해 firing/resolved 알림 흐름을 유지한다.
- n8n/Gemini 알림이 실패해도 기본 Slack fallback 알림은 유지한다.
- token usage가 불안정한 구간에서도 fallback rate와 latency로 운영 판단을 시작할 수 있다.
6. 그라파나 AI 관측 캡쳐


