RAG의 작동 순서도

- 가장 먼저 결정해야할 것: 모델의 종류
- RAG는 어떤 LLM을 활용하는지에 따라서 그 품질이 크게 좌우됨
- API를 통해 Closed source LLM을 활용하는 경우 품질이 안정적이고, 빠르게 구축 가능하기 때문에 가장 기초적인 형태
- ⇒ 여기서는 OpenAI의 GPT를 API 형태로 활용해봄
- RAG에 담을 문서에 따라 적합한 Document Loader를 선정해야 함
- 만약 PDF 형식의 여러 파일을 RAG에 활용하고자 한다면 → PDF Loader
- 다양한 파일을 활용할 경우 → 각 형식을 불러오기 위한 Document Loader
- ⇒ 여기서는 대한민국 헌법 PDF 파일을 로드하기 위해 PDF Loader를 활용
- 문서를 적절한 크기의 텍스트 청크로 분할하기 위해서 Text Splitter를 선언해야 함
- 다른 요소들 대비 고려할 요소가 적음
- Splitter 종류 자체가 많지 않음
- 대부분의 경우에 RecursiveCharacterTextSplitter와 chunk_overlap 파라미터의 설정으로 충분한 성능이 보장됨
- ⇒ 이 두 가지 매개변수 설정을 기반으로 문서를 여러 청크로 분할할 예정
- 분할된 청크를 임베딩 벡터로 변환하기 위해 알맞은 임베딩 모델을 활용해야 함
- 이를 위해서는 문서의 언어와 청크의 길이를 고려해야 함
- 문서의 언어가
- 대부분 영어라면 ⇒ 영어 데이터로 사전 학습된 임베딩 모델을 활용
- 한글이라면 ⇒ 한글 데이터가 충분히 사전 학습된 임베딩 모델 활용
- 청크의 길이
- 컨텍스트 윈도우가 충분한 임베딩 모델을 활용해야 함
- ⇒ 여기서는 한글 데이터셋으로 충분히 사전 학습되고, 맥락을 충분히 담을 만큼 긴 길이의 임베딩(max_token = 8,191)이 가능한 OpenAI의 text-embedding-3-small 모델 활용
- 임베딩 벡터를 저장할 벡터 스토어를 정해야 함
- 필요한 목적이 무엇인지, 또 어떤 작업의 성능이 중요한지 우선순위를 따져 결정할 수 있음
- 대부분의 경우 순수 벡터 데이터베이스로 RAG를 구축하는 것이 간단하고 쉬운 편
- 그러나 순수 벡터 데이터베이스의 경우 일반 데이터베이스와의 결합이 까다로울 수 있으며, 대규모로 확장할 때 속도나 안정성이 기존 데이터베이스 대비 보장되지 않을 수 있음
- 따라서 만약 프로덕트에 RAG를 구축할 경우, 기존 데이터베이스에 벡터 형태로 저장할 수 있는 방법을 강구하는 것도 좋은 아이디어임
- ⇒ 이번 실습에서는 파일럿 형태의 RAG 시스템을 구축할 것이므로 Chroma를 활용함
- 사용자의 질문과 유사한 문장을 검색하기 위한 Retriever를 결정해야 함
- 다섯 번째 구성 요소로 Chroma로 설정했기 때문에, 벡터 스토어 기반 검색기를 활용할 수 있음.
- ⇒ 이번 절에서는 이 Retriever를 통해 사용자 질문과 유사한 문장을 검색해 볼 예정
- 만약 RAG의 성능이 충분치 않은 경우에는, Parent Document Retriever나 Long Context Reorder와 같은 기법을 합께 사용할 수 있음
- LCEL(랭체인을 표현하는 언어)을 기반으로 앞선 구성 요소들을 하나로 묶어 구성함
대화 컨텍스트 처리
contextualize_q_system_prompt = """Given a chat history and the latest user question \\
which might reference context in the chat history, formulate a standalone question \\
which can be understood without the chat history. Do NOT answer the question, \\
just reformulate it if needed and otherwise return it as is."""
- 이 접근법의 장점
- 참조 해결: "그것은 무엇인가요?", "위의 내용에 대해 더 설명해줘" 같은 모호한 참조 표현을 구체적인 질문으로 변환함
- 2단계 검색 전략: 먼저 질문을 재구성한 후 검색을 수행함으로써 검색 정확도를 높임
- 시스템 사고 분리: LLM에게 "질문 이해" 역할과 "질문 응답" 역할을 명확히 구분하여 할당함으로써 각 단계에 집중할 수 있게 함
- 에러 감소: 잘못된 맥락이나 불완전한 참조로 인한 오류 가능성을 줄임
chat_history = [
HumanMessage(content='대통령의 임기는 몇년이야?'),
AIMessage(content='대통령의 임기는 5년입니다.')
]
# 불완전한 후속 질문 "국회의원은?"를 맥락화
contextualize_q_prompt.invoke({"input":"국회의원은?", "chat_history": chat_history})
- 이 접근법은 "국회의원은?"라는 불완전한 질문을 "국회의원의 임기는 몇 년인가요?" 또는 "헌법에서 국회의원에 대해 어떻게 규정하고 있나요?"와 같은 완전한 질문으로 변환할 수 있습니다.
추가: 평가 및 모니터링 시스템