λ³Έ νλ‘μ νΈλ ν¬κ² λ°μ΄ν° νμ΄νλΌμΈ(Airflow) κ³Ό API μλ²(FastAPI) λ‘ κ΅¬μ±λμ΄ μμ΅λλ€.
AI μλ²λ μ¬μ©μμ λ§μ΄λ°μ΄ν°μ κΈμ΅ μ±ν₯μ μ μ²λ¦¬ λ° λ²‘ν°ννμ¬, **RAG(Retrieval-Augmented Generation)**λ₯Ό νμ©νμ¬ κ°μΈ λ§μΆ€ν κΈμ΅ μν(μκΈ, μ κΈ, νλ, μ°κΈ)μ μΆμ²νκ³ μλ΄ν΄μ£Όλ μλ²μ λλ€.
νΉν Airflowλ₯Ό νμ©νμ¬ κΈμ΅μμν, κΈμ΅κ°λ μ λ± λ€μν 곡곡 λ°μ΄ν° OpenAPIλ‘λΆν° μ΅μ κΈμ΅ μν μ 보λ₯Ό μ£ΌκΈ°μ μΌλ‘ μμ§νκ³ μ μ²λ¦¬νμ¬ λ°μ΄ν°μ **μ μ λ(Freshness)**λ₯Ό μ μ§ν©λλ€. μμ§λ λ°μ΄ν°μ λ²‘ν° μλ² λ©μ Cloud NoSQL DBμΈ MongoDB Atlasμ μ μ¬λμ΄, κ³ μ±λ₯μ νμ΄λΈλ¦¬λ κ²μ(Hybrid Search)μ μ§μν©λλ€.
- κ°μΈ λ§μΆ€ν μν μΆμ²: μ¬μ©μμ μμ° νν©, ν¬μ μ±ν₯, μ°λ Ήλ λ±μ λΆμνμ¬ μ΅μ μ κΈμ΅ μν μΆμ².
- μ§λ₯ν μ±λ΄ μλ΄: κΈμ΅ μνμ λν μ§μμλ΅μ μ€μκ° μ€νΈλ¦¬λ°μΌλ‘ μ 곡.
- λ°μ΄ν° μ΅μ ν (ETL): κΈμ΅μμν λ° κ° κΈμ΅μ¬ λ°μ΄ν°λ₯Ό μ£ΌκΈ°μ μΌλ‘ μμ§, μ μ²λ¦¬, μλ² λ©νμ¬ λ²‘ν° DBμ μ μ¬.
- μ μ νλ₯΄μλ 벑ν°ν: μ¬μ©μ λ°μ΄ν°λ₯Ό ν μ€νΈν λ° λ²‘ν°ννμ¬ μ μ¬λ κ²μμ νμ©.
- Framework: FastAPI
- Language: Python 3.10+
- AI & LLM:
- LangChain (Core, Community, Google GenAI)
- Google Gemini Pro (Generative AI)
- LangGraph (Agent Workflow)
- Database:
- MongoDB Atlas (Vector Store & Data Lake)
- MySQL (User Data - Read Only)
- Deployment: Docker, Uvicorn
- Orchestrator: Apache Airflow 3.1.1
- Executor: Celery Executor
- Message Broker: Redis
- Metadata DB: PostgreSQL
- Key Libraries:
pandas,requests(Data Processing)pymongo(DB Interaction)langchain-google-genai(Embedding Generation)
main-project-ai/
βββ airflow/ # Airflow κ΄λ ¨ μ€μ λ° DAG
β βββ dags/ # λ°μ΄ν° νμ΄νλΌμΈ (ETL) μ μ
β βββ config/ # Airflow μ€μ νμΌ
β βββ Dockerfile # Airflow 컀μ€ν
μ΄λ―Έμ§ λΉλ μ€μ
β βββ requirements.txt # Airflow μμ‘΄μ± ν¨ν€μ§
βββ server/ # AI API μλ²
β βββ app/ # FastAPI μ ν리μΌμ΄μ
μ½λ
β β βββ api/ # API λΌμ°ν° (v1)
β β βββ core/ # μ€μ (Config) λ° κ³΅ν΅ λͺ¨λ
β β βββ services/ # λΉμ¦λμ€ λ‘μ§ (RAG, Chatbot λ±)
β β βββ main.py # μ± μ§μ
μ
β βββ Dockerfile # Server μ΄λ―Έμ§ λΉλ μ€μ
β βββ requirements.txt # Server μμ‘΄μ± ν¨ν€μ§
βββ docker-compose.local.yml # λ‘컬 κ°λ° νκ²½ μ€ν μ€μ (Airflow + Server + Mongo)
βββ scripts/ # μ νΈλ¦¬ν° μ€ν¬λ¦½νΈμ£Όμ API μλν¬μΈνΈλ λ€μκ³Ό κ°μ΅λλ€. μμΈ λͺ
μΈλ μλ² μ€ν ν /docsμμ νμΈν μ μμ΅λλ€.
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
μλ² μν λ° λ²μ μ 보 νμΈ |
GET |
/api/v1/recommendations/{user_id} |
νΉμ μ μ λ₯Ό μν λ§μΆ€ν κΈμ΅ μν μΆμ² |
POST |
/api/v1/chat/stream |
μ±λ΄κ³Όμ λν (μ€νΈλ¦¬λ° μλ΅) |
POST |
/api/v1/chat/feedback |
μΆμ²/μλ΄ κ²°κ³Όμ λν νΌλλ°± μ μ₯ |
POST |
/api/v1/users/{user_id}/vectorize |
μ μ λ°μ΄ν° 벑ν°ν λ° κ°±μ (νΈλ¦¬κ±°) |
κ΄λ¦¬μ νμ΄μ§λ₯Ό μν ν΅κ³ λ° λ‘κ·Έ μ‘°ν APIμ λλ€.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1/admin/stats/overview |
λμ보λ μ 체 ν΅κ³ μ‘°ν |
GET |
/api/v1/admin/stats/trends |
λν λ° API μμ² μΆμ΄ μ‘°ν |
GET |
/api/v1/admin/stats/feedback |
νΌλλ°± λΆν¬ μ‘°ν |
GET |
/api/v1/admin/users |
μ¬μ©μλ³ AI μ¬μ© ν΅κ³ μ‘°ν |
GET |
/api/v1/admin/logs |
μ±λ΄ λν λ‘κ·Έ λͺ©λ‘ μ‘°ν |
GET |
/api/v1/admin/logs/{user_id} |
νΉμ μ¬μ©μμ λν μμΈ λ΄μ μ‘°ν |
- Production URL: https://knowwhohow.cloud/ai
- Dev/Client URL: https://knowwhohow.site
- Docker λ° Docker Compose μ€μΉ
.envνμΌ μ€μ (κ° λλ ν 리μ.env.exampleμ°Έκ³ )
# airflow 루νΈμμ μ€ν
docker-compose up -d --buildcd server
pip install -r requirements.txt
python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000- API Server: http://localhost:8000 μ μ μ μλ² μν νμΈ κ°λ₯.
- API Docs: http://localhost:8000/docs μμ Swagger UI νμΈ.
- Airflow Webserver: http://localhost:8080 μ μ (ID/PW:
airflow/airflowμ€μ μ).
κ°μ₯ λ¨Όμ , RDBMSμ ν©μ΄μ Έ μλ μ¬μ©μ λ°μ΄ν°λ₯Ό μ‘°ννμ¬ AIκ° μ΄ν΄ κ°λ₯ν **"ν μ€νΈ μμ¬(Persona)"**λ‘ λ³νν©λλ€.
UserVectorizationServiceλ SQLAlchemyλ₯Ό μ¬μ©νμ¬ MySQLμμ λ€μ λ°μ΄ν°λ₯Ό μ§μ 쿼리ν©λλ€.
- Users ν μ΄λΈ: λμ΄, μ±λ³, ν¬μ μ±ν₯
- Assets ν μ΄λΈ: μμ° κ·λͺ¨ λ° ν¬νΈν΄λ¦¬μ€(μμ κΈ, λΆλμ° λ±) λΆν¬
- UserInfo ν μ΄λΈ: μ° μλ, μν΄ λͺ©ν, ν¬λ§ μνλΉ
- Keyword ν μ΄λΈ: μ¬μ©μκ° μ νν κ΄μ¬ ν€μλ
μ‘°νλ λ°μ΄ν°λ₯Ό μμ°μ΄ ν μ€νΈλ‘ ν©μΉ ν μλ² λ©ν©λλ€.
- Model:
Google text-embedding-004(vialangchain-google-genai) - Process: ν μ€νΈ -> 768μ°¨μ(μμ) λ²‘ν° λ³ν
- Storage: κ²°κ³Ό 벑ν°λ MongoDB
user_vectors컬λ μ μ μ μ₯λ©λλ€.
κΈμ΅μν λ°μ΄ν° μμ AIκ° κ²μν μ μλλ‘ λ²‘ν°νλμ΄ μ€λΉλ©λλ€.
λ§€μΌ μλ²½, Airflow DAGκ° κΈμ΅κΈ°κ΄(κΈκ°μ, κΈμ΅μ, KVIC) APIλ₯Ό νΈμΆν©λλ€.
- Extract: JSON λ°μ΄ν° μμ§
- Transform: μν νΉμ§, μ°λ 쑰건 λ±μ
rag_textνλλ‘ κ°κ³΅ - Embed: λμΌν Gemini Embedding λͺ¨λΈμ μ¬μ©νμ¬ λ²‘ν° μμ±
- Load: MongoDBμ μ μ¬ (API μλ²μ 곡μ νλ DB)
μ¬μ©μκ° μ§λ¬Έμ νλ©΄, μμ€ν μ μ¬μ©μ 벑ν°μ μν 벑ν°λ₯Ό λΉκ΅νμ¬ κ°μ₯ μ ν©ν μνμ μ°Ύμλ λλ€.
- μ¬μ©μμ μ§λ¬Έ + νλ₯΄μλλ₯Ό κ²°ν©νμ¬ μΏΌλ¦¬ 벑ν°λ₯Ό μμ±ν©λλ€.
- MongoDB Atlasμ
vectorSearchκΈ°λ₯μ μ¬μ©νμ¬ μ½μ¬μΈ μ μ¬λ(Cosine Similarity)κ° λμ μμ μνμ κ²μν©λλ€.- ν¨κ³Ό: λ¨μ ν€μλ λ§€μΉμ΄ μλ, "λ§₯λ½"μ λ§λ μν κ²μ κ°λ₯
κ²μλ ν보 μνλ€κ³Ό μ¬μ©μ νλ₯΄μλλ₯Ό LangGraph κΈ°λ°μ Agentμκ² μ λ¬ν©λλ€.
- Prompt: "λΉμ μ κΈμ΅ μ λ¬Έκ°μ λλ€. [μ¬μ©μ νλ₯΄μλ]μ [κ²μλ μν]μ λ³΄κ³ μ΅μ μ μνμ μΆμ²ν΄μ£ΌμΈμ."
- Output: μΆμ² μ¬μ κ° λ΄κΈ΄ μ ννλ JSON λ°μ΄ν°
μΆμ²μ μΌλ°©μ μ΄μ§ μκ³ μ¬μ©μμ λ°μμ λ°λΌ κ³μ μ§νν©λλ€.
μ¬μ©μκ° μ±λ΄μμ λ¨κΈ΄ 'μ’μμ/μ«μ΄μ' λ‘κ·Έλ MongoDB chat_logsμ μ μ₯λ©λλ€.
- Logic:
ProductsServiceμμ μΆμ² κ²°κ³Όλ₯Ό λ°ννκΈ° μ , MongoDB λ‘κ·Έλ₯Ό μ‘°ννμ¬ Dislike μνμ νν°λ§νκ³ Like μνμ κ°μ€μΉλ₯Ό λΆμ¬ν΄ μ¬μ λ ¬ν©λλ€.
graph TD
subgraph "Data Source"
MySQL[(MySQL DB)] -->|SQLAlchemy| Service[User Service]
end
subgraph "User Processing"
Service -->|Data Aggregation| Persona[νλ₯΄μλ ν
μ€νΈ]
Persona -->|Gemini Embedding| UserVec[μ¬μ©μ 벑ν°]
UserVec -->|Store| Mongo[(MongoDB)]
end
subgraph "RAG System"
Query[μ¬μ©μ μ§λ¬Έ] --> Chat[Chat Service]
Chat -->|Context| UserVec
UserVec -->|Vector Search| Atlas[MongoDB Atlas Search]
Atlas -->|Candidates| Candidates[ν보 μνκ΅°]
Candidates -->|LangGraph| Final[μ΅μ’
μΆμ²]
end