이 글은 누구를 위한 것인가
- AI 기능을 출시했지만 "잘 되고 있는가"를 측정할 지표가 없는 PM
- LLM 응답 품질을 정량적으로 추적하고 싶은 엔지니어
- AI 제품의 성과를 경영진에게 보고해야 하는 팀 리더
들어가며
AI 제품에 DAU, 전환율, NPS만 보면 충분할까? 아니다. "사용자가 AI의 답변에 만족했는가", "AI가 할 수 없는 요청을 얼마나 받는가", "응답 지연이 이탈에 얼마나 영향을 미치는가"는 일반 지표로 잡히지 않는다.
AI 제품에는 AI 고유의 지표가 필요하다.
이 글은 bluefoxdev.kr의 AI 제품 지표 프레임워크 를 참고하고, North Star Metric 설계 관점에서 확장하여 작성했습니다.
1. AI 제품 지표 레이어
[AI 제품 지표 4개 레이어]
레이어 1: 비즈니스 지표 (모든 제품 공통)
├── MAU / DAU
├── 리텐션 (Day 7, Day 30)
├── 전환율
└── 수익 (ARPU, MRR)
레이어 2: 사용 지표 (AI 기능 사용)
├── AI 기능 활성화율 (AI 쓴 사용자 / 전체 사용자)
├── AI 세션당 평균 쿼리 수
├── 쿼리 → 액션 전환율 (AI 답변 후 실제 행동)
└── 재질문율 (첫 답변이 불만족)
레이어 3: 품질 지표 (AI 응답 품질)
├── Helpfulness Rate (도움됨 비율)
├── Task Completion Rate (목표 달성율)
├── Fallback Rate (처리 불가 비율)
└── Hallucination Rate (사실 오류 비율)
레이어 4: 시스템 지표 (운영)
├── Latency P50/P95/P99
├── Error Rate
├── 비용 per Query
└── Token Usage (입력/출력 비율)
2. North Star Metric 선택
[AI 제품 유형별 North Star 예시]
AI 글쓰기 보조 (Notion AI, ChatGPT):
North Star: "AI 제안을 수락한 사용자 비율"
이유: 사용자가 실제로 채택 → 품질 + 유용성 동시 반영
AI 고객지원 챗봇:
North Star: "AI 단독 해결율" (Human 에스컬레이션 없이 해결)
이유: 비용 절감 + 사용자 만족 동시 측정
AI 코드 리뷰:
North Star: "AI 제안 채택 후 버그 감소율"
이유: 실제 품질 개선 기여도 측정
AI 검색:
North Star: "제로 클릭 해결율" (검색 후 추가 질문 없음)
이유: 첫 번째 답변에서 해결됐음을 의미
[공통 원칙]
✅ 사용자가 원하는 결과에 가까울수록 좋음
✅ 양이 아닌 질을 측정
❌ AI 응답 횟수, 토큰 사용량 (= 활동량, 품질 무관)
❌ 응답 속도만 (= 빠르지만 틀린 답도 포함)
3. 피드백 루프 구현
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from enum import Enum
import uuid
from datetime import datetime
router = APIRouter(prefix="/feedback")
class FeedbackType(str, Enum):
THUMBS_UP = "thumbs_up"
THUMBS_DOWN = "thumbs_down"
COPIED = "copied" # 복사 = 암묵적 긍정
REGENERATED = "regenerated" # 재생성 = 암묵적 부정
EDITED = "edited" # 편집 = 부분 긍정
class FeedbackReason(str, Enum):
WRONG_INFO = "wrong_info"
NOT_HELPFUL = "not_helpful"
TOO_LONG = "too_long"
TOO_SHORT = "too_short"
GOOD = "good"
class FeedbackRequest(BaseModel):
conversation_id: str
message_id: str
feedback_type: FeedbackType
reason: FeedbackReason | None = None
comment: str | None = None
@router.post("/")
async def submit_feedback(
req: FeedbackRequest,
user_id: str = Depends(get_current_user),
):
feedback = {
"id": str(uuid.uuid4()),
"user_id": user_id,
"conversation_id": req.conversation_id,
"message_id": req.message_id,
"feedback_type": req.feedback_type,
"reason": req.reason,
"comment": req.comment,
"created_at": datetime.utcnow().isoformat(),
}
# DB 저장
await db.feedbacks.insert(feedback)
# 실시간 품질 지표 업데이트
await update_helpfulness_rate(req.message_id, req.feedback_type)
# 부정 피드백이면 리뷰 큐에 추가
if req.feedback_type == FeedbackType.THUMBS_DOWN:
await queue_for_human_review(req.conversation_id)
return {"status": "recorded"}
async def update_helpfulness_rate(message_id: str, feedback: FeedbackType):
"""Helpfulness Rate 실시간 업데이트"""
positive = feedback in [FeedbackType.THUMBS_UP, FeedbackType.COPIED]
await db.execute("""
UPDATE ai_messages
SET
positive_count = positive_count + :pos,
negative_count = negative_count + :neg,
helpfulness_rate = positive_count::float / NULLIF(positive_count + negative_count, 0)
WHERE id = :id
""", {"id": message_id, "pos": 1 if positive else 0, "neg": 0 if positive else 1})
4. 자동화된 품질 평가 (LLM-as-Judge)
import anthropic
client = anthropic.Anthropic()
EVAL_PROMPT = """
다음 AI 응답을 평가하세요.
[사용자 질문]
{question}
[AI 응답]
{response}
[평가 기준]
1. 정확성 (1-5): 사실에 기반하며 오류가 없는가
2. 관련성 (1-5): 질문에 직접적으로 답하는가
3. 완성도 (1-5): 충분히 자세하고 완전한가
4. 간결성 (1-5): 불필요한 내용 없이 간결한가
JSON 형식으로만 응답하세요:
{{"accuracy": N, "relevance": N, "completeness": N, "conciseness": N, "overall": N, "issues": ["issue1", "issue2"]}}
"""
async def evaluate_response(question: str, response: str) -> dict:
"""LLM으로 LLM 응답 평가 (LLM-as-Judge)"""
eval_response = client.messages.create(
model="claude-haiku-4-5-20251001", # 평가는 저렴한 모델로
max_tokens=200,
messages=[{
"role": "user",
"content": EVAL_PROMPT.format(question=question, response=response)
}]
)
import json
try:
scores = json.loads(eval_response.content[0].text)
except json.JSONDecodeError:
scores = {"error": "평가 파싱 실패"}
return scores
# 배치 품질 평가 (하루 샘플링)
async def daily_quality_audit(sample_size: int = 100):
"""매일 랜덤 샘플링하여 품질 감사"""
samples = await db.get_random_conversations(
date=datetime.now().date(),
limit=sample_size
)
scores = []
for sample in samples:
score = await evaluate_response(
sample["user_message"],
sample["ai_response"]
)
scores.append(score)
# 집계
avg_accuracy = sum(s.get("accuracy", 0) for s in scores) / len(scores)
avg_overall = sum(s.get("overall", 0) for s in scores) / len(scores)
await save_daily_quality_report({
"date": datetime.now().date().isoformat(),
"sample_size": sample_size,
"avg_accuracy": round(avg_accuracy, 2),
"avg_overall": round(avg_overall, 2),
})
5. 대시보드 지표 구성
[AI 제품 대시보드 — 권장 구성]
실시간 지표 (1분 갱신):
├── 현재 활성 대화 수
├── P95 응답 지연 (ms)
├── 오류율 (%)
└── 분당 쿼리 수 (QPS)
일별 지표:
├── Helpfulness Rate (목표: 80%+)
├── Task Completion Rate (목표: 70%+)
├── Fallback Rate (목표: 5% 미만)
├── 재질문율 (목표: 15% 미만)
└── AI 기능 활성화율
주별/월별 추이:
├── North Star Metric 추이
├── 코호트별 AI 리텐션
├── 피드백 유형 분포
└── 비용 per 해결된 쿼리
알림 설정:
🚨 오류율 > 5%
🚨 P95 지연 > 10초
⚠️ Helpfulness Rate < 60%
⚠️ Fallback Rate > 15%
마무리
AI 제품에서 "잘 되고 있다"의 기준은 응답 횟수가 아니라 사용자가 원하는 결과를 달성한 비율이다. Helpfulness Rate와 Task Completion Rate를 North Star로 삼고, LLM-as-Judge로 자동 품질 감사를 구축하면 사람이 모든 응답을 리뷰하지 않아도 품질 추이를 추적할 수 있다.
피드백 루프는 단순히 데이터를 수집하는 것이 아니다. 부정 피드백을 모아 파인튜닝이나 프롬프트 개선에 활용하는 것이 핵심이다.