09 · Vận hành agent production — chi phí, độ trễ, độ tin cậy
LLM API là một dependency rất khác database hay REST nội bộ: mỗi lời gọi mất vài giây tới vài phút, tính tiền theo token, bị giới hạn tốc độ theo tổ chức, và trả kết quả khác nhau mỗi lần. Đặt nó vào một hệ Laravel + PHP-FPM mà giữ thói quen "gọi đồng bộ trong request" là cách nhanh nhất để có sự cố lúc 3h sáng. Bài này là phần "SRE cho AI": retry, timeout, streaming, capacity, rate limit, chi phí — phần lớn có lab bằng SDK PHP thật.
Đã verify gì, chưa verify gì. Lab chạy thật với SDKanthropic-ai/sdk0.52.0 (PHP 8.4.19, Guzzle 8.2.0) gọi mock server Messages API có thể trả 529/429,retry-after, treo, và stream SSE: hành vi retry, timeout, TTFT đã đo. Đọc mã nguồn SDK để xác nhận chính sách retry. Chưa verify: giá trị rate limit thật, header rate limit, Batch API, fallback server-side, fast mode — lấy từ tài liệu tham chiếu Claude API (cache 2026-09-25). Con số capacity/chi phí là tính toán minh hoạ. Không có Context7.
Câu trả lời 60 giây
"Tôi coi mỗi lời gọi LLM là một job chậm, tính tiền, không đáng tin: chạy trong queue chứ không trong request FPM, có timeout ở tầng transport, retry đúng một tầng, ngân sách theo task, và stream để người dùng thấy chữ ngay. Lab của tôi với SDK PHP chính thức cho thấy ba bẫy: tham sốtimeoutcủa SDK không được áp — nó phụ thuộc HTTP client, Guzzle mặc định chờ vô hạn, server treo 20 giây thì request chờ đủ 20 giây; khi đặt timeout 3 giây ở Guzzle thì SDK retry 2 lần, server nhận 3 request cho một lời gọi; và SDK tuân theoretry-afterkhông giới hạn — 429 với retry-after 12 giây là worker bị khoá 12 giây. Về capacity, Little's law: 20 request/giây × 8 giây mỗi lượt là 160 kết nối đồng thời — không thể để FPM giữ chúng. Về chi phí, đòn bẩy theo thứ tự: prompt cache, chọn model/effort theo route, giới hạn output, Batch API cho việc không gấp."
Thuật ngữ
| Thuật ngữ | Nghĩa ngắn |
|---|---|
| RPM / ITPM / OTPM | Giới hạn request / token input / token output mỗi phút, theo tổ chức và theo model. |
| 429 / 529 | 429: bạn vượt rate limit. 529 (overloaded_error): nhà cung cấp đang quá tải. |
| retry-after | Header server báo nên chờ bao lâu trước khi thử lại. |
| jitter | Thêm ngẫu nhiên vào thời gian chờ retry để các client không cùng thử lại một lúc. |
| circuit breaker | Ngắt tạm lời gọi tới dependency đang lỗi để không dồn thêm tải và trả lỗi nhanh. |
| Little's law | Số việc đang xử lý đồng thời L = tốc độ đến λ × thời gian xử lý W. |
| Batch API | Gửi lô request xử lý bất đồng bộ (tối đa 24 giờ), giá bằng 50%. |
| FinOps | Quản trị chi phí hạ tầng như một chỉ số sản phẩm: đo, phân bổ, đặt ngân sách. |
1. Bài toán gốc và bối cảnh ra đời
Kiến trúc web PHP truyền thống giả định dependency trả lời trong mili-giây: request vào FPM, query DB 5 ms, trả HTML. LLM phá giả định đó theo bốn chiều:
| Chiều | DB / REST nội bộ | LLM API | Hệ quả |
|---|---|---|---|
| Độ trễ | 1–50 ms | 1–60 s mỗi lời gọi; agent nhiều phút | Không giữ worker FPM được; cần stream và queue |
| Chi phí | Cố định theo server | Theo token, tăng theo mức dùng | Một vòng lặp lỗi là hoá đơn lỗi; cần ngân sách |
| Giới hạn | Connection pool của bạn | Rate limit theo tổ chức, chia chung mọi tính năng | Một tính năng batch làm tính năng chat bị 429 |
| Độ tin cậy | Bạn kiểm soát | Nhà cung cấp quá tải (529), model bị ngừng hỗ trợ | Cần fallback và kế hoạch nâng cấp |
Bài này không có "công nghệ ra đời" — nó là tập hợp mẫu vận hành cho một loại dependency mới. Mục tiêu tối ưu: trải nghiệm người dùng (TTFT), chi phí trên mỗi nhiệm vụ hoàn thành, và khả năng chịu lỗi. Không nhằm: giảm latency tổng xuống mức mili-giây (không làm được với LLM lớn).
2. Mô hình tư duy cốt lõi
Mỗi lời gọi LLM là một job từ xa: chậm, có đồng hồ tính tiền, và có thể thất bại. Đối xử với nó như gọi cổng thanh toán hay xử lý video: chạy nền, timeout rõ, retry idempotent một tầng, ngân sách, quan sát được. Latency ≈ TTFT + số token output ÷ tốc độ decode. Chi phí ≈ Σ token × đơn giá. Capacity ≈ λ × W.
flowchart LR
U["Trình duyệt"] -->|"POST /chat"| C["Controller (FPM): tạo run, dispatch job, trả run_id ngay"]
C --> Q[("Queue llm (Redis/Horizon)")]
Q --> W["Worker: gateway LLM (timeout, retry 1 tầng, ngân sách, trace)"]
W -->|"stream token"| B["Broadcast / SSE (Reverb, process riêng)"]
B --> U
W --> API["Messages API"]
W --> DB[("agent_runs: checkpoint, usage, chi phí")]
3. Internals và quyết định thiết kế
3.1 Retry của SDK — lab
Mã nguồn SDK PHP (BaseClient::shouldRetry, retryDelay): mặc định maxRetries = 2; retry khi lỗi kết nối, 408, 409, 429, ≥500; tôn trọng
header x-should-retry; thời gian chờ = retry-after-ms hoặc retry-after nếu server gửi, không thì 0,5 × 2^n giây (tối đa 8 s) trừ
ngẫu nhiên tới 25%. Lab với mock server:
=== R1 529 hai lần đầu control={"fail_first":2,"fail_status":529} mode=default
OK sau 1.43s, stop_reason=end_turn
server nhận request #1 t=+ 0.00s retry_count=0 -> 529
server nhận request #2 t=+ 0.50s retry_count=1 -> 529
server nhận request #3 t=+ 1.41s retry_count=2 -> 200
=== R2 529 ba lần liền control={"fail_first":3,"fail_status":529} mode=default
EXCEPTION sau 1.32s: Anthropic\Core\Exceptions\InternalServerException — Anthropic Internal Server Exception
server nhận request #1 t=+ 0.00s retry_count=0 -> 529
server nhận request #2 t=+ 0.40s retry_count=1 -> 529
server nhận request #3 t=+ 1.31s retry_count=2 -> 529
=== R3 429 + retry-after: 12 control={"fail_first":1,"fail_status":429,"retry_after":12} mode=default
OK sau 12.01s, stop_reason=end_turn
server nhận request #1 t=+ 0.00s retry_count=0 -> 429
server nhận request #2 t=+12.00s retry_count=1 -> 200
- R1/R2: backoff ~0,4–0,5 s rồi ~0,9 s; hết 2 lần retry thì ném
InternalServerException(status 529). SDK gửi headerX-Stainless-Retry-Count— hữu ích khi đọc log phía server/gateway. - R3: SDK chờ đúng 12 giây theo
retry-after, không có trần. Trong request FPM, đó là một worker bị khoá 12 giây; nếu 429 xảy ra hàng loạt, cả pool FPM đứng. Trong queue job thì chấp nhận được — hoặc tắt retry của SDK vàrelease($delay)job để giải phóng worker.
3.2 Timeout — lab: tham số timeout của SDK không được áp
=== R4 server treo 20s, mặc định control={"hang_seconds":20} mode=default
OK sau 20.01s, stop_reason=end_turn
server nhận request #1 t=+ 0.00s retry_count=0 -> treo 20s
=== R5 treo 20s, requestOptions timeout=3 control={"hang_seconds":20} mode=opt-timeout
OK sau 20.01s, stop_reason=end_turn
server nhận request #1 t=+ 0.00s retry_count=0 -> treo 20s
=== R6 treo 20s, Guzzle timeout=3 control={"hang_seconds":20} mode=guzzle-timeout
EXCEPTION sau 10.45s: Anthropic\Core\Exceptions\APIConnectionException — Anthropic API Connection Error
server nhận request #1 t=+ 0.00s retry_count=0 -> treo 20s
server nhận request #2 t=+ 3.44s retry_count=1 -> treo 20s
server nhận request #3 t=+ 7.44s retry_count=2 -> treo 20s
=== R7 treo 20s, Guzzle timeout=3, maxRetries=0 control={"hang_seconds":20} mode=guzzle-timeout-noretry
EXCEPTION sau 3.01s: Anthropic\Core\Exceptions\APIConnectionException — Anthropic API Connection Error
server nhận request #1 t=+ 0.00s retry_count=0 -> treo 20s
- R4, R5: đặt
requestOptions: ['timeout' => 3.0]không có tác dụng — request chờ đủ 20 s. Mã nguồn SDK ghi rõ: timeout "chỉ mang tính gợi ý: được áp bởi transport do caller cung cấp, SDK không đọc". Guzzle mặc địnhtimeout = 0(chờ vô hạn). Nghĩa là trong một app Laravel cài SDK mặc định, một sự cố treo phía nhà cung cấp có thể giữ worker vô thời hạn. - R6: truyền Guzzle có
timeout 3→ timeout được áp, nhưng SDK coi timeout là lỗi kết nối và retry: 3 request tới server cho một lời gọi, tổng 10,45 s. Với API thật, request bị client bỏ ngang có thể vẫn được xử lý và tính tiền ở phía server — chưa verify. - R7: muốn "hỏng nhanh" trong request web: timeout ngắn +
maxRetries = 0.
// Đã chạy trong lab (labs/ai/php/retry_timeout.php, mode guzzle-timeout)
$client = new Anthropic\Client(apiKey: env('ANTHROPIC_API_KEY'), requestOptions: [
'transporter' => new GuzzleHttp\Client(['timeout' => 60.0, 'connect_timeout' => 5.0]),
'maxRetries' => 0, // trong queue job: để job retry (release với backoff), không retry 2 tầng
]);
Chọn timeout theo độ dài output: output 2 000 token ở ~50–100 token/s là 20–40 s chưa kể thinking. Request dài hoặc max_tokens lớn → dùng streaming (timeout
áp lên khoảng lặng giữa các chunk thay vì cả request) — tài liệu SDK khuyên stream khi output dài để tránh timeout HTTP.
3.3 Streaming và TTFT — lab
non-stream : chữ đầu tiên hiện ra sau 3.88s, xong sau 3.88s
stream : chữ đầu tiên hiện ra sau 0.09s, xong sau 3.88s (48 delta)
Tổng thời gian như nhau; thời gian người dùng nhìn màn hình trống giảm từ 3,9 s xuống 0,09 s. Với Laravel: stream từ worker qua broadcast (Reverb/Pusher) hoặc SSE; nếu SSE
đi qua nginx thì tắt buffering (X-Accel-Buffering: no), nếu không nginx gom lại và trả một cục (chưa verify trong lab này). Lưu ý: SSE trực tiếp từ FPM giữ một
worker suốt thời gian stream — capacity như mục 3.4.
3.4 Capacity: Little's law
L = λ × W. Ví dụ (minh hoạ): giờ cao điểm 20 lượt chat/giây, mỗi lượt 8 giây (TTFT + stream) → 160 lượt đồng thời. Nếu mỗi lượt giữ một worker FPM, cần 160 worker chỉ cho
chat — trong khi pool thường 20–50 worker/máy. Một agent 30 giây/lượt ở cùng tải là 600 đồng thời. Kết luận: phần chờ LLM phải nằm ở tiến trình rẻ khi chờ (queue worker, process
event-loop như Octane/Swoole/Node, hoặc broadcast server), còn FPM chỉ nhận request và trả run_id. Queue worker cho LLM tách riêng (queue: llm) để không làm
nghẽn job gửi mail/đơn hàng; số worker = giới hạn song song mong muốn (cũng là cách chặn bão 429).
3.5 Rate limit
- Giới hạn theo tổ chức, theo model: RPM, ITPM, OTPM. Mọi tính năng dùng chung — tính năng batch ban đêm có thể "ăn" hết hạn mức của chatbot.
- Theo tài liệu, token đọc từ prompt cache không tính vào ITPM trên đa số model → cache vừa rẻ vừa tăng thông lượng.
- Cách làm: semaphore/queue tập trung theo tính năng với hạn mức riêng; ưu tiên traffic người dùng thật; retry có jitter một tầng; workspace/API key riêng cho batch và realtime nếu cần tách hạn mức.
3.6 Đòn bẩy chi phí — theo thứ tự nên làm
| # | Đòn bẩy | Tác động điển hình | Đánh đổi |
|---|---|---|---|
| 1 | Prompt cache (bài 03) | Lab: phiên 30 lượt Opus 5.5 $6,03 → $0,86 | Gần như không — chỉ cần tiền tố ổn định |
| 2 | Dọn input: tool result gọn, bớt tool không dùng, system prompt tiếng Anh nếu dài | Tuỳ, thường 20–50% input | Công sức |
| 3 | Vòng lặp: max_iter, phát hiện lặp, ngân sách task | Chặn đuôi chi phí (task lỗi đắt nhất) | Một số task dừng sớm |
| 4 | Output: max_tokens hợp lý, yêu cầu ngắn gọn, structured outputs | Output đắt 5× input | Cắt cụt nếu quá thấp |
| 5 | Batch API cho việc không gấp (phân loại review hằng đêm, sinh mô tả sản phẩm) | −50% | Kết quả trong tới 24 giờ, thứ tự bất kỳ (khớp bằng custom_id) |
| 6 | Model/effort theo route (router) | Chênh 10× giữa Haiku và Fable | Chất lượng; phải eval từng route; cache tách theo model |
Ngược lại, fast mode (Opus 5.5, beta, chỉ Claude API) chạy nhanh hơn tới ~2,5 lần với giá $8/$40 (gấp đôi) — chỉ đáng cho luồng mà tốc độ có giá trị kinh doanh.
3.7 Fallback và suy giảm có kiểm soát
- Refusal: model mới có thể trả
stop_reason: "refusal"(HTTP 200); API có tham sốfallbacks(beta) chuyển sang model khác phía server — chưa verify. - 529 kéo dài: chuyển sang model dự phòng (cùng hãng khác model, hoặc hãng khác qua gateway) — nhớ: đổi model là mất prompt cache, và model dự phòng phải qua eval.
- Circuit breaker quanh gateway: lỗi vượt ngưỡng → mở mạch → trả ngay phương án suy giảm (FAQ tĩnh, chuyển người, "hệ thống bận, sẽ trả lời qua email").
- Nhà cung cấp khác chạy cùng model: Claude có trên Claude API, Amazon Bedrock, Google Vertex AI, Microsoft Foundry — tính năng và giá không hoàn toàn giống nhau; dùng làm đường dự phòng hoặc vì hợp đồng/vị trí dữ liệu.
4. Đảm bảo và giới hạn
| Khía cạnh | Bạn có | Bạn không có |
|---|---|---|
| Độ trễ | Stream để giảm TTFT cảm nhận | SLA latency cứng; latency dao động theo tải nhà cung cấp |
| Chi phí | usage chính xác mỗi response; ngân sách do bạn áp | Chi phí đoán trước cho agent (số vòng do model quyết định) |
| Retry | SDK retry an toàn cho lời gọi model (không side effect) | Exactly-once cho tool có side effect (bài 02) |
| Timeout | Khi bạn tự cấu hình transport | Mặc định an toàn (lab R4) |
| Sẵn sàng | Status page, nhiều nền tảng phân phối | Model sống mãi — model cũ bị ngừng theo lịch |
5. Failure modes
| Lỗi | Hệ quả | Phát hiện | Xử lý |
|---|---|---|---|
| Không timeout (lab R4/R5) | Worker treo, pool cạn | Số worker busy, request > X giây | Timeout ở transport; stream cho output dài |
retry-after dài trong FPM (lab R3) | Pool FPM đứng khi có 429 hàng loạt | p99 latency nhảy, 429 tăng | Không gọi LLM trong FPM; trong job: release với delay |
| Retry nhân chồng (lab R6) | 1 lời gọi = 3 request × job tries × agent tự thử lại | Đếm request/task | Một tầng retry cho mỗi loại lỗi |
| Bão 429 khi fan-out (bài 05) | 43% request bị từ chối ở 20 worker | Tỉ lệ 429 | Semaphore tập trung, jitter |
| Vòng lặp tốn tiền | Một task $5 thay vì $0,05 | Chi phí p99 theo task, alert theo giờ | max_iter, ngân sách, kill switch |
| Regression cache | Hoá đơn tăng 5× sau một deploy | Cache hit ratio | Test CI cho cache (bài 03) |
| Nhà cung cấp sự cố | 529 diện rộng | Tỉ lệ 529, status page | Circuit breaker, fallback, suy giảm |
| Model bị ngừng hỗ trợ | Request lỗi từ một ngày cụ thể | Lịch deprecation | Kế hoạch nâng cấp + eval định kỳ |
| Buffer của proxy | Stream "không chạy", người dùng chờ cả cục | TTFT phía client = tổng thời gian | Tắt buffering ở nginx/CDN cho route stream |
6. Vận hành trong production
Dashboard tối thiểu
- Theo route/tính năng: request/phút, TTFT p50/p95, tổng latency p95, tỉ lệ lỗi theo mã (429, 529, 5xx, timeout), phân bố
stop_reason. - Token: input/cache read/cache write/output; cache hit ratio; chi phí/giờ, chi phí/task (p50, p99), chi phí/tenant.
- Agent: số vòng/task, % chạm
max_iter, tỉ lệ lỗi tool. - Hạ tầng: độ dài queue
llm, worker busy, thời gian chờ trong queue.
Cấu hình nên tường minh
| Cấu hình | Chat realtime | Agent nền | Batch đêm |
|---|---|---|---|
| Timeout transport | Stream; idle timeout 30–60 s | 120–300 s hoặc stream | Batch API |
| Retry | 0–1, hỏng nhanh, suy giảm | Job-level, backoff + jitter, trần delay | Theo kết quả batch (errored/expired) |
| Song song | Theo traffic | Giới hạn số worker | Theo lô |
| Ngân sách | Token/lượt | Token + thời gian/task | Ngân sách/lô |
Runbook lúc 3h sáng
- Status page nhà cung cấp có sự cố không? Tỉ lệ 529 tăng? → bật suy giảm/fallback.
- 429 tăng? Tính năng nào đang dùng nhiều bất thường (batch chạy sai giờ, vòng lặp)? → giảm song song/tắt tính năng đó.
- Chi phí/giờ tăng? Cache hit ratio tụt sau deploy? Task nào p99 chi phí cao? → rollback prompt, kill task.
- Latency tăng mà không lỗi? Prompt dài ra (TTFT), output dài ra (
max_tokenshit), hay queue đợi lâu?
Nâng cấp model
Theo dõi lịch ngừng hỗ trợ; mỗi quý chạy eval với model mới; đọc migration guide (breaking change API như effort mặc định, tool_choice, thinking); canary; so chi phí — model mới có thể dùng tokenizer khác (Opus 4.7+ sinh ~1–1,35× token so với đời trước trên cùng văn bản).
7. So sánh và ma trận quyết định
| Kiến trúc gọi LLM | Độ phức tạp | Chịu tải | UX | Dùng khi |
|---|---|---|---|---|
| Đồng bộ trong FPM, không stream | Thấp nhất | Kém (giữ worker) | Chờ trắng màn hình | Công cụ nội bộ ít người, lời gọi ngắn |
| Đồng bộ trong FPM, SSE stream | Thấp | Kém | Tốt | Traffic nhỏ |
| Queue + polling | TB | Tốt | Trung bình | Tác vụ dài, không cần thấy từng chữ |
| Queue + broadcast/SSE từ process riêng | TB–cao | Tốt | Tốt | Chat/agent cho khách hàng — mặc định khuyến nghị |
| Octane/Swoole (event loop) | Cao (đổi mô hình runtime) | Rất tốt | Tốt | Team đã quen long-running PHP |
| Batch API | TB | Rất tốt | Không realtime | Xử lý hàng loạt, rẻ 50% |
| Kênh phân phối model | Ưu | Nhược |
|---|---|---|
| Claude API trực tiếp | Tính năng mới sớm nhất (beta), đủ tính năng | Hợp đồng/hoá đơn riêng |
| Bedrock / Vertex AI / Foundry | Hợp đồng cloud sẵn có, IAM, vị trí dữ liệu, dự phòng | Một số tính năng đến muộn/khác; model id và giá khác |
| Gateway đa nhà cung cấp | Đổi model/hãng bằng cấu hình, fallback | Mẫu số chung thấp nhất; thêm một hop; che tính năng riêng |
8. Góc nhìn tech lead: tác động tổ chức
- SLO cho tính năng AI (ví dụ minh hoạ): TTFT p95 < 2 s, tỉ lệ lỗi < 1%, chi phí trung bình < 1 000đ/cuộc hội thoại, tỉ lệ chuyển người < 15%. Không có SLO thì không biết khi nào phải hành động.
- FinOps: chi phí AI phân bổ theo tính năng và tenant; ngân sách tháng có cảnh báo 50/80/100%; review chi phí trong sprint review như review hiệu năng. Với SaaS nhiều tenant, chi phí/tenant quyết định giá bán gói có AI.
- ADR mẫu: "Mọi lời gọi LLM đi qua
LlmGateway: Guzzle timeout tường minh,maxRetriestheo loại route, trace + usage vào DB, ngân sách theo task, circuit breaker. Không gọi LLM trong request FPM ngoài route stream có giới hạn. Queuellmriêng." - Vendor strategy và exit: gateway nội bộ + eval set cho phép đổi nhà cung cấp; giữ đường dự phòng đã test (cùng model qua cloud khác, hoặc model khác); chấp nhận rằng mất các tối ưu riêng (cache, compaction) trong thời gian chuyển.
- Kỹ năng trực: đọc
usage, đọc trace, phân biệt 429/529, bật/tắt tính năng bằng flag. Viết runbook trước sự cố đầu tiên.
9. Anti-patterns và bài học thực tế
- Gọi LLM đồng bộ trong controller, không timeout (lab R4).
- Tin tham số
timeoutcủa SDK mà không kiểm transport (lab R5). - Retry ba tầng: SDK × queue × agent (lab R6).
- Dùng chung hạn mức cho batch và realtime — batch đêm làm chatbot 429 sáng hôm sau khi chạy trễ.
- Không ngân sách theo tenant — một khách hàng lạm dụng đốt hoá đơn của cả hệ thống (OWASP LLM10).
- Đổi model trong production không eval, không canary.
- Fallback sang model chưa từng test — sự cố thứ hai chồng lên sự cố thứ nhất.
Điều người vận hành lâu năm đều biết: sự cố AI tốn kém nhất thường không phải "API sập" (mọi người đều thấy) mà là "chi phí tăng im lặng" hoặc "chất lượng giảm im lặng" sau một thay đổi nhỏ. Metric chi phí/task và eval định kỳ là hệ thống báo động cho hai thứ đó.
10. Câu hỏi phụ interviewer hay hỏi tiếp
- 429 khác 529 thế nào, xử lý khác nhau ra sao?
- 429: bạn vượt hạn mức của mình — giảm tốc, xếp hàng, tôn trọng retry-after. 529: nhà cung cấp quá tải — retry có backoff ngắn, sau đó fallback/suy giảm; không phải lỗi của bạn nhưng là vấn đề của bạn.
- Tại sao không gọi LLM trong request PHP-FPM?
- Little's law: lời gọi vài chục giây nhân với tải là hàng trăm worker bị giữ; kèm retry-after và timeout mặc định vô hạn, pool FPM cạn. Đưa vào queue, stream qua process riêng.
- Giảm chi phí LLM thế nào mà không giảm chất lượng?
- Prompt cache trước (lab: 7×), dọn input, chặn vòng lặp, giới hạn output, Batch API cho việc không gấp; chỉ đổi model rẻ hơn khi eval chứng minh đủ tốt ở route đó.
- Làm sao dự báo chi phí?
- Đo token/task thật (p50, p99) × số task dự kiến × giá, cộng hệ số cho retry và vòng lặp; theo dõi hằng ngày vì phân phối đổi khi prompt/tool đổi.
- Streaming có làm tổng thời gian nhanh hơn không?
- Không (lab: cùng 3,88 s). Nó giảm thời gian chờ chữ đầu tiên (3,88 s → 0,09 s) — thứ quyết định cảm nhận của người dùng.
Phụ lục: lab đã chạy
Môi trường: PHP 8.4.19, anthropic-ai/sdk 0.52.0, guzzlehttp/guzzle 8.2.0, mock server Python 3.11. Output gốc: labs/ai/out/02-retry-timeout.txt,
labs/ai/out/09-stream-ttft.txt.
cd labs/ai/php && composer install && cd ..
bash php/run_retry_lab.sh # R1–R7: retry, retry-after, timeout (~2 phút)
./mockctl.sh start echo '{"stream_delay":0.08}'
php php/stream_ttft.php # TTFT stream vs không stream
./mockctl.sh stop
# Đọc chính sách retry trong mã nguồn SDK:
grep -n "function shouldRetry" -A30 php/vendor/anthropic-ai/sdk/src/Core/BaseClient.php
grep -n "DEFAULT_" php/vendor/anthropic-ai/sdk/src/RequestOptions.php
Chưa verify: hạn mức và header rate limit thật; việc request bị client bỏ ngang có bị tính tiền không; Batch API; fallback server-side; fast mode; buffering của nginx. Nguồn:
mã nguồn anthropic-ai/sdk PHP 0.52.0; tài liệu tham chiếu Claude API (cache 2026-09-25) về rate limit, Batch, fast mode, fallback; OWASP LLM10 (Unbounded Consumption).
Bài trước: 08 · Bảo mật agent · Về đầu series: 00 · Lộ trình.