Bỏ qua để vào nội dung chính
Gọi LLM trong production: timeout kép, JSON schema, cap chi phí

Gọi LLM trong production: timeout kép, JSON schema, cap chi phí

Bởi Amelia Brooks
25 thg 10, 20264 phút đọc

Prompt 1.8 giây ở p50 vẫn vọt lên 18–35 giây ở p99, còn JSON hỏng 1–3%. Bốn guardrail đặt quanh mỗi lần gọi LLM để worker pool và hóa đơn không gãy.

Service của bạn gọi LLM ngay trong request cycle và chạy ngon suốt giai đoạn prototype. Rồi một đêm provider chậm lại, worker pool kẹt cứng, và sáng hôm sau hóa đơn API nhảy ba chữ số. Bài này rút phần kỹ thuật của một phân tích LLM gateway mới đăng trên Dev.to: những con số bạn phải thiết kế theo, và các guardrail đặt quanh mỗi lần gọi model.

Ba thất bại kinh điển khi prototype lên production

Tác giả mô tả đúng ba kiểu gãy: endpoint "stalls for 45 seconds and exhausts your PHP-FPM or Node worker pool", model trả JSON bọc trong markdown code fence, và một retry loop không chặn "burns $400 overnight".

Gốc rễ là LLM API không hành xử như REST API thường. REST trả về trong 30–150 mili giây với schema xác định. Còn prompt trung bình 1.8 giây ở p50 vẫn vọt lên 18–35 giây ở p99 khi provider quá tải. Gọi đồng bộ trong controller, chỉ hai chục request chậm là đủ chiếm hết web worker.

Về output, ràng buộc bằng system prompt kiểu "respond only with valid JSON" vẫn hỏng 1–3% số lần — model bọc fence, thiếu key, hoặc cắt giữa chừng vì max_tokens đặt thấp.

Bốn guardrail đặt quanh mỗi call

  • Structured output, và kiểm finish_reason. Dùng response_format: { type: "json_schema", strict: true } để ràng buộc ngay lúc sampling. Nhưng có một bẫy: nếu model chạm max_tokens trước khi đóng object, API vẫn trả HTTP 200 kèm JSON dở dang và finish_reason: "length". Luôn assert finish_reason === "stop" trước khi parse.
  • Timeout kép. Cấu hình đề xuất là connect_timeout = 3s và timeout = 18s cho đường đồng bộ, thay vì để mặc định 60–120 giây của SDK; phần việc dài hơn đẩy sang background queue.
  • Backoff có jitter kèm model fallback. Thay vì retry ngay 3 lần trên mọi lỗi 5xx hay 429, dùng exponential backoff với full jitter rồi tự chuyển sang model dự phòng. Trong code mẫu, danh sách model là primary rồi fallback, với khoảng nghỉ ngẫu nhiên 150–400 mili giây giữa hai lần thử.
  • Cap trước khi tokenize. Chặn độ dài input ở tầng ứng dụng (mẫu đặt ngưỡng 16.000 ký tự) và đếm budget theo tenant mỗi ngày trong Redis (mẫu dùng 250.000 token/ngày). Đây là thứ chặn được một script chạy loạn hoặc một user dán nguyên log 60 trang vào ô chat.

Cache: con số đẹp nhưng là claim của tác giả

Bài đề xuất cache deterministic bằng SHA-256 của bộ ba (model + schema_version + input đã normalize), lưu trong Redis, và khẳng định cách này "cuts token spend and latency by 35%–60% on repeated inputs". Lưu ý đây là con số của chính tác giả — một bài viết gắn với dịch vụ phát triển web của họ — chứ không phải benchmark độc lập. Mức tiết kiệm thật phụ thuộc hoàn toàn vào tỉ lệ input lặp lại trong workload của bạn, nên hãy đo hit rate trước khi đưa nó vào dự toán chi phí.

Phần token bạn tự đốt ở phía editor

Hóa đơn của team còn một nửa nữa nằm ngoài production. Một bài khác cùng ngày chỉ ra cơ chế: "Every message you send to an AI coding tool also sends the context with it: the earlier chat history, the files it has read, and the tool output" — nên usage tăng "rarely because of one huge prompt", mà vì context dồn lại âm thầm. Ba việc đáng làm ngay: clear context khi xong mỗi task; giữ rules file (CLAUDE.md, .cursorrules) thật ngắn vì nó nạp vào mọi session; và chuyển model nhỏ hơn cho rename, boilerplate hay test scaffolding.

Làm gì trong sprint này

Nếu service đang gọi model trong request cycle, hai việc đầu tiên không phải là cache mà là timeout kép và cap input length — đó là hai thứ chặn được sự cố nửa đêm. Sau đó mới thêm assert finish_reason vào chỗ parse, rồi đo hit rate để quyết định có cần cache hay không.

Không spam, hủy đăng ký bất kỳ lúc nào.

Bài viết liên quan