5백만 건 이상의 문서를 처리하며 얻은 Production RAG 경험
(blog.abdellatif.io)- 8개월간 RAG(검색 증강 생성) 프로젝트를 진행하며 실제로 효과적인 방법과 시간 낭비였던 방법을 구분함
- 초기에는 Langchain과 Llamaindex를 사용하여 프로토타입을 빠르게 완성했지만, 실제 사용자 피드백에서 성능 한계를 경험함
- 문서 검색 성능을 개선한 가장 큰 요인은 쿼리 생성, 리랭킹, 청킹 전략, 메타데이터 활용, 쿼리 라우팅으로 확인됨
- 실무에서는 벡터 데이터베이스, 임베딩, 리랭킹, LLM 등을 유동적으로 선택하며 커스텀 파이프라인을 구축함
- 모든 경험과 노하우를 오픈 소스 프로젝트(agentset-ai/agentset) 에 집약하여 공개함
8개월간의 Production RAG 구축 경험 개요
- 총 9백만 페이지(Usul AI), 4백만 페이지(모 익명 법률 AI 기업) 등 대규모 데이터셋에서 RAG 시스템을 구축 및 운영하는 경험을 공유함
- 초기에는 YouTube 튜토리얼을 따라 Langchain에서 Llamaindex로 옮기며 며칠 만에 프로토타입을 완성했으나, 실제 배포시 사용자만이 알아챌 수 있는 낮은 성능 문제가 드러남
- 몇 달에 걸쳐 시스템 구성 요소를 부분적으로 고치며 최적의 성능에 도달했음
성능 개선에 실질적으로 기여한 요소들 (ROI 순)
-
쿼리 생성(Query Generation)
- 사용자의 마지막 쿼리로는 모든 맥락을 담을 수 없기 때문에, LLM으로 대화 내용을 검토해 의미론적 쿼리와 키워드 쿼리 여러 개를 생성함
- 이 쿼리들을 병렬로 처리하고 이를 리랭커에 넘겨 검색 범위 확대 및 하이브리드 서치의 편향 보완 효과를 얻음
-
리랭킹(Reranking)
- 5줄 정도의 코드로 구현 가능한 리랭킹이 성능에 미치는 영향이 상상 이상으로 큼
- 대량 청크 입력(예: 50개)에서 상위 일부(예: 15개) 청크를 재정렬 및 선별하는 과정이 가장 ROI가 큼
- 리랭킹만으로도 설계가 미흡한 파이프라인의 부족분을 상당히 상쇄할 수 있음
-
청킹 전략(Chunking Strategy)
- 전체 개발 과정의 주요 시간을 차지하는 부분
- 데이터 구조와 패턴을 정확히 이해하고, 논리적 단위로 청킹하며 글자나 문장이 중간에 잘리지 않도록 직접 검수 필요
- 청크별로 독립적 의미가 유지되어야 함
-
LLM 입력 메타데이터 활용
- 단순 청크 텍스트만 LLM에 전달하지 않고, 메타데이터(title, author 등) 를 추가하여 문맥 및 답변 품질을 크게 개선함
-
쿼리 라우팅(Query Routing)
- RAG로는 답변 불가능한 유형(예: 기사 요약, 작성자 정보 요청 등)에는 경량 라우터를 도입, 해당 쿼리를 API+LLM 처리 경로로 분기함
실전 스택(Our stack)
- 벡터 데이터베이스: Azure → Pinecone → Turbopuffer(저렴하며 기본적으로 키워드 검색 지원)
- 문서 추출: 커스텀 방식 적용
- 청킹 툴: 기본적으로 Unstructured.io, 기업용 파이프라인은 커스텀(Chonkie도 좋은 평 있음)
- 임베딩 모델: text-embedding-large-3 사용(다른 모델 미테스트)
- 리랭커: None → Cohere 3.5 → Zerank(대중적이지 않으나 실제로 우수함)
- LLM: GPT 4.1 → GPT 5 → GPT 4.1(주로 Azure 크레딧 사용)
오픈 소스화 및 결론
- 모든 학습 결과와 실전 경험을 오픈 소스 프로젝트agentset-ai/agentset 로 귀결
- MIT 라이선스로 공개되어 자유롭게 사용 및 문의 가능(연락처 제공)
Hacker News 의견
- 합성 쿼리 생성에 대해 언급한 부분이 정말 중요함을 느낌, 실제 사용자가 쿼리를 매우 부정확하게 입력하는 경우가 많아서 처음엔 LLM이 합성 쿼리를 만들어줌, 하지만 매번 생성되는 합성 쿼리에 따라 결과 편차가 커서, 한 번의 LLM 호출로 쿼리 세 가지 버전을 다양하게 생성하도록 하고, 이를 병렬 탐색 후 reciprocal rank fusion으로 강력한 결과 리스트를 조합함, 검색엔 hybrid dense + sparse bm25를 사용함, dense만으론 전문 용어에 약함, 여기에 reranker까지 붙이니 검색 관련 문제는 대부분 해결됨
- dense 모델이 기술 용어에 약한 점을 보완하려 hybrid 방식(bm25 + dense search)을 쓰는 것에 흥미를 느낌, SPLADE처럼 의미적·어휘적 검색 균형을 잘 맞추는 v3 모델이 성능도 좋아 보이는데 좀 더 단순한 솔루션으로 대체 가능할지 항상 궁금증을 느낌
- 이런 세부 쿼리 생성/최적화는 결국 Amazon, Microsoft, OpenAI 같은 RAG 솔루션 제공 업체들이 신경 쓸 부분임을 강조함
- 이런 방법들이 현재 베스트 프랙티스임은 맞지만, 성능을 한 단계 더 끌어올릴 수 있는 추가 전략(embedding 모델 선택적 분기, 다양한 re-ranker 조합 등)에는 여전히 무언가 빠진 듯한 허전함을 느낌
- 마지막 팁으로 LLM이 사용자의 검색 의도를 어떻게 해석했는지 결과와 함께 사용자에게 전달해주면, LLM의 이해가 정확한지 사용자가 직접 확인할 수 있다는 점을 제안함
- self-hosted 옵션에 대해 혼란스러움을 느낌, 관련 문서를 보면 최소 6개 이상의 제3자 서비스 계정이 필요하다는데, 이게 진정한 self-hosted의 의미와 너무 다름을 지적함
- 해당 코드 자체는 직접 self-host 가능함을 설명, “self-hosted" 용어엔 명확한 공식 기준이 없다는 생각임, 예를 들어 self-hosted 서비스에 오프사이트 백업 기능이 있어도 그건 self-hosted인 동시에 설계가 잘 된 서비스일 뿐임
- 이런 식의 self-hosted 마케팅이 자연스러울 수도 있는데, 원래 업체 비즈니스 모델이 호스팅 버전 판매에 있다는 점에서 완전히 독립형 self-hosted 제품을 의무적으로 제공할 필요는 없음
- 제한된 네트워크 환경에서 일해온 경험을 공유함, 지난 20년 간 완전히 차단된 내부망 환경에서 일해왔다 보니 최신 기술 물결을 많이 놓쳤을 수 있다는 생각임, 유튜브도 자동차 수리 외엔 거의 안 보며 온라인 연결 필수인 기술 트렌드에 다소 소극적임
- 본인은 오픈소스 버전을 매우 만족스럽게 사용 중임, 호스팅 의존성을 원치 않으면 직접 Rust로 모두 짜면 된다는 실용적 의견임
- 대형 LLM 기반 reranker(Qwen3-reranker 등)는 예전부터 크로스 인코더에서 원하던 성능을 보여주니 꼭 시도해보기를 권장함, 다만 계산량이 상당히 큼, 또 메타데이터/테이블 정보는 사람에겐 너무 당연한 기초 정보지만 텍스트 청크에는 반복되지 않다 보니, 이를 LLM 입력에 주입하면 훨씬 “똑똑해” 보여서 좋음, 단순 RAG로는 잘 처리되지 않는 복잡 쿼리(예: 최신 20개 문서 요약 등)는 주의할 필요가 있음, 그래서 우리는 UI를 검색에 초점 맞추고 챗 UX를 줄인 뒤, 모델이 실제로 보는 정보만 사용자도 보도록 함
- 사용자 입장에서 LLM의 콘텍스트 처리 구조를 올바르게 이해시키는 것이 매우 어렵다는 의견에 전적으로 동의함, 챗 UX에서 벗어난 공개 사례가 드문데 대형 회사들이 시도했다가 포기한 이유가 정말 “성과가 없어서”인지 회의감도 듦, 실제로 대규모 소비자용 앱은 콘텍스트/추론 비용이 주요 비용으로서 이를 관리해야 하다 보니, context explicit UI가 외면받은 것 같음, 반면 사내 자체 RAG 시스템은 비용 부담이 적어서 결과 품질과 직원 시간 절감에 훨씬 집중하는 것을 경험적으로 느낌
- 기술적 최적화도 중요하지만, 실제 고객 업무 효율이 얼마나 개선됐는지 실제 사용사례를 더 알고 싶음, 그렇지 않으면 그저 또 하나의 기술 유행일 뿐임
- 수백만 페이지(특히 기술 자료) RAG 처리 관련해 1년 반 전 작성한 정리글 공유함 https://jakobs.dev/learnings-ingesting-millions-pages-rag-azure/
- 기술 검색을 위한 RAG 시스템도 전년도에 구축해봤는데, 지금 봐도 많은 부분이 거의 동일함을 느낌
- 이런 RAG 시스템을 구축하거나 도입하려는 입장에서 문서를 Google Workspace 폴더에 API 업로드하고 Google AI search API로 문서 검색을 하면 실용적 구조가 될 수 있는지 궁금함, 사용자마다 다른 폴더로 분리해서 넣는 식, 또는 Azure에서도 비슷하게 실현할 수 있을지도 고민함
- embedding 버전 관리 방법을 궁금해 함, 데이터 업데이트나 특정 버전을 노출하거나, 날짜별로 필터링하려면 어떻게 해야 할지 질문하며 chunk 앞에 context 버전을 추가하는 방안도 생각 중임
- 벡터 저장소에 원본 텍스트 및 메타데이터(버전 정보 포함)를 함께 저장하면 됨, 예를 들어 turbopuffer에선 attribute 필터링이 편하다며 문서 링크 소개함
- 이 질문 자체가 상당히 유용하고 중요한 질문임을 느낌
- LLM 버전 사용 순서가 GPT 4.1 → GPT 5 → 다시 GPT 4.1로 돌아간 이유와 stack 내 다른 컴포넌트 버전(예: text-embedding-large-3)과의 불일치가 이상하다고 느낌
- OP 답변: GPT-5가 출시되자마자 사용해봤지만, 많은 context(최대 10만 토큰) 입력 시 GPT-4.1보다 성능이 못 해서 다시 4.1로 회귀함, 구체적으로 a) instruction follow가 약해 system prompt를 잘 안 따름, b) 답변이 너무 장황해져 UX를 크게 해침, c) 컨텍스트 창 12.5만 토큰 제한 때문에 아주 큰 입력은 에러로 이어짐, 이러한 문제는 “RAG”에서 많은 청크 전달 시 두드러졌고, 일반적인 태스크엔 GPT-5가 더 좋을 수도 있음
- AWS 옹호자는 아니지만 S3 Vectors가 현재 이 분야 state-of-the-art임을 강조함, 여기에 Bedrock Knowledge Base까지 연동하면 Discovery/Rebalance도 간단해져서 시장에서 제일 심플한 솔루션이 될 것임, 곧 정식 출시되면 대부분의 경쟁자를 압도할 것으로 생각함
- “schlep”이 아니라 “shill”이라는 단어가 맞다고 장난스럽게 교정함
- S3 Vectors가 왜 SOTA(최첨단)인지 궁금증, 결국은 벡터 저장소일 뿐 아닌지 반문함
- Embedding 기반 RAG는 실제론 최선의 방법까진 아님, 단일 태스크나 데모엔 좋지만, 현실 상황에선 종종 한계에 부딪힘을 경험함
- 꼭 그렇지는 않다는 경험도 있음, 내가 작업한 Honeycomb 제품 Query Assistant 블로그도 2023년부터 데이터쿼리에 기반했고, 이 기능이 심플한 목적만 두고 설계되어서 오히려 LLM 기반 제품의 이상적인 방향성이라고 느낌, non-LLM 처리와 결합하는 게 좋음
- rag는 계속 다르게 재해석되고 용도가 많음, 우리는 rag를 하나의 도구로 agentic search에 통합했고, 동시에 실시간 소스 검색이나 기존 chunk 방식도 혼합함, 곧 등장할 대용량 context 윈도우 덕분에 한 번 요청에 문서 전체를 넣는 것도 가능해질 전망임
- 어떤 대안을 추천하는지 궁금함, 쿼리 생성 방식을 말하는 것인지 질문함
- ChatGPT의 경우도 RAG에 기반해 최신 정보를 얻는 데 효과적임, 이런 현실적 효용성이 지금 모든 주요 업체가 이를 제공하는 이유임을 강조함
- 비교 대상이 무엇인지 명확히 질문함
- chunk 선정 전략에 많은 시간과 노력이 들어간다 했는데, 사용했던 구체적 전략에 대해 더 자세히 듣고 싶음