티스토리 뷰

반응형

RAG 검색 성능 튜닝 실전기

– “응답 느린 AI 서비스”를 실제로 빠르게 만든 방법들
(NestJS + Vector DB + OpenAI 기준, 삽질 경험 그대로)


솔직히 고백부터 할게요.
RAG 처음 붙였을 때 “와, 똑똑하다” 보다 먼저 든 생각은 이거였습니다.

“아… 느리다.”

AI 답변은 정확한데

  • 검색 한 번
  • 임베딩 생성
  • Vector DB 조회
  • LLM 호출

이 과정을 거치다 보니
사용자는 5초 이상 기다리게 되고,
이 순간 SaaS는 바로 욕먹습니다.

이번 글은 이 문제를 실제로 겪고, 하나씩 깨부순 기록이에요.
이론 정리 ❌
실무에서 바로 쓰는 RAG 성능 튜닝 포인트만 정리합니다.


🧭 이 글의 목표

  • RAG 구조에서 어디가 느린지 분해
  • Vector DB 쿼리 튜닝 포인트
  • LLM 호출 비용 & 속도 줄이는 방법
  • 캐싱 전략 (이거 안 하면 RAG 쓰면 안 됨)
  • “아, 이 사람은 진짜 운영해봤네” 소리 듣는 포인트

1️⃣ RAG가 느린 이유부터 정확히 보자

RAG 응답 시간은 보통 이렇게 나뉩니다.

1. 사용자 입력
2. 쿼리 임베딩 생성 (OpenAI)
3. Vector DB 검색
4. 검색 결과 가공
5. LLM 호출

👉 여기서 가장 느린 구간은 2번과 5번
👉 가장 최적화하기 쉬운 구간은 3번과 4번

즉,

❌ “LLM만 바꾸면 빨라지겠지”
✅ “RAG 앞단을 먼저 줄여야 한다”


2️⃣ 첫 번째 튜닝: Vector DB 검색 결과 수 줄이기

처음엔 욕심이 생깁니다.

“문맥은 많을수록 좋겠지?”

그래서 이런 코드가 나오죠.

const results = await qdrant.search("documents", {
  vector,
  limit: 20, // ❌ 너무 많음
});

❌ 문제점

  • 컨텍스트가 길어짐
  • LLM 토큰 증가
  • 응답 속도 ↓ 비용 ↑

✅ 실전 기준

반응형

대부분 3~5개면 충분

const results = await qdrant.search("documents", {
  vector,
  limit: 4,
});

📌 체감 포인트

  • 응답 속도: 확실히 빨라짐
  • 정확도: 거의 차이 없음

3️⃣ 두 번째 튜닝: 검색 결과를 그대로 LLM에 넘기지 말 것

초기엔 이렇게 했습니다.

const context = results.map(r => r.payload.text).join("\n");

이 방식, 진짜 위험합니다.

❌ 문제

  • 쓸모없는 문장까지 다 들어감
  • 토큰 낭비
  • hallucination 증가

✅ 해결: “요약 후 전달”

function compactContext(results: any[]) {
  return results.map(r => {
    const text = r.payload.text;
    return text.length > 500 ? text.slice(0, 500) : text;
  }).join("\n---\n");
}

또는 더 좋은 방식👇

const context = results.map(r => `
- 핵심 요약:
${r.payload.summary}
`).join("\n");

👉 Vector DB에 저장할 때 요약본도 같이 저장
이거 하나로 RAG 품질이 달라집니다.


4️⃣ 세 번째 튜닝: 임베딩 캐싱 (안 하면 진짜 바보)

이건 운영하면서 제일 먼저 터지는 문제예요.

같은 질문을
같은 사용자가
하루에 10번 한다?

그럼 임베딩도 10번 생성합니다.
이건 그냥 돈 태우는 구조예요.


✅ Redis 임베딩 캐싱

const cacheKey = `embed:${query}`;

const cached = await redis.get(cacheKey);
if (cached) {
  return JSON.parse(cached);
}

const embedding = await openai.embeddings.create({
  model: "text-embedding-3-small",
  input: query,
});

await redis.set(cacheKey, JSON.stringify(embedding), 'EX', 60 * 60);

return embedding;

📌 체감 변화

  • 응답 시간: 30~40% 감소
  • OpenAI 비용: 눈에 띄게 줄어듦

5️⃣ 네 번째 튜닝: LLM 호출 모델 전략 바꾸기

RAG 답변이라고
무조건 좋은 모델 쓰면 안 됩니다.

❌ 초반 실수

model: "gpt-4"

→ 느림 + 비쌈


✅ 실전 전략

용도모델

RAG 답변 gpt-4o-mini
요약 gpt-3.5
내부 태깅 local LLM
최종 응답(프리미엄) 상위 모델
const completion = await openai.chat.completions.create({
  model: user.plan === 'PRO' ? 'gpt-4o-mini' : 'gpt-3.5-turbo',
  messages,
});

👉 요금제별 모델 분기
이거 하나로 원가 구조가 달라집니다.


6️⃣ 다섯 번째 튜닝: RAG도 결국 “검색 서비스”다

RAG를 AI라고 생각하면 안 됩니다.
검색 서비스라고 생각해야 튜닝이 됩니다.

실전 체크리스트

  • 자주 검색되는 질문 TOP 20 캐싱
  • 유사 질문 정규화 (전처리)
  • 불필요한 문서 제외 필터
  • 사용자 컨텍스트(권한/도메인) 제한
await qdrant.search("documents", {
  vector,
  filter: {
    must: [
      { key: "category", match: { value: "guide" } }
    ]
  }
});

7️⃣ RAG 성능 튜닝 전/후 비교 (체감 기준)

항목튜닝 전튜닝 후

평균 응답 시간 6.2초 2.4초
OpenAI 비용 기준 -35%
사용자 이탈 높음 눈에 띄게 감소
AI 답변 품질 보통 더 안정적

👉 속도 = UX = 서비스 평가


8️⃣ 실무 인사이트 (이 글의 핵심)

  • RAG는 AI 이전에 검색 문제
  • 컨텍스트는 짧을수록 좋다
  • 임베딩 캐싱은 필수
  • 모델은 상황별로 써라
  • “정확한 답변”보다 “빠른 쓸모”가 중요할 때가 많다

 


 

RAG, 벡터DB, Qdrant, AI성능튜닝, NestJS, OpenAI, LLM최적화, AI서비스운영, 백엔드실무, SaaS개발

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함
반응형