
Đặt proxy trước API LLM: cache ngữ nghĩa và fallback 4 tầng
Cache theo hash chỉ hit dưới 4%. Dựng proxy ba tầng trước mọi lời gọi LLM: cache ngữ nghĩa, chuỗi fallback LiteLLM và circuit breaker theo tenant.
Hóa đơn OpenAI tháng này gấp ba tháng trước, nhưng sản phẩm không có thêm người dùng nào. Bạn mở log ra và thấy cùng một câu hỏi support được hỏi đi hỏi lại, chỉ khác dấu chấm phẩy.
Bài này dựng một lớp proxy đứng trước mọi lời gọi LLM, gồm ba tầng: cache ngữ nghĩa, chuỗi fallback, và circuit breaker theo tenant. Toàn bộ số liệu bên dưới trích từ hai bài hướng dẫn kỹ thuật được dẫn nguồn ở cuối — phần nào là tuyên bố của tác giả, tôi ghi rõ.
Vì sao cache theo hash gần như vô dụng
Phản xạ đầu tiên của hầu hết team là hash prompt rồi cache trong Redis. Tác giả bài FastAPI proxy cho biết cách này đạt tỷ lệ hit dưới 4%, vì chỉ cần thừa một khoảng trắng hay một dấu câu là cache miss.
Con số đáng chú ý hơn nằm ở phía cầu: tác giả ước tính 30–60% truy vấn LLM trong các ứng dụng SaaS doanh nghiệp là trùng lặp hoặc diễn đạt lại. Đây là ước tính của tác giả, không phải số đo công khai — nhưng nếu hệ thống của bạn có chatbot support, job enrichment tự động hay pipeline hỏi đáp tài liệu, hãy tự đo tỷ lệ này trước khi làm bất cứ thứ gì khác. Nó quyết định toàn bộ phần còn lại có đáng làm không.
Tầng 1 — Cache ngữ nghĩa: ngưỡng cosine là tham số sản phẩm
Thay vì so khớp chuỗi, proxy vector hoá prompt rồi chạy KNN trên Redis Vector Search. Bài gốc dùng chỉ mục HNSW với DISTANCE_METRIC: COSINE và VECTOR_DIM = 1536 — đúng chiều của text-embedding-3-small. Nếu bạn đổi sang model embedding khác, chiều vector đổi theo và chỉ mục phải tạo lại.
Điểm quan trọng nhất của tầng này không phải code, mà là ngưỡng. Bài gốc mặc định 0.92, nhưng nói thẳng rằng ngưỡng phải thay đổi theo nghiệp vụ: chatbot FAQ chạy tốt ở 0.88, còn tác vụ trích xuất pháp lý hoặc tài chính cần 0.97 hoặc cache khớp tuyệt đối. Tác giả đề xuất cho phép override qua header X-Semantic-Threshold: 0.95.
Đây là chỗ dễ gây sự cố nhất trong thực tế. Ngưỡng quá thấp thì hai câu hỏi khác nhau về bản chất sẽ dùng chung câu trả lời — với nội dung giá cả, hạn mức hay điều khoản hợp đồng, đó là trả lời sai cho khách. Hãy đặt ngưỡng theo từng route, không đặt một con số chung cho cả hệ thống.
Tầng 2 — Chuỗi fallback: chạy local trước, trả tiền sau
Cache chỉ xử lý câu lặp. Câu mới vẫn phải đi tiếp, và đây là lúc LiteLLM vào cuộc. Bài hướng dẫn homelab mô tả vấn đề quen thuộc: mỗi backend một endpoint và một SDK — Ollama ở :11434, llama.cpp ở :8080, rồi OpenAI, Anthropic, Groq mỗi bên một key. Hệ quả tác giả liệt kê gồm trùng lặp code, quản lý 6+ bộ credential, và quan trọng nhất: "khi Ollama chết, app của bạn chết theo".
LiteLLM gom tất cả về một endpoint tương thích OpenAI (http://homelab:4000/v1/*), nên mọi SDK OpenAI sẵn có dùng được ngay. Cấu hình waterfall trong bài chia bốn tầng theo chi phí tăng dần:
| Tầng | Model | Giới hạn |
|---|---|---|
| 1 — local nhanh | qwen2.5-coder:14b (Ollama) | tpm: 5000 |
| 2 — local mạnh hơn | deepseek-r1:14b (Ollama) | tpm: 3000 |
| 3 — remote dự phòng | gpt-3.5-turbo | tpm: 1000 |
| 4 — remote cao cấp | gpt-4o | tpm: 500 |
Hạn mức token giảm dần theo tầng chính là cơ chế kiểm soát chi phí: tầng đắt nhất bị bóp nhỏ nhất, nên traffic tràn xuống đó chỉ ở mức nhỏ giọt. Cùng file cấu hình còn có num_retries: 3, default_timeout: 120 và khối budget với max_budget: 10.0 cho mỗi chu kỳ.
Với team Việt Nam, thứ tự này đáng đảo lại theo ràng buộc thực tế: nếu bạn không có GPU tại chỗ, tầng 1 nên là model rẻ nhất của nhà cung cấp chứ không phải Ollama. Logic waterfall không đổi — chỉ đổi nội dung từng tầng.
Tầng 3 — Circuit breaker: chặn hoá đơn trước khi nó phát sinh
Rate limit thông thường không cứu được bạn khi một workflow tự động chạy loạn. Bài FastAPI proxy đề xuất đếm token bằng INCRBY nguyên tử trên Redis, key theo tenant và theo phút — dạng tenant:{id}:budget:{YYYYMMDDHHmm}. Vượt trần thì trả HTTP 429 ngay, không gọi lên upstream.
Chi tiết "không gọi lên upstream" mới là điểm mấu chốt. Một 429 trả về sau khi đã gọi API chỉ bảo vệ người dùng; trả về trước khi gọi mới bảo vệ ví tiền. Hãy đặt tầng này trước tầng gọi mạng trong thứ tự xử lý.
Cái giá phải trả: độ trễ và vận hành
Thêm proxy là thêm một chặng mạng. Tác giả tuyên bố overhead tra cứu cache dưới 12ms nếu embedding chạy bất đồng bộ, kèm khuyến nghị đặt connect timeout 2.0s và read timeout 30.0s cho pool HTTPX. Con số 12ms là kết quả tự báo cáo trong môi trường của tác giả — hãy đo lại trên hạ tầng của bạn trước khi hứa SLA với ai.
Tương tự, tiêu đề bài gốc tuyên bố cắt được 58% hóa đơn LLM. Phần thân bài không trình bày phương pháp đo cho con số này, và bài viết cũng bán một gói triển khai thương mại ở cuối. Hãy coi 58% là mức trần quảng cáo, không phải kỳ vọng. Mức tiết kiệm thực tế của bạn bị chặn trên bởi đúng một thứ: tỷ lệ truy vấn trùng lặp mà bạn tự đo ở bước đầu tiên.
Làm gì trong tuần này
Thứ tự triển khai nên ngược với thứ tự hấp dẫn. Dựng LiteLLM bằng Docker trước — ảnh ghcr.io/berriai/litellm:main, map cổng 4000, trỏ base_url của app sang đó. Bước này không đổi hành vi hệ thống, nên rollback chỉ là đổi lại một dòng config.
Sau đó bật log và đo tỷ lệ prompt trùng lặp trong vài tuần. Nếu tỷ lệ đó thấp, dừng lại: cache ngữ nghĩa không đáng công sức, hãy dồn sức vào chuỗi fallback và budget. Chỉ khi tỷ lệ trùng lặp đủ lớn mới dựng tầng Redis Vector Search — và hãy bắt đầu ở ngưỡng chặt rồi nới dần, vì sai theo hướng cache miss rẻ hơn nhiều so với sai theo hướng trả nhầm câu trả lời.
Thứ đáng theo dõi tiếp: khi đã có một endpoint duy nhất cho mọi model, việc đổi nhà cung cấp trở thành thao tác sửa config. Đó mới là đòn bẩy đàm phán giá thực sự, chứ không phải vài phần trăm cache hit.
Nguồn
- "Slashing LLM Bills by 58%: Building an OpenTelemetry Semantic Cache Proxy in FastAPI" — Ruesch Manny, Dev.to
- "LiteLLM on the Homelab: One Proxy, Every Model, Waterfall Routing Included" — ayraix, Dev.to
Không spam, hủy đăng ký bất kỳ lúc nào.
Bài viết liên quan

Ollama cắt prompt âm thầm: cách phát hiện và sửa num_ctx
10 thg 10, 2026
Benchmark LLM cho 1.00 nhờ bỏ qua 3 câu fail: 4 bug cần biết
09 thg 10, 2026