스트리밍(Streaming)과 프롬프트 캐싱(Prompt Caching)으로 AI API 지연 시간(Latency)을 해결한 방법
요약
AI API 응답 지연 문제를 해결하기 위해 스트리밍(Streaming)과 프롬프트 캐싱 기술을 적용하는 방법을 다룹니다. 전체 응답을 기다리는 대신 토큰 단위로 즉시 출력하여 사용자 경험을 개선하는 실무적인 가이드를 제공합니다.
핵심 포인트
- 전체 응답을 기다리는 방식은 높은 지연 시간(Latency)을 유발함
- 비동기 HTTP(Async)만으로는 서버의 응답 대기 문제를 근본적으로 해결할 수 없음
- 스트리밍(SSE)을 통해 첫 번째 토큰(TTFT)을 즉시 보여줌으로써 체감 속도 개선
- 프롬프트 캐싱을 결합하여 반복적인 쿼리에 대한 응답 시간을 1초 미만으로 단축
몇 달 전, 저는 한 고객의 문서 사이트를 위한 실시간 채팅 어시스턴트를 구축하고 있었습니다. 아이디어는 간단했습니다. 사용자가 질문을 입력하면 AI 모델이 문서에서 추출한 답변을 반환하는 것이었죠. 간단해 보이나요? 아닙니다.
모든 쿼리(Query)에 10~15초가 소요되었습니다. 사용자들은 빈 스피너(Spinner)만 보게 되었습니다. 그리고 떠나갔습니다. 고객은 만족하지 못했습니다.
저는 문제가 모델 자체에 있는 것이 아니라, 저의 미숙한 통합 방식에 있다는 것을 알았습니다. 저는 전체 요청을 보내고, 아무것도 보여주기 전에 전체 응답이 올 때까지 기다리고 있었습니다. 이는 피자를 주문하고 피자가 다 구워질 때까지 먹기를 거부하는 것과 같습니다. 터무니없는 일이죠.
그래서 저는 처음부터 다시 시작했습니다. 제가 시도했던 것들, 효과가 없었던 것들, 그리고 마침내 응답 시간을 1초 미만(캐시된 프롬프트의 경우)으로 줄이고 새로운 쿼리에 대해 즉각적인 부분 출력(Partial outputs)을 가능하게 만든 방법들을 소개합니다.
전통적인 접근 방식 (실패함)
저의 초기 구현은 다음과 같았습니다:
import requests
def ask_ai(question, context):
...
매우 단순합니다. 하지만 requests.post()는 전체 500개 토큰(Token)의 응답이 다시 전송될 때까지 차단(Block)됩니다. 이것이 제 사용자들이 10초 동안 빈 페이지를 바라보게 만든 이유입니다. 모델은 초당 약 50개 토큰 정도로 빠르지만, 500개 토큰을 기다린다는 것은 최소 10초를 기다려야 함을 의미합니다.
도움이 되지 않았던 시도들
1. 비동기 HTTP (Async HTTP, aiohttp)
비동기(Async)로 전환하면 마법처럼 해결될 것이라고 생각했습니다. 아니었습니다. 요청은 여전히 전체 응답이 준비된 후에만 반환됩니다. 비동기는 동시성(Concurrency)에는 도움이 되지만, 서버의 동작 방식을 바꾸지는 않습니다.
2. 응답 사전 계산 (Pre-computing responses)
가능한 모든 질문을 캐싱(Caching)하려고 시도했습니다. FAQ에는 효과적이었지만, 어시스턴트는 문서 내의 어떤 질문에도 답할 수 있어야 했습니다. 사전 계산은 불가능했습니다.
3. 더 짧은 max_tokens
토큰을 100개로 제한하자 지연 시간(Latency)이 약 2초로 줄었지만, 답변이 중간에 끊겨 쓸모가 없는 경우가 많았습니다. 이러한 트레이드오프(Trade-off)는 받아들일 수 없었습니다.
실제로 효과가 있었던 것: 스트리밍(Streaming)과 시맨틱 캐싱(Semantic Caching)
두 가지 기술을 함께 사용했을 때 문제가 해결되었습니다.
스트리밍(Streaming): 텍스트가 도착하는 대로 보여주기
대부분의 AI API는 스트리밍 (Server-Sent Events)을 지원합니다. 응답은 청크 (chunks) 단위로 돌아옵니다. 첫 번째 토큰 (token)이 도착하자마자 이를 표시할 수 있습니다. 사용자는 어시스턴트가 "실시간으로 생각하는" 모습을 보게 됩니다.
Python과 httpx를 사용하여 이를 구현한 방법은 다음과 같습니다:
import httpx
def stream_ai(prompt, context):
...
프론트엔드 (JavaScript)에서는 다음과 같습니다:
const eventSource = new EventSource('/api/assistant/stream?q=' + encodeURIComponent(question));
eventSource.onmessage = (event) => {
// 토큰이 도착하는 대로 div에 추가
...
첫 번째 토큰은 약 200~300ms 만에 도착합니다 (첫 번째 토큰까지의 시간, time to first token). 전체 응답에는 여전히 10초가 걸리지만, 사용자는 즉시 진행 상황을 볼 수 있습니다. 참여 시간 (Engagement time)이 증가했습니다.
시맨틱 캐싱 (Semantic Caching): 동일한 내용을 다시 작성하지 않기
하지만 반복되거나 거의 동일한 질문은 어떻게 할까요? 저는 정확한 문자열 일치 (string matches)가 아닌 의미를 이해하는 캐시가 필요했습니다.
저는 문장 임베딩 (sentence embeddings, 예: sentence-transformers)을 사용하여 간단한 시맨틱 캐시를 구축했습니다. 요청을 보내기 전에 사용자 질문의 임베딩을 계산하고, 이를 벡터 데이터베이스 (vector database, 저는 인메모리 FAISS를 사용했습니다)에 있는 이전 질문들과 비교합니다.
from sentence_transformers import SentenceTransformer
import numpy as np
import faiss
...
이전에 유사한 질문이 던져졌다면, 캐시된 답변을 즉시 (10ms 미만) 반환합니다. API 호출이 전혀 발생하지 않습니다. 이를 통해 약 30%의 쿼리에 대한 지연 시간 (latency)을 거의 제로에 가깝게 줄였습니다.
교훈 및 트레이드오프 (Trade-offs)
스트리밍 (Streaming)
- 장점 (Pro): 엄청난 UX 개선. 사용자는 무언가 일어나고 있음을 보게 됩니다.
- 단점 (Con): 백엔드에서 처리하기 더 어렵습니다 (열린 연결 관리, 연결 끊김 처리 필요).
- 단점 (Con): 모든 AI API가 스트리밍을 지원하는 것은 아닙니다. 구현하기 전에 확인하십시오.
시맨틱 캐싱 (Semantic Caching)
- 장점 (Pro): API 호출과 비용을 획기적으로 줄입니다.
- 단점 (Con): 임베딩 모델과 벡터 인덱스 (vector index)가 필요합니다. 캐시 조회(cache lookup)를 위해 약 100ms의 오버헤드 (overhead)가 추가되었습니다.
- 단점 (Con): 임계값 (Threshold) 튜닝이 까다롭습니다. 너무 낮으면 → 캐시 미스 (cache misses) 발생; 너무 높으면 → 오래된 답변 (stale answers) 제공.
이것을 사용하지 말아야 할 때
- 사용자가 읽기 시작하기 전에 완전한 답변을 기대하는 경우(예: 팩트 체크 도구), 스트리밍 (Streaming)은 사용자에게 혼란을 줄 수 있습니다.
- 프롬프트 (Prompts)가 매우 동적이라면 (모든 쿼리가 고유하다면), 캐싱 (Caching)은 큰 도움이 되지 않습니다.
- 지연 시간 (Latency)이 이미 2초 미만이라면, 스트리밍을 도입하는 복잡성이 그만한 가치가 없을 수도 있습니다.
다음에 제가 다르게 할 일
저는 첫날부터 전용 스트리밍 서버(Server-Sent Events를 사용하는 비동기 FastAPI와 같은 방식)를 사용할 것입니다. 저는 단순한 블로킹 (Blocking) 방식에 일주일이라는 시간을 낭비했습니다.
또한, 폴백 (Fallback) 메커니즘을 추가하겠습니다. 캐시 히트 (Cache hit)가 발생했지만 사용자가 만족하지 못하는 것 같다면, 사용자가 "다시 생성 (Regenerate)"를 클릭하여 강제로 새로운 API 호출을 할 수 있도록 하겠습니다.
마지막으로, 첫 번째 토큰까지의 시간 (Time-to-first-token)을 전체 응답 시간과 별도로 측정하겠습니다. 이것이 스트리밍 UX에서 중요한 지표이며, 대부분의 모니터링 도구는 이를 기본적으로 추적하지 않습니다.
여러분의 차례
지연 시간 (Latency)은 AI 기반 기능의 소리 없는 살인자입니다. 모델을 탓하기 전에, 여러분이 API를 어떻게 소비하고 있는지 냉정하게 살펴보세요.
AI 응답을 빠르게 유지하기 위한 여러분만의 비결은 무엇인가요? 여러분이 사용해 본 다른 캐싱 전략이나 스트리밍 패턴에 대해 듣고 싶습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기