# 2. Kiến trúc & Luồng dữ liệu (Data Flow)

## Architecture Diagram

```mermaid
graph TD
    User[User Feedback] --> API[POST /autotrain/feedback]
    API --> DB[feedbacks Table]
    DB --> Worker[Background Worker]
    Worker --> AI[Mock AI Analyzer]
    AI --> Rule[generated_rules Table]
    Rule --> Admin[Admin Review UI]
    Admin --> Decision{Approve?}
    Decision -->|Yes| Deploy[Deploy Endpoint]
    Decision -->|No| Reject[Reject]
    Deploy --> Log[deployments Table]
    Log --> Production[Production Rules]
    Production --> Chatbot[Next Query Uses Rule]
    
    style User fill:#ef4444
    style API fill:#3b82f6
    style DB fill:#64748b
    style Worker fill:#0ea5e9
    style AI fill:#8b5cf6
    style Rule fill:#64748b
    style Admin fill:#f59e0b
    style Deploy fill:#10b981
    style Production fill:#059669
```

---

## Detailed Flow

### Step 1: User Submits Feedback

User sends feedback via API or through chatbot interface:

```json
POST /autotrain/feedback
{
  "query": "tìm áo khoác cho trời 35 độ",
  "ai_response": "Mình gợi ý áo khoác nỉ này...",
  "feedback_text": "Trời nóng quá, AI còn tư vấn áo khoác",
  "feedback_type": "bug",
  "user_id": "CNF-98453"
}
```

**API Processing:**
1. Generate `feedback_id` (FB-XXXX format)
2. Insert into `feedbacks` table with status `pending`
3. Return `FeedbackOut` with ID

**File:** `backend/api/autotrain/autotrain_route.py:229-282`

---

### Step 2: Background Worker

Triggered manually via `/autotrain/run-all` or scheduled:

```python
# Get all pending feedbacks
cur.execute("SELECT feedback_id FROM feedbacks WHERE status = 'pending'")
pending = [row['feedback_id'] for row in cur.fetchall()]

# Process each
for fb_id in pending:
    await generate_rule(fb_id)
```

**File:** `backend/api/autotrain/autotrain_route.py:544-571`

---

### Step 3: AI Rule Generation

The `generate_rule()` function:

```python
async def generate_rule(feedback_id: str):
    # 1. Fetch feedback
    cur.execute("SELECT * FROM feedbacks WHERE feedback_id = ?", (feedback_id,))
    fb = cur.fetchone()
    
    # 2. Update status to processing
    cur.execute("UPDATE feedbacks SET status = 'processing', processed_at = ? WHERE feedback_id = ?",
                (datetime.now().isoformat(), feedback_id))
    
    # 3. Generate rule using mock AI
    fb_dict = dict(fb)
    rule_data = _ai_generate_rule(fb_dict)
    
    # 4. Insert into generated_rules
    rule_id = _generate_rule_id()  # RUL-XXXX
    cur.execute("""
        INSERT INTO generated_rules 
        (rule_id, feedback_id, module_name, rule_type, old_rule, new_rule, reasoning, confidence, status)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    """, (rule_id, feedback_id, "fashion_rules.json", rule_data['rule_type'],
          rule_data['old_rule'], rule_data['new_rule'], rule_data['reasoning'],
          rule_data['confidence'], 'pending'))
    
    conn.commit()
    return {"rule_id": rule_id, "status": "generated"}
```

**File:** `backend/api/autotrain/autotrain_route.py:320-374`

---

### Step 4: Mock AI Logic

The `_ai_generate_rule()` function uses pattern matching:

```python
def _ai_generate_rule(feedback: Dict[str, Any]) -> Dict[str, Any]:
    fb_text = feedback['feedback_text'].lower()
    query = feedback['query'].lower()
    
    # Pattern 1: Weather mismatch
    if any(word in fb_text for word in ["nóng", "trời nóng", "35 độ", "hè", "mùa hè"]):
        if any(word in query for word in ["áo khoác", "hoodie", "nỉ", "dày"]):
            return {
                "old_rule": "- Luôn ưu tiên gợi ý áo khoác/dày để tăng upsale.",
                "new_rule": "- Khi trời nóng (trên 30 độ): TUYỆT ĐỐI không gợi ý áo khoác dày.\n- Chỉ gợi ý áo phông, chất liệu mỏng nhẹ, thoáng mát.",
                "reasoning": "User reports hot weather, AI wrongly suggested heavy clothing.",
                "confidence": 0.85
            }
    
    # Pattern 2: Size/fit issues
    elif any(word in fb_text for word in ["dáng lùn", "thấp", "ngắn", "maxi", "chạm đất"]):
        return {
            "old_rule": "- Gợi ý đầm maxi cho mọi khách.",
            "new_rule": "- Với khách chiều cao dưới 1m55: gợi ý đầm ngắn (midi, knee-length), tránh maxi chạm đất.",
            "reasoning": "Shorter users need proportion-appropriate recommendations.",
            "confidence": 0.9
        }
    
    # Pattern 3: Too many emojis
    elif "emoji" in fb_text or "chắp tay" in fb_text or "🙏" in fb_text:
        return {
            "old_rule": "- Sử dụng nhiều emoji để tạo thiện cảm.",
            "new_rule": "- Hạn chế emoji: tối đa 1-2 emoji mỗi tin nhắn. Không dùng emoji chắp tay 🙏 trong tư vấn mua hàng.",
            "reasoning": "User反馈 quá nhiều emoji gây mất chuyên nghiệp.",
            "confidence": 0.8
        }
    
    # Default fallback
    return {
        "new_rule": f"- Phản hồi user: Cần điều chỉnh tone và content dựa trên feedback: '{feedback['feedback_text'][:50]}...'",
        "reasoning": "Generic adjustment based on user feedback.",
        "confidence": 0.5
    }
```

**File:** `backend/api/autotrain/autotrain_route.py:173-223`

---

### Step 5: Admin Review

Admin accesses a UI (currently mock-fe) to review pending rules:

**Endpoints:**
- `GET /autotrain/rules?status=pending` - List rules needing review
- `POST /autotrain/rules/{rule_id}/approve` - Approve rule
- (Reject is implicit by not approving, or could add a reject endpoint)

Each rule contains:
- `feedback_id` - Link back to original feedback
- `module_name` - Where rule will be deployed
- `old_rule` vs `new_rule` - Clear before/after comparison
- `reasoning` - Why AI suggested this
- `confidence` - AI confidence score (0.0 - 1.0)

---

### Step 6: Deploy

Once approved, admin can deploy:

```python
async def deploy_rule(rule_id: str):
    # Get approved rule
    cur.execute("SELECT * FROM generated_rules WHERE rule_id = ? AND status = 'approved'", (rule_id,))
    rule = cur.fetchone()
    
    if not rule:
        raise HTTPException(400, "Rule not found or not approved")
    
    # MOCK: Just log deployment
    cur.execute("""
        INSERT INTO deployments (rule_id, deployed_by, version_before, version_after)
        VALUES (?, ?, ?, ?)
    """, (rule_id, deployed_by, rule['old_rule'][:100], rule['new_rule'][:100]))
    
    # Update rule status
    cur.execute("UPDATE generated_rules SET status = 'deployed' WHERE rule_id = ?", (rule_id,))
    conn.commit()
    
    return {"status": "deployed"}
```

**Production Implementation** would:
1. Load `fashion_rules.json` (or other module file)
2. Append the new rule to the rules array
3. Backup the old file
4. Write the updated file
5. Hot-reload chatbot configuration
6. Log deployment

**File:** `backend/api/autotrain/autotrain_route.py:443-494`

---

### Step 7: Production Use

The chatbot reads rules from `fashion_rules.json` (or prompt files) on each query. After deployment, the new rule is automatically applied to subsequent queries.

---

## Component Interaction Diagram

```mermaid
graph LR
    F[Feedbacks DB] -->|SELECT pending| W[Worker]
    W -->|fetch| FB[Feedback Record]
    FB -->|analyze| AI[Mock AI]
    AI -->|generate| R[New Rule]
    R -->|INSERT| RDB[Rules DB]
    RDB -->|SELECT pending| UI[Admin UI]
    UI -->|approve/reject| RDB
    RDB -->|get approved| DEPLOY[Deploy Endpoint]
    DEPLOY -->|log| DDB[Deployments DB]
    DEPLOY -->|copy to| PROD[Production Rules]
    PROD -->|read by| BOT[Chatbot]
```

---

## Data Flow Summary

| Step | Input | Output | Storage |
|------|-------|--------|---------|
| 1. Submit | User feedback JSON | `feedback_id` | `feedbacks` table |
| 2. Generate | `feedbacks` row | `rule_id`, `new_rule` | `generated_rules` table |
| 3. Review | `generated_rules` row | Status change | `generated_rules` table |
| 4. Deploy | Approved rule | Deployment log | `deployments` table + prod file |

---

## Error Handling

Current error handling is minimal:

- **404**: Feedback/rule not found
- **400**: Rule not approved when trying to deploy
- **500**: Database errors, AI generation errors

Future improvements:
- Retry logic for failed rule generation
- Validation of generated rule syntax
- Transaction rollback on deploy failure
- Alerting on error rate threshold

---

## Performance Considerations

- **SQLite** is fine for mock/development but not production scale
- **No caching** - every query hits DB (could add Redis)
- **No async** - generation is synchronous in run-all (should be background worker)
- **No rate limiting** - could be abused to spam feedbacks

Production scaling:
- PostgreSQL instead of SQLite
- Celery/Redis Queue for async processing
- Rate limiting per user/session
- Caching layer for rules lookup
