# Part 7.3: LangChain으로 LLM 애플리케이션 개발 맛보기

**⬅️ 이전 시간: [Part 7.2: Transformer와 LLM의 핵심 원리](./part_7.2_transformer_and_llm_principles.md)**
**➡️ 다음 시간: [Part 7.4: 그래프 신경망 (GNN)](./part_7.4_graph_neural_networks.md)**

---

## 1. 학습 목표 (Learning Objectives)

이번 파트가 끝나면, 여러분은 다음을 할 수 있게 됩니다.

- LLM의 한계점(최신성 부족, 환각 등)을 설명하고, RAG가 이를 어떻게 해결하는지 이해할 수 있습니다.
- LangChain의 역할을 이해하고, RAG 파이프라인의 핵심 5단계(Load, Split, Embed, Store, Retrieve)를 설명할 수 있습니다.
- 문서 분할(Chunking)의 중요성을 이해하고, 기본적인 분할 전략을 적용할 수 있습니다.
- LangChain을 사용하여 외부 문서의 내용을 기반으로 질문에 답변하는 RAG 시스템을 직접 코드로 구현할 수 있습니다.

## 2. 핵심 키워드 (Keywords)

`거대 언어 모델(LLM)`, `LangChain`, `검색 증강 생성(RAG)`, `환각(Hallucination)`, `임베딩(Embedding)`, `벡터 저장소(Vector Store)`, `FAISS`, `Chroma`, `문서 분할(Chunking)`

## 3. 도입: 똑똑한 LLM을 더 똑똑하게 만들기 (Introduction)

우리는 앞에서 LLM이라는 강력한 '두뇌'를 배웠습니다. 하지만 이 두뇌는 몇 가지 결정적인 한계를 가집니다. 훈련 데이터에 없는 최신 정보나, 우리 회사 내부 문서 같은 사적인 내용은 전혀 알지 못합니다. 가끔은 그럴듯한 거짓말, 즉 '환각' 현상을 보이기도 하죠.

이번 시간에는 이 한계를 극복하는 가장 효과적인 기술인 **검색 증강 생성(RAG, Retrieval-Augmented Generation)**을 배웁니다. RAG의 핵심 아이디어는 간단합니다.

> **"LLM에게 정답을 바로 묻지 말고, 먼저 관련 정보를 찾아서 '오픈북 시험'을 보게 하자!"**

그리고 이 복잡한 '오픈북 시험' 과정을 손쉽게 만들어주는 도구가 바로 **LangChain**입니다. LangChain을 통해 LLM이 외부 지식과 소통하게 하여, 훨씬 더 정확하고 신뢰성 있는 AI 애플리케이션을 만드는 방법을 탐험해 봅시다.

> [!TIP]
> 본 파트의 모든 예제 코드는 `../../source_code/part_7_5_llm_application_development.py` 파일에서 직접 실행하고 수정해볼 수 있습니다.

---

## 4. RAG 파이프라인: 5단계로 완전 정복

RAG는 크게 **'인덱싱(Indexing)'**과 **'검색 및 생성(Retrieval & Generation)'** 두 단계로 나뉩니다. 이 과정을 5개의 세부 단계로 나누어 살펴보겠습니다.

![RAG Pipeline](https://raw.githubusercontent.com/gogodov/Images/main/ai_expert_course/rag_pipeline.png)

### [인덱싱 단계: 도서관에 책 정리하기]

#### 1단계: Load (문서 불러오기)
- PDF, 웹사이트, DB 등 다양한 소스에서 원본 문서를 불러옵니다. (`Document Loaders`)

#### 2단계: Split (문서 분할하기)
- 문서를 LLM이 처리하기 좋은 작은 조각(Chunk)으로 나눕니다. 의미가 잘 유지되도록 자르는 것이 중요합니다. (`Text Splitters`)
- **왜 중요할까?** 너무 작게 자르면 문맥을 잃고, 너무 크면 관련 없는 내용이 섞여 LLM의 집중력을 방해합니다.

#### 3단계: Embed (의미의 벡터화)
- 각 Chunk를 텍스트 임베딩 모델을 사용해, 의미를 담은 숫자들의 배열, 즉 '벡터'로 변환합니다.

#### 4단계: Store (벡터 저장소에 저장)
- 변환된 벡터와 원본 Chunk를 '벡터 저장소(Vector Store)'에 저장하여 언제든 빠르게 검색할 수 있도록 준비합니다.
- > **💡 비유: 벡터 저장소는 '의미로 책을 찾는 도서관 사서'**
  > 일반 DB가 '정확한 제목'으로 책을 찾는다면, 벡터 저장소는 "사랑과 희생에 관한 이야기"처럼 **추상적인 의미**로 책을 찾아주는 똑똑한 사서와 같습니다. **FAISS** (빠른 임시 저장), **Chroma** (영구 저장) 등이 대표적입니다.

### [검색 및 생성 단계: 똑똑하게 질문하고 답변하기]

#### 5단계: Retrieve & Generate (검색 후 생성)
- **Retrieve**: 사용자의 질문도 벡터로 변환한 뒤, 벡터 저장소에서 의미적으로 가장 유사한 Chunk들을 찾아냅니다.
- **Generate**: 검색된 Chunk들을 사용자의 원본 질문과 함께 프롬프트에 담아 LLM에게 전달합니다. LLM은 이 '참고 자료'를 바탕으로 신뢰성 높은 답변을 생성합니다.

---

## 5. 직접 해보기 (Hands-on Lab): 나만의 RAG 챗봇 만들기

이제 LangChain을 사용하여, 주어진 텍스트에 대해 답변할 수 있는 간단한 RAG 시스템을 직접 구축해 보겠습니다.

> [!WARNING]
> 이 실습은 OpenAI의 `gpt-3.5-turbo` 모델을 사용하므로, `OPENAI_API_KEY`가 필요합니다. [이곳](https://platform.openai.com/api-keys)에서 API 키를 발급받아 아래 코드에 입력해주세요.

### 문제:
아래 `my_document` 텍스트를 RAG 파이프라인에 넣어, 마지막 `query`에 대한 답변을 생성하는 전체 코드를 완성하세요.

```python
# 필요한 라이브러리 설치
# !pip install langchain langchain-openai faiss-cpu tiktoken sentence-transformers

import os
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_openai import ChatOpenAI
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain.chains import RetrievalQA

# 1. OpenAI API 키 설정
# os.environ["OPENAI_API_KEY"] = "sk-..." # 여기에 자신의 API 키를 입력하세요.

# 2. RAG를 위한 샘플 문서 정의
my_document = """
AI 기술이 발전하면서, 많은 사람들이 AI 비전공자를 위한 최고의 입문서가 무엇인지 궁금해합니다.
여러 전문가들은 '핸즈온 머신러닝(2판)'을 최고의 책으로 꼽습니다.
이 책은 머신러닝의 기초부터 딥러닝, 그리고 실제 프로젝트에 적용하는 방법까지 폭넓게 다룹니다.
특히, 복잡한 수학적 이론보다는 실제 코드를 통해 개념을 익힐 수 있도록 구성되어 있어,
프로그래밍에 익숙한 비전공자들이 접근하기에 매우 용이합니다.
물론, '밑바닥부터 시작하는 딥러닝' 시리즈도 훌륭한 선택지이지만,
이 책은 이론적인 깊이가 상당하여 전공자에게 더 적합할 수 있습니다.
"""

# 3. 문서 분할 (Split)
# Hint: CharacterTextSplitter를 사용하여 문서를 chunk_size=200, chunk_overlap=0 으로 분할
text_splitter = CharacterTextSplitter(chunk_size=200, chunk_overlap=0)
docs = text_splitter.create_documents([my_document])


# 4. 임베딩 모델 준비 (Embed)
# Hugging Face의 오픈소스 한국어 임베딩 모델 사용
model_name = "jhgan/ko-sroberta-multitask"
hf_embeddings = HuggingFaceEmbeddings(model_name=model_name)

# 5. 벡터 저장소 생성 (Store)
# FAISS를 사용하여 docs를 임베딩하고 벡터 저장소(db)를 생성
db = FAISS.from_documents(docs, hf_embeddings)

# 6. RAG 체인 생성 및 실행 (Retrieve & Generate)
# OpenAI 모델을 LLM으로 사용
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

# RetrievalQA 체인 생성
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=db.as_retriever(),
)

# 7. 질문 및 답변
query = "AI 비전공자가 입문하기 좋은 책은 무엇인가요? 그리고 그 이유는 무엇인가요?"
response = qa_chain.invoke(query)
print(response["result"])
```

---

### 💡 Capstone Project 연계 미니 프로젝트: "회사 내부 문서 Q&A 봇 만들기 (실전 RAG)"

앞서 배운 RAG 파이프라인을 활용하여, 단순 텍스트가 아닌 실제 파일(`.md`)을 기반으로 질문에 답변하는 실용적인 Q&A 봇을 구축합니다. 이 과제는 최종 캡스톤 프로젝트에서 특정 도메인(예: 법률, 의료, 금융)의 문서를 다루는 LLM 애플리케이션을 구현할 때 훌륭한 기반이 됩니다.

-   **목표**: LangChain의 `DocumentLoader`를 사용하여 외부 마크다운 파일을 로드하고, 해당 문서의 내용에 대해서만 정확하게 답변하는 RAG 시스템을 완성합니다.
-   **과제**:
    1.  **샘플 문서 확인**: `../../source_code/data/rag_sample_company_rules.md` 파일의 내용을 확인합니다. 이 문서는 가상의 회사 보안 규정에 대한 내용을 담고 있습니다.
    2.  **Document Loader 사용**: 기존의 `my_document` 변수 대신, LangChain의 `TextLoader`를 사용하여 위 마크다운 파일을 로드하도록 코드를 수정하세요.
    3.  **성능 테스트**:
        -   **문서 기반 질문**: "업무용 노트북에서 개인 이메일 사용이 가능한가요?" 와 같이 문서에 명시된 내용에 대해 질문하고, RAG 시스템이 정확한 답변을 생성하는지 확인합니다.
        -   **문서 외 질문 (Grounding 테스트)**: "회사의 연봉 정책에 대해 알려줘" 와 같이 문서에 없는 내용에 대해 질문했을 때, 모델이 "문서에서 관련 정보를 찾을 수 없습니다" 또는 "알 수 없습니다" 와 같이 솔직하게 답변하는지(Grounding) 확인합니다.
-   **기대 효과**:
    -   `DocumentLoader`의 역할을 이해하고, 다양한 포맷의 문서를 LLM 애플리케이션에 통합하는 방법을 학습합니다.
    -   RAG 시스템의 핵심 역량인 'Grounding'(제공된 정보에 기반하여 답변하는 능력)의 중요성을 체감합니다.
    -   환각(Hallucination) 현상을 줄이고 LLM의 신뢰도를 높이는 실용적인 방법을 터득합니다.
-   **구현 가이드**: `../../source_code/part_7_5_llm_application_development.py` 파일의 주석 가이드를 따라 코드를 완성해보세요.

---

## 6. 되짚어보기 (Summary)

이번 시간에는 LLM의 한계를 넘어, 외부 세계와 소통하는 AI를 만드는 강력한 기술인 RAG를 배웠습니다.

- **RAG의 필요성**: LLM의 최신성 부족, 환각 현상과 같은 문제를 해결하기 위해, LLM에게 '오픈북 시험'을 보게 하는 방식이 RAG임을 이해했습니다.
- **RAG 파이프라인**: **Load → Split → Embed → Store → Retrieve & Generate** 로 이어지는 5단계의 과정을 통해 RAG 시스템이 어떻게 작동하는지 파악했습니다.
- **실전 RAG 구축**: LangChain을 사용하여, 주어진 문서의 내용을 기반으로 질문에 답변하는 RAG 챗봇을 직접 코드로 구현하는 경험을 쌓았습니다.

이제 여러분은 단순히 LLM을 사용하는 것을 넘어, LLM을 다른 데이터, 다른 도구와 '연결'하여 훨씬 더 지능적이고 실용적인 애플리케이션을 만들 수 있는 첫걸음을 뗐습니다.

## 7. 더 깊이 알아보기 (Further Reading)
- [LangChain 공식 문서: RAG](https://python.langchain.com/v0.2/docs/concepts/#retrieval-augmented-generation-rag): LangChain이 설명하는 RAG의 개념과 다양한 활용법
- [Pinecone: What is RAG?](https://www.pinecone.io/learn/retrieval-augmented-generation/): 대표적인 벡터 DB 회사인 Pinecone이 설명하는 RAG 가이드
- [Hugging Face 블로그: RAG](https://huggingface.co/docs/transformers/main/en/rag): 허깅페이스에서 제공하는 RAG에 대한 기술적인 설명

---

### ⚠️ What Could Go Wrong? (토론 주제)

LangChain은 LLM 기반 애플리케이션 개발을 가속화하는 강력한 도구이지만, 실제 프로덕션 환경에 적용할 때는 여러 잠재적 위험과 한계를 고려해야 합니다.

1.  **지나친 추상화의 함정 (The Pitfall of Over-abstraction)**
    *   LangChain의 고수준 추상화가 내부 동작을 이해하기 어렵게 만들어, 특정 요구사항에 맞게 커스터마이징하거나 디버깅할 때 오히려 더 복잡해지는 상황이 발생할 수 있습니다. "간단한 일은 더 간단하게, 복잡한 일은 불가능하게" 만드는 경우가 될 수 있을까요?
    *   특정 컴포넌트(예: Vectorstore)를 교체하거나 세부 동작을 수정하려 할 때, LangChain의 추상화 계층 때문에 예상보다 많은 노력이 드는 경우를 겪어본 적이 있나요?

2.  **프롬프트 주입 및 보안 취약점 (Prompt Injection & Security Vulnerabilities)**
    *   RAG(Retrieval-Augmented Generation) 파이프라인에서 사용자가 악의적으로 조작한 문서를 데이터 소스에 포함시켜, 시스템 프롬프트를 탈취하거나 의도치 않은 결과를 유도하는 '프롬프트 주입' 공격에 어떻게 대비해야 할까요?
    *   LangChain 에이전트가 파일 시스템 접근이나 API 호출과 같은 위험한 도구(Tool)를 사용할 때, 권한 상승이나 데이터 유출과 같은 보안 사고를 방지하기 위한 안전장치는 무엇이 있을까요?

3.  **에이전트의 예측 불가능성과 제어의 어려움 (Unpredictability and Controllability of Agents)**
    *   ReAct와 같은 복잡한 에이전트가 잘못된 추론 경로에 빠져 무한 루프를 돌거나, 의도치 않게 비싼 유료 API를 계속 호출하여 막대한 비용을 발생시키는 상황을 어떻게 방지할 수 있을까요?
    *   "목표를 줄 테니 알아서 해줘"라는 에이전트의 방식이, 실제 비즈니스 로직처럼 엄격한 제어가 필요한 경우에 적합할까요? 에이전트의 자율성과 시스템의 안정성 사이의 균형점은 어디일까요?

4.  **디버깅과 투명성 부족 (Lack of Debuggability and Transparency)**
    *   여러 체인과 에이전트, 도구가 복잡하게 얽혀 있을 때, 최종 결과가 어떤 중간 단계를 거쳐 나왔는지 추적하기 어려운 경험을 한 적이 있나요? (e.g., "왜 이 답변이 나왔지?")
    *   LangSmith와 같은 추적 도구가 이러한 문제를 어느 정도 해결해주지만, 근본적인 복잡성 자체는 여전히 높은 진입 장벽이 될 수 있지 않을까요?

5.  **프레임워크 종속성 문제 (Framework Lock-in)**
    *   애플리케이션의 핵심 로직을 LangChain의 특정 구조와 기능에 깊이 의존하여 개발했을 때, 나중에 다른 프레임워크나 순수 API 호출 기반의 코드로 전환하기 어려워지는 '기술 부채'가 될 수 있습니다. 이러한 종속성을 최소화하며 LangChain을 현명하게 활용하는 전략은 무엇일까요?

---

**➡️ 다음 시간: [Part 7.4: 그래프 신경망 (GNN)](./part_7.4_graph_neural_networks.md)** 