AI 채점 기능을 구현할 때 가장 현실적인 고민은 'API 비용을 어떻게 감당할 것인가'다. 사용자가 문장을 입력할 때마다 GPT API를 호출하면, 사용자 수가 늘어날수록 비용이 기하급수적으로 증가한다. 듣기론, AI를 이용하는 서비스에서 골머리를 썩게 만드는 것이 바로 이 비용 관리라고 한다. (언커버 더 스모킹 건이라는 게임을 플레이 했을 때 관련 이야기를 들었다.) 아래로는 모아보카 프로젝트 개발에서 고려해 본 것들을 정리한다.
1. 모델 선택은 어떻게 해야 하는가?
가격 비교
OpenAI는 2024년 7월 gpt-4o-mini 모델을 출시했다. 기존 모델들과 비교하면 가격 차이가 극명하다.
모델 입력 (1M 토큰) 출력 (1M 토큰) 상대 비용
| gpt-4 | $30.00 | $60.00 | 100% |
| gpt-4-turbo | $10.00 | $30.00 | 33% |
| gpt-4o | $2.50 | $10.00 | 10% |
| gpt-4o-mini | $0.15 | $0.60 | 1% |
| gpt-3.5-turbo | $0.50 | $1.50 | 2% |
여기서 집중할 점은 gpt-4o-mini가 gpt-3.5-turbo보다 더 저렴하면서 성능은 더 좋다는 것이다.
성능에 비례해 타협을 해야 한다.
우리 프로젝트의 요구사항은 다음과 같다:
- 한국어 문장을 영어로 번역한 결과 평가
- 문법, 의미 전달, 자연스러움 등 채점
- JSON 형식의 구조화된 응답
테스트 결과, gpt-4o-mini는 이 모든 요구사항을 충족했다. 특히 JSON 모드에서의 안정성이 뛰어나 파싱 에러가 거의 발생하지 않는다.
2. 토큰 사용량 최적화
프롬프트 길이 줄이기
API 비용은 토큰 수에 비례한다.
따라서 같은 기능을 하는 프롬프트라도 토큰을 적게 쓰면 비용이 줄어든다(!)
Before: 장황한 프롬프트 (약 250 토큰)
You are an English language evaluator for a language learning application.
Your task is to evaluate the user's English translation of a Korean sentence.
Please evaluate the translation based on the following criteria:
1. Meaning: How accurately does the translation convey the original meaning?
2. Grammar: Is the translation grammatically correct?
3. Word Usage: Did the user correctly use the target vocabulary word?
4. Naturalness: Does the translation sound natural to a native speaker?
Please provide your evaluation in JSON format with the following structure:
- score: overall score from 0 to 100
- breakdown: individual scores for each criterion
- feedback: helpful feedback in Korean
...
After: 간결한 프롬프트 (약 120 토큰)
[번역 평가 - 중급]
한국어: {korean}
학습 단어: {word}
사용자 번역: {answer}
모범 답안: {ideal}
평가항목: meaning, grammar, word_usage, naturalness (각 0-100)
JSON 응답: {score, breakdown, feedback(한국어), correction}
결과: 토큰 사용량 52% 감소
시스템 프롬프트 최소화
시스템 프롬프트는 매 요청마다 전송되므로 최대한 짧게 유지하는 것이 중요하다.
SYSTEM_PROMPT = """영어 번역 평가자. JSON만 응답. 0-100점 채점. 피드백은 한국어로."""
4. 추가 최적화 전략
4.1 캐싱 활용
동일한 입력에 대해 반복 호출을 피한다.
import hashlib
import json
# 간단한 인메모리 캐시 (프로덕션에서는 Redis 사용)
response_cache = {}
def get_cached_evaluation(korean, answer, word, level):
# 캐시 키 생성
cache_key = hashlib.md5(
f"{korean}:{answer}:{word}:{level}".encode()
).hexdigest()
if cache_key in response_cache:
return response_cache[cache_key]
# API 호출
result = evaluate_translation(korean, answer, word, level)
response_cache[cache_key] = result
return result
같은 문제에 같은 답을 입력하면 캐시된 결과를 반환한다.
4.2 배치 처리
여러 평가 요청을 모아서 한 번에 처리하면 오버헤드를 줄일 수 있다.
async def batch_evaluate(evaluations: list) -> list:
"""여러 평가를 병렬로 처리"""
tasks = [
evaluate_single(e["korean"], e["answer"], e["word"], e["level"])
for e in evaluations
]
return await asyncio.gather(*tasks)
4.3 max_tokens 제한
응답 길이를 제한하여 불필요한 출력을 방지한다.
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
max_tokens=300, # 응답 길이 제한
temperature=0.3 # 낮은 temperature로 일관된 응답
)
4.4 불필요한 호출 방지
클라이언트 단에서 기본적인 검증을 수행한다.
def should_call_api(user_answer: str, target_word: str) -> bool:
"""API 호출이 필요한지 사전 검증"""
# 빈 답안
if not user_answer.strip():
return False
# 너무 짧은 답안 (의미 있는 문장이 아님)
if len(user_answer.split()) < 2:
return False
# 한글만 입력한 경우
if not any(c.isalpha() and ord(c) < 128 for c in user_answer):
return False
return True
5. 모델별 사용 시나리오
모든 상황에 gpt-4o-mini가 최선은 아니다. 상황에 따라 모델을 선택한다.
시나리오 권장 모델 이유
| 일반 번역 평가 | gpt-4o-mini | 비용 효율 최고 |
| 고급 레벨 정밀 평가 | gpt-4o | 미묘한 뉘앙스 판단 필요 |
| 문제 생성 (창의성 필요) | gpt-4o | 다양한 문장 생성 |
| 단순 문법 체크 | gpt-4o-mini | 과한 스펙 불필요 |
def select_model(user_level: str, task_type: str) -> str:
"""상황에 맞는 모델 선택"""
if task_type == "evaluation" and user_level == "advanced":
return "gpt-4o" # 고급 평가는 정밀하게
return "gpt-4o-mini" # 나머지는 비용 효율 우선
참고 자료: