# Modification Guide — Cẩm nang thay đổi luồng

Khi có yêu cầu từ phía Business (thêm tag mùa lễ, đổi cách AI xưng hô, thêm tool mới), Developer cần biết chính xác điểm phẫu thuật để không làm hỏng toàn bộ quy trình.

## Tổng hợp File Map

```mermaid
graph LR
    G["graph.py (Core)"] --> P["prompts.py (Tone)"]
    G --> E["product_search_engine.py (Search)"]
    G --> T["tools/ (Mở rộng)"]
    P -.-> G
    E -.-> G
    T -.-> G

    style G fill:#0c4a6e,stroke:#0ea5e9,color:#e0f2fe
    style P fill:#5b21b6,stroke:#8b5cf6,color:#ede9fe
    style E fill:#14532d,stroke:#22c55e,color:#dcfce7
    style T fill:#92400e,stroke:#f59e0b,color:#fef3c7
```

| File | Chức năng | Khi nào sửa |
|---|---|---|
| `graph.py` | Core: State, InsightJSON, ClassifierOutput, Tool Registry | Thêm tag, thêm insight, thêm tool |
| `prompts.py` | Prompt: Classifier + Stylist system prompts | Đổi giọng, thêm guardrail |
| `product_search_engine.py` | Engine: Cascading Search, Enrichment | Đổi logic Upsell, thêm/bớt tầng |
| `controller.py` | Controller: Redis, Langfuse, API route | Đổi TTL cache, thêm metric |

## 1. Sửa Tags và Insights (Phân loại khách)

**Yêu cầu mẫu:** Marketing muốn thêm tag `occ:di_dam_cuoi` (Đi tiệc cưới), hoặc lưu thêm thông tin `PET_OWNER` vào bộ nhớ khách.

| Hành động | File | Vị trí trong code |
|---|---|---|
| Thêm tag mới | `graph.py` | Class `InferredSearchArgs` — thêm Enum trong field `tags` |
| Thêm trường Insight | `graph.py` | Class `InsightJSON` — thêm field mới |

```python
# Ví dụ thêm field mới vào InsightJSON
class InsightJSON(BaseModel):
    # ... các field cũ ...
    PET_OWNER: str = Field(default="Chưa rõ")  # Field mới
```

**Lưu ý:** Sau khi thêm tag mới, kiểm tra bảng StarRocks có cột BITMAP tương ứng chưa.

## 2. Sửa giọng điệu, văn phong AI

**Yêu cầu mẫu:** Sếp bắt AI phải xưng "Stylist Canifa / Quý khách" thay vì "Mình / Bạn". Hoặc cấm AI tự ý đề cập giảm giá.

| Hành động | File | Vị trí trong code |
|---|---|---|
| Đổi xưng hô | `prompts.py` | Hằng số `STYLIST_SYSTEM_PROMPT` |
| Thêm guardrail | `prompts.py` | Thêm gạch đầu dòng CHỮ HOA vào prompt |

```python
# Ví dụ thêm guardrail
STYLIST_SYSTEM_PROMPT = """
...
- TUYỆT ĐỐI KHÔNG BÁO GIẢM GIÁ NẾU KHÔNG CÓ TRONG DATA
- LUÔN XƯNG "Stylist Canifa" VÀ GỌI KHÁCH "Quý khách"
...
"""
```

**Lưu ý:** Prompt LLM rất nhạy cảm. Thêm 1 dòng sai có thể ảnh hưởng toàn bộ hành vi. Nên test lại với User Simulator sau khi sửa.

## 3. Đổi thuật toán rớt tầng (Upsell Logic)

**Yêu cầu mẫu:** Đội Sales bảo "Nới giá 1.5x cao quá khách chạy mất, chỉnh xuống 1.2x thôi".

| Hành động | File | Vị trí trong code |
|---|---|---|
| Chỉnh hệ số giá | `product_search_engine.py` | Hàm `_cascading_search()` |
| Thêm/bớt tầng | `product_search_engine.py` | Cấu trúc list các tầng trong hàm trên |

```python
# Ví dụ chỉnh hệ số Upsell
# Trước: price_max * 1.5
# Sau:   price_max * 1.2
tier_5_price = original_price_max * 1.2
tier_6_price = original_price_max * 1.5
```

Có thể thêm tầng mới tuỳ ý. VD: Thêm tầng chỉ lọc đồ đang cần xả kho (`sale_priority = 1`).

## 4. Thêm Tool mới cho Classifier

**Yêu cầu mẫu:** Tích hợp tool `check_store_location` để AI báo khách cửa hàng nào gần nhà còn đồ.

| Bước | File | Hành động |
|---|---|---|
| 1. Viết logic tool | `backend/agent/tools/store_locator.py` | Tạo file mới, implement tool function |
| 2. Đăng ký tool | `graph.py` | Thêm tên tool vào ClassifierOutput description |
| 3. Kết nối tool | `graph.py` | Thêm tool vào dictionary `self._tool_registry` |

```python
# Bước 2: Thêm tên tool vào ClassifierOutput
class ClassifierOutput(BaseModel):
    tool_name: str | None = Field(
        description="Ten tool (lead_search_tool, check_store_location). "
                    "Null neu khach chi dang tam su."
    )
```

**Lưu ý:** Bất kỳ sửa đổi nào ở Classifier (đầu vào) sẽ ảnh hưởng toàn bộ luồng phía sau. Sau khi sửa, luôn dùng User Simulator bắn 50 test case.
