접근 방식을 바꾸기 전까지 나의 AI 통합 비용은 끔찍했다
요약
AI 요약 서비스 운영 중 발생한 높은 API 비용 문제를 해결하기 위해 추출적(Extractive) 방식과 생성적(Abstractive) 방식을 결합한 2단계 하이브리드 파이프라인을 구축했습니다. 이 방식을 통해 요약 품질은 유지하면서 비용을 80% 절감하는 데 성공했습니다.
핵심 포인트
- GPT-4의 높은 비용과 GPT-3.5의 낮은 품질 사이의 트레이드오프 발생
- 추출적 요약으로 입력 토큰을 90% 제거하여 비용 효율성 극대화
- 추출 단계(로컬)와 생성 단계(API)를 분리한 하이브리드 접근법 제안
- 품질 유지와 비용 절감을 동시에 달성하는 실무적인 엔지니어링 패턴
문제점: AI 요약 기능이 좋았지만 청구서를 보고 나서야 깨달았다
지난달 저는 사용자들을 위해 긴 기사를 요약해 주는 SaaS 제품을 작업하고 있었습니다. TL;DR 생성기 같은 것이라고 생각하면 됩니다. 저는 아주 단순한 프롬프트인 _"이 기사를 3개의 불렛 포인트로 요약해줘."_를 사용하여 GPT-4로 첫 번째 프로토타입을 구축했습니다. 결과는 훌륭했습니다. 요약은 명확하고 정확했으며, 사용자들도 매우 좋아했습니다.
그 후 API 청구서가 도착했습니다. 한 달간의 적당한 사용량만으로 1,200달러가 넘는 비용이 발생했습니다. 이는 사이드 프로젝트로서 지속 가능하지 않았습니다. 저는 이 문제를 해결하거나 기능을 폐기해야만 했습니다.
시도했지만 실패했던 방법들
먼저, GPT-3.5-turbo로 전환해 보았습니다. 가격은 극적으로 낮아졌지만, 품질이 급락했습니다. 요약이 모호해졌고, 때로는 핵심 내용을 놓치기도 했습니다. "구체적으로 작성해줘" 또는 "숫자를 포함해줘"와 같은 프롬프트 엔지니어링 (Prompt Engineering)을 시도해 보았지만, GPT-4의 결과물을 안정적으로 따라잡을 수 있는 것은 없었습니다.
다음으로, 입력 크기를 줄이는 방법을 시도했습니다. GPT로 보내기 전에 sumy와 같은 추출적 요약 (Extractive Summarization) 라이브러리를 사용하여 가장 중요한 문장들을 추출했습니다. 이것이 약간의 도움이 되긴 했지만, 추출된 텍스트 자체가 여전히 컸고, GPT-3.5는 긴 입력값에 대해 여전히 환각 (Hallucination) 현상을 보였기 때문에 비용은 여전히 높았습니다.
또한 Llama 2와 같은 로컬 모델을 사용하는 것도 고려했지만, 제 서버는 추론 지연 시간 (Inference Latency)을 감당할 수 없었습니다. 제 사용자들은 3초 이내의 요약을 기대했습니다.
결국 성공한 방법: 2단계 하이브리드 접근 방식
요약에 관한 연구 논문들을 읽으면서 통찰을 얻었습니다. 순수 추출적 방식 (Extractive, 문장을 선택하는 방식)은 빠르지만 경직되어 있습니다. 순수 생성적 방식 (Abstractive, 새로운 문장을 생성하는 방식)은 유연하지만 비용이 많이 듭니다. 최적의 지점은 무엇일까요? 추출적 방식을 사용하여 텍스트를 축소한 다음, 작은 생성적 모델을 사용하여 요약을 우아하게 다시 쓰는 것입니다.
저는 다음과 같은 파이프라인을 구현했습니다:
- 추출 단계 (Extractive phase): 저렴하고 빠른 문장 점수 산출기(예:
textrank또는 아주 작은 모델을 사용하는bert-extractive-summarizer)를 사용하여 기사에서 상위 5~10개의 문장을 선정합니다. - 생성 단계 (Abstractive phase): 선정된 문장들을 비용 효율적인 작은 API(예: GPT-3.5-turbo 또는 커스텀 파인튜닝된 T5)에 전달하며, 다음과 같은 간단한 프롬프트를 사용합니다: "이 핵심 문장들을 유창한 3개의 불렛 포인트 요약으로 결합하세요."
이 방식은 품질을 GPT-4에 가깝게 유지하면서도 비용을 80% 절감했습니다. 추출 단계에서 입력 토큰의 90%를 제거하기 때문에, API 호출 규모가 매우 작아집니다.
실제 코드: 파이프라인
다음은 일반적인 API 엔드포인트를 사용하는 단순화된 Python 버전입니다 (호환 가능한 서비스라면 무엇이든 교체 가능합니다. 예를 들어 https://ai.interwestinfo.com/ 또는 OpenAI):
import requests
from sumy.parsers.plaintext import PlaintextParser
from sumy.nlp.tokenizers import Tokenizer
...
이 코드는 추출형 요약기(extractive summarizer)를 로컬에서 실행하여(빠르고 무료) 단 한 번의 작은 API 호출만 수행합니다. 저는 추출 단계에 캐싱(caching)을 추가하여, 동일한 기사가 반복될 때 같은 문장들을 다시 처리하지 않도록 했습니다.
트레이드오프(Trade-offs) 및 교훈
이 접근 방식이 완벽한 것은 아닙니다. 추출 단계에서 문장 간의 연결 논리를 놓칠 수 있기 때문에, 어떤 기사들은 완전한 생성형 요약(fully abstractive summary)을 필요로 합니다. 예를 들어, 원문이 단계별로 논거를 구축하는 경우, 무작위로 핵심 문장을 뽑으면 흐름을 놓치게 됩니다. 그런 경우에는 GPT-4를 단일 호출하는 방식으로 돌아가지만, 문장 수나 개체명 밀도(named entity density)로 감지된 특정 복잡도 이상의 기사로 제한하여 사용합니다.
또한, TextRank를 사용하는 추출 단계는 비결정론적(non-deterministic)입니다. 즉, 다시 실행할 때마다 결과가 달라질 수 있습니다. 저는 일관성을 보장하기 위해 (고정된 랜덤 시드를 사용하는) 결정론적 변형 모델로 전환했습니다.
또 다른 교훈은 캐싱(caching)이 최고의 친구라는 점입니다. 저는 추출 결과(기사 해시 기준)와 생성 결과(해시 + 모델 기준)를 모두 캐싱합니다. 프로덕션 환경에서는 수천 명의 사용자가 동일한 기사를 읽기 때문에 캐시 적중률(cache hit rates)이 높습니다.
다음에 다시 한다면 다르게 할 점
만약 제가 이것을 다시 구축한다면, 다음과 같이 하겠습니다:
- 제 도메인(금융 뉴스)에 특화된 추출적 점수 매기기(extractive scoring)를 위해 아주 작은 증류된 BERT (distilled BERT) 모델을 학습시키겠습니다. 기성 요약기(off-the-shelf summarizers)는 일반적입니다.
- 생성적(abstractive) 단계에는 스트리밍 API (streaming API)를 사용하여, 나머지 내용이 생성되는 동안 사용자가 부분적인 출력을 볼 수 있도록 하겠습니다.
- 사용자 피드백 버튼을 추가하여 평점을 수집하고, GPT-4로 폴백(fallback)하기 위한 임계값(threshold)을 미세 조정(fine-tune)하겠습니다.
더 큰 그림
진정한 교훈은 특정 API나 라이브러리에 관한 것이 아닙니다. 그것은 계층적으로 생각하는 것에 관한 것입니다. 항상 대형 해머(sledgehammer)가 필요한 것은 아닙니다. 복잡한 AI 작업을 더 저렴한 하위 작업(sub-tasks)으로 나눔으로써, 비용과 지연 시간(latency) 문제를 모두 해결할 수 있습니다.
이제 궁금합니다: 프로덕션 환경에서 AI의 품질과 비용 사이의 균형을 맞추기 위해 당신은 어떤 접근 방식을 사용하시나요? 캐스케이딩 모델(cascading models)이나 다른 기술들을 사용하시나요?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기