티스토리 뷰

LLM 기반 챗봇을 도입하는 초기 단계에서는 모델이 생성하는 답변 품질 자체에 주목하게 됩니다. 그러나 실제 서비스 환경에서 운영을 시작하면 관심사는 빠르게 바뀝니다. 응답 속도가 일정하게 유지되는지, 요청이 늘어날수록 비용이 어떻게 증가하는지, 품질 문제가 발생했을 때 어느 단계에서 문제가 생겼는지를 추적할 수 있는지가 더 중요해집니다.

 

이 글에서는 langchain4j를 활용해 RAG 기반 챗봇을 구축하면서 운영 환경에서 마주칠 수 있는 구조적 문제와 이를 설계적으로 해결하기 위해 고려했던 선택들을 정리합니다. 단순한 구현 방법이 아니라, 실제 운영 환경에서 유지 가능한 구조를 만들기 위해 어떤 판단 기준이 필요했는지 중심으로 설명합니다.

 


 

RAG가 필요한 이유와 LLM 단독 사용의 한계

 

대부분의 LLM은 학습 시점 이후의 정보를 알지 못하며, 내부 파라미터에 포함되지 않은 도메인 지식에 대해서는 정확한 답변을 제공하기 어렵습니다. 특히 기업 내부 문서, 정책 정보, 제품 가이드처럼 지속적으로 변경되는 데이터는 모델 학습에 직접 반영하기 어렵습니다.

 

RAG(Retrieval-Augmented Generation)는 이러한 문제를 해결하기 위해 등장한 구조입니다. 외부 지식 저장소에서 관련 정보를 검색하고, 이를 프롬프트에 포함하여 모델이 답변을 생성하도록 하는 방식입니다. 이 구조를 사용하면 모델을 다시 학습시키지 않고도 최신 정보를 반영할 수 있으며, 특정 도메인에 특화된 응답을 생성할 수 있습니다.

 

운영 환경에서 RAG의 장점은 단순히 답변 정확도 향상에 그치지 않습니다. 어떤 데이터가 답변에 사용되었는지 제어할 수 있기 때문에 품질 관리가 가능해집니다. 이는 실제 서비스 운영에서 매우 중요한 요소입니다.

 


 

langchain4j가 Java 환경에서 가지는 의미

 

langchain4j는 Java 환경에서 LLM을 활용한 애플리케이션을 구축할 수 있도록 설계된 라이브러리입니다. 프롬프트 구성, 체인 구조, 메모리, RAG 파이프라인 등 LLM 기반 애플리케이션에서 필요한 구성 요소를 Java 코드로 명확하게 표현할 수 있도록 제공합니다.

 

Java 기반 서버 환경에서는 스레드 관리, 자원 사용, 예외 처리 구조가 중요한데, langchain4j는 이러한 환경과 자연스럽게 통합됩니다. 특히 Spring 기반 서비스에서 추가적인 복잡한 구조 없이 적용할 수 있다는 점이 장점으로 작용합니다.

 

운영 관점에서 중요한 부분은 체인 단위로 책임을 나눌 수 있다는 점입니다. 하나의 프롬프트에 모든 로직을 몰아넣는 구조가 아니라, 각 단계의 역할을 코드 단위로 분리할 수 있기 때문에 문제 발생 시 추적이 쉬워집니다.

 


 

langchain4j 기반 RAG 파이프라인의 실제 동작 흐름

 

RAG 파이프라인은 일반적으로 다음 단계로 구성됩니다. 유저 입력을 받아 임베딩을 생성하고, 벡터 스토어에서 관련 문서를 검색한 뒤, 검색 결과를 프롬프트에 포함하여 모델이 답변을 생성하는 구조입니다.

 

문서 상의 구조는 단순하지만 실제 운영에서는 각 단계가 서로 다른 비용 구조와 지연 시간을 가지게 됩니다. 예를 들어 임베딩 생성과 벡터 검색은 상대적으로 빠르지만, 모델 호출 단계는 비용과 지연이 가장 크게 발생하는 구간입니다.

 

따라서 운영 환경에서는 이 파이프라인을 하나의 처리 과정으로 묶기보다, 단계별로 성능과 비용을 관찰할 수 있도록 분리하는 것이 중요합니다. langchain4j의 컴포넌트 기반 구조는 이러한 분리를 구현하기에 적합합니다.

 


 

MongoDB Atlas Vector Search를 선택할 때의 장단점

 

MongoDB Atlas Vector Search는 기존 MongoDB 컬렉션에 벡터 데이터를 저장하고, 이를 기반으로 유사도 검색을 수행할 수 있도록 지원합니다. 이미 MongoDB Atlas를 사용 중인 환경에서는 별도의 벡터 데이터베이스를 추가하지 않아도 된다는 점이 큰 장점으로 작용합니다.

 

데이터와 벡터를 동일한 저장소에서 관리할 수 있기 때문에 운영 복잡도를 줄일 수 있으며, 데이터 동기화 문제도 상대적으로 단순해집니다. 그러나 검색 트래픽이 증가할 경우 기존 데이터베이스 워크로드와 자원을 공유하게 되어 성능에 영향을 줄 수 있다는 점도 함께 고려해야 합니다.

 

실제 운영 환경에서는 검색 정확도를 최대화하기보다, 서비스 요구사항을 충족하는 수준의 정확도를 정의하고 그 범위 내에서 설정을 조정하는 것이 현실적인 접근이었습니다.

 


 

유저 입력 전처리가 검색 품질을 결정하는 이유

 

RAG 시스템 품질 문제가 발생했을 때 벡터 검색이나 모델 성능을 먼저 의심하게 되지만, 실제로는 유저 입력 품질이 문제인 경우가 많습니다. 지나치게 긴 문장이나 질문 형태가 아닌 입력은 검색 결과의 정확도를 낮추는 원인이 됩니다.

 

그래서 입력 단계에서 길이 제한과 기본적인 필터링을 적용했습니다. 의미 없는 입력이나 검색에 도움이 되지 않는 입력은 초기 단계에서 제거했습니다. 이 과정은 LLM 호출 이전 단계에서 수행되기 때문에 비용과 응답 시간을 동시에 줄이는 효과가 있었습니다.

 

복잡한 자연어 처리 로직을 추가하기보다, 구조적으로 불필요한 입력을 줄이는 방식이 운영 환경에서는 더 안정적인 선택이었습니다.

 


 

토큰 사용량 제어는 검색 단계에서 이미 결정됩니다

 

토큰 비용 문제는 흔히 프롬프트 길이를 줄이는 방향으로 접근하지만, 실제로는 검색 결과 단계에서 이미 대부분 결정됩니다. 검색 결과로 가져오는 문서 수와 각 문서의 길이가 프롬프트 토큰 수를 좌우합니다.

 

검색 결과를 많이 포함한다고 항상 품질이 좋아지는 것은 아니었습니다. 관련성이 낮은 문서가 포함될 경우 오히려 응답 품질이 떨어지는 경우도 확인되었습니다. 따라서 검색 결과 수를 제한하고, 필요한 정보만 포함하는 구조로 조정했습니다.

 

이 접근은 비용과 응답 품질 사이에서 균형을 유지하는 데 도움이 되었습니다.

 


 

단일 프롬프트 구조의 한계와 체인 기반 설계

 

초기에는 모든 처리를 하나의 프롬프트로 해결하는 구조를 사용했습니다. 구현은 간단했지만, 문제 발생 시 어느 부분에서 품질이 깨졌는지 확인하기 어려웠습니다.

 

langchain4j 체인 구조를 활용하여 입력 해석, 검색 결과 정제, 답변 생성 단계를 분리했습니다. 각 단계는 독립적으로 조정할 수 있으며, 특정 단계의 문제를 빠르게 확인할 수 있게 되었습니다.

 

이 방식은 성능 개선뿐 아니라 운영 중 발생하는 품질 이슈를 분석하는 데에도 도움이 되었습니다.

 


 

운영 환경에서 비용 통제를 위해 고려했던 전략

 

비용 통제는 단순히 토큰 사용량을 줄이는 문제로 끝나지 않았습니다. 어떤 요청이 비용 증가를 유발하는지를 파악하는 것이 먼저 필요했습니다. 요청 단위로 호출 정보를 기록하고, 특정 유형의 요청이 반복적으로 높은 비용을 발생시키는지 분석했습니다.

 

그 과정에서 모든 요청에 RAG를 적용할 필요가 없다는 점을 확인했습니다. 단순한 질의는 일반 프롬프트 처리로 분리했고, 이는 품질 저하 없이 비용을 줄이는 데 도움이 되었습니다.

 

운영 환경에서는 “어디에 모델을 쓰지 않을 것인가”를 정하는 판단도 중요합니다.

 


 

실제 운영을 시작하기 전에 고민해야 할 기준

 

RAG 기반 챗봇을 운영하려는 경우 가장 먼저 고민해야 할 것은 기술 선택이 아니라 적용 범위와 목표 품질입니다. 모든 요청에 고품질 검색을 적용할 필요가 있는지, 어느 수준의 정확도를 목표로 할 것인지에 대한 기준이 먼저 정해져야 합니다.

 

langchain4j나 MongoDB Atlas Vector Search는 이러한 구조를 구현하기 위한 도구입니다. 중요한 것은 도구 선택 자체가 아니라, 운영 환경에서 유지 가능한 설계 기준을 세우는 일이라고 생각합니다.

 

기술 선택은 시간이 지나면서 바뀔 수 있지만, 운영 환경에서의 판단 기준과 설계 원칙은 더 오래 남습니다. 이러한 기준을 계속 점검하고 정리하는 과정이 개발자로서의 성장에도 중요한 부분이라고 생각합니다.