프로젝트 목록
Backend & LLM · 2026.02 - 2026.03 · 개인 프로젝트

Personal RAG - 로컬 모델 / LLM API 사용가능 챗봇

Tech Stack

Java 17 Spring Boot Spring AI Ollama pgVector PostgreSQL React TypeScript Docker GitHub Actions

주요 기능

  • Ollama qwen3:8b 로컬 추론 + bge-m3 임베딩
  • pgVector 유사도 검색 기반 RAG
  • LLM Tool Calling (모델이 필요하다고 판단할 때만 벡터 검색 호출)
  • 비동기 ETL (URL·PDF·DOCX·XLSX·PPTX·TXT) + SSE 프로그레스 바 구현
  • WebSocket 토큰 스트리밍 채팅
  • 지식베이스 관리 UI (소스별 청크 조회·삭제)
  • 골든셋 기반 자동 평가 하네스 4종

성과

  • 청크 1500 → 800 토큰으로 문항 통과율 0.0% → 50.0%
  • 정답 핵심내용 포함률 38.1% → 76.2% - gpt-4o-mini 와 동일한 값
  • 콜드스타트 37.2s → 1.2s (-97%), k6 에러율 100% → 0%
  • 평균 지연 88.9s → 33.0s (-63%), p95 188.6s → 74.5s (-60%)
  • 8GB VRAM 단일 GPU 에서 상용 API 없이 로컬 Ollama qwen3:8b + bge-m3 로 RAG 챗봇 구동

상세내용

ChatGPT, Gemini 등의 상용 LLM API는 품질과 생성 속도를 보장하지만 기본적으로 인터넷 연결이 필요하고, 문서 본문 또한 외부로 전송되고 사용량만큼 과금됩니다. 정부 공고문·계약서·사내 매뉴얼 등 외부 유출에 민감한 문서를 다루기 위해 로컬에서 끝나는 파이프라인이 필요했습니다. 과거 Java 8 · eGovFramework 기반이던 LLM 챗봇을 Java 17 + Spring Boot 3.5 + Spring AI 로 다시 설계하고, Ollama(qwen3:8b)와 pgVector 를 붙여 로컬에서 구동할 수 있도록 했습니다.

RTX 4060 Ti 8GB 기준으로 qwen3:8b(5.6GB)와 bge-m3(0.7GB)를 함께 사용하는 상황에서의 적절 청크 크기, 재정렬 방식, GPU 성능 까지 잡는것이 메인 포인트였습니다.

개선에 앞서 먼저 측정 체계부터 만들었습니다. LLM은 같은 질문에도 매번 다른 답을 내놓기 때문에, 한 번의 테스트 결과를 그대로 믿지 않고 모든 비교 실험을 3회 이상 반복해 재현되는지 확인하는 걸 원칙으로 삼았습니다. 실제로 로컬 모델과 상용 모델을 처음 비교했을 땐 62.5% 대 62.5%로 정확히 동률이 나와 "차이가 없다"고 결론 낼 뻔했지만, 반복하자 50.0% 대 58.3%로 격차가 드러났습니다. 게다가 결과가 들쭉날쭉했던 쪽은 오히려 상용 모델이었습니다(편차 12.5%p 대 0.0%p) — 한 번의 우연한 결과만 믿었다면 정반대 결론을 냈을 것입니다.

트러블슈팅

콜드스타트 37.2초, 부하 테스트 에러율 100%

원인
설정 파일에는 올바른 값이 적혀 있었지만 요청에 실리지 않았습니다. 응답 유지시간(keepAlive)을 YAML 정수 -1 로 쓰자 문자열 "-1" 로 직렬화됐고, 단위가 없는 값이라 Ollama측에서 거부하고 있었습니다.
해결
단위를 명시(-1s)해 콜드스타트 37.2s → 1.2s (-97%), 에러율 100% → 0% 로 회복했습니다.

100 ~ 450자 답변시간 평균 88.9초, p95 188.6초

원인
모델이 답하기 전 내부적으로 추론하는 과정(thinking mode)을 끄는 설정이 적용되지 않고 있었습니다. application.yaml 에는 있었지만 ChatClientFactory 가 defaultOptions 를 통째로 지정하면서 덮어쓰고 있었습니다.
해결
전달 경로를 고쳐 평균 88.9s → 33.0s (-63%), p95 188.6s → 74.5s (-60%). 다만 사실 포함률이 77.8% → 57.1% 로 함께 떨어져, 트레이드오프를 규명하고 기본값은 품질 우선으로 되돌렸습니다.

검색은 정답을 찾아오는데 모델이 그 내용을 쓰지 못함

원인
청크 1500토큰이 한국어 공고문에서 평균 5,425자 청크를 만들었고, topK=3 근거만으로 약 4,521토큰이 되어 검색 단계에서 이미 num_ctx 4,096 을 초과하고 있었습니다.
해결
청크를 800토큰으로 낮춰 문항 통과율 0.0% → 50.0%, 정답 핵심내용 포함률 38.1% → 76.2% 로 올렸습니다.
시스템 아키텍처 - 클라이언트는 WebSocket으로 토큰을 받고 SSE로 ETL 진행률을 받습니다. 백엔드는 Advisor 체인으로 관심사를 분리하고, RAG 는 모든 질문에 검색하는 대신 Tool Calling을 통해 모델이 필요할 때만 호출합니다. 추론과 저장은 전부 로컬 Ollama · PostgreSQL 에서 이루어집니다.
문서 인덱싱 — URL 크롤링 · 파일 업로드 · 지식베이스 3개 탭으로 RAG 대상 문서를 등록합니다.

기여도 · 개인 프로젝트 1인 개발 - 기획 · 백엔드 · 프론트엔드 · 인프라

“AI시대, AI가 대체할 수 없는 책임질 수 있는 개발자가 되겠습니다.”