학습 목적: 이 프로젝트는 AI 에이전트, RAG(Retrieval-Augmented Generation), ReAct, Tool Calling 등 최신 LLM 기술을 학습하기 위한 교육용 프로젝트입니다.
AgenticRAG는 반도체 인터커넥트 재료(구리, 마그네슘 합금 등)에 관한 연구 논문을 분석하고, 사용자의 질문에 지능적으로 답변하는 AI 시스템입니다.
- 논문 분석: PDF 논문에서 재료의 구성(Composition), 제조공정(Process), 물성(Property)을 자동 추출
- 지능형 검색: 사용자 질문에 따라 적절한 도구를 자동 선택
- 다중 데이터 소스: 논문 DB + 계산 데이터베이스 + 최신 논문 검색 + 웹 정보 검색
stateDiagram-v2
[*] --> Query: 사용자 질문<br/>"Cu-Mg 합금의<br/>저항률과 최신 연구는?"
Query --> Think1: Thought 1
state Think1 {
[*] --> Analysis: 실험 데이터 + 최신 논문<br/>두 가지 필요
Analysis --> Decision: vectordb_search 사용
}
Think1 --> Action1: Action 1
state Action1 {
[*] --> Tool: vectordb_search
Tool --> Input: "Cu-Mg alloy resistivity"
}
Action1 --> Obs1: Observation 1
state Obs1 {
[*] --> Result: 2.0 μΩ·cm (350℃)<br/>DC sputtering 공정
}
Obs1 --> Think2: Thought 2
state Think2 {
[*] --> Check: 최신 연구도 필요
Check --> Decision2: crossref_search 사용
}
Think2 --> Action2: Action 2
state Action2 {
[*] --> Tool2: crossref_search
Tool2 --> Input2: "Cu-Mg alloy 2024"
}
Action2 --> Obs2: Observation 2
state Obs2 {
[*] --> Papers: 5개 논문 발견<br/>Kim et al. (2024)
}
Obs2 --> Think3: Thought 3
state Think3 {
[*] --> Eval: 충분한 정보 확보
Eval --> Ready: 답변 준비
}
Think3 --> Answer: Final Answer
state Answer {
[*] --> Response: Cu-Mg 합금 저항률 2.0 μΩ·cm<br/>2024년 최신 연구 5편 포함<br/>상세 분석 제공
}
Answer --> [*]
graph LR
A[PDF 논문] --> B[텍스트 추출]
B --> C[청크 분할]
C --> D[VectorDB 저장]
D --> E[사용자 질문]
E --> F[VectorDB 검색]
F --> G[LLM]
G --> H[답변]
style A fill:#e1f5ff
style D fill:#ffe1e1
style H fill:#e1ffe1
특징 및 한계:
⚠️ 단일 파일: 모든 코드가 하나의 Jupyter Notebook 파일에 집중⚠️ 단순 RAG: VectorDB에서만 검색 (단일 데이터 소스)⚠️ 수동 선택: 사용자가 직접 검색 방법 결정⚠️ 배치 처리 중심: 모든 문서를 한 번에 처리하고 시각화⚠️ 재사용 어려움: 함수/모듈 분리 안됨
graph TB
User[👤 사용자 질문] --> Agent[🤖 ReAct Agent<br/>LLM 기반]
Agent -->|생각| Think{어떤 도구를<br/>사용할까?}
Think -->|실험 데이터| VDB[📚 VectorDB<br/>논문 검색]
Think -->|계산 데이터| MP[🔬 Materials Project<br/>DFT 데이터]
Think -->|최신 논문| CR[📰 Crossref<br/>논문 검색]
VDB --> Obs1[관찰 결과 1]
MP --> Obs2[관찰 결과 2]
CR --> Obs3[관찰 결과 3]
Obs1 --> Agent
Obs2 --> Agent
Obs3 --> Agent
Agent -->|충분한 정보| Answer[✅ 종합 답변]
Agent -->|더 필요| Think
style User fill:#e1f5ff
style Agent fill:#ffe1e1
style Think fill:#fff4e1
style Answer fill:#e1ffe1
style VDB fill:#f0e1ff
style MP fill:#f0e1ff
style CR fill:#f0e1ff
주요 개선 사항:
| 항목 | 기존 코드 | 새로운 코드 (AgenticRAG) |
|---|---|---|
| 구조 | 단일 파일 (1개) | 모듈화 (10개 파일) |
| 데이터 소스 | VectorDB만 | VectorDB + Materials Project + Crossref |
| 의사결정 | 사용자가 결정 | AI가 자동 결정 (Agentic) |
| 프레임워크 | 없음 | ReAct (Reasoning + Acting) |
| 도구 사용 | 수동 호출 | Tool Calling (자동) |
| UI | CLI만 | CLI + Streamlit 웹 |
| 설정 관리 | 하드코딩 | config.py + .env |
| 확장성 | 낮음 | 높음 (도구 추가 쉬움) |
코드 비교:
# 기존 코드 (단순 RAG)
def answer_question(question):
# VectorDB에서만 검색
docs = vectordb.search(question)
# LLM에게 전달
prompt = f"Context: {docs}\nQuestion: {question}"
answer = llm(prompt)
return answer
# 새로운 코드 (AgenticRAG)
def answer_question(question):
# Agent가 자동으로:
# 1. 질문 분석
# 2. 필요한 도구 선택 (vectordb/MP/crossref)
# 3. 여러 도구를 순차적으로 사용
# 4. 결과 종합
result = agent.invoke({"input": question})
return result["output"]RAG는 "검색 증강 생성"이라는 의미로, LLM의 지식 부족 문제를 외부 문서 검색으로 보완하는 기술입니다.
문제점:
# LLM만 사용한 경우
질문: "2024년에 발표된 Cu-Mg 합금의 저항률은?"
LLM: "죄송하지만 2024년 데이터는 학습하지 않았습니다." ❌해결책 (RAG):
# RAG 적용
질문: "2024년에 발표된 Cu-Mg 합금의 저항률은?"
1. 검색 (Retrieval): VectorDB에서 관련 논문 검색
→ 논문 발견: "2024년 Kim et al."
2. 증강 (Augmented): 검색 결과를 LLM에게 제공
Context: [논문 내용]
Question: [질문]
3. 생성 (Generation): LLM이 답변 생성
→ "2024년 Kim et al. 논문에 따르면 2.0 μΩ·cm입니다." ✅sequenceDiagram
participant U as 사용자
participant S as 시스템
participant V as VectorDB
participant L as LLM
U->>S: "Cu-Mg 합금의 저항률은?"
S->>V: 유사 문서 검색
V->>S: 관련 논문 3개 반환
S->>L: 질문 + 검색 결과
L->>S: 답변 생성
S->>U: "2.0 μΩ·cm입니다 (출처: ...)"
- 📄 논문: "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks" (Lewis et al., 2020)
- 🔗 LangChain RAG Tutorial
Agent(에이전트)는 스스로 생각하고(Reasoning), 도구를 선택하고(Tool Selection), 행동하는(Acting) AI 시스템입니다.
# 일반 AI (수동)
답변 = LLM("Cu-Mg 합금의 저항률은?")
# 사람이 직접 도구 선택해야 함
# Agentic AI (자동)
답변 = Agent("Cu-Mg 합금의 저항률은?")
# Agent가 스스로:
# 1. "이건 실험 데이터 질문이네"
# 2. "VectorDB 검색해야겠다"
# 3. [도구 실행]
# 4. "충분한가? 더 필요한가?"
# 5. 최종 답변graph TD
A[Agent] --> B[1. 추론 Reasoning]
A --> C[2. 계획 Planning]
A --> D[3. 도구 사용 Tool Use]
A --> E[4. 기억 Memory]
A --> F[5. 반성 Reflection]
B --> B1[상황 분석]
B --> B2[전략 결정]
C --> C1[작업 분해]
C --> C2[순서 결정]
D --> D1[도구 선택]
D --> D2[도구 실행]
E --> E1[대화 기록]
E --> E2[과거 결과]
F --> F1[결과 평가]
F --> F2[전략 수정]
style A fill:#ffe1e1
style B fill:#e1f5ff
style C fill:#e1f5ff
style D fill:#e1f5ff
style E fill:#e1f5ff
style F fill:#e1f5ff
사용자: "Cu2O의 밴드갭과 이를 연구한 최신 논문을 찾아줘"
=== Agent의 사고 과정 ===
[분석] 두 가지 정보 필요:
1. 밴드갭 (계산 데이터)
2. 최신 논문 (문헌 검색)
[계획] Step 1: Materials Project에서 밴드갭 조회
Step 2: Crossref에서 최신 논문 검색
Step 3: 결과 종합
[실행]
Step 1:
Action: materials_project
Input: Cu2O
Result: 밴드갭 = 2.1 eV ✓
Step 2:
Action: crossref_search
Input: Cu2O optical properties 2024
Result: 논문 5개 발견 ✓
[종합]
"Cu2O의 밴드갭은 2.1 eV이며,
2024년 최신 연구로는..."
- 📄 논문: "ReAct: Synergizing Reasoning and Acting in Language Models" (Yao et al., 2023)
- 🔗 LangChain Agents
- 🔗 AutoGPT - 유명한 Agent 예시
ReAct는 "추론(Reasoning)"과 "행동(Acting)"을 결합한 프레임워크로, LLM이 단계별로 생각하면서 도구를 사용하도록 만드는 방법입니다.
# Chain-of-Thought (CoT)만 사용
질문: "Cu-Mg 합금에 대해 알려줘"
LLM: [생각만 하고 도구 사용 안함]
"Cu-Mg 합금은... [학습 데이터 기반 추측]"
→ 부정확하거나 오래된 정보 ❌
# Action만 사용
질문: "Cu-Mg 합금에 대해 알려줘"
LLM: [생각 없이 도구만 사용]
Action: vectordb_search
Input: "Cu-Mg"
→ 너무 광범위한 검색 ❌질문: "Cu-Mg 합금에 대해 알려줘"
Thought: "Cu-Mg 합금" = 실험 데이터
→ VectorDB 검색 필요
Action: vectordb_search
Input: Cu-Mg alloy properties
Observation: [저항률 데이터 발견]
Thought: 이론적 배경도 있으면 좋겠다
→ Materials Project 조회
Action: materials_project
Input: CuMg
Observation: [결정구조 데이터 발견]
Thought: 충분한 정보 확보
Final Answer: [종합 답변] ✅graph LR
T1[Thought 1:<br/>분석] --> A1[Action 1:<br/>도구 선택]
A1 --> O1[Observation 1:<br/>결과]
O1 --> T2[Thought 2:<br/>재분석]
T2 --> A2[Action 2:<br/>도구 선택]
A2 --> O2[Observation 2:<br/>결과]
O2 --> T3[Thought 3:<br/>판단]
T3 --> FA[Final Answer:<br/>답변]
style T1 fill:#e1f5ff
style T2 fill:#e1f5ff
style T3 fill:#e1f5ff
style A1 fill:#ffe1e1
style A2 fill:#ffe1e1
style O1 fill:#f0e1ff
style O2 fill:#f0e1ff
style FA fill:#e1ffe1
# prompts.py에서
REACT_SYSTEM_PROMPT = """
=== FORMAT ===
Thought: [현재 상황 분석 + 다음 행동 계획]
Action: [사용할 도구 이름]
Action Input: [도구에 전달할 입력]
Observation: [도구 실행 결과]
... (필요시 반복)
Final Answer: [최종 답변]
=== EXAMPLE ===
Thought: 저항률 데이터는 실험 결과이므로 논문 검색 필요
Action: vectordb_search
Action Input: Cu-Mg alloy resistivity
Observation: 2.0 μΩ·cm at 350℃
Thought: 충분한 데이터 확보
Final Answer: Cu-Mg 합금의 저항률은 2.0 μΩ·cm입니다.
"""- 📄 논문: "ReAct: Synergizing Reasoning and Acting in Language Models"
- 🔗 ReAct 공식 페이지
- 🔗 LangChain ReAct 구현
CoT는 LLM이 최종 답변을 내기 전에 중간 추론 단계를 거치도록 하는 프롬프팅 기법입니다.
# CoT 없음 (Zero-shot)
질문: "23 * 47은?"
LLM: "1081입니다" ❌ (틀림)
# CoT 적용 (Few-shot)
질문: "23 * 47은?"
LLM: """
단계 1: 20 * 47 = 940
단계 2: 3 * 47 = 141
단계 3: 940 + 141 = 1081
답: 1081
""" ✅ (정확)# Few-shot CoT
PROMPT = """
예시 1:
질문: Cu-Mg 합금의 저항률은?
생각: 저항률은 실험으로 측정하는 물성이다.
논문에서 찾아봐야겠다.
답변: 논문에 따르면 2.0 μΩ·cm입니다.
예시 2:
질문: Cu2O의 밴드갭은?
생각: 밴드갭은 DFT 계산으로 알 수 있다.
Materials Project를 확인해야겠다.
답변: 계산 결과 2.1 eV입니다.
이제 당신 차례:
질문: {user_question}
생각:
"""- 📄 논문: "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models" (Wei et al., 2022)
Tool Calling은 LLM이 미리 정의된 함수나 API를 호출할 수 있도록 하는 기능입니다. 최신 모델들은 단순히 함수를 호출하는 것을 넘어, 원하는 데이터 구조(예: JSON)를 생성하도록 요청할 수 있습니다.
이 프로젝트에서는 LLM이 단순히 텍스트를 반환하는 대신, 우리가 정의한 CPPData라는 데이터 구조에 맞춰 JSON 형식으로 출력하도록 요청합니다. 이는 LLM의 출력을 더 안정적이고 예측 가능하게 만듭니다.
sequenceDiagram
participant L as LLM
participant S as 시스템
participant P as JsonOutputParser
Note over L: 사용자: "이 텍스트에서 C-P-P 추출해줘"
L->>S: "```json\n{\n \"composition\": \"Cu, Mg\",\n ...\n}\n```"
S->>P: LLM이 생성한 JSON 텍스트 전달
P->>S: 파싱 성공! C-P-P 데이터 객체 반환
S->>S: 데이터 객체를 메타데이터에 저장
# 1. 원하는 데이터 구조 정의 (prompts.py)
from langchain_core.pydantic_v1 import BaseModel, Field
class CPPData(BaseModel):
"""C-P-P 데이터 구조"""
composition: str = Field(description="합금의 구성 요소")
process: str = Field(description="제조 및 실험 공정")
property: str = Field(description="주요 특성")
# 2. JSON 파서 생성 (prompts.py)
from langchain.output_parsers import JsonOutputParser
json_parser = JsonOutputParser(pydantic_object=CPPData)
# 3. 프롬프트에 JSON 형식 포함 (prompts.py)
from langchain_core.prompts import PromptTemplate
CPP_EXTRACTION_PROMPT = PromptTemplate(
template="""...
분석할 텍스트:
{text}
JSON 출력 형식:
{format_instructions}
""",
input_variables=["text"],
# 파서가 LLM에게 JSON 형식을 알려줌
partial_variables={"format_instructions": json_parser.get_format_instructions()},
)
# 4. LLM과 연결하여 체인 생성 (vectordb.py)
extraction_chain = CPP_EXTRACTION_PROMPT | llm | json_parser
# 5. 체인 실행
# LLM이 JSON 텍스트를 생성하면, json_parser가 자동으로 파싱하여
# 우리가 정의한 CPPData 객체로 변환해줍니다.
cpp_object = extraction_chain.invoke({"text": "..."})이점:
- 안정성: 정해진 JSON 형식으로 결과를 받으므로, 후속 처리(예: DB 저장)가 매우 쉬워집니다.
- 정확성: LLM이 어떤 정보를 추출해야 하는지 명확하게 이해하게 됩니다.
- 오류 처리: 만약 LLM이 유효하지 않은 JSON을 생성하면, 파서가 오류를 발생시켜 문제를 즉시 인지할 수 있습니다.
Vector Database는 텍스트를 수치 벡터로 변환하여 저장하고, 의미적 유사도로 검색하는 데이터베이스입니다.
# 일반 키워드 검색 (Exact Match)
검색어: "Cu alloy"
결과: "Cu alloy"를 포함한 문서만
"copper alloy" → 검색 안됨 ❌
"구리 합금" → 검색 안됨 ❌
# 벡터 검색 (Semantic Search)
검색어: "Cu alloy"
1. 벡터로 변환: [0.2, 0.8, -0.3, ...]
2. 유사 벡터 찾기:
"copper alloy" → [0.21, 0.79, -0.31, ...] ✅ 유사!
"구리 합금" → [0.19, 0.81, -0.29, ...] ✅ 유사!graph TD
A[텍스트 문서] --> B[Embedding 모델]
B --> C[벡터<br/>0.2, 0.8, -0.3, ...]
C --> D[Vector DB 저장]
E[검색 쿼리] --> F[Embedding 모델]
F --> G[쿼리 벡터]
G --> H[유사도 계산]
D --> H
H --> I[가장 유사한<br/>문서 반환]
style A fill:#e1f5ff
style D fill:#ffe1e1
style I fill:#e1ffe1
# 1. 텍스트 → 벡터 (Embedding)
text1 = "Cu-Mg alloy"
vector1 = [0.2, 0.8, 0.1, ...] # 300차원
text2 = "copper magnesium alloy"
vector2 = [0.21, 0.79, 0.11, ...]
# 2. 유사도 계산 (코사인 유사도)
similarity = cosine_similarity(vector1, vector2)
# = 0.98 (매우 유사!)
# 3. 검색
query = "Cu alloy"
query_vector = [0.19, 0.81, 0.09, ...]
# 모든 문서와 유사도 계산
results = db.search_by_vector(query_vector, top_k=5)Embedding은 텍스트를 고차원 벡터(숫자 배열)로 변환하는 기술입니다.
컴퓨터는 텍스트를 이해 못함 → 숫자로 변환 필요
"Cu-Mg alloy" → 컴퓨터가 이해 못함 ❌
[0.2, 0.8, -0.3, ...] → 계산 가능 ✅
2D 공간에 단어 배치 (실제는 300차원+)
copper (0.8, 0.9)
↑
|
Cu (0.7, 0.85) ← 가까움!
|
↓
gold (0.6, 0.75)
banana (-0.5, 0.3) ← 멀리 떨어짐
from langchain_ollama import OllamaEmbeddings
# Ollama 임베딩 초기화 (로컬 실행, API 키 불필요)
embeddings = OllamaEmbeddings(model="qwen3-embedding")
# 텍스트 → 벡터 변환
text1 = "Cu-Mg alloy has low resistivity"
vector1 = embeddings.embed_query(text1)
# [0.234, -0.567, 0.891, ..., 0.123] (1536차원)
# 의미가 유사하면 벡터도 유사
text2 = "Copper magnesium compound shows low resistance"
vector2 = embeddings.embed_query(text2)
import numpy as np
similarity = np.dot(vector1, vector2) / (np.linalg.norm(vector1) * np.linalg.norm(vector2))
print(f"유사도: {similarity:.3f}") # 0.9+ (매우 유사)참고:
qwen3-embedding은 Ollama 모델 라이브러리에서 확인할 수 있습니다.
- 📄 논문: "Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks"
- 🔗 Ollama 공식 사이트
- 🔗 qwen3-embedding 모델 페이지
Few-shot Learning은 LLM에게 몇 개의 예시를 보여주면 패턴을 학습하는 능력입니다.
# Zero-shot (예시 없음)
prompt = """
다음 텍스트에서 composition, process, property를 추출해줘:
[논문 텍스트]
"""
# 결과: 불규칙한 형식 ❌
# Few-shot (예시 3개)
prompt = """
예시 1:
입력: Cu damascene interconnects...
출력:
composition: Cu
process: damascene fabrication
property: EM lifetime
예시 2:
입력: Cu-Mg alloy DC sputtering...
출력:
composition: Cu, Mg
process: DC magnetron sputtering
property: resistivity 2.0 μΩ·cm
예시 3:
입력: Co-Cr barrier layer...
출력:
composition: Co, Cr
process: sputtering, annealing
property: breakdown voltage
이제 다음을 분석해줘:
입력: [논문 텍스트]
"""
# 결과: 일관된 형식 ✅- 📄 논문: "Language Models are Few-Shot Learners" (Brown et al., GPT-3 논문)
graph TB
subgraph "사용자 인터페이스"
UI1[🌐 Streamlit Web UI]
UI2[💻 CLI Interface]
end
subgraph "AgenticRAG 코어"
Agent[🤖 ReAct Agent<br/>agent.py]
Config[⚙️ 설정 관리<br/>config.py]
Prompts[📝 프롬프트<br/>prompts.py]
end
subgraph "데이터 처리 레이어"
VDB_Builder[🔨 VectorDB 구축<br/>vectordb.py]
PDF[📄 PDF 처리<br/>PyMuPDF]
end
subgraph "Tool 레이어"
Tool1[📚 VectorDB Search<br/>vectordb_search.py]
Tool2[🔬 Materials Project<br/>materials_project.py]
Tool3[📰 Crossref<br/>crossref.py]
Tool4[🌐 Web Search<br/>web_search.py]
end
subgraph "외부 서비스"
LLM[🧠 Google Gemini<br/>LLM]
Chroma[(💾 Chroma DB<br/>Vector Storage)]
MP[🔬 Materials Project<br/>API]
CR[📚 Crossref<br/>API]
WS[🌐 Brave Search<br/>Web Search]
end
UI1 --> Agent
UI2 --> Agent
Agent --> Config
Agent --> Prompts
Agent --> Tool1
Agent --> Tool2
Agent --> Tool3
Agent --> LLM
Tool1 --> Chroma
Tool2 --> MP
Tool3 --> CR
VDB_Builder --> PDF
VDB_Builder --> Chroma
VDB_Builder --> LLM
Config -.->|설정| VDB_Builder
Prompts -.->|프롬프트| VDB_Builder
style Agent fill:#ffe1e1
style LLM fill:#e1f5ff
style Chroma fill:#f0e1ff
style Tool1 fill:#fff4e1
style Tool2 fill:#fff4e1
style Tool3 fill:#fff4e1
sequenceDiagram
autonumber
actor User as 👤 사용자
participant UI as 🌐 UI
participant Agent as 🤖 Agent
participant LLM as 🧠 Gemini
participant Tool as 🔧 Tools
participant DB as 💾 Data
User->>UI: "Cu-Mg 합금의 저항률은?"
UI->>Agent: query
Agent->>LLM: ReAct Prompt + Query
LLM->>Agent: Thought + Action
Note over Agent: Action: vectordb_search
Agent->>Tool: execute(tool_name, input)
Tool->>DB: search query
DB->>Tool: results
Tool->>Agent: observation
Agent->>LLM: Thought + Observation
LLM->>Agent: Final Answer
Agent->>UI: response
UI->>User: "2.0 μΩ·cm입니다..."
rect rgb(255, 240, 200)
Note over Agent,LLM: ReAct Loop<br/>(필요시 반복)
end
flowchart TD
A[📄 PDF 파일들] --> B[PyMuPDF로 읽기]
B --> C[페이지별 텍스트 추출]
C --> D{텍스트가<br/>있는가?}
D -->|Yes| E[청크로 분할<br/>800자 단위]
D -->|No| C
E --> F[각 청크에 대해]
F --> G[LLM으로 C-P-P 추출]
G --> H[메타데이터 추가]
H --> I[Embedding 변환<br/>텍스트 → 벡터]
I --> J[Chroma DB 저장]
J --> K[✅ VectorDB 완성]
style A fill:#e1f5ff
style G fill:#ffe1e1
style K fill:#e1ffe1
agentRAG/
│
├── 📄 config.py # ⚙️ 설정 관리
│ ├── API 키 로드 (.env)
│ ├── LLM 설정 (Gemini 모델)
│ ├── Embedding 모델 설정
│ └── 하이퍼파라미터 (청크 크기 등)
│
├── 📄 prompts.py # 📝 프롬프트 관리
│ ├── Few-shot 예제 (26개)
│ ├── SYSTEM_TEMPLATE (C-P-P 추출용)
│ ├── CPP_EXTRACTION_PROMPT (C-P-P 추출용)
│ └── REACT_SYSTEM_PROMPT (Agent용)
│
├── 📄 vectordb.py # 🗄️ VectorDB 생성
│ ├── load_pdfs() - PDF 로드
│ ├── split_documents() - 청크 분할
│ ├── extract_cpp_from_chunk() - C-P-P 추출
│ ├── create_or_load_vectordb() - DB 생성/로드
│ └── build_vectordb_pipeline() - 전체 파이프라인
│
├── 📄 agent.py # 🤖 ReAct Agent
│ ├── create_agent() - Agent 초기화
│ ├── run_agent() - Agent 실행
│ └── interactive_chat() - CLI 인터페이스
│
├── 📄 app.py # 🌐 Streamlit UI
│ ├── 웹 인터페이스
│ ├── 대화 기록 관리
│ ├── 설정 패널 (온도, verbose)
│ └── 예시 질문 버튼
│
├── 📂 tools/ # 🔧 도구 모음
│ ├── 📄 vectordb_search.py # 논문 검색
│ │ ├── search_vectordb()
│ │ └── vectordb_search_tool (LangChain Tool)
│ │
│ ├── 📄 materials_project.py # DFT 데이터
│ │ ├── search_materials_project()
│ │ └── materials_project_tool
│ │
│ ├── 📄 crossref.py # 최신 논문
│ │ ├── search_crossref()
│ │ └── crossref_tool
│ │
│ └── 📄 web_search.py # 웹 검색
│ ├── web_search()
│ └── web_search_tool (DuckDuckGo)
│
├── 📄 .env.example # 🔑 환경변수 템플릿
├── 📄 .env # 🔑 실제 API 키 (사용자 생성)
├── 📄 requirements.txt # 📦 필수 패키지 목록
├── 📄 README.md # 📖 이 문서
│
├── 📂 chroma_db/ # 💾 VectorDB (자동 생성)
│ ├── chroma.sqlite3
│ └── [UUID 폴더들]/
│
└── 📂 data/ # 📂 사용자 데이터
└── pdfs/ # PDF 파일 보관
├── paper1.pdf
├── paper2.pdf
└── ...
"""
이 파일의 역할:
- API 키를 안전하게 관리
- 모델 설정을 한 곳에 모아서 변경 용이
- 다른 파일에서 import하여 사용
"""
# 예시
import config
# 다른 파일에서 이렇게 사용
llm = ChatGoogleGenerativeAI(
model=config.LLM_MODEL_NAME, # "gemini-2.5-flash"
temperature=config.LLM_TEMPERATURE, # 0.0
google_api_key=config.GOOGLE_API_KEY
)주요 내용:
GOOGLE_API_KEY: Gemini API 키MATERIALS_PROJECT_API_KEY: MP API 키LLM_MODEL_NAME: 사용할 LLM 모델EMBEDDING_MODEL_NAME: 임베딩 모델CHUNK_SIZE: 텍스트 분할 크기RETRIEVAL_TOP_K: 검색 개수
"""
이 파일의 역할:
- Few-shot 예제 관리
- 프롬프트 템플릿 관리
- 프롬프트 변경 시 여기만 수정
"""
# 27개의 Few-shot 예제
FEW_SHOT_EXAMPLES = [...]
# C-P-P 추출 프롬프트
SYSTEM_TEMPLATE = """..."""
# ReAct Agent 프롬프트
REACT_SYSTEM_PROMPT = """...""""""
실행 방법:
python vectordb.py
순서:
1. PDF 로드 (PyMuPDF)
2. 청크 분할 (800자)
3. 각 청크에서 C-P-P 추출 (LLM)
4. 임베딩 변환
5. Chroma DB 저장
""""""
각 도구의 공통 구조:
1. 실제 기능 함수
def search_xxx(query):
# API 호출 또는 DB 검색
return results
2. LangChain Tool 래퍼
xxx_tool = Tool(
name="도구명",
description="도구 설명",
func=search_xxx
)
""""""
실행 방법:
python agent.py # 대화형 모드
python agent.py --query "질문" # 단일 질문
python agent.py --verbose # 상세 로그
핵심 로직:
1. LLM + Tools + ReAct Prompt → Agent 생성
2. 사용자 질문 → Agent 실행
3. Agent가 자동으로 도구 선택 & 실행
4. 결과 종합하여 답변
""""""
실행 방법:
streamlit run app.py
기능:
- 채팅 인터페이스
- 대화 기록 유지
- 설정 조절 (온도, verbose)
- 예시 질문 버튼
"""Windows:
- Python 공식 사이트 접속
- "Download Python 3.11.x" 클릭
- 다운로드한 설치 파일 실행
⚠️ 중요: "Add Python to PATH" 체크박스 선택!- "Install Now" 클릭
확인 방법:
# 명령 프롬프트(cmd) 또는 PowerShell 실행
python --version
# 출력: Python 3.11.x
pip --version
# 출력: pip 23.x.xMac:
# Homebrew로 설치
brew install python@3.11
# 확인
python3 --versionLinux (Ubuntu/Debian):
sudo apt update
sudo apt install python3.11 python3-pip
# 확인
python3 --versionWindows:
- Git 공식 사이트 접속
- 다운로드 후 설치
- 기본 설정으로 진행
Mac:
brew install gitLinux:
sudo apt install gitAgenticRAG는 텍스트를 벡터로 변환하는 임베딩 모델로 Ollama의 qwen3-embedding을 사용합니다.
Ollama는 AI 모델을 인터넷 연결 없이 로컬에서 실행하는 도구입니다.
왜 Ollama를 쓰나요?
- API 키 불필요 (완전 무료)
- HuggingFace 방식(
torch,sentence-transformers)에 비해 설치가 훨씬 간단 - 한 번 다운로드 후 오프라인에서도 동작
Windows / Mac:
- https://ollama.com/download 접속
- 운영체제에 맞는 설치 파일 다운로드 후 실행
- 기본 설정으로 설치 완료
Linux:
curl -fsSL https://ollama.com/install.sh | sh임베딩 모델 다운로드 (설치 후 한 번만):
ollama pull qwen3-embedding용량 약 1~2GB, 최초 1회만 다운로드합니다.
설치 확인:
ollama list
# 출력 예시:
# NAME ID SIZE
# qwen3-embedding:latest abc123def456 1.1 GB중요: VectorDB 생성 및 검색 시 Ollama가 실행 중이어야 합니다.
Windows/Mac은 설치 후 백그라운드에서 자동 실행됩니다.
Linux는 별도 터미널에서ollama serve를 먼저 실행하세요.
이 방법은 Git 설치가 필요 없어 가장 쉽습니다.
-
브라우저에서 GitHub 리포지토리 페이지 접속:
https://github.com/JDeun/SKKU_RAG -
페이지 상단의 "Code" 버튼 클릭 (초록색 버튼)
-
드롭다운 메뉴에서 "Download ZIP" 선택
-
ZIP 파일이 다운로드됨 (파일명:
SKKU_RAG-main.zip)
Windows:
- 다운로드 폴더에서
SKKU_RAG-main.zip파일 찾기 - 파일을 마우스 오른쪽 버튼으로 클릭
- "압축 풀기..." 또는 "여기에 압축 풀기" 선택
- 압축 해제된 폴더가 생성됨:
SKKU_RAG-main
Mac:
- 다운로드 폴더에서 ZIP 파일 더블클릭
- 자동으로 압축 해제됨
Linux:
unzip SKKU_RAG-main.zipWindows:
- 파일 탐색기에서 압축 해제된 폴더 열기:
SKKU_RAG-main - 주소 표시줄에 폴더 경로 복사 (예:
C:\Users\YOUR_NAME\Downloads\SKKU_RAG-main) - 명령 프롬프트 실행 (Win + R →
cmd입력) - 다음 명령어 입력:
cd C:\Users\YOUR_NAME\Downloads\SKKU_RAG-main
Mac/Linux:
cd Downloads/SKKU_RAG-maindir # Windows
ls # Mac/Linux출력에 다음 파일들이 보이면 성공:
agent.py app.py config.py prompts.py README.md requirements.txt tools/ .env.example
Git이 설치된 경우 이 방법을 사용하세요.
git clone https://github.com/JDeun/SKKU_RAG.git
cd SKKU_RAG- Python 패키지를 프로젝트별로 분리하는 방법
- 다른 프로젝트와 충돌 방지
- 반드시 사용하세요!
Windows:
# 1. 가상환경 생성
python -m venv venv
# 2. 활성화
venv\Scripts\activate
# 3. 활성화 확인
# 프롬프트 앞에 (venv)가 표시되면 성공
(venv) C:\Users\...\agentRAG>Mac/Linux:
# 1. 가상환경 생성
python3 -m venv venv
# 2. 활성화
source venv/bin/activate
# 3. 활성화 확인
(venv) user@computer:~/agentRAG$❗ 가상환경이 활성화되지 않으면:
# Windows
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# 다시 시도
venv\Scripts\activate# pip 업그레이드
python -m pip install --upgrade pip
# requirements.txt로 한 번에 설치
pip install -r requirements.txt설치 진행 과정:
Collecting langchain==0.3.30
Downloading langchain-0.3.30-py3-none-any.whl (...)
Installing collected packages: ...
Successfully installed langchain-0.3.30 langchain-core-0.3.86 ...
⏱️ 예상 시간: 5-10분 (인터넷 속도에 따라)
아래 버전은 requirements.txt와 동일하게 고정된 버전입니다. 충돌 방지를 위해 반드시 이 버전을 사용하세요.
# LangChain 관련 (버전 고정 필수 — 1.x와 0.3.x 혼용 시 충돌 발생)
pip install "langchain==0.3.30"
pip install "langchain-community==0.3.21"
pip install "langchain-google-genai==2.0.7"
pip install "langchain-core==0.3.86"
pip install "langchain-text-splitters==0.3.11"
pip install "langchain-chroma==0.2.5"
pip install "langchain-ollama==0.3.10"
pip install "langchain-groq==0.2.4"
# Vector Database
pip install "chromadb==1.0.16"
# PDF 처리
pip install "pymupdf==1.25.4"
# 기타
pip install "tiktoken==0.9.0"
pip install "python-dotenv==1.0.1"
pip install "tqdm==4.67.1"
pip install "mp-api==0.45.3"
pip install "habanero==2.3.0"
pip install "streamlit==1.44.0"
pip install "duckduckgo-search==8.0.2"# 설치된 패키지 목록
pip list
# 특정 패키지 확인
pip show langchain# Windows
python -m ensurepip --upgrade
# Mac/Linux
python3 -m ensurepip --upgrade# --user 옵션 추가
pip install --user -r requirements.txt- Visual C++ Redistributable 다운로드
- 설치 후 재시도
임베딩 생성 또는 VectorDB 검색 시 Connection refused 오류가 나면 Ollama가 실행 중이지 않은 것입니다.
Windows/Mac: 시스템 트레이에서 Ollama 아이콘 확인 후 클릭하여 시작
Linux:
ollama serve & # 백그라운드 실행모델이 없는 경우:
ollama list # 설치된 모델 확인
ollama pull qwen3-embedding # 없으면 다시 다운로드- LLM 사용: 텍스트 이해, 생성, C-P-P 추출 등 모든 AI 기능
- 비용: 무료
1단계: Google AI Studio 접속
브라우저에서 https://aistudio.google.com/app/apikey 접속
2단계: Google 계정 로그인
- Gmail 계정으로 로그인
- 계정이 없다면 새로 생성
3단계: API 키 생성
- 페이지 상단의 "Create API Key" 버튼 클릭
- 프로젝트 선택 또는 새 프로젝트 생성
- "Create API key in new project" 선택 (처음이라면)
- API 키가 생성됨 (예:
AIzaSyAbc123...)
4단계: API 키 복사
┌─────────────────────────────────┐
│ Your API Key │
│ AIzaSyAbc123def456ghi789... │
│ [📋 Copy] [👁️ Show] [🗑️ Delete] │
└─────────────────────────────────┘
- 📋 Copy 버튼 클릭하여 복사
⚠️ 중요: 이 키는 다시 볼 수 없으므로 안전한 곳에 보관!
5단계: API 키 저장
# agentRAG 폴더에서
notepad .env # Windows
code .env # VS Code
nano .env # Mac/Linux
# 다음 내용 입력
GOOGLE_API_KEY=AIzaSyAbc123def456ghi789...- API Console에서 확인
- 무료: 월 60회
- 유료: 1M 토큰당 $0.075 (매우 저렴)
- DFT 계산 데이터: 밴드갭, 생성 에너지, 결정 구조
- 비용: 완전 무료
- 데이터: 150,000+ 재료
1단계: 계정 생성
1. https://next-gen.materialsproject.org/ 접속
2. 우측 상단 "Sign Up" 클릭
3. 이메일, 비밀번호 입력
4. 이메일 인증
2단계: API 키 발급
1. 로그인 후 https://next-gen.materialsproject.org/api 접속
2. "Generate New API Key" 버튼 클릭
3. API 키 표시 (예: mp-abc123...)
4. 복사
3단계: API 키 저장
# .env 파일에 추가
MATERIALS_PROJECT_API_KEY=mp-abc123...from mp_api.client import MPRester
with MPRester("mp-abc123...") as mpr:
doc = mpr.materials.summary.get_data_by_id("mp-1234")
print(doc.formula_pretty)- 학술 논문 검색: 최신 연구 동향
- 비용: 완전 무료
- API 키: 불필요! (이메일만)
# .env 파일에 추가
CROSSREF_MAILTO=your.email@example.com왜 이메일?
- API 키 대신 이메일 제공
- 더 빠른 응답 속도
- Crossref가 문제 발생 시 연락 가능
from habanero import Crossref
cr = Crossref(mailto="your.email@example.com")
results = cr.works(query="copper alloy", limit=3)
print(results["message"]["items"][0]["title"])- 웹 검색: 최신 뉴스, 트렌드, 산업 동향
- 비용: 무료 티어 월 1000회 검색
- 장점: DuckDuckGo보다 안정적이고 rate limit이 관대함
1단계: Brave Search API 접속
브라우저에서 https://api.search.brave.com/ 접속
2단계: 계정 생성
- Brave Search API 계정 만들기
- 이메일 인증
3단계: API 키 발급
1. 대시보드 접속
2. "Create API Key" 버튼 클릭
3. API 키 생성 (예: `BSAxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`)
4. 복사하여 안전하게 저장
4단계: API 키 저장
# .env 파일에 추가
BRAVE_API_KEY=BSAxxxxxxxxxxxxxxxxxxxxxxxxxxxxximport requests
api_key = "BSAxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
url = "https://api.search.brave.com/res/v1/web/search"
headers = {"X-Subscription-Token": api_key}
params = {"q": "copper interconnect technology"}
response = requests.get(url, headers=headers, params=params)
print(response.json()["web"]["results"][0]["title"])- Gemini 무료 티어 한도 초과 시 자동 대체: Gemini API가 사용량 제한(Rate Limit)이나 오류를 반환하면, 설정된 Groq API로 자동 전환됩니다.
- 비용: 완전 무료 (무료 티어 제공)
- 모델: OpenAI GPT-OSS 20B (
openai/gpt-oss-20b) - 속도: 매우 빠름 (Groq의 LPU 칩 사용)
1단계: Groq Console 접속
브라우저에서 https://console.groq.com/ 접속
2단계: 계정 생성
- "Sign Up" 클릭
- Google / GitHub 계정으로 소셜 로그인 또는 이메일 가입
- 이메일 인증
3단계: API 키 발급
1. 로그인 후 좌측 메뉴에서 "API Keys" 클릭
2. "Create API Key" 버튼 클릭
3. 키 이름 입력 (예: "agentRAG")
4. 생성된 키 복사 (예: gsk_xxxxxxxxxxxxxxxxxxxx)
⚠️ 이 키는 한 번만 표시됩니다. 반드시 복사해두세요!
4단계: API 키 저장
# .env 파일에 추가
GROQ_API_KEY=gsk_xxxxxxxxxxxxxxxxxxxx| 모델 | 분당 요청 | 일일 토큰 |
|---|---|---|
| openai/gpt-oss-20b | Groq 콘솔에서 확인 | Groq 콘솔에서 확인 |
정확한 무료 티어 한도는 Groq 공식 문서에서 확인하세요.
사용자 질문
↓
Gemini API 호출 시도
├─ 성공 → Gemini 답변 반환
└─ 실패 (한도 초과·오류) → Groq API 자동 재시도
├─ 성공 → GPT-OSS 20B 답변 반환
└─ 실패 → 오류 메시지
참고: Groq API 키를 설정하지 않아도 동작합니다. 단, Gemini가 실패하면 오류가 발생합니다.
# .env 파일 내용
GOOGLE_API_KEY=AIzaSyAbc123def456ghi789...
GROQ_API_KEY=gsk_xxxxxxxxxxxxxxxxxxxx # 선택, Gemini fallback용
MATERIALS_PROJECT_API_KEY=mp-abc123def456...
CROSSREF_MAILTO=your.email@example.com확인 방법:
# Python에서 확인
python
>>> import os
>>> from dotenv import load_dotenv
>>> load_dotenv()
True
>>> os.getenv("GOOGLE_API_KEY")
'AIzaSyAbc123...' # ✅ 출력되면 성공!# 1. 가상환경 활성화
venv\Scripts\activate # Windows
source venv/bin/activate # Mac/Linux
# 2. PDF 준비 및 VectorDB 생성 (최초 1회)
mkdir -p data/pdfs
# 논문 PDF를 data/pdfs/ 폴더에 복사
python vectordb.py # ⚠️ 이 단계는 최초 1회만 실행
# 3. 웹 UI 실행 (VectorDB 생성 후)
streamlit run app.py
# 또는 CLI로 실행
python agent.pyvectordb.py)은 PDF를 처음 추가하거나 변경할 때만 실행하세요.
# Windows
mkdir data
cd data
mkdir pdfs
cd ..
# Mac/Linux
mkdir -p data/pdfs결과:
agentRAG/
└── data/
└── pdfs/
(여기에 PDF 파일 넣기)
권장사항:
- 📄 파일 형식: PDF만 가능
- 📝 언어: 영어 논문 (한국어는 추가 작업 필요)
- 🔓 암호화: 암호화되지 않은 파일
- 📊 페이지 수: 10-50페이지 권장
- 📁 파일 수: 처음에는 2-3개로 테스트
예시:
data/pdfs/
├── Cu_Mg_alloy_2024.pdf
├── electromigration_study.pdf
└── interconnect_materials.pdf
중요: 이 단계는 PDF를 처음 추가하거나 변경할 때만 실행합니다. 한 번 생성된 VectorDB는 계속 재사용됩니다.
python vectordb.py================================================================
VectorDB 구축 시작
================================================================
PDF 파일 또는 폴더 경로를 입력하세요: data/pdfs
📂 3개의 PDF 파일 발견
PDF 처리 중: 100%|████████████| 3/3 [00:15<00:00, 5.2s/it]
✓ Cu_Mg_alloy_2024.pdf: 15 페이지 로드
✓ electromigration_study.pdf: 20 페이지 로드
✓ interconnect_materials.pdf: 18 페이지 로드
✅ 총 53 페이지 로드 완료
📄 문서 분할 중 (청크 크기: 800, 오버랩: 100)...
✅ 127개 청크 생성 → 중복 제거 후 118개
🔬 C-P-P 추출 중 (총 118개 청크)...
C-P-P 추출: 100%|████████████| 24/24 [02:30<00:00, 6.3s/batch]
✅ C-P-P 추출 완료
💾 VectorDB 생성 중 (118개 청크)...
✅ VectorDB 생성 완료: chroma_db/
================================================================
VectorDB 구축 완료
================================================================
| PDF 수 | 페이지 수 | 예상 시간 |
|---|---|---|
| 1개 | 10 페이지 | 2-3분 |
| 3개 | 30 페이지 | 5-7분 |
| 5개 | 50 페이지 | 10-15분 |
느린 단계: C-P-P 추출 (LLM API 호출)
# chroma_db 폴더 생성 확인
ls chroma_db/ # Mac/Linux
dir chroma_db\ # Windows
# 출력 예시:
chroma.sqlite3
ae4f2a8b-1234-5678-9abc-def012345678/
...전제 조건: VectorDB가 생성되어 있어야 합니다 (Step 2 완료).
streamlit run app.py❗ VectorDB가 없다면:
❌ VectorDB 로드 실패: chroma_db 폴더가 없습니다.
먼저 'python vectordb.py'를 실행하세요.
You can now view your Streamlit app in your browser.
Local URL: http://localhost:8501
Network URL: http://192.168.0.100:8501
- 브라우저가 자동으로 열림
- 안 열리면
http://localhost:8501직접 접속
┌────────────────────────────────────────────────────────┐
│ 🔬 AgenticRAG [사이드바 접기] │
│ 재료과학 연구 AI 에이전트 │
├────────────────────────────────────────────────────────┤
│ │
│ 💡 예시 질문 │
│ [Cu-Mg 합금의 저항률은?] [Cu2O의 밴드갭은?] │
│ [electromigration 최신 논문] [Cu 합금 제조 공정] │
│ │
├────────────────────────────────────────────────────────┤
│ 대화 기록 │
│ │
│ 👤 사용자: Cu-Mg 합금의 저항률은? │
│ 🤖 AI: 논문에 따르면 2.0 μΩ·cm입니다... │
│ │
├────────────────────────────────────────────────────────┤
│ 💬 질문을 입력하세요... [전송] │
└────────────────────────────────────────────────────────┘
사이드바:
┌─ 🔬 AgenticRAG ─────────────┐
│ 재료과학 연구 AI 에이전트 │
├─────────────────────────────┤
│ 📚 사용 가능한 도구 │
│ 1. VectorDB Search │
│ 논문에서 C-P-P 데이터 검색│
│ 2. Materials Project ✅ │
│ DFT 계산 데이터 조회 │
│ 3. Crossref ✅ │
│ 최신 논문 검색 │
├─────────────────────────────┤
│ ⚙️ 설정 │
│ Temperature: [==== ] 0.0 │
│ ☐ 상세 로그 │
│ [🔄 대화 초기화] │
├─────────────────────────────┤
│ ℹ️ 정보 │
│ 모델: gemini-2.5-flash │
│ 청크 크기: 800 │
│ 검색 Top-K: 10 │
└─────────────────────────────┘
질문: "Cu-Mg 합금의 저항률은?"
🔍 Agent 사고 과정:
┌─────────────────────────────────────┐
│ Thought: 저항률 = 실험 데이터 │
│ 논문 검색 필요 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Action: vectordb_search │
│ Input: Cu-Mg alloy resistivity │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Observation: │
│ [1] paper1.pdf (p.5) │
│ Composition: Cu, Mg │
│ Property: Resistivity 2.0 μΩ·cm │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Final Answer: │
│ Cu-Mg 합금의 저항률은 350℃ │
│ 어닐링 후 2.0 μΩ·cm입니다. │
│ (출처: paper1.pdf, p.5) │
└─────────────────────────────────────┘
질문: "Cu2O의 밴드갭은?"
Thought: 밴드갭 = 계산 데이터
Materials Project 조회
Action: materials_project
Input: Cu2O
Observation: Band Gap: 2.1 eV
Final Answer:
Materials Project의 DFT 계산에 따르면
Cu2O의 밴드갭은 2.1 eV입니다.
질문: "Cu-Mg 합금에 대한 최신 연구는?"
Thought 1: 기존 논문 + 최신 연구
Action 1: vectordb_search
Observation 1: [기존 논문 3개]
Thought 2: 최신 연구 확인
Action 2: crossref_search
Input: Cu-Mg alloy 2024
Observation 2: [2024년 논문 5개]
Final Answer:
VectorDB에는 Cu-Mg 합금의 저항률과...
최근 2024년 연구로는 Kim et al.의...
전제 조건: VectorDB가 생성되어 있어야 합니다 (Step 2 완료).
python agent.py============================================================
AgenticRAG 챗봇
============================================================
재료과학 연구를 위한 AI 에이전트입니다.
- VectorDB: 논문의 C-P-P 데이터 검색
- Materials Project: DFT 계산 데이터
- Crossref: 최신 논문 검색
종료하려면 'exit', 'quit', 또는 'q'를 입력하세요.
============================================================
🤖 에이전트 초기화 중...
✅ 준비 완료!
💬 질문: Cu-Mg 합금의 저항률은?
🔍 검색 중...
============================================================
📝 답변:
============================================================
Cu-Mg 합금의 저항률은 350℃ 어닐링 후 2.0 μΩ·cm입니다...
============================================================
💬 질문:
# 기본
python agent.py --query "Cu2O의 밴드갭은?"
# 상세 로그 포함
python agent.py --query "Cu2O의 밴드갭은?" --verboseverbose 모드 출력:
============================================================
답변:
============================================================
Cu2O의 밴드갭은 2.1 eV입니다.
============================================================
중간 단계:
============================================================
Action: materials_project
Input: Cu2O
Output: {'material_id': 'mp-361', 'band_gap': 2.137...}
-
RAG
- 📄 Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
- Lewis et al., 2020
- RAG의 기본 개념과 원리
-
ReAct
- 📄 ReAct: Synergizing Reasoning and Acting in Language Models
- Yao et al., 2023
- ReAct 프레임워크의 핵심 논문
-
Chain-of-Thought
- 📄 Chain-of-Thought Prompting Elicits Reasoning in Large Language Models
- Wei et al., 2022
- CoT의 기본 개념
-
LangChain Agent
- 📄 LangChain: Building applications with LLMs through composability
- LangChain 프레임워크
- 🔗 LangChain Documentation
- 🔗 LangChain Agents Guide
- 🔗 Chroma Documentation
- 🔗 Streamlit Documentation
- 🔗 Materials Project API
- 🔗 LangChain GitHub
- 🔗 AutoGPT - Agent 예시
- 🔗 BabyAGI - Agent 예시
Q1: "python을 찾을 수 없습니다"
# PATH 확인
echo %PATH% # Windows
echo $PATH # Mac/Linux
# Python 재설치 (PATH 옵션 체크)Q2: "가상환경이 활성화되지 않습니다"
# Windows - ExecutionPolicy 변경
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# 다시 시도
venv\Scripts\activateQ3: "GOOGLE_API_KEY를 찾을 수 없습니다"
# .env 파일 존재 확인
ls -la .env # Mac/Linux
dir .env # Windows
# 내용 확인
cat .env # Mac/Linux
type .env # Windows
# Python에서 확인
python -c "from dotenv import load_dotenv; import os; load_dotenv(); print(os.getenv('GOOGLE_API_KEY'))"Q4: "VectorDB를 로드할 수 없습니다"
# chroma_db 폴더 확인
ls -la chroma_db/ # Mac/Linux
dir chroma_db\ # Windows
# 없다면 생성 필요
python vectordb.py원인: VectorDB가 생성되지 않았거나 삭제됨
해결: vectordb.py를 실행하여 VectorDB 생성
Q5: "PDF 로드 실패"
- 암호화된 PDF인지 확인
- 파일 경로에 한글/특수문자 없는지 확인
- PDF가 손상되지 않았는지 확인
Q6: "Agent가 답변하지 않습니다"
- API 키 확인
- 인터넷 연결 확인
- verbose 모드로 로그 확인:
python agent.py --query "질문" --verbose
Q7: "Agent가 잘못된 도구를 선택합니다"
- 프롬프트 개선 (prompts.py)
- Temperature 낮추기 (0.0)
- Few-shot 예제 추가
Q8: "Streamlit이 실행되지 않습니다"
# 포트 충돌 확인
streamlit run app.py --server.port 8502
# 캐시 삭제
rm -rf ~/.streamlit/cache # Mac/Linux
rmdir /s %USERPROFILE%\.streamlit\cache # Windows- Issue 등록
- Email: [이메일 주소]
- Fork
- Feature Branch 생성
- Commit
- Pull Request
MIT License - 자유롭게 사용, 수정, 배포 가능
마지막 업데이트: 2026-05-29
제작자: Kevin.Cho
버전: 1.4.0
⭐ 프로젝트가 도움이 되었다면 Star를 눌러주세요!
배경: >= 방식의 버전 범위 지정으로 인해 학생들이 설치 시 서로 다른 버전이 설치되어 충돌 발생. 특히 langchain 1.x와 langchain-core 1.x가 설치되면 langchain-community, langchain-google-genai, langchain-groq 등 나머지 패키지 전체가 요구하는 langchain-core<0.4.0 제약과 충돌해 임포트 오류가 발생.
수정 내용:
requirements.txt— 모든 패키지 버전을>=→==정확한 버전으로 고정 (재현성 보장)requirements.txt—langchain==1.2.8→langchain==0.3.30으로 다운그레이드langchain 1.x는 나머지 생태계 패키지(langchain-community 0.3.x,langchain-google-genai 2.x,langchain-groq 0.2.x)가 요구하는langchain-core<0.4.0과 충돌
requirements.txt—langchain-core==1.3.3→langchain-core==0.3.86으로 다운그레이드0.3.86은 모든 패키지의 버전 제약(<0.4.0,>=0.3.51등)을 동시에 만족하는 마지막 안정 버전
requirements.txt—langchain-text-splitters==0.3.8→==0.3.11로 업그레이드langchain 0.3.30이langchain-text-splitters>=0.3.9를 요구하기 때문
requirements.txt—langchain-ollama==1.0.1→==0.3.10으로 다운그레이드1.0.1은langchain-core 1.x를 요구하므로0.3.x에코시스템과 충돌
호환 버전 검증: 변경 후 pip install -r requirements.txt --dry-run에서 오류 없음 확인, 모든 핵심 임포트(AgentExecutor, create_react_agent, OllamaEmbeddings, ChatGoogleGenerativeAI 등) 정상 동작 확인.
학생 참고: 기존에 이미 설치한 환경이 있다면 새 가상환경을 만들어 설치하는 것을 권장합니다.
python -m venv venv
venv\Scripts\activate # Windows
source venv/bin/activate # Mac/Linux
python -m pip install --upgrade pip
pip install -r requirements.txt배경: HuggingFace 임베딩 방식은 torch, sentence-transformers, transformers 등 무거운 패키지가 필요해 설치 오류가 잦고 시간이 오래 걸렸음. Ollama는 별도 앱으로 설치하며 Python 의존성이 없어 훨씬 간단함.
임베딩 변경 내용:
config.py—EMBEDDING_MODEL_NAME = "qwen3-embedding",OLLAMA_BASE_URL추가;EMBEDDING_DEVICE제거vectordb.py—HuggingFaceEmbeddings→OllamaEmbeddings(model, base_url)로 교체requirements.txt—torch,transformers,sentence-transformers,langchain-huggingface제거;langchain-ollama>=0.2.0추가.env.example—OLLAMA_BASE_URL항목 추가README.md— Ollama 설치 가이드(3단계), 임베딩 코드 예시, 문제해결 섹션 추가
코드 품질 수정 내용:
config.py—input()런타임 호출 제거 (Streamlit 환경 크래시 방지);LLM_STREAMING=False로 변경 (ReAct 파서 안정성);CROSSREF_MAILTO기본값None으로 변경tools/materials_project.py— 빈 입력IndexError방지;None값 포맷팅TypeError방지; 정렬 키None/0.0처리;mpr.summary.search경로 업데이트tools/crossref.py—abstract: None반환 시TypeError방지tools/web_search.py— Brave Search JSON 파싱 실패 시ValueErrorfallback 처리tools/vectordb_search.py— C-P-P 메타데이터None슬라이싱 방지vectordb.py— ChromaDB에None메타데이터 저장 방지; 청크 불변성 보장 (새Document생성);langchain_chroma임포트 경로 업데이트agent.py—max_execution_time=config.AGENT_TIMEOUT적용 (미사용 설정 버그 수정)app.py— temperature 슬라이더 변경 시 에이전트 자동 재생성
설정 방법: Ollama 설치 후 ollama pull qwen3-embedding 실행, 기존 chroma_db 폴더 삭제 후 VectorDB 재생성 필요.
배경: 무료 티어를 사용하는 학생들이 Gemini API 사용량 한도에 걸리는 문제 발생.
수정 내용:
requirements.txt—langchain-groq>=0.2.0추가config.py—GROQ_API_KEY,GROQ_MODEL_NAME설정 추가agent.py—_build_llm()헬퍼 추가:gemini.with_fallbacks([groq])체인 구성vectordb.py— C-P-P 추출 LLM에도 동일한 fallback 적용app.py— 사이드바에 Groq fallback 활성화 여부 표시.env.example—GROQ_API_KEY항목 및 설명 추가README.md— Groq API 발급 가이드 섹션 추가
설정 방법: .env 파일에 GROQ_API_KEY=gsk_... 추가하면 즉시 적용됩니다.
문제: langchain==0.1.20을 사용하면 최신 langchain-core와 충돌하여 ImportError 발생.
langchain_classic으로 임시 우회하는 것은 근본적인 해결책이 아님.
수정 내용:
requirements.txt— 버전 핀을 최신 호환 범위(>=)로 전면 교체langchain==0.1.20→langchain>=0.3.7langchain-community==0.0.38→langchain-community>=0.3.7langchain-google-genai==1.0.3→langchain-google-genai>=2.0.7langchain-core,langchain-text-splitters,pydantic>=2.0.0명시 추가
prompts.py—from pydantic.v1 import BaseModel, Field→from pydantic import BaseModel, Field
(pydantic v2/v3에서.v1호환 shim이 없어지는 문제 대응)tools/*.py4개 파일 —from langchain.tools import Tool→from langchain_core.tools import Tool
(deprecated 경로 → 현재 권장 경로)agent.py—from langchain.prompts import PromptTemplate→from langchain_core.prompts import PromptTemplate
의존성 충돌 경고가 표시되면: 가상환경을 새로 만들고 아래 빠른 설치 가이드를 따르세요.
# 가상환경 새로 생성 (기존 환경의 충돌 완전 초기화)
python -m venv venv
# 활성화
venv\Scripts\activate # Windows
source venv/bin/activate # Mac/Linux
# pip 최신화 후 설치
python -m pip install --upgrade pip
pip install -r requirements.txt문제: tool 2개를 호출한 후 LLM이 Final Answer를 생성할 때
"Invalid Format: Missing 'Action:' after 'Thought:'" 오류가 3~5회 반복되는 현상.
원인:
- LangChain의 ReAct 파서는
Thought:다음에Action:또는Final Answer:가
공백 줄 없이 바로 이어져야 정상 파싱됨. - 기존 프롬프트 예시가
Observation:다음에Thought:없이Final Answer:로 바로 점프하여
LLM이 잘못된 패턴을 학습. handle_parsing_errors=True만 설정하면 오류 메시지가 불명확하여 LLM이 같은 실수를 반복.
수정 내용:
prompts.py→REACT_SYSTEM_PROMPT- "CRITICAL RULES" 섹션 추가: Thought 다음에 반드시 Action 또는 Final Answer만 허용
- 공백 줄 금지,
Observation:직접 작성 금지 등 명시 - 예시에서
Final Answer:직전에Thought:추가 (올바른 ReAct 패턴)
agent.py→AgentExecutor의handle_parsing_errors를 구체적인 재시도 지침 문자열로 변경
ReAct 올바른 형식:
Thought: 필요한 정보를 모두 수집했다. 이제 답변할 수 있다.
Final Answer: [최종 답변]
문제: "상세 로그" 체크박스를 켜도 가장 최근 응답의 사고 과정만 볼 수 있었음.
이전 질문/답변의 사고 과정(tool 호출 내역)을 다시 볼 수 없었음.
수정 내용 (app.py):
run_agent()호출 시return_steps=True로 고정 (verbose 여부와 관계없이 항상 steps 수집)- 각 어시스턴트 메시지를 저장할 때
"steps"키로 중간 단계 함께 저장:st.session_state.messages.append({ "role": "assistant", "content": response, "steps": steps # 추가 })
- 대화 기록 표시 루프에서
verbose=True이고steps가 있는 어시스턴트 메시지마다
"🔍 사고 과정 보기" expander 표시:for idx, message in enumerate(st.session_state.messages): with st.chat_message(message["role"]): if verbose and message["role"] == "assistant" and message.get("steps"): with st.expander("🔍 사고 과정 보기"): ... st.markdown(message["content"])
사용 방법: 사이드바의 "상세 로그" 체크박스를 켜면 이전 모든 답변의 사고 과정을 함께 볼 수 있습니다.