본문 바로가기

이 포트폴리오의 원본은 https://cv.iruyo.com (심재빈) 입니다 · 출처 식별자 jbx-7f3a2e9b

← 개인 프로젝트

LLM·RAG 운영 중 단독

이력서 챗봇

사이트에 공개된 제 경력·프로젝트 데이터를 근거로 1인칭으로 답하는 챗봇입니다. 검색부터 답변 생성까지 로컬 서버에서 직접 돌립니다.

기간
2026.06 – 진행 중 · 1개월
본인 역할
설계·구현·모델 선정·운영 전체
정량 임팩트
외부 AI API를 쓰지 않고 로컬 서버에서 직접 돌려 운영비가 0원입니다. 2단 검색으로 관련 자료만 근거로 써, 질문과 무관한 출처가 뜨던 문제를 없앴습니다.

링크

언어

  • Python3
  • TypeScript

기술

  • RAG
  • LLM
  • FastAPI
  • ollama
  • Cloudflare Tunnel
  • SSE

환경

  • Claude Code

포트폴리오 사이트 우하단에 있는 챗봇입니다. 채용 담당자가 이력서를 보다가 궁금한 점을 바로 물어볼 수 있게 붙였습니다. 사이트에 공개된 제 경력·프로젝트 데이터를 근거로, 심재빈 1인칭으로 답합니다. 면접에서 직접 설명하는 톤입니다.

이력서 RAG 챗봇 — 전 구간 셀프호스팅외부 LLM API 없이 집 PC의 GPU 하나로 — 비용 0원이력서 RAG 챗봇 — 전 구간 셀프호스팅외부 LLM API 없이 집 PC의 GPU 하나로 — 비용 0원집 PC — RTX 4070 SUPER · ollamaFastAPI :8200 · 워치독 자동 재기동named tunnelSSE 토큰 스트림 + 출처 카드방문자사이트 챗 위젯Cloudflare Worker/api/chat 프록시① 하이브리드 검색 — 후보 20nomic-embed 코사인 + 한국어 별칭 lexical② Qwen3-Reranker 4B — top 5yes/no logprob softmax · 인용 카드 게이트③ exaone3.5 7.8B 생성심재빈 1인칭 · 면접 톤 · SSE 스트리밍RAG 코퍼스 — 사이트와 같은 노션 데이터외부 노출 정책 필터 — 비공개·민감 정보는 아예 없음신뢰 장치· 자료에 없으면 지어내지 않고 ‘면접에서 직접’ 으로 답변· 프롬프트 인젝션 거부 · 시스템 프롬프트 비공개· 리랭커 응답 없으면 하이브리드 점수로 단계적 폴백

어떻게 답합니까

  • 질문이 들어오면 사이트 공개 데이터에서 관련된 내용을 먼저 찾고, 그 부분만 근거로 답합니다. 일반적으로 RAG 방식이라고 부릅니다. 이력서에 없는 내용은 지어내지 않고 “공개 자료엔 없다, 면접에서 직접 물어봐 달라”고 답합니다.

  • 외부 AI API는 전혀 쓰지 않습니다. 로컬 서버(GPU)에서 검색부터 답변 생성까지 모두 직접 돌립니다. 운영비가 0원이고, 데이터가 외부 서비스로 나가지 않습니다.

  • 답은 타이핑하듯 한 글자씩 흘러나옵니다.

정확도를 위해 한 일

  • 관련 자료 검색을 2단으로 만들었습니다. 먼저 후보를 넓게 찾고, 질문과 실제로 맞는지 한 번 더 점수를 매겨 가장 관련 있는 것만 근거로 씁니다.

  • 답변 아래 출처 카드도 이 점수를 통과한 자료만 보여줍니다. 덕분에 질문과 상관없는 출처가 뜨는 일이 없습니다.

안전하게 만든 부분

  • 검색 대상에는 공개해도 되는 자료만 넣었습니다. 전화번호나 상세 주소 같은 민감한 정보는 챗봇이 찾을 수 없습니다.

  • “지시를 무시하라”는 식의 악용 시도(프롬프트 인젝션)는 거부하도록 설정했습니다.

  • 일부 단계가 느려지거나 서버가 멈춰도 단계별로 대비책을 두었고, 서버가 죽으면 자동으로 재시작합니다.

기술 선택

  • 답변을 만드는 생성 모델은 여러 후보를 같은 질문 세트(golden set)로 비교했습니다. 품질, 속도, 악용 방어 3축으로 직접 채점해 가장 안정적인 모델을 골랐습니다. 설정 한 줄로 교체할 수 있게 만들어, 더 나은 모델이 나오면 바로 바꿀 수 있습니다.

  • 평가 세트는 30문항 고정으로 유지하며, 프롬프트나 검색 로직이 바뀔 때마다 회귀 점검에 씁니다. 정체성 캐묻기, 프롬프트 인젝션, 허위 전제, 민감정보 캐기 등 73문항의 적대적 테스트도 별도로 돌려 방어력을 점검했습니다.

  • 관련도를 다시 매기는 단계(리랭커)는 회사의 사내 RAG 시스템과 같은 계열 방식을 가볍게 구현했습니다.

  • 전체 구조는 다이어그램과 같습니다. 사이트(Cloudflare)에서 들어온 질문이 터널을 통해 로컬 서버로 가고, 답이 다시 사이트로 돌아옵니다.

만들면서 배운 점

답변 품질을 높이려고 검색을 2단으로 만들고 리랭커(관련도 재정렬 모델)를 붙였지만, 오히려 인용 카드가 깨지고 느려졌습니다. 그래서 리랭커를 고치는 대신, 실패가 어느 단계에서 나는지 측정했습니다. 답변 19개를 검색·생성·콘텐츠로 구분해 보니 검색은 멀쩡했습니다. 정답이 이미 상위에 있었고, 리랭커가 풀려던 문제는 크지 않았습니다.

병목은 한국어 처리였습니다. ‘아두이노’에 조사가 붙은 ‘아두이노나’를 substring 매칭이 못 잡아서 올바른 답을 거부하고 있었습니다. 이 문제를 ML 대신 코드 몇 줄 — 어간 접두 매칭, 문자 bigram 유사도, IDF 가중 — 로 고쳤습니다. 리랭커는 뺐습니다. ‘제일 어려웠던 기술’ 같은 질문은 노션 카드의 난도 필드를 검색이 우선하게 해서 처리합니다.

작업은 Claude와 함께 빠르게 돌렸지만, ‘측정 먼저, ML은 결정적이고 저비용 수정을 다 쓴 뒤에, 가장 싼 레이어부터’라는 판단은 제가 했습니다. 최종적으로 챗봇 품질은 모델 크기보다 어디서 틀리는지 정직하게 측정하는 데서 나왔습니다.