RAG 챗봇 아키텍처 및 요청 처리 흐름
이 사이트의 AI Q&A 챗봇이 질문 하나를 처리하는 전 과정입니다. 카테고리 선택은 선택 사항(기본값 '전체')이며, 구체적인 카테고리를 고르면 카테고리 스코프 검색으로 빠르게 답하고, 부족할 때만 Tool Calling 재검색과 Human-in-the-Loop 카테고리 힌트로 검색을 확장하는 Agentic RAG 구조를 직접 설계·구현·운영하고 있습니다.
1전체 구조
사용자의 질문은 챗 위젯 → GCP VM → Google Cloud 관리형 서비스 순서로 처리됩니다.
챗 위젯
- 질문 입력 및 검색 카테고리 선택(선택 사항, 기본값 '전체')
- 멀티턴 대화 히스토리 보관
- SSE 기반 토큰 단위 답변 렌더링
- 재검색 상태와 추천 카테고리 표시
nginx
- 외부 요청을 FastAPI로 전달하는 리버스 프록시
- Let's Encrypt 인증서 기반 HTTPS 종단
- SSE 스트리밍 경로의 프록시 버퍼링 해제
FastAPI Docker 컨테이너
- 검색 및 답변 생성 요청 처리
- SSE 스트리밍 API 제공
- Docker 컨테이너로 상시 운영
Vertex AI 임베딩
- text-multilingual-embedding-002 · 질문/문서 벡터화
Gemini 3.6 Flash
- 검색 결과 충분성 판단 및 최종 답변 생성
Cloud SQL
- PostgreSQL 15 + pgvector 벡터 검색
2오프라인 데이터 처리
원본 문서(논문 · 학위논문 · 이력서)는 다음 과정을 거쳐 검색 가능한 벡터 데이터로 변환됩니다.
3온라인 요청 처리 흐름
구체적인 카테고리를 선택한 일반 질문은 최초 검색만으로 빠르게 답하고, 부족한 경우에만 단계적으로 복구 경로가 열립니다. 카테고리를 '전체'로 두면 이 최초 검색 없이 Tool Calling이 첫 단계부터 필수로 실행됩니다.
질문 및 카테고리 입력
사용자가 챗 위젯에서 질문을 입력합니다. 카테고리 선택은 선택 사항이며, 기본값은 '전체'입니다.
pgvector 선검색
구체적인 카테고리를 선택했다면 그 카테고리의 문서 청크를 대상으로 먼저 벡터 검색을 수행합니다. 일반적인 질문은 이 최초 검색 결과만으로 처리하며, 별도의 Tool Calling은 실행하지 않습니다. 반면 카테고리를 '전체'로 두면(선택하지 않으면) 이 선검색 자체를 생략하고 곧바로 다음 단계에서 Tool Calling을 필수로 수행합니다.
Gemini 3.6 Flash의 충분성 판단
Gemini가 검색된 발췌문을 검토하고 "현재 검색 결과만으로 질문에 충분히 답할 수 있는가?"를 자체 판단합니다. 카테고리를 '전체'로 두어 2단계의 선검색 자체가 없었다면 이 판단 없이 곧바로 도구 호출로 넘어갑니다.
- 충분한 경우: 바로 최종 답변 생성
- 부족한 경우: search_chunks / read_section 도구 호출
Tool Calling 재검색1차 복구
검색 결과가 부족하다고 판단되면 Gemini가 도구를 호출해 스스로 근거를 보강합니다.
- search_chunks: 최초 선택 카테고리 외의 범위로 검색 확장, 질문 의도에 맞는 카테고리를 모델이 자체 선택
- read_section: top-k 발췌로 부족한 경우 특정 섹션의 전체 원문을 통째로 읽어 보강
- 최대 3라운드까지 도구 호출, 마지막 라운드는 도구 없이 답변 강제
- 구체적인 카테고리를 선택했다면 재검색은 일반 경로가 아닌 예외 상황을 위한 escape hatch. 반대로 카테고리를 '전체'로 두면 이 도구 호출이 예외가 아니라 항상 거치는 필수 첫 단계
Human-in-the-Loop 카테고리 힌트2차 복구
독립적인 임베딩 유사도 계산 대신, 모델이 4단계에서 실제로 실행한 재검색 결과를 그대로 활용합니다. 사용자가 다른 카테고리를 선택했을 때든 '전체'로 두어 카테고리를 고르지 않았을 때든, 모델이 재검색해 최종 답변 텍스트와 실제로 겹치는(실질적으로 인용된) 카테고리가 정확히 하나로 좁혀질 때만 채팅 UI가 비차단 힌트 버튼을 제시합니다. 재검색·인용한 카테고리가 여럿이거나 최종 답변이 "자료에 없다"는 결론이면 힌트 자체를 억제해 근거 없는 전환 제안을 막습니다.
SSE 스트리밍 응답
최종 결과는 SSE(Server-Sent Events)를 통해 채팅 UI로 전달됩니다.
- 생성된 답변 토큰
- Tool Calling 및 재검색 진행 상태
- 추가 검색이 가능한 카테고리 힌트
nginx의 기본 프록시 버퍼링을 해제해 토큰이 일정 크기로 모일 때까지 지연되지 않고, 생성 즉시 브라우저에 도달하도록 구성했습니다.
4핵심 설계 원칙
무상태 백엔드
멀티턴 대화 히스토리는 클라이언트가 보관하고 매 요청마다 서버에 전달합니다. 서버가 사용자별 대화 상태를 저장하지 않기 때문에 서버 재시작, 복제 및 수평 확장이 쉽습니다.
카테고리 스코프 검색
논문 청크는 전체 코퍼스 74개 중 40개로 약 54%를 차지합니다. 전역 Top-K 검색을 사용하면 논문 데이터가 결과를 독식할 가능성이 있으므로, 사용자가 구체적인 카테고리를 선택하면 최초 검색 범위를 그 카테고리로 제한합니다. 다만 카테고리 선택은 필수가 아니며, 기본값인 '전체'를 두면 이 범위 제한 없이 모델이 처음부터 Tool Calling으로 스스로 카테고리를 판단해 검색합니다. 그만큼 첫 응답까지는 더 걸릴 수 있습니다.
단계적 검색 확장
구체적인 카테고리를 선택했다면 처음부터 전체 데이터를 검색하지 않고 검색 범위를 단계적으로 확장합니다. 일반 질문의 응답 속도와 정확도를 유지하면서, 예외적인 질문에도 대응할 수 있습니다.
끊김 없는 스트리밍
답변뿐 아니라 재검색 진행 상태와 카테고리 힌트도 SSE로 함께 전달합니다. 사용자는 모델이 답변을 생성하거나 추가 자료를 찾는 과정을 채팅 화면에서 실시간으로 확인할 수 있습니다.
5기술 스택
이 아키텍처가 실제로 동작하는 모습이 궁금하다면
메인 페이지에서 챗봇 직접 체험하기 →