Streaming với Claude API: SSE, các loại sự kiện và xử lý lỗi giữa chừng
Tóm tắt
Streaming trả kết quả dần qua SSE, vừa cải thiện trải nghiệm vừa tránh timeout với max_tokens lớn. Dùng helper SDK, gom delta theo index, chỉ parse JSON tool khi khối đóng và luôn xử lý sự kiện error giữa stream.
Người dùng nhìn màn hình trắng 20 giây chờ câu trả lời dài, hoặc request với max_tokens lớn bị đứt vì timeout HTTP. Cả hai đều được giải quyết bằng streaming: đặt "stream": true và Claude API trả kết quả dần dần qua server-sent events (SSE). Chữ hiện ra ngay khi model sinh, còn kết nối được giữ sống suốt thời gian xử lý. Bài này giải thích luồng sự kiện, cách dùng SDK và những bẫy khi xử lý lỗi giữa chừng.
Dùng SDK: cách đơn giản nhất
with client.messages.stream(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Giải thích SSE trong 3 câu"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message() # Message đầy đủ, có usage
Không cần hiển thị dần? Vẫn nên dùng stream rồi gọi get_final_message(). Tài liệu nêu rõ cách này đặc biệt hữu ích khi max_tokens lớn, vì SDK yêu cầu streaming để tránh timeout HTTP.
Thứ tự sự kiện
| Sự kiện | Ý nghĩa |
|---|---|
message_start | Mở message, chứa object Message rỗng nội dung |
content_block_start | Bắt đầu một khối (text, tool_use, thinking) với index |
content_block_delta | Từng mảnh nội dung của khối |
content_block_stop | Kết thúc khối |
message_delta | Cập nhật cấp message: stop_reason, usage cộng dồn |
message_stop | Kết thúc stream |
ping / error | Giữ kết nối / báo lỗi giữa chừng |
Các loại delta
text_delta: mảnh văn bản, nối lại để hiển thị.input_json_delta: tham số tool dưới dạng chuỗi JSON chưa hoàn chỉnh. Phải gom đủ đếncontent_block_stopmới parse được.thinking_delta+signature_delta: khi bật thinking; signature đến ngay trước khi khối đóng, dùng để xác thực khối thinking.
event: content_block_delta
data: {"type":"content_block_delta","index":1,
"delta":{"type":"input_json_delta","partial_json":"{\"location\": \"San Fra"}}
Lỗi giữa stream
Stream đã trả về HTTP 200 vẫn có thể nhận sự kiện error, ví dụ overloaded_error lúc tải cao (tương đương HTTP 529 khi không stream). Code của bạn phải xử lý trường hợp này, không thể chỉ kiểm tra status code ban đầu.
Rủi ro và lưu ý
- Parse JSON tool quá sớm: lỗi kinh điển. Chỉ parse khi khối đóng, hoặc dùng helper tích luỹ của SDK.
- Bỏ qua
index: một message có nhiều khối; gộp delta theoindex, không theo thứ tự đến. - Proxy/buffer chặn SSE: nginx hay gateway có thể buffer làm mất hiệu ứng streaming; tắt buffering cho endpoint này.
- Đứt kết nối giữa chừng: phần đã nhận có thể dùng được, nhưng phải quyết định gửi lại hay tiếp tục; với UX chấp nhận được, gửi lại request là đơn giản nhất.
- Bỏ qua sự kiện lạ: API có thể thêm loại sự kiện mới; code nên bỏ qua loại không biết thay vì crash.
Áp dụng thế nào?
- Dev: Mặc định dùng
messages.stream()cho mọi request dài; lưuget_final_message()vào log để có usage chính xác. - Tech Lead: Viết một lớp adapter duy nhất xử lý SSE, lỗi giữa stream và retry, thay vì mỗi service tự làm.
- PM: Đo "thời gian tới chữ đầu tiên" thay vì tổng thời gian; đây là chỉ số người dùng cảm nhận rõ nhất.
Nguồn & tham khảo
- Streaming messages — Claude Docs