κ΅λ΄μΈ ETF μ 보 κΈ°λ° RAG(Retrieval-Augmented Generation) μμ€ν
μ₯κΈ°ν¬μλ₯Ό μν μ ννκ³ μ΅μ μ ETF μ 보λ₯Ό RAG λ°©μμΌλ‘ μ 곡νλ AI κΈ°λ° μ§μμλ΅ μμ€ν μ λλ€.
- μ£Όμ κΈ°λ₯
- μμ€ν μν€ν μ²
- μ€μΉ λ°©λ²
- νκ²½ μ€μ
- μ¬μ© λ°©λ²
- API λ¬Έμ
- λ°μ΄ν° μμ€
- νλ‘μ νΈ κ΅¬μ‘°
- κ°λ° λ‘λλ§΅
π Hugging Face Spaces: https://huggingface.co/spaces/Yugwon/etf-rag-agent
- RAG κΈ°λ° μ§μμλ΅: μ΅μ ETF μ 보λ₯Ό λ°νμΌλ‘ μ νν λ΅λ³ μ 곡
- π₯οΈ Gradio μΉ UI: μ±ν μΈν°νμ΄μ€λ‘ μ½κ² ETF μ 보 μ§μ (λ‘컬 & ν΄λΌμ°λ)
- λ©ν°μμ€ λ°μ΄ν° μμ§:
- π°π· κ΅λ΄ ETF (λ€μ΄λ² κΈμ΅)
- πΊπΈ ν΄μΈ ETF (yfinance)
- π 곡μ λ¬Έμ (DART API)
- μμ λ¬΄λ£ μ΄μ κ°λ₯: λ‘컬 μλ² λ© λͺ¨λΈλ‘ OpenAI API μμ΄ μ¬μ© κ°λ₯
- LLM μ ν μ΅μ : Ollama (qwen2.5:3b) λλ OpenAI GPT (μ ν)
- μλ μ€μΌμ€λ§: λ§€μΌ μλμΌλ‘ μ΅μ ETF μ 보 μμ§
- λ²‘ν° DB κ΄λ¦¬: μ€λ³΅ μ κ±° λ° λ²μ κ΄λ¦¬λ‘ ν¨μ¨μ μ μ₯
- βοΈ ν΄λΌμ°λ λ°°ν¬: Hugging Face Spaces λ¬΄λ£ λ°°ν¬ μ§μ
- Backend: FastAPI, gRPC (ConnectRPC)
- Frontend: Gradio (Python μΉ UI)
- Vector DB: Weaviate
- LLM: Ollama (qwen2.5:3b) / OpenAI GPT-4
- Embedding: sentence-transformers (all-MiniLM-L6-v2)
- Crawling: BeautifulSoup4, yfinance, DART API
- Scheduler: APScheduler
- Deployment: Hugging Face Spaces + GitHub Actions
ββββββββββββββββββββββββββββββββββββββββββββββ
β User Interface β
β (REST API / gRPC Client) β
ββββββββββββββββββββββ¬ββββββββββββββββββββββββ
β
ββββββββββββββ΄βββββββββββββ
β β
βββββββββΌβββββββββ βββββββββΌβββββββββ
β FastAPI REST β β gRPC Server β
β Server β β (ConnectRPC) β
βββββββββ¬βββββββββ βββββββββ¬βββββββββ
β β
ββββββββββ¬ββββββββββββββββ
β
ββββββββββΌβββββββββ
β RAG Handler β
β (Query Engine) β
ββββββββββ¬βββββββββ
β
ββββββββββΌβββββββββββββββββββββ
β β
βββββββββΌβββββββββ βββββββββββΌβββββββββ
β Weaviate β β LLM Model β
β Vector Store β β (OpenAI/Local) β
βββββββββ²βββββββββ ββββββββββββββββββββ
β
β
βββββββββ΄ββββββββββββββββββββββββββββββββββββββ
β Data Collectors β
ββββββββββββββββ¬βββββββββββββββ¬ββββββββββββββββ€
β Naver Crawlerβ yfinance API β DART API β
β (κ΅λ΄ ETF) β (ν΄μΈ ETF) β (곡μλ¬Έμ) β
ββββββββββββββββ΄βββββββββββββββ΄ββββββββββββββββ
β²
β
βββββββββ΄βββββββββ
β Scheduler β
β (Daily Cron) β
ββββββββββββββββββ
- Python 3.10 μ΄μ
- Docker (Weaviate μ€νμ©)
- Git
git clone https://github.com/YugwonWon/etf-rag-agent.git
cd etf-rag-agentpython -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activatepip install -r requirements.txt# νλ‘μ νΈ λλ ν 리μμ μ€ν
docker run -d --name weaviate \
-p 8081:8080 \
-p 50051:50051 \
-e QUERY_DEFAULTS_LIMIT=25 \
-e AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true \
-e PERSISTENCE_DATA_PATH='/var/lib/weaviate' \
-e DEFAULT_VECTORIZER_MODULE='none' \
-e ENABLE_MODULES='' \
-e CLUSTER_HOSTNAME='node1' \
-v $(pwd)/data/weaviate:/var/lib/weaviate \
semitechnologies/weaviate:1.32.13πΎ λ°μ΄ν° μ μ₯ μμΉ:
./data/weaviate/(νλ‘μ νΈ λ΄ λ‘컬 λλ ν 리)
cp .env.example .envκΈ°λ³Έ μ€μ :
# LLM Provider μ ν
LLM_PROVIDER=local # λ‘컬 λͺ¨λΈ μ¬μ©
# Weaviate μ€μ
WEAVIATE_URL=http://localhost:8081
# μ€μΌμ€λ¬ μ€μ
ENABLE_SCHEDULER=true
CRAWL_TIME_HOUR=9
CRAWL_TIME_MINUTE=0μ ν μ¬ν:
# OpenAI API Key (μ λ£, LLM_PROVIDER=openai μ¬μ© μμλ§)
# OPENAI_API_KEY=your-openai-api-key-here
# DART API Key
# DART_API_KEY=your-dart-api-key-hereDART 곡μ λ¬Έμλ₯Ό μμ§νλ €λ©΄:
- DART μ€ν API μ μ
- νμκ°μ λ° μΈμ¦ν€ λ°κΈ
.envνμΌμ ν€ μ λ ₯
# λμλ§ νμΈ
./server.sh --help
# μλ² μμ
./server.sh start
# μλ² μν νμΈ
./server.sh status
# λ‘κ·Έ μ€μκ° νμΈ
./server.sh logs
# λ‘κ·Έ λ§μ§λ§ 50μ€ νμΈ
./server.sh logs -n 50
# μλ² μ€μ§
./server.sh stop
# μλ² μ¬μμ
./server.sh restart
# ν¬νΈ λ³κ²½ (κΈ°λ³Έ: 8000)
./server.sh start --port 8080# Gradio μμ‘΄μ± μ€μΉ
pip install gradio
# Gradio UI μ€ν
python gradio_app.py
# λΈλΌμ°μ μμ μ μ
# http://localhost:7860Gradio UI μ£Όμ κΈ°λ₯:
- π¬ μ±ν μΈν°νμ΄μ€λ‘ ETF μ§μ
- π μ€μκ° ν΅κ³ νμΈ
- π μ°Έκ³ λ¬Έμ μΆμ² νμ
- π κ²μ λ¬Έμ μ μ‘°μ (top_k)
# λμλ§
python cli.py --help
# μλ² μν νμΈ
python cli.py health
# ETF μ§μ (κΈ°λ³Έ)
python cli.py query "KODEX 200 ETFμ λν΄ μ€λͺ
ν΄μ€"
# μμΈ μ 보 ν¬ν¨
python cli.py query "λ―Έκ΅ S&P 500 ETF μΆμ²ν΄μ€" --top-k 5 --verbose
# ν΅κ³ μ 보 μ‘°ν
python cli.py stats
# λ°μ΄ν° μμ§ μ€ν
python cli.py collectpython -m grpc_tools.protoc \
-I./protos \
--python_out=./protos/__generated__ \
--grpc_python_out=./protos/__generated__ \
./protos/etf_query.protoFastAPI REST μλ²:
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000μλ² μ€ν ν: http://localhost:8000/docs
gRPC μλ² (μ ν):
python -m app.connect_rpcλ°©λ² 1: CLI μ¬μ© (κΆμ₯)
python cli.py collectλ°©λ² 2: API νΈμΆ
curl -X POST "http://localhost:8000/api/collect"λ°©λ² 3: Python μ€ν¬λ¦½νΈ
from app.crawler.collector import ETFDataCollector
from app.vector_store.weaviate_handler import WeaviateHandler
handler = WeaviateHandler()
collector = ETFDataCollector(vector_handler=handler)
results = collector.collect_all(insert_to_db=True)
print(f'Total collected: {results["total"]}')CLI μ¬μ© (κΆμ₯):
python cli.py query "KODEX 200 ETFμ μ₯λ¨μ μ?" --verboseREST API:
curl -X POST "http://localhost:8000/api/query" \
-H "Content-Type: application/json" \
-d '{
"question": "KODEX λ―Έκ΅S&P500 ETFμ λν΄ μλ €μ€",
"top_k": 3
}'Python ν΄λΌμ΄μΈνΈ:
from app.retriever.query_handler import RAGQueryHandler
handler = RAGQueryHandler()
response = handler.query(
question="TIGER κΈμ λ¬Ό ETFμ μ΄ λ³΄μλ μΌλ§μΈκ°μ?"
)
print(f"λ΅λ³: {response['answer']}")
print(f"μ°Έκ³ λ¬Έμ: {response['num_sources']}κ°")POST /api/query
Content-Type: application/json
{
"question": "string",
"model_type": "openai", // or "local"
"etf_type": "domestic", // optional: "domestic", "foreign"
"top_k": 5,
"temperature": 0.7
}GET /api/etf/{etf_code}POST /api/collection/trigger
Content-Type: application/json
{
"domestic": true,
"foreign": true,
"dart": true,
"domestic_max": 100 // optional
}GET /api/healthGET /api/collection/statusνλ‘ν μ μ: protos/etf_query.proto
Services:
AskQuestion: μ§μμλ΅GetETFSummary: ETF μμ½ μ 보TriggerCollection: λ°μ΄ν° μμ§ νΈλ¦¬κ±°HealthCheck: ν¬μ€ 체ν¬
- μμ€: https://finance.naver.com/sise/etf.naver
- μμ§ νλͺ©: ETFλͺ , μ½λ, κ°κ²©, NAV, μ€λͺ , λΆλ₯ λ±
- μμ€: Yahoo Finance API
- λμ: μ£Όμ λ―Έκ΅ ETF (SPY, QQQ, ARKK λ±)
- μμ§ νλͺ©: κ°κ²©, 보μμ¨, μμ°κ·λͺ¨, λ°°λΉμμ΅λ₯ λ±
- μμ€: https://opendart.fss.or.kr/
- μμ§ νλͺ©: ETF ν¬μμ€λͺ μ, μ΄μ©λ³΄κ³ μ λ±
etf-rag-agent/
βββ app/
β βββ __init__.py
β βββ main.py # FastAPI REST μλ²
β βββ connect_rpc.py # gRPC μλ²
β βββ config.py # νκ²½μ€μ
β βββ scheduler.py # μ€μΌμ€λ¬
β βββ model/
β β βββ __init__.py
β β βββ openai_model.py # OpenAI λͺ¨λΈ νΈλ€λ¬
β β βββ local_model.py # λ‘컬 LLM νΈλ€λ¬
β β βββ model_factory.py # λͺ¨λΈ ν©ν 리
β βββ crawler/
β β βββ __init__.py
β β βββ naver_kr.py # λ€μ΄λ² ν¬λ‘€λ¬
β β βββ yfinance_us.py # yfinance ν¬λ‘€λ¬
β β βββ dart_api.py # DART API ν¬λ‘€λ¬
β β βββ collector.py # ν΅ν© μμ§κΈ°
β βββ vector_store/
β β βββ __init__.py
β β βββ weaviate_handler.py # Weaviate νΈλ€λ¬
β βββ retriever/
β βββ __init__.py
β βββ query_handler.py # RAG 쿼리 νΈλ€λ¬
βββ protos/
β βββ __init__.py
β βββ etf_query.proto # gRPC proto μ μ
β βββ __generated__/ # μμ±λ proto μ½λ
βββ data/
β βββ raw/ # μμ λ°μ΄ν°
β βββ metadata.json # λ©νλ°μ΄ν°
βββ .env.example # νκ²½λ³μ μμ
βββ .gitignore
βββ requirements.txt
βββ LICENSE
βββ README.md
# Config ν
μ€νΈ
python -m app.config
# λ€μ΄λ² ν¬λ‘€λ¬ ν
μ€νΈ
python -m app.crawler.naver_kr
# yfinance ν¬λ‘€λ¬ ν
μ€νΈ
python -m app.crawler.yfinance_us
# Weaviate νΈλ€λ¬ ν
μ€νΈ
python -m app.vector_store.weaviate_handler
# RAG 쿼리 νΈλ€λ¬ ν
μ€νΈ
python -m app.retriever.query_handler# μλ² λ‘κ·Έ
./server.sh logs
# Gradio λ‘κ·Έ
tail -f gradio.log
# μ ν리μΌμ΄μ
λ‘κ·Έ
tail -f logs/etf-rag-agent.logGradio UIλ₯Ό Hugging Face Spacesμ 무λ£λ‘ λ°°ν¬ν μ μμ΅λλ€!
λ°°ν¬ λ¨κ³:
- Hugging Face κ³μ μμ± (κ°μ νκΈ°)
- Access Token μμ± (Write κΆν)
- GitHub Secrets μ€μ :
HF_TOKEN: Hugging Face Access TokenHF_SPACE: Space ID (μ:username/etf-rag-agent)
- μ½λ νΈμ:
git add spaces/ git commit -m "Deploy to Hugging Face Spaces" git push origin main - μλ λ°°ν¬: GitHub Actionsκ° μλμΌλ‘ λ°°ν¬ μ€ν
μμΈ κ°μ΄λ: DEPLOYMENT.md μ°Έμ‘°
λ°λͺ¨ URL μμ: https://huggingface.co/spaces/[username]/etf-rag-agent
λ¬΄λ£ μ΅μ :
- β Hugging Face Spaces (CPU basic)
- β Railway/Render/Fly.io λ¬΄λ£ νλ
- β GitHub Actions (μ 2,000λΆ λ¬΄λ£)
κΈ°μ¬λ μΈμ λ νμν©λλ€!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
μ΄ νλ‘μ νΈλ MIT λΌμ΄μ μ€ νμ λ°°ν¬λ©λλ€. μμΈν λ΄μ©μ LICENSE νμΌμ μ°Έμ‘°νμΈμ.
Yugwon Won
- GitHub: @YugwonWon
μ΄ νλ‘μ νΈλ λ€μ μ€νμμ€ νλ‘μ νΈλ€μ μ¬μ©ν©λλ€:
νλ‘μ νΈμ λν μ§λ¬Έμ΄λ μ μμ¬νμ΄ μμΌμλ©΄ μ΄μλ₯Ό μμ±ν΄μ£ΌμΈμ.
β μ΄ νλ‘μ νΈκ° μ μ©νλ€λ©΄ Starλ₯Ό λλ¬μ£ΌμΈμ! ETF-focused RAG system with daily crawling, vectorized document storage (Weaviate), OpenAI/sLLM support, and flexible server deployment with REST & ConnectRPC APIs. Ideal for long-term investment Q&A services.