# 4. State Management & LangGraph Pipeline

Toàn bộ luồng Dual-Agent được quản lý bởi **LangGraph StateGraph** — mỗi node nhận state đầu vào, xử lý, và trả state mới.

## LeadGraphState Schema

```python
class LeadStageState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    user_insight: str | None          # InsightJSON cũ (từ Redis)

    # Internal pipeline
    tool_result: str | None           # Kết quả từ ProductSearchEngine
    tool_name_used: str | None        # Tên tool đã gọi

    # Output
    updated_insight: dict | None      # InsightJSON mới (Stylist cập nhật)
    lead_stage: dict | None           # Stage transition info
    product_ids: list[str]            # Danh sách SKU trả về
    early_exit: bool | None           # True = Classifier bypass Stylist

    diagnostics: Annotated[list[dict[str, Any]], lambda x, y: x + y]
```

## Graph Definition

```python
workflow = StateGraph(LeadStageState)

# 2 node chính
workflow.add_node("classifier", _classifier_node)
workflow.add_node("stylist", _stylist_node)

# Entry point
workflow.set_entry_point("classifier")

# Conditional routing: Early Exit → END, otherwise → Stylist
workflow.add_conditional_edges(
    "classifier",
    lambda state: END if state.get("early_exit") else "stylist"
)

# Stylist always ends
workflow.add_edge("stylist", END)
```

## Tại sao không dùng ReAct Loop?

ReAct (Reasoning + Acting) loop cho phép Agent tự quyết định gọi tool bao nhiêu lần. Lead Agent **không dùng** vì:

| ReAct Loop | Dual-Agent (No Loop) |
|---|---|
| Agent có thể gọi tool 3-5 lần → latency cao | Luôn chỉ 1 lần gọi tool → latency thấp |
| Agent tự quyết định → khó kiểm soát | Classifier ép cứng → kiểm soát 100% |
| Có thể rơi vào vòng lặp vô hạn | Không có cycle → không bao giờ loop |
| Khó debug khi lỗi | 2 node tuyến tính → debug đơn giản |

File tham chiếu: `graph.py` — cuối file, phần `StateGraph` definition
