RAG 챗봇 고도화 - 벡터검색부터 컨텍스트 검증까지.
지자체 정보를 제공하는 ai 챗봇을 개발. 고도화를 위한 파이프라인 설계
지자체 정보를 제공하는 AI 챗봇을 만들었다. 처음에는 단순한 RAG(Retrieval-Augmented Generation) 파이프라인으로 시작했지만, 실제 질문을 테스트하다 보니 여러 문제점을 발견했고, 이를 해결하기 위해 7단계 파이프라인으로 고도화했다.
기본 RAG의 한계
이전에 AWS Bedrock을 이용한 RAG 파이프라인 및 오케스트레이션을 이용한 프로젝트를 진행해본 경험이 있어서 이번엔 직접 RAG파이프라인을 구성 해보고자 했다. 처음 구성한 RAG 파이프라인은 단순했다.
사용자 질의 -> 임베딩 -> 벡터 검색 -> LLM 답변 생성사용자 질의 -> 임베딩 -> 벡터 검색 -> LLM 답변 생성하지만 위 방식은 여러 문제가 있었다.
문맥 파악 실패
사용자 : "집에 세탁기가 없는데, 세탁서비스 없을까?"
챗봇 : "XX구에서는 저소득층을 위한 가전제품 지원 프로그램이 있습니다..."사용자 : "집에 세탁기가 없는데, 세탁서비스 없을까?"
챗봇 : "XX구에서는 저소득층을 위한 가전제품 지원 프로그램이 있습니다..."실제로 준비 된 데이터에는 '세탁 및 짐 보관 서비스 지원'에 관련한 내용이 있었는데, 세탁기라는 키워드에 꽂혀서 엉뚱한 답변을 생성했다. (실제로 저런 지원프로그램도 없었다.)
대화 히스토리 무시
사용자: "장애인 복지혜택 뭐 있어?"
챗봇 : (장애인 복지 정보 제공)
사용자 : "언제까지 신청 가능해??"
챗봇 : "죄송합니다. 신청 기한에 대한 정보를 찾을 수 없습니다."사용자: "장애인 복지혜택 뭐 있어?"
챗봇 : (장애인 복지 정보 제공)
사용자 : "언제까지 신청 가능해??"
챗봇 : "죄송합니다. 신청 기한에 대한 정보를 찾을 수 없습니다."사용자 질의를 임베딩한걸로 벡터검색을 하다보니, 후속질문 그 자체에 대한 벡터검색 결과값으로 나온 컨텍스트는 이전 질문 히스토리와 무관하거나 맥락에 맞지 않는 경우가 있었다.
7단계 파이프라인 설계
위와 같은 문제들을 해결하기 위해서, 다음과 같은 파이프라인을 재설계했다.
[1단계] 사용자 질의 수신
↓
[2단계] 대화 히스토리 기반 쿼리 재구성(LLM 이용)
↓
[3단계] 임베딩 생성
↓
[4단계] 벡터 검색 (top_k = 5)
↓
[5단계] 관련 문서 전체 청크 결합
↓
[6단계] 첨부파일 메타데이터 포함
↓
[7단계] LLM 컨텍스트 검증 및 답변 생성[1단계] 사용자 질의 수신
↓
[2단계] 대화 히스토리 기반 쿼리 재구성(LLM 이용)
↓
[3단계] 임베딩 생성
↓
[4단계] 벡터 검색 (top_k = 5)
↓
[5단계] 관련 문서 전체 청크 결합
↓
[6단계] 첨부파일 메타데이터 포함
↓
[7단계] LLM 컨텍스트 검증 및 답변 생성2단계 대화 히스토리 기반 쿼리 재구성
챗봇과 나누던 대화를 바탕으로, 이번에 들어온 사용자 질의를 맥락에 맞게 재구성하도록 했다.
결과는 다음과 같았다.
사용자: "장애인 복지 뭐 있어?"
→ 재구성된 쿼리: "장애인 복지 정책 종류"
사용자: "언제까지 신청 가능해?"
→ 재구성된 쿼리: "장애인 복지 정책 신청 기간" # 이전 질문을 바탕으로 맥락 유지!!사용자: "장애인 복지 뭐 있어?"
→ 재구성된 쿼리: "장애인 복지 정책 종류"
사용자: "언제까지 신청 가능해?"
→ 재구성된 쿼리: "장애인 복지 정책 신청 기간" # 이전 질문을 바탕으로 맥락 유지!!5단계 관련 문서 전체 청크 결합
이게 이 프로젝트의 핵심이었다고 본다. 기존에는 벡터 검색 결과로 나온 청크만 컨텍스트로 사용했는데, 이렇게되면 답변을 생성할때 참고할 데이터가 부족했다.
사전에 벡터DB에 저장된 데이터들은 각 문서들을 고정크기 청킹방식으로 약 500토큰 단위 청킹 및 30토큰 오버랩 설정하여 적재되어 있었는데, 해당 청크만으로 정확한 정보를 제공하기는 어렵다 판단하여, 검색된 청크가 속한 원본문서의 모든 청크를 결합하여 컨텍스트로 제공하기로 했다.
[4단계] 벡터 검색 완료 - 결과 개수: 5
[1] 유사도: 0.4939 | 제목: 세탁 및 짐보관 서비스 지원... | ID: doc_123
[2] 유사도: 0.4062 | 제목: 홈케어 서비스 지원... | ID: doc_456
[5단계] 관련 문서 전체 청크 결합
- doc_123: 3개 청크 결합
- doc_456: 2개 청크 결합[4단계] 벡터 검색 완료 - 결과 개수: 5
[1] 유사도: 0.4939 | 제목: 세탁 및 짐보관 서비스 지원... | ID: doc_123
[2] 유사도: 0.4062 | 제목: 홈케어 서비스 지원... | ID: doc_456
[5단계] 관련 문서 전체 청크 결합
- doc_123: 3개 청크 결합
- doc_456: 2개 청크 결합정확한 답변을 제공하기 위해서는 해당 문서의 일부분만으로 답변을 생성하기보다는, 벡터검색으로 유사관계를 찾은 뒤 해당 문서의 전체 내용을 바탕으로 답변을 생성해야한다고 판단했다.
7단계 LLM 컨텍스트 검증
실제로 사용자에게 제공할 답변을 생성하기 앞서, LLM을 활용하여 벡터 검색결과가 실제로 사용자 질의와 관련이 있는지. 적절한 데이터인지를 한번 더 검증 후 답변을 제공하도록 했다.
LLM을 통해 다음과 같은 형식으로 응답을 받았다.
- is_relevant: true/false // 컨텍스트가 적절한지 여부
- confidence: high/medium/low // 신뢰도
- action: answer/refine_search/no_data // 답변진행,재검색,검색결과없음안내LLM을 통해 다음과 같은 형식으로 응답을 받았다.
- is_relevant: true/false // 컨텍스트가 적절한지 여부
- confidence: high/medium/low // 신뢰도
- action: answer/refine_search/no_data // 답변진행,재검색,검색결과없음안내기술 스택
Python FastAPI
FAISS (벡터스토어)
jhgan/ko-sroberta-multitask (한국어 특화 임베딩 오픈소스)
React
최종 로그 예시
INFO: RAG streaming query: 장애수당에 대해서 알려줘 (session: ea2431fb...)
INFO:=======================================================================
INFO: [1단계] 사용자 질의: 장애수당에 대해서 알려줘
INFO: [2단계] 재구성된 검색 쿼리: 장애수당 지원 내용 신청 방법
INFO: [3단계] 임베딩 생성 완료
INFO: [4단계] 벡터 검색 완료 - 결과 개수: 5
INFO: [1] 유사도: 0.6234 | 제목: 장애수당 지원... | 부서: 장애인복지과
INFO: [2] 유사도: 0.5891 | 제목: 장애인연금 안내... | 부서: 장애인복지과
INFO: [5단계] 관련 문서 전체 청크 결합 - 2개 문서, 7개 청크
INFO: [6단계] 첨부파일 확인 - 1개 문서 포함
INFO: [7단계] LLM 검증 완료
INFO: - 적절성: True
INFO: - 신뢰도: high
INFO: - 액션: answer
INFO: 답변 생성 시작...INFO: RAG streaming query: 장애수당에 대해서 알려줘 (session: ea2431fb...)
INFO:=======================================================================
INFO: [1단계] 사용자 질의: 장애수당에 대해서 알려줘
INFO: [2단계] 재구성된 검색 쿼리: 장애수당 지원 내용 신청 방법
INFO: [3단계] 임베딩 생성 완료
INFO: [4단계] 벡터 검색 완료 - 결과 개수: 5
INFO: [1] 유사도: 0.6234 | 제목: 장애수당 지원... | 부서: 장애인복지과
INFO: [2] 유사도: 0.5891 | 제목: 장애인연금 안내... | 부서: 장애인복지과
INFO: [5단계] 관련 문서 전체 청크 결합 - 2개 문서, 7개 청크
INFO: [6단계] 첨부파일 확인 - 1개 문서 포함
INFO: [7단계] LLM 검증 완료
INFO: - 적절성: True
INFO: - 신뢰도: high
INFO: - 액션: answer
INFO: 답변 생성 시작...마치며
단순한 RAG 프로젝트에서 시작해서, AWS Bedrock의 Agent 서비스와 같이 고도화된 오케스트레이션 처리방식에 대해서 고민하여 7단계 파이프라인으로 고도화하는 과정을 진행하다보니, 각 단계를 추가할 때마다 눈에 띄게 응답이 개선되었다.