들어가며
이전 포스팅에서는 실서비스 운영을 고려한 성능 최적화에 대해 다뤘다. 물론 빠른 응답 속도와 정확도도 중요하지만 실제 사용자에게 AI 기반 서비스를 제공할 때 가장 중요한 요소는 신뢰할 수 있는 품질의 응답을 일관되게 제공하는 것이다. EATceed의 AI 영양사 기능은 사용자의 건강에 직접적인 영향을 주는 정보를 제공하기 때문에 응답의 정확성과 안정성은 곧 서비스의 핵심 경쟁력이라고 볼 수 있다.
하지만 현실적으로 LLM 기반의 응답은 품질이 항상 일정하지 않고, OpenAI와 같은 외부 API에 대한 높은 의존도 역시 시스템 안정성을 위협할 수 있는 요소이다. 이를 해결하기 위해 두 가지 전략을 도입하게 되었다.
- Testing Chain
- LLM이 생성한 답변을 스스로 평가(G-Eval)하고 기준치에 미달할 경우 A/B 테스트를 통해 더 나은 답변을 제공하는 품질 검증 체계
- Fallback 메커니즘
- OpenAI API에서 오류 또는 Rate Limit(요청 제한) 발생 시 자동으로 Claude API로 전환하여 서비스 중단없이 안정적으로 대응할 수 있는 구조
이러한 구조를 통해 단순한 응답 생성에서 끝나는 것이 아니라, "검증 → 개선 → 대응" 루프를 갖춘 안정적인 AI 응답 시스템을 구축할 수 있었다. 이번 포스팅에서는 어떻게 두 가지 전략을 어떻게 설계하고 했는지와 이를 통해 어떤 효과를 얻었는지를 소개하고자 한다.
정량화가 어려운 AI 응답, 어떻게 테스트할까?
AI 영양사 기능은 사용자의 신체 정보와 식사 데이터를 바탕으로 식습관 조언, 영양소 분석, 식사 개선점, 개인 맞춤 식단 추천까지 다양한 텍스트 기반 결과를 생성한다. 이때 핵심적인 문제는 LLM의 응답에는 "정답"이 존재하지 않는다는 점이다.
푸드렌즈 기능의 경우 탐지율이나 정확도처럼 정답 데이터(Golden Dataset)를 기반으로 정량적인 평가가 가능했지만, AI 영양사 기능은 정성적 판단이 필요한 결과를 생성하고 영양학 도메인에 대한 지식이 없기 때문에 정성적 판단 또한 하기가 어려웠다. "이 답변이 정말 좋은 답변인가?", "사용자에게 신뢰를 줄 수 있는가?"를 객관적으로 검증하고 필요 시 어떻게 보완할 것인지에 대한 고민이 있었는데, 이를 해결하기 위해 이때 사용하게 된 방식이 G-Eval 방법론과 A/B 테스트 기반의 검증이다.
테스트 프로세스
아래는 전체적인 테스트 프로세스를 시각화한 다이어그램이다.
테스트 과정에 대해 하나씩 설명하면 다음과 같다.
1. 첫 번째 Multi-Chain 실행(➊)
- 사용자 데이터에 기반으로 식습관 분석 체인(Multi-Chain) 실행
- 해당 체인은 Diet advice → Nutrition analysis → Improvement → Recommendation 순으로 구성되어 있으며 각 단계에서 LLM 기반 결과를 생성하고 이전 결과값이 다음 단계에서 입력값으로 사용
2. G-Eval을 통한 품질 평가
- Multi-Chain을 통해 생성된 결과는 Testing Chain을 통해 자동 평가
- 평가 항목은 Relevance(연관성), Faithfulness(신뢰성)으로 총 두 가지
- 설정한 임계값 이상이라면 해당 결과를 DB에 바로 적재
3. 임계값 미달 시, 두 번째 Multi-Chain 재실행(➋)
- 첫 번째 응답이 평가 항목 중 하나라도 임계값을 만족하지 못한다면 동일한 입력 데이터로 Multi-Chain 재실행
- 이후 동일한 방식으로 G-Eval 평가 재수행
4. A/B 테스트 로직 수행
- 두 번의 실행 결과(순서대로 A,B)를 비교하기 위해 G-Eval 점수 기반의 스코어 계산
- 이때, 아래와 같은 가중 평균 방식으로 최종 점수 산출한 후 더 높은 실행 결과 DB에 적재
# 가중치 설정
score = (Relevance * 0.7) + (Faithfulness * 0.3)
여기서 Relevance(연관성)에 더 높은 가중치를 부여한 이유가 궁금할 것이다. 이는, 최종적으로 사용자에게 질문에 맞는 직관적이고 정확한 응답을 제공하는 것을 가장 중요하다고 판단했기 때문이다. 예를 들어, "영양소 개선점"을 묻는 질문에 이에 대한 구체적인 개선점 내용이 빠져 있다면 아무리 문법적이고 자연스러운 답변이라도 사용자는 신뢰하지 않을 가능성이 크다. 따라서 Relevance(연관성)에 더 높은 가중치를 두고, Faithfulness(신뢰성)는 상대적으로 낮은 가중치를 적용했다.
또한, 프롬프트 설계시 사용자 데이터(나이, 키, 체중, 식사 내역 등)가 그대로 변수로 주입되므로 기본적인 신뢰성은 어느정도 확보될 수 있다고 판단했기 때문에 Relevance 중심의 판단 기준을 선택했다.
G-Eval: LLM이 만든 응답을 LLM이 평가한다
앞서 언급한 테스트 과정에서 사용된 방법론인 G-Eval에 대해 조금 더 자세히 설명하고 가면 좋을 것 같아서 내용을 작성한다.
LLM의 특성상 동일한 입력이라도 매번 다른 응답이 나올 수 있고, 이 응답이 실제로 적절한지 아닌지를 사람이 직접 판단하기에는 시간과 리소스가 매우 많이 소모된다. 물론 도메인 지식을 가진 전문가가 수작업으로 평가할 수도 있지만, 정성적인 결과를 정량화 없이 단순히 "좋다/나쁘다"로 판단하는 방식은 주관적일 수 밖에 없다.
이러한 문제를 해결하기 위해 평가의 일관성과 자동화를 동시에 해결할 수 있는 방법이 바로 G-Eval 방식이다.
G-Eval은 LLM이 생성한 응답을 다시 LLM이 평가하는 구조이다. 쉽게 말해, AI가 생성한 결과를 AI가 사용자가 미리 정의한 기준(평가 지표)에 따라 점수화하고 분석하는 방식이다. 이때 프로젝트에서는 연관성과 신뢰성을 도입해서 각각 1-5, 0-1 사이의 값을 얻는 것이다.
이러한 기준을 통해 응답의 품질을 수치로 표현할 수 있게 해주는 것 뿐만 아니라 이후 진행하는 A/B 테스트 로직과 연동되어 더 나은 응답을 자동으로 선택할 수 있게 해줄 수 있다.
Fallback 메커니즘: OpenAI 의존도 낮추기
외부 모델을 사용하는 AI 서비스에서 가장 예측하기 어려운 상황 중 하나는 외부 API 장애이다. EATceed의 핵심 기능인 푸드렌즈와 AI 영양사는 모두 OpenAI의 API에 의존하고 있는데, 만약 OpenAI 서버에 장애가 발생하거나 Rate Limit에 도달하면 자연스럽게 우리의 서비스도 함께 중단될 위험이 존재한다.
실제 운영 환경에서 이러한 상황은 사용자 경험을 크게 해치게 되므로 단순한 에러 로깅이나 실패 반환이 아닌 "대응 가능한 구조"가 필요했다. 그래서 도입하게 된 것이 Fallback 메커니즘이다.
언제 Fallback이 실행될까?
아래와 같은 상황에 Fallback 로직이 자동으로 실행된다.
- RateLimitError: RPM / TPM
- APIError, APIConnectionError, APITimeoutError
- OpenAI 응답이 비정상적일 경우
RateLimitError 같은 경우 분당 요청 제한인 RPM(requests per minute), 분당 토큰(데이터) 제한인 TPM(tokens per minute)일 경우에 발생되는 에러이다. AI 영양사 같은 경우는 크게 문제가 없지만, 푸드렌즈 기능은 이미지 데이터를 base64로 인코딩한 후 전송하기 때문에 전송량이 많아 Token 사용량 초과가 발생될 가능성이 존재해 구성하게 되었다.
나머지 에러 같은 경우는 OpenAI 서버 측 장애 또는 일시적 연결 불가 상황을 대비해서 설정했다.
OpenAI API 오류가 발생할 경우, 먼저 최대 3회까지 지수 백오프 방식으로 재시도를 하고난 뒤 이후에도 실패한다면 Claude API로 자동 전환(Fallback)이 된다.
# OpenAI 오류 발생 시 Claude API로 전환(백오프 방식 적용)
if attemp == max_retries - 1:
logger.error("OpenAI 3회 재시도 후 실패 → Claude API로 Fallback")
result = await food_image_analyze_fallback(image_base64, prompt)
return result
이때 Claude API를 선택한 이유는 다음과 같다.
- OpenAI와 비슷한 수준의 성능 제공
- 멀티모달 지원
- 동일한 프롬프트를 적용했을때 안정적 결과 생성
이러한 이유와 함께 사용자 관점에서 기능 정확도 차이가 거의 없기 때문에 Claude를 Fallback 모델로 선택하게 되었다.
서비스 안정성 확보 효과
Fallback 메커니즘 도입을 통해 OpenAI 단독 사용 시 발생할 수 있었던 응답 실패율을 사실상 0에 가깝게 줄일 수 있었다. 실제로 조사한 바에 따르면 2024년 6월 4일에 OpenAI API와 Anthropic의 Claude API가 동시에 서버 오류를 겪은 사례가 있었지만, 이는 드물게 발생하는 예외적인 상황이다.
실제 운영 환경에서 OpenAI API 장애나 Rate Limit 상황에서도 Fallback이 자동으로 작동하며 서비스 중단되지 않고 정상적으로 응답을 반환했다. 결과적으로 시스템의 응답 성공률은 최대 99.9% 이상으로 향상되었고, 이는 사용자에게 보다 안정적인 서비스를 제공할 수 있게 되었다.
'Project > EATceed' 카테고리의 다른 글
[EATceed] CH07. AWS Lambda와 Slack을 활용한 데이터 파이프라인 구축기 (0) | 2025.03.29 |
---|---|
[EATceed] CH05. 실서비스를 고려한 성능 최적화 (1) | 2025.03.22 |
[EATceed] CH04. 돌고 돌아 마침내 정식 출시까지 (0) | 2025.03.21 |
[EATceed] CH03. 2024년 상반기 개발 진행 과정 (1) | 2024.07.11 |
[EATceed] CH02. 프로젝트 소개 (0) | 2024.02.27 |