대규모 프로덕션 RAG: 일일 10,000개 이상의 리스팅 처리에서 얻은 교훈

저는 채용 게시판을 위한 RAG 파이프라인을 구축했습니다. 스테이징 환경에서는 잘 작동했지만, 실제 부하가 걸리자 어려움을 겪었습니다. 매일 수천 개의 리스팅을 처리하려면 단순히 좋은 벡터 스토어만으로는 부족합니다. 시스템이 어디에서 무너지는지 반드시 이해해야 합니다.

청킹(chunking), 임베딩(embeddings), 비용, 그리고 관측성(observability)에 관한 저의 교훈을 공유합니다.

  1. 청킹 전략을 짐작으로 정하지 마세요

대부분의 튜토리얼은 청킹을 단순한 설정값으로 취급합니다. 하지만 프로덕션 환경에서는 청킹 전략이 정확도와 비용을 결정합니다.

저는 채용 리스팅에 대해 세 가지 방법을 테스트했습니다:

  • 고정 크기 청크(Fixed-size chunks): 실패했습니다. "자격 요건(requirements)"이나 "혜택(benefits)" 같은 섹션을 무작위 지점에서 잘라버렸습니다. 이는 노이즈가 섞인 검색 결과를 초래합니다.
  • 의미론적 청킹(Semantic chunking): 더 나았지만 일관성이 없었습니다. 어떤 청크는 너무 길고, 어떤 것은 너무 짧았습니다.
  • 오버랩을 포함한 재귀적 문자 분할(Recursive character splitting with overlap): 가장 효과적이었습니다. 줄바꿈과 문장을 기준으로 분할했습니다. 400 토큰 크기에 50 토큰의 오버랩을 사용했습니다. 이를 통해 두 청크에 걸쳐 있는 문장이 연결된 상태를 유지할 수 있었습니다.

전문가 팁: 청킹 전에 데이터를 정규화하세요. Greenhouse나 Lever와 같은 서로 다른 소스는 각기 다른 형식을 반환합니다. 청커(chunker)가 일관된 구조를 인식할 수 있도록 텍스트를 먼저 정제해야 합니다.

  1. 임베딩: 비용 vs. 정확도

Ollama를 통한 Llama 3.1과 OpenAI의 text-embedding-3-small을 비교 테스트했습니다. 로컬 모델은 무료였지만 "주식 보상(equity compensation)"과 같은 도메인 특화 용어 처리에 어려움을 겪었습니다. 결과적으로 노이즈가 섞인 결과가 나왔습니다. OpenAI는 비용이 더 들었지만 정확한 매칭을 제공했습니다. 저는 나중에 잘못된 검색으로 인해 발생하는 LLM 호출 비용이 더 크기 때문에 OpenAI를 선택했습니다.

시간을 절약하기 위해 요청을 배치(batch) 처리합니다. 한 번의 호출에 최대 100개의 청크를 보냅니다. 이를 통해 지연 시간을 줄이고 파이프라인의 속도를 유지합니다.

  1. 벡터 스토어의 트레이드오프(Trade-off)

프로토타이핑 단계에서는 설정이 빠른 Pinecone을 사용했습니다. 하지만 규모가 커지면서 비용이 너무 높아졌습니다.

그래서 PostgreSQL 내부의 pgvector로 전환했습니다.

  • 설정하는 데 더 많은 노력이 필요했습니다.
  • 엄청난 비용을 절감했습니다.
  • 트랜잭션 일관성을 제공했습니다. 임베딩이 채용 데이터와 동일한 데이터베이스에 저장되므로, 단일 진실 공급원(single source of truth)을 가질 수 있습니다. 두 개의 서로 다른 시스템을 동기화할 필요가 없습니다.
  1. LLM 비용 제어하기

모든 리스팅을 GPT-4o로 점수화하는 것은 비용이 많이 듭니다. 저는 비용을 낮추기 위해 세 가지 전략을 사용했습니다:

  • OpenAI Batch API: 점수화 작업을 밤사이에 처리합니다. 이를 통해 큰 할인 혜택을 받을 수 있습니다.
  • 캐싱(Caching): 반복되는 후보자 프로필에 대한 결과를 캐싱합니다.
  • 모델 계층화(Model tiering): "영업 사원(Sales Representative)"과 같은 일반적인 직무에는 GPT-4o-mini를 사용합니다. 정밀도가 필수적인 니치(niche) 직무에만 GPT-4o를 사용합니다.
  1. 관측성(Observability)을 먼저 구축하세요

제 파이프라인은 한때 아무런 오류 메시지 없이 조용히 실패한 적이 있습니다. 잘못된 형식의 데이터가 빈 청크를 생성했고, 시스템은 이를 오류 없이 건너뛰었습니다.

저는 상관관계 ID(correlation ID)를 포함한 구조화된 로깅을 추가하여 이 문제를 해결했습니다. 이를 통해 데이터 수집부터 점수화까지 하나의 리스팅을 추적할 수 있게 되었습니다. 마침내 어떤 데이터 소스가 실패를 유발하는지 확인할 수 있었습니다.

가장 큰 교훈: 대부분의 문제는 AI가 아니라 지저분한 데이터에서 발생합니다. 데이터 파이프라인(data plumbing)부터 먼저 정비하세요.

출처: https://dev.to/abdul___rehman/production-rag-at-scale-lessons-from-processing-10000-listings-daily-22gm

선택 사항 학습 커뮤니티: https://t.me/GyaanSetuAi