spinny:~/writing $ less rag-langchain-deep-dive.md
12GPT-4와 Claude 같은 대규모 언어 모델(LLM)은 놀라울 정도로 강력하지만, 근본적인 한계가 있습니다: 학습 시점의 지식이 고정되어 있다는 것입니다. 내부 문서, 데이터베이스, 실시간 정보에 접근할 수 없습니다. **검색 증강 생성(RAG)**은 LLM의 생성 능력과 외부 소스에서 정보를 검색하는 능력을 결합하여 바로 이 문제를 해결합니다.34## 문제점: LLM의 한계56RAG에 대해 이야기하기 전에, 왜 이것이 필요한지 이해하는 것이 중요합니다.781. **정적 지식**: LLM은 학습 중에 본 것만 알고 있습니다. 컷오프 이후에 발생한 사건에 대해 물어보면 답할 수 없습니다.92. **환각**: LLM이 답을 모를 때, 그럴듯하지만 완전히 거짓된 정보를 생성하여 답을 지어내는 경향이 있습니다.103. **비공개 데이터에 대한 접근 불가**: 일반적인 LLM은 회사의 내부 문서, 티켓 또는 코드베이스에 접근할 수 없습니다.1112RAG는 쿼리 시점에 외부 소스에서 검색된 **관련 컨텍스트**를 모델에 제공함으로써 이 세 가지 문제를 모두 해결합니다.1314## RAG란 무엇인가?1516검색 증강 생성은 외부 지식 베이스에서 검색된 정보로 LLM에 전송되는 프롬프트를 풍부하게 하는 아키텍처입니다. 모델의 파라메트릭 지식에만 의존하는 대신, RAG는 먼저 관련 정보를 **검색**한 다음 프롬프트에 **주입**하여 모델이 정확하고 근거 있는 응답을 생성할 수 있게 합니다.1718```mermaid19graph LR20 User["User"] -- "Question" --> Retriever21 Retriever -- "Search relevant\ndocuments" --> VectorStore["Vector Store"]22 VectorStore -- "Relevant\ndocuments" --> Retriever23 Retriever -- "Context + Question" --> LLM24 LLM -- "Grounded\nresponse" --> User25```2627## RAG의 상세 작동 방식2829RAG 아키텍처는 두 가지 주요 단계로 구성됩니다: **인덱싱** (오프라인)과 **검색 + 생성** (온라인).3031### 단계 1: 인덱싱 (문서 수집)3233인덱싱 단계는 시맨틱 검색을 위해 문서를 준비합니다. 네 가지 단계로 구성됩니다.3435```mermaid36graph TD37 A["Documents\n(PDF, HTML, MD, DB)"] --> B["Document Loader"]38 B --> C["Text Splitter"]39 C --> D["Text Chunks"]40 D --> E["Embedding Model"]41 E --> F["Numerical Vectors"]42 F --> G["Vector Store\n(ChromaDB, Pinecone, FAISS)"]43```4445#### 1. 문서 로딩4647문서는 어떤 소스에서든 올 수 있습니다: PDF 파일, 웹 페이지, 데이터베이스, Markdown 파일, API. **Document Loader**는 이러한 문서를 읽고 구조화된 텍스트로 변환합니다.4849#### 2. 텍스트 분할 (Chunking)5051LLM은 제한된 컨텍스트 윈도우를 가지고 있으며, 문서는 매우 길 수 있습니다. **Text Splitter**는 문서를 *chunks*라고 불리는 작은 조각으로 나눕니다. 청킹의 품질은 매우 중요합니다: 너무 작은 chunks는 컨텍스트를 잃고, 너무 큰 chunks는 관련성을 희석시킵니다.5253가장 일반적인 전략은 다음과 같습니다:54- **재귀적 문자 분할**: `\n\n`, `\n`, `. ` 같은 구분자를 사용하여 문서 구조를 존중하면서 텍스트를 재귀적으로 분할합니다.55- **시맨틱 분할**: embeddings를 사용하여 텍스트에서 자연스러운 분기점을 찾습니다.56- **Chunk 오버랩**: 경계에서 컨텍스트를 보존하기 위해 연속된 chunks 간에 겹침을 포함합니다.5758#### 3. Embedding5960각 chunk는 embedding 모델(예: OpenAI의 `text-embedding-3-small`)을 통해 **숫자 벡터** (embedding)로 변환됩니다. 이 벡터는 텍스트의 의미적 의미를 포착합니다: 비슷한 의미를 가진 문장은 다차원 공간에서 가까운 벡터를 갖게 됩니다.6162#### 4. Vector Store6364벡터는 ChromaDB, Pinecone, Weaviate 또는 FAISS와 같은 **Vector Store** (또는 벡터 데이터베이스)에 저장됩니다. 이 데이터베이스는 **유사도 검색**에 최적화되어 있습니다: 주어진 쿼리에 대해 가장 유사한 벡터(따라서 가장 관련성 높은 텍스트 chunks)를 찾습니다.6566### 단계 2: 검색 + 생성6768사용자가 질문을 할 때:69701. 질문은 동일한 embedding 모델을 사용하여 embedding으로 변환됩니다.712. Vector Store가 **유사도 검색** (일반적으로 코사인 유사도 또는 유클리드 거리)을 통해 가장 유사한 chunks를 찾습니다.723. 검색된 chunks가 컨텍스트로 프롬프트에 삽입됩니다.734. LLM이 제공된 컨텍스트를 기반으로 응답을 생성합니다.7475## LangChain으로 RAG 파이프라인 구축하기7677**LangChain**은 LLM 기반 애플리케이션을 구축하기 위한 가장 인기 있는 Python (및 JavaScript) 프레임워크입니다. RAG 파이프라인의 모든 구성 요소에 대한 높은 수준의 추상화를 제공합니다.7879### 설치8081```bash82pip install langchain langchain-openai langchain-community chromadb83```8485### 1단계: 문서 로드8687LangChain은 다양한 데이터 소스를 위한 수십 개의 Document Loader를 제공합니다.8889```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)9697# Load a PDF98pdf_loader = PyPDFLoader("docs/manual.pdf")99pdf_docs = pdf_loader.load()100101# Load a web page102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104105# Load all .md files from a directory106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108109all_docs = pdf_docs + web_docs + md_docs110```111112### 2단계: 문서를 Chunks로 분할113114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122123chunks = text_splitter.split_documents(all_docs)124print(f"Original documents: {len(all_docs)}, Chunks: {len(chunks)}")125```126127`chunk_overlap` 파라미터는 매우 중요합니다: 연속된 chunks 간에 겹침을 만들어 경계에서 컨텍스트가 손실되지 않도록 합니다.128129### 3단계: Embeddings와 Vector Store 생성130131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143144### 4단계: Retriever 생성145146Retriever는 주어진 쿼리에 대해 vector store에서 가장 관련성 높은 chunks를 가져오는 구성 요소입니다.147148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153154relevant_docs = retriever.invoke("How does authentication work?")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159160### 5단계: RAG 체인 구축161162이제 LLM과 프롬프트 템플릿으로 모든 것을 결합해 봅시다.163164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169170llm = ChatOpenAI(model="gpt-4o", temperature=0)171172prompt = ChatPromptTemplate.from_template("""173Answer the question based only on the provided context.174If the context does not contain enough information, say you don't know.175176Context:177{context}178179Question: {question}180181Answer:182""")183184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193194response = rag_chain.invoke("How does authentication work in the system?")195print(response)196```197198## 고급 RAG 기법199200기본 파이프라인은 잘 작동하지만, 응답 품질을 크게 향상시키는 여러 기법이 있습니다.201202### 멀티 쿼리 검색203204때때로 사용자의 쿼리가 모호하거나 문서에서 사용되는 언어와 일치하지 않습니다. **Multi-Query Retriever**는 여러 관점을 포착하기 위해 원래 질문의 변형을 자동으로 생성합니다.205206```python207from langchain.retrievers import MultiQueryRetriever208209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213214docs = multi_retriever.invoke("What are the security best practices?")215```216217### 컨텍스트 압축218219chunk의 모든 내용이 쿼리와 관련된 것은 아닙니다. **Contextual Compression Retriever**는 검색된 각 chunk에서 관련 부분만 추출하기 위해 LLM을 사용합니다.220221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231232### 하이브리드 검색233234순수한 시맨틱 검색이 항상 최적은 아닙니다. **하이브리드 검색**은 더 나은 결과를 얻기 위해 시맨틱 검색(embeddings)과 어휘 검색(BM25, 키워드 매칭)을 결합합니다.235236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250251### 대화형 RAG (메모리 포함)252253대화 컨텍스트를 기억하는 RAG 챗봇을 구축하려면, 대화 기록을 고려하여 사용자의 질문을 재구성하는 메모리를 추가해야 합니다.254255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "Given the chat history and the user's latest question, "261 "reformulate the question so it is understandable without the history."),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270271## 모범 사례2722731. **적절한 chunk 크기 선택**: 다양한 크기(500-1500 토큰)로 실험하세요. 정확한 답변을 위한 작은 chunks, 넓은 컨텍스트를 위한 큰 chunks.2742. **문서 메타데이터 사용**: chunks에 소스, 날짜, 카테고리를 메타데이터로 추가하세요. 이를 통해 검색 시 결과를 필터링할 수 있습니다.2753. **품질 평가**: *faithfulness*, *relevancy*, *context precision* 같은 메트릭을 측정하기 위해 [RAGAS](https://docs.ragas.io/) 같은 프레임워크를 사용하세요.2764. **문서 업데이트 관리**: 데이터 소스와 vector store의 동기화를 유지하기 위한 재수집 파이프라인을 구현하세요.2775. **re-ranker 추가**: 초기 검색 후, 실제 관련성에 기반하여 결과를 재정렬하기 위해 re-ranking 모델(예: Cohere Rerank)을 사용하세요.278279## 결론280281RAG는 특정하고 최신의 지식에 접근해야 하는 AI 애플리케이션을 구축하기 위한 표준 아키텍처가 되었습니다. LangChain은 파이프라인의 모든 구성 요소에 대한 추상화를 제공하여 구현을 크게 단순화합니다.282283**다음 단계:**284- **로컬에서 실험하기**: 파이프라인에 익숙해지기 위해 ChromaDB와 몇 개의 문서로 시작하세요.285- **LangSmith 탐색하기**: 프로덕션에서 체인을 모니터링하고 디버깅하기 위해 [LangSmith](https://smith.langchain.com/)를 사용하세요.286- **다양한 embedding 모델 시도**: `text-embedding-3-small`, `text-embedding-3-large`, 그리고 Sentence Transformers의 오픈소스 모델을 비교해 보세요.287- **문서 확인**: [LangChain 문서](https://python.langchain.com/docs/)는 훌륭하고 지속적으로 업데이트되는 리소스입니다.288
:RAG와 LangChain: 검색 증강 생성에 대한 완벽 가이드lines 1-288 (END) — press q to close