# Kiến trúc Caching (LLM & System Cache)

Để đảm bảo hiệu năng (giảm latency) và tối ưu chi phí sử dụng LLM, hệ thống áp dụng nhiều cấp độ caching tại các module khác nhau.

## 1. LLM Response Cache (Redis)

Tại tầng điều khiển (`agent/controller.py`), mỗi khi có một câu truy vấn từ người dùng, hệ thống sẽ thực hiện kiểm tra (nếu `REDIS_CACHE_TURN_ON` được kích hoạt).

- **Kiểm tra (Get)**: `redis_cache.get_response(user_id=identity_key, query=query)` tìm kiếm dữ liệu đã trả lời trước đó trong vòng 5 phút (TTL mặc định là 300s).
- **Log Langfuse**: Nếu **Cache Hit**, sẽ không gọi LLM API. Hệ thống khôi phục `user_insight` và ghi log trace lên Langfuse với tên `chat-request-cache-hit` (tiết kiệm hoàn toàn token chi phí).
- **Lưu trữ (Set)**: Nếu **Cache Miss**, yêu cầu đi qua LLM xử lý. Sau khi có `response_payload`, hệ thống sẽ gọi `redis_cache.set_response` để ghi lại vào Redis để dùng cho các request giống nhau về sau.
- Cơ chế bỏ qua: Có thể bypass cache thông qua biến `skip_cache: bool` trên API request (thường dùng cho dev).

## 2. In-Memory Graph & Chain Cache (LangGraph)

Trong quá trình khởi tạo Graph (`agent/graph.py`), hệ thống cache lại chuỗi logic xử lý và đồ thị (Graph) để tránh phải compile nhiều lần.

- Sử dụng `langgraph.cache.memory.InMemoryCache`.
- Graph được biên dịch một lần qua `workflow.compile(cache=self.cache)`.
- **Chain Hashing**: LLM chain (`prompt_template | self.llm_with_tools`) được hash lại cùng nội dung của system prompt. Nếu prompt không đổi, `_cached_chain` sẽ được dùng lại, chỉ tải lại khi file prompt thay đổi nội dung (kích hoạt hàm `reset_chain_cache`).

## 3. Tool & Prompt Memory Cache

Tại file `agent/prompt_utils.py`, các nội dung System Prompt và Tool Prompt (từ file txt/md) được đọc và lưu trực tiếp lên RAM của service FastAPI (`_system_prompt_cache` và `_tool_prompt_cache`).
- Khi update prompt trên filesystem, có thể gọi lại logic làm mới để invalid memory cache (`invalidate_prompt_caches`), tránh việc restart lại server FastAPI.

## Luồng hoạt động LLM Response Cache

```mermaid
sequenceDiagram
    participant API as Endpoint (/chat)
    participant Ctrl as Agent Controller
    participant Redis as Redis Cache
    participant LF as Langfuse
    participant Graph as LangGraph (LLM)

    API->>Ctrl: handle_message(query)
    Ctrl->>Redis: get_response(identity_key, query)
    alt CACHE HIT
        Redis-->>Ctrl: cached_response
        Ctrl->>Redis: load_user_insight_from_redis()
        Redis-->>Ctrl: cached_insight
        Ctrl->>LF: Ghi log 'chat-request-cache-hit'
        Ctrl-->>API: Trả về cached_response (cached: True)
    else CACHE MISS
        Redis-->>Ctrl: None
        Ctrl->>Graph: invoke()
        Graph-->>Ctrl: response_payload
        Ctrl->>Redis: set_response(..., ttl=300)
        Ctrl-->>API: Trả về response_payload (cached: False)
    end
```