ChromaDB + LangChain RAG κΈ°λ° μ μ¬μ AI μ±λ΄ μλΉμ€
- νλ‘μ νΈ κ°μ
- μμ€ν μꡬμ¬ν
- νκ²½ μ€μ
- νλ‘μ νΈ κ΅¬μ‘°
- ν΅μ¬ μ»΄ν¬λνΈ
- API λͺ μΈμ
- LangChain ꡬ쑰
- μλ μ리
- μ€μΉ λ° μ€ν
- ν μ€νΈ
- λ°°ν¬
- λ¬Έμ ν΄κ²°
μ΄ νλ‘μ νΈλ μ μ¬μμ μ±κ²©κ³Ό λ§ν¬λ₯Ό λͺ¨λ°©ν AI μ±λ΄ μλΉμ€μ λλ€. ChromaDBλ₯Ό λ²‘ν° λ°μ΄ν°λ² μ΄μ€λ‘ μ¬μ©νκ³ , LangChain RAG(Retrieval-Augmented Generation) λ°©μμΌλ‘ ꡬνλμμ΅λλ€.
- FastAPI: κ³ μ±λ₯ μΉ API νλ μμν¬
- ChromaDB: λ‘컬 λ²‘ν° λ°μ΄ν°λ² μ΄μ€λ‘ μ μ¬μ κ΄λ ¨ μ§μ μ μ₯
- LangChain RAG: κ²μ μ¦κ° μμ±μΌλ‘ μ νν λ΅λ³ μ 곡
- Upstage Solar API: νκ΅μ΄ νΉν LLM λ° μλ² λ© λͺ¨λΈ μ¬μ©
- μ μ¬μ νλ₯΄μλ: μΉκ·Όνκ³ μ λ¨Έλ¬μ€ν κ΅λ―ΌMC μ€νμΌ
- Backend: FastAPI, Python 3.11
- Vector DB: ChromaDB (λ‘컬 μ μ₯)
- AI Models: Upstage Solar Pro (LLM), Solar Embedding (μλ² λ©)
- Framework: LangChain
- Deployment: AWS EC2 (κ³ν)
μ€μ: langchain_upstageμ langchain_chroma ν¨ν€μ§λ Python 3.13.xμμ νΈνμ± λ¬Έμ κ° μμ΅λλ€.
λ°λμ Python 3.11 λ²μ μ μ¬μ©ν΄μΌ ν©λλ€.
- Python: 3.11.x (νμ)
- λ©λͺ¨λ¦¬: μ΅μ 4GB RAM
- μ μ₯곡κ°: μ΅μ 2GB (ChromaDB ν¬ν¨)
- λ€νΈμν¬: Upstage API μ κ·Όμ μν μΈν°λ· μ°κ²°
# 1. venv311μ΄λΌλ μ΄λ¦μΌλ‘ κ°μνκ²½ μμ±
py -3.11 -m venv venv311
# 2. κ°μνκ²½ νμ±ν
.\venv311\Scripts\activate
# 3. pip μ
κ·Έλ μ΄λ λ° μμ‘΄μ± μ€μΉ
pip install --upgrade pip
pip install -r requirements.txt.env νμΌμ μμ±νκ³ λ€μ λ΄μ©μ μΆκ°:
# Upstage API ν€ (νμ)
UPSTAGE_API_KEY=your_upstage_api_key_here
# ChromaDB μ€μ
CHROMA_PERSIST_DIR=./chroma_db
# FastAPI μλ² μ€μ
HOST=0.0.0.0
PORT=8000
LOG_LEVEL=info
# νκ²½ μ€μ
ENVIRONMENT=developmentbackend/
βββ π main.py # FastAPI λ©μΈ μ ν리μΌμ΄μ
βββ π chatbot.py # μ μ¬μ RAG μ±λ΄ ν΄λμ€
βββ π run.py # μλ² μ€ν μ€ν¬λ¦½νΈ
βββ π test_chatbot.py # μ±λ΄ λ¨λ
ν
μ€νΈ μ€ν¬λ¦½νΈ
βββ π requirements.txt # Python μμ‘΄μ± ν¨ν€μ§
βββ π .env # νκ²½λ³μ μ€μ νμΌ
βββ π README.md # νλ‘μ νΈ λ¬Έμ (μ΄ νμΌ)
βββ π Dockerfile # Docker 컨ν
μ΄λ μ€μ
βββ π chroma_db/ # ChromaDB λ²‘ν° λ°μ΄ν°λ² μ΄μ€ (νμ μ 곡 μμ )
βββ π venv311/ # Python 3.11 κ°μνκ²½
βββ π .git/ # Git λ²μ κ΄λ¦¬
βββ π .github/ # GitHub Actions μν¬νλ‘μ°
βββ π events/ # ν
μ€νΈμ© μ΄λ²€νΈ νμΌ
βββ π tests/ # λ¨μ ν
μ€νΈ νμΌλ€
βββ π __pycache__/ # Python μΊμ νμΌ
μν : μΉ API μλ²μ μ§μ μ
μ£Όμ κΈ°λ₯:
- FastAPI μ ν리μΌμ΄μ μ€μ λ° CORS μ²λ¦¬
- μλͺ μ£ΌκΈ° κ΄λ¦¬ (lifespan μ΄λ²€νΈ)
- API μλν¬μΈνΈ μ μ (
/answer,/health,/) - μμ²/μλ΅ λͺ¨λΈ μ μ (Pydantic)
- μμΈ μ²λ¦¬ λ° λ‘κΉ
ν΅μ¬ ν΄λμ€:
ChatRequest: μ¬μ©μ μμ² λͺ¨λΈChatResponse: μ±λ΄ μλ΅ λͺ¨λΈHealthResponse: ν¬μ€μ²΄ν¬ μλ΅ λͺ¨λΈ
μν : μ μ¬μ AI μ±λ΄μ ν΅μ¬ λ‘μ§
μ£Όμ κΈ°λ₯:
- Upstage Solar API μ°λ (LLM + μλ² λ©)
- ChromaDB λ²‘ν° λ°μ΄ν°λ² μ΄μ€ μ°κ²°
- LangChain RAG νμ΄νλΌμΈ ꡬμ±
- μ μ¬μ νλ₯΄μλ ν둬ννΈ κ΄λ¦¬
- λ΅λ³ μμ± λ° νμ§ κ΄λ¦¬
ν΅μ¬ λ©μλ:
_setup_models(): AI λͺ¨λΈ μ΄κΈ°ν_load_chromadb(): λ²‘ν° DB λ‘λ_setup_rag_chain(): RAG μ²΄μΈ κ΅¬μ±get_answer(): λ΅λ³ μμ±health_check(): μν νμΈ
μν : FastAPI μλ² μ€ν λ° νκ²½ κ΄λ¦¬
μ£Όμ κΈ°λ₯:
- νκ²½λ³μ κ²μ¦ λ° μ€μ
- κ°λ°/μ΄μ νκ²½ ꡬλΆ
- μλ² μ€μ μ΅μ ν
- AWS EC2 λ°°ν¬ μ§μ
μν : μ±λ΄ κΈ°λ₯ λ¨λ ν μ€νΈ
μ£Όμ κΈ°λ₯:
- μ±λ΄ μ΄κΈ°ν ν μ€νΈ
- ν¬μ€μ²΄ν¬ κ²μ¦
- λν κΈ°λ₯ ν μ€νΈ
- λνν ν μ€νΈ λͺ¨λ
http://localhost:8000
GET /μλ΅ μμ:
{
"service": "μ μ¬μ AI μ±λ΄",
"version": "1.0.0",
"description": "ChromaDB + LangChain RAG κΈ°λ°",
"endpoints": {
"POST /answer": "μ±λ΄κ³Ό λν",
"GET /health": "μλΉμ€ μν νμΈ"
},
"example": {
"request": {"prompt": "μλ
νμΈμ!"},
"response": {"answer": "μλ
νμΈμ! μ μ¬μμ
λλ€! ννν!"}
}
}POST /answer
Content-Type: application/jsonμμ² λ³Έλ¬Έ:
{
"prompt": "μ μ¬μλμ λν νλ‘κ·Έλ¨μ λμμ?"
}μλ΅ λ³Έλ¬Έ:
{
"answer": "μ! μ λν νλ‘κ·Έλ¨μ΄λΌκ³ νλ©΄ 무νλμ κ³Ό λ°λλ§¨μ΄ μμ£ ! 무νλμ μ 2006λ
λΆν° 2018λ
κΉμ§ μ λ§ μ¦κ²κ² νκ³ , λ°λ맨μ 2010λ
λΆν° μ§κΈκΉμ§ κ³μνκ³ μμ΄μ! ννν!"
}μ€λ₯ μλ΅:
// 400 Bad Request - λΉ ν둬ννΈ
{
"detail": "λΉ λ©μμ§λ λ³΄λΌ μ μμ΅λλ€."
}
// 503 Service Unavailable - μ±λ΄ λ―Έμ΄κΈ°ν
{
"detail": "μ±λ΄μ΄ μ΄κΈ°νλμ§ μμμ΅λλ€. μ μ ν λ€μ μλν΄μ£ΌμΈμ."
}
// 500 Internal Server Error - μλ² μ€λ₯
{
"detail": "λ΅λ³ μμ± μ€ μ€λ₯κ° λ°μνμ΅λλ€. λ€μ μλν΄μ£ΌμΈμ."
}GET /healthμλ΅ μμ:
{
"status": "healthy",
"message": "μλΉμ€κ° μ μ μλ μ€μ
λλ€.",
"vectorstore_status": "connected",
"documents_count": 1247
}| μ½λ | μ€λͺ |
|---|---|
| 200 | μ±κ³΅ |
| 400 | μλͺ»λ μμ² (λΉ ν둬ννΈ λ±) |
| 405 | μ§μνμ§ μλ HTTP λ©μλ |
| 500 | μλ² λ΄λΆ μ€λ₯ |
| 503 | μλΉμ€ μ¬μ© λΆκ° (μ±λ΄ λ―Έμ΄κΈ°ν) |
graph LR
A[μ¬μ©μ μ§λ¬Έ] --> B[μλ² λ© λ³ν]
B --> C[ChromaDB κ²μ]
C --> D[κ΄λ ¨ λ¬Έμ μΆμΆ]
D --> E[ν둬ννΈ κ΅¬μ±]
E --> F[Solar Pro LLM]
F --> G[μ μ¬μ λ΅λ³]
embeddings = UpstageEmbeddings(
model="solar-embedding-1-large",
api_key=upstage_api_key
)- μν : ν μ€νΈλ₯Ό 벑ν°λ‘ λ³ν
- μ¬μ© μμ : μ§λ¬Έ κ²μ μ, λ°μ΄ν° μ μ₯ μ
llm = ChatUpstage(
model="solar-pro",
api_key=upstage_api_key
)- μν : μ΅μ’ λ΅λ³ μμ±
- νΉμ§: νκ΅μ΄ νΉν, λν νν
vectorstore = Chroma(
client=client,
embedding_function=embeddings,
collection_name="yujaeseuk_knowledge"
)- μν : μ μ¬μ κ΄λ ¨ μ§μ μ μ₯ λ° κ²μ
- κ²μ μκ³ λ¦¬μ¦: MMR (Maximal Marginal Relevance)
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 3}
)- μν : μ§λ¬Έκ³Ό κ΄λ ¨λ μμ 3κ° λ¬Έμ κ²μ
- μκ³ λ¦¬μ¦: MMR (κ΄λ ¨μ±κ³Ό λ€μμ± κ³ λ €)
prompt = ChatPromptTemplate.from_messages([
("system", "λΉμ μ μ μ¬μμ
λλ€. μ±κ²©: λ°κ³ κΈμ μ ..."),
("human", "{question}")
])- μν : μ μ¬μ νλ₯΄μλ μ μ
- ꡬμ±: μμ€ν ν둬ννΈ + μ¬μ©μ μ§λ¬Έ
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)- μν : μ 체 νμ΄νλΌμΈ μ°κ²°
- νλ¦: κ²μ β ν둬ννΈ β LLM β νμ±
sequenceDiagram
participant User as μ¬μ©μ
participant FastAPI as FastAPI μλ²
participant ChatBot as YuJaeSukRAGBot
participant Upstage as Upstage API
participant ChromaDB as ChromaDB
User->>FastAPI: python run.py
FastAPI->>ChatBot: μ±λ΄ μ΄κΈ°ν
ChatBot->>Upstage: API ν€ κ²μ¦
ChatBot->>ChromaDB: λ²‘ν° DB μ°κ²°
ChatBot->>ChatBot: RAG μ²΄μΈ κ΅¬μ±
ChatBot-->>FastAPI: μ΄κΈ°ν μλ£
FastAPI-->>User: μλ² μ€ν μ€λΉ
sequenceDiagram
participant User as μ¬μ©μ
participant FastAPI as FastAPI
participant ChatBot as μ±λ΄
participant Embeddings as μλ² λ© λͺ¨λΈ
participant ChromaDB as ChromaDB
participant LLM as Solar Pro
User->>FastAPI: POST /answer {"prompt": "μ§λ¬Έ"}
FastAPI->>ChatBot: get_answer("μ§λ¬Έ")
ChatBot->>Embeddings: μ§λ¬Έ 벑ν°ν
Embeddings-->>ChatBot: μ§λ¬Έ 벑ν°
ChatBot->>ChromaDB: μ μ¬ λ¬Έμ κ²μ
ChromaDB-->>ChatBot: κ΄λ ¨ λ¬Έμ 3κ°
ChatBot->>LLM: ν둬ννΈ + κ²μ κ²°κ³Ό
LLM-->>ChatBot: μ μ¬μ μ€νμΌ λ΅λ³
ChatBot-->>FastAPI: μ΅μ’
λ΅λ³
FastAPI-->>User: {"answer": "λ΅λ³"}
# μ¬μ©μ μ§λ¬Έ: "μ μ¬μ νλ‘κ·Έλ¨μ?"
question_vector = embeddings.embed_query("μ μ¬μ νλ‘κ·Έλ¨μ?")
# κ²°κ³Ό: [0.1, 0.5, -0.3, 0.8, ...]# ChromaDBμμ MMR μκ³ λ¦¬μ¦μΌλ‘ κ²μ
similar_docs = vectorstore.similarity_search(
query="μ μ¬μ νλ‘κ·Έλ¨μ?",
k=3
)
# κ²°κ³Ό: ["무νλμ μ...", "λ°λ맨μ...", "ν΄νΌν¬κ²λ..."]system_prompt = "λΉμ μ μ μ¬μμ
λλ€..."
context = "무νλμ μ... λ°λ맨μ... ν΄νΌν¬κ²λ..."
user_question = "μ μ¬μ νλ‘κ·Έλ¨μ?"
final_prompt = f"{system_prompt}\nμ°Έκ³ μ 보: {context}\nμ§λ¬Έ: {user_question}"response = llm.invoke(final_prompt)
# κ²°κ³Ό: "μ! μ λν νλ‘κ·Έλ¨μ΄λΌκ³ νλ©΄ 무νλμ κ³Ό λ°λλ§¨μ΄ μμ£ !"π μ¬μ©μ μ
λ ₯
β (FastAPI κ²μ¦)
π μ§λ¬Έ 벑ν°ν (Solar Embedding)
β (MMR κ²μ)
π ChromaDB λ¬Έμ κ²μ
β (컨ν
μ€νΈ ꡬμ±)
π μ μ¬μ ν둬ννΈ + κ²μ κ²°κ³Ό
β (Solar Pro LLM)
π¬ μ μ¬μ μ€νμΌ λ΅λ³ μμ±
β (FastAPI μλ΅)
β
JSON ννλ‘ λ°ν
# νλ‘μ νΈ ν΄λ‘
git clone <repository-url>
cd backend
# Python 3.11 κ°μνκ²½ μμ±
py -3.11 -m venv venv311
# κ°μνκ²½ νμ±ν
.\venv311\Scripts\activate
# μμ‘΄μ± μ€μΉ
pip install --upgrade pip
pip install -r requirements.txt.env νμΌ μμ±:
UPSTAGE_API_KEY=your_api_key_here
CHROMA_PERSIST_DIR=./chroma_db
HOST=0.0.0.0
PORT=8000
LOG_LEVEL=info
ENVIRONMENT=developmentνμ¬ μν©: νμμΌλ‘λΆν° chroma_db/ ν΄λλ₯Ό λ°μμΌ ν¨
# νμμΌλ‘λΆν° λ°μ chroma_db/ ν΄λλ₯Ό νλ‘μ νΈ λ£¨νΈμ λ°°μΉ
# ν΄λ ꡬ쑰:
# backend/
# βββ chroma_db/
# β βββ chroma.sqlite3
# β βββ [collection-id]/
# β βββ ...# μλ² μμ μ λ°λμ λ°μ΄ν° μλ² λ© νμ
python setup_data.py
# λ°©λ² 1: run.py μ¬μ© (κΆμ₯)
python run.py
# λ°©λ² 2: uvicorn μ§μ μ¬μ©
uvicorn main:app --reload --host 0.0.0.0 --port 8000
# λ°©λ² 3: main.py μ§μ μ€ν
python main.py# κΈ°λ³Έ ν
μ€νΈ μ€ν
python test_chatbot.pyν μ€νΈ λ΄μ©:
- μ±λ΄ μν¬νΈ λ° μ΄κΈ°ν
- ν¬μ€μ²΄ν¬ κ²μ¦
- 5κ° μ§λ¬Έ λν ν μ€νΈ
- λνν ν μ€νΈ λͺ¨λ
# μλ² μ€ν ν λ€λ₯Έ ν°λ―Έλμμ
curl -X POST "http://localhost:8000/answer" \
-H "Content-Type: application/json" \
-d '{"prompt":"μλ
νμΈμ!"}'μλ² μ€ν ν λΈλΌμ°μ μμ μ μ:
- API λ¬Έμ: http://localhost:8000/docs
- μλΉμ€ μ 보: http://localhost:8000
- ν¬μ€μ²΄ν¬: http://localhost:8000/health
| ν μ€νΈ | μ λ ₯ | κΈ°λ κ²°κ³Ό |
|---|---|---|
| κΈ°λ³Έ μΈμ¬ | "μλ νμΈμ!" | μ μ¬μ μ€νμΌ μΈμ¬ |
| νλ‘κ·Έλ¨ μ§λ¬Έ | "λν νλ‘κ·Έλ¨μ?" | 무νλμ , λ°λ맨 μΈκΈ |
| μ±κ²© μ§λ¬Έ | "μ΄λ€ μ±κ²©μ΄μΈμ?" | λ°κ³ κΈμ μ λ± μΈκΈ |
| λΉ μ λ ₯ | "" | 400 μλ¬ |
| μλͺ»λ νμ | μλͺ»λ JSON | 400 μλ¬ |
- AMI: Ubuntu 22.04 LTS
- μΈμ€ν΄μ€ νμ : t3.medium (μ΅μ t3.small)
- 보μκ·Έλ£Ή: 22(SSH), 8000(FastAPI) ν¬νΈ μ€ν
# μμ€ν
μ
λ°μ΄νΈ
sudo apt update && sudo apt upgrade -y
# Python 3.11 μ€μΉ
sudo apt install python3.11 python3.11-venv python3-pip -y
# νλ‘μ νΈ λ°°ν¬
git clone <repository>
cd backend
# κ°μνκ²½ μ€μ
python3.11 -m venv venv311
source venv311/bin/activate
pip install -r requirements.txt# .env νμΌ μμ±
nano .env
# UPSTAGE_API_KEY λ± μ€μ # systemd μλΉμ€ νμΌ μμ±
sudo nano /etc/systemd/system/yujaeseuk-chatbot.service
# μλΉμ€ λ΄μ©:
[Unit]
Description=YuJaeSuk AI Chatbot
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/backend
Environment=PATH=/home/ubuntu/backend/venv311/bin
ExecStart=/home/ubuntu/backend/venv311/bin/python run.py
Restart=always
[Install]
WantedBy=multi-user.target
# μλΉμ€ λ±λ‘ λ° μμ
sudo systemctl daemon-reload
sudo systemctl enable yujaeseuk-chatbot
sudo systemctl start yujaeseuk-chatbot# μλΉμ€ μν νμΈ
sudo systemctl status yujaeseuk-chatbot
# λΈλΌμ°μ μμ μ μ
http://<EC2-PUBLIC-IP>:8000# μ€λ₯: langchain_upstage import μ€ν¨
β ImportError: cannot import name 'UpstageEmbeddings'
# ν΄κ²°: Python 3.11 μ¬μ© νμΈ
python --version # Python 3.11.x μ¬μΌ ν¨# μ€λ₯: ChromaDB λλ ν 리 μμ
β FileNotFoundError: ChromaDB λλ ν λ¦¬κ° μμ΅λλ€
# ν΄κ²°: νμμΌλ‘λΆν° chroma_db/ ν΄λ λ°κΈ°
# λλ μμ λλ―Έ λ°μ΄ν° μμ±# μ€λ₯: API ν€ μμ
β ValueError: UPSTAGE_API_KEYκ° μ€μ λμ§ μμμ΅λλ€
# ν΄κ²°: .env νμΌ νμΈ
cat .env | grep UPSTAGE_API_KEY# μ€λ₯: ν¬νΈ 8000 μ΄λ―Έ μ¬μ© μ€
β OSError: [Errno 98] Address already in use
# ν΄κ²°: λ€λ₯Έ ν¬νΈ μ¬μ© λλ κΈ°μ‘΄ νλ‘μΈμ€ μ’
λ£
lsof -ti:8000 | xargs kill -9# μ€λ₯: ChromaDB λ‘λ© μ€ λ©λͺ¨λ¦¬ λΆμ‘±
β MemoryError
# ν΄κ²°: λ ν° μΈμ€ν΄μ€ νμ
μ¬μ©
# λλ Swap λ©λͺ¨λ¦¬ μ€μ # μ ν리μΌμ΄μ
λ‘κ·Έ
tail -f app.log
# systemd μλΉμ€ λ‘κ·Έ
sudo journalctl -u yujaeseuk-chatbot -f
# FastAPI κ°λ° μλ² λ‘κ·Έ
python run.py # μ½μμ μ§μ μΆλ ₯- μ μ ν chunk_size μ€μ (νμ¬: 1000)
- MMR μκ³ λ¦¬μ¦μ k κ° μ‘°μ (νμ¬: 3)
- μ΄μ νκ²½μμ λ©ν°μ컀 μ¬μ©
- μ μ ν μΈμ€ν΄μ€ νμ μ ν
- ν둬ννΈ κΈΈμ΄ μ΅μ ν
- μΊμ± μ λ΅ κ³ λ €