Bạn đã bao giờ mở một ứng dụng AI, nhấn "Gửi" rồi nhìn chằm chằm vào màn hình trắng suốt 8 giây chờ đợi? Cảm giác đó rất khó chịu — và 53% người dùng sẽ rời bỏ trang web nếu phải chờ hơn 3 giây (Google/Marketing Dive, 2024). Streaming responses là giải pháp Claude đã tích hợp sẵn để xử lý vấn đề này: thay vì chờ toàn bộ câu trả lời, người dùng thấy text xuất hiện ngay từ mili-giây đầu tiên.
Bài viết này sẽ hướng dẫn bạn từ lý thuyết đến implementation thực tế — bao gồm Python, JavaScript, xử lý lỗi, và cách chọn giữa SSE và WebSocket cho dự án của bạn.
[INTERNAL-LINK: tổng quan Claude API → pillar page về Claude API và các tính năng chính]
Key Takeaways - Claude streaming dùng Server-Sent Events (SSE), token đầu tiên xuất hiện trong ~1.23s với Claude Sonnet 4 (Artificial Analysis, 2026). - Streaming giảm perceived latency 75% so với non-streaming trong các case study thực tế (Medium/Coinmonks, 2025). - SSE phù hợp cho 95% AI chat app; WebSocket cần thiết khi cần bidirectional communication như tool call approval. - Python SDK và TypeScript SDK đều hỗ trợ streaming với cú pháp đơn giản, async/sync đều được.
Streaming Là Gì Và Tại Sao Nó Quan Trọng Với UX Của AI App?
Thay vì chờ model sinh toàn bộ câu trả lời rồi mới gửi một lần, streaming gửi từng token ngay khi được tạo ra — giảm perceived latency lên đến 75% so với non-streaming (Medium/Coinmonks, 2025). Điều này tạo ra cảm giác "AI đang suy nghĩ và trả lời theo thời gian thực" thay vì "AI đang xử lý ẩn rồi dump kết quả".
Tại sao điều này quan trọng về mặt kinh doanh?
63% người dùng chuyển sang dịch vụ khác sau một trải nghiệm xấu (SparkAgentAI, 2025). Với AI app, "trải nghiệm xấu" thường là màn hình trắng chờ đợi. Streaming giải quyết đúng pain point này.
Ba lợi ích kỹ thuật cụ thể:
- Interrupt khả năng sinh: Nếu model đang đi sai hướng, người dùng có thể dừng sớm thay vì chờ hết response.
- Giảm memory footprint: Server không cần buffer toàn bộ response trước khi gửi.
- Timeout resilience: Long-generation không bị kill bởi HTTP timeout mặc định của server.
Kinh nghiệm thực tế: Khi chúng tôi test Claude với
max_tokens=4096mà không bật streaming, AWS API Gateway timeout sau 29 giây — mất hoàn toàn response. Sau khi bậtstream=True, không còn timeout nữa và user thấy kết quả ngay từ giây đầu.
[INTERNAL-LINK: Claude API integration guide → bài B-F1 về tích hợp Claude API cơ bản]
Server-Sent Events (SSE) Là Nền Tảng Kỹ Thuật Của Claude Streaming Thế Nào?
Claude streaming dựa trên SSE — một giao thức HTTP một chiều cho phép server push events liên tục đến client (Anthropic Docs, 2026). Với HTTP/2, một TCP connection có thể multiplexed ~100 SSE streams đồng thời, loại bỏ giới hạn connection cũ.

Cấu trúc một SSE stream từ Claude gồm các event types:
event: message_start
data: {"type":"message_start","message":{"id":"msg_01...","model":"claude-opus-4-7","usage":{"input_tokens":25}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Streaming"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" responses"}}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":128}}
event: message_stop
data: {"type":"message_stop"}
Luồng hoạt động theo thứ tự:
| Thứ tự | Event | Mô tả |
|---|---|---|
| 1 | message_start |
Metadata: model, usage ban đầu |
| 2 | content_block_start |
Bắt đầu block text hoặc thinking |
| 3 | content_block_delta |
Token mới — lặp lại nhiều lần |
| 4 | content_block_stop |
Kết thúc block hiện tại |
| 5 | message_delta |
Stop reason + final usage |
| 6 | message_stop |
Stream kết thúc |
Theo đo lường thực tế, TTFB (Time To First Byte) của Claude là khoảng 300ms, và sau đó tokens stream đến với tốc độ 50-100 tokens/giây (Anthropic Docs, 2026).
Làm Thế Nào Để Implement Claude Streaming Với Python Đúng Cách?
Python SDK của Anthropic hỗ trợ hai cách stream, với TTFT trung bình 1.23s cho Claude Sonnet 4 (Artificial Analysis, 2026). Cách đơn giản nhất dùng context manager:
import anthropic
client = anthropic.Anthropic()
# Cách 1: text_stream — đơn giản nhất
with client.messages.stream(
model="claude-opus-4-7",
max_tokens=1024,
messages=[{"role": "user", "content": "Giải thích streaming responses"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
# Lấy final message sau khi stream xong
final_message = stream.get_final_message()
print(f"\nInput tokens: {final_message.usage.input_tokens}")
Async streaming cho FastAPI hoặc async app:
import anthropic
import asyncio
async def stream_response(prompt: str):
client = anthropic.AsyncAnthropic()
async with client.messages.stream(
model="claude-opus-4-7",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
) as stream:
async for text in stream.text_stream:
yield text # Generator cho FastAPI StreamingResponse
# FastAPI endpoint
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
@app.post("/chat")
async def chat(prompt: str):
return StreamingResponse(
stream_response(prompt),
media_type="text/plain"
)
Xử lý raw events khi cần access usage và metadata:
with client.messages.stream(
model="claude-opus-4-7",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
) as stream:
for event in stream:
if event.type == "content_block_delta":
print(event.delta.text, end="", flush=True)
elif event.type == "message_delta":
# Final usage stats
print(f"\nOutput tokens: {event.usage.output_tokens}")
Lưu ý quan trọng: Với max_tokens lớn (>4096), Python SDK yêu cầu bắt buộc dùng streaming để tránh HTTP timeout. Anthropic SDK tự động handle điều này khi bạn dùng .stream().
[INTERNAL-LINK: Claude webhook patterns → bài B-F8 về Claude webhook patterns và event handling]
Cách Implement Claude Streaming Trên Frontend JavaScript Và Node.js?

TypeScript SDK của Anthropic có API tương tự Python, với TTFT 1.23s cho Sonnet 4 non-reasoning (Artificial Analysis, 2026). Đây là implementation đầy đủ cho Node.js backend + browser frontend:
Node.js / TypeScript Backend:
import Anthropic from "@anthropic-ai/sdk";
import express from "express";
const client = new Anthropic();
const app = express();
app.post("/api/stream", async (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
const { message } = req.body;
const stream = client.messages.stream({
model: "claude-opus-4-7",
max_tokens: 1024,
messages: [{ role: "user", content: message }],
});
stream.on("text", (text) => {
// Gửi từng token về browser theo SSE format
res.write(`data: ${JSON.stringify({ text })}\n\n`);
});
stream.on("finalMessage", (message) => {
res.write(`data: ${JSON.stringify({ done: true, usage: message.usage })}\n\n`);
res.end();
});
stream.on("error", (err) => {
res.write(`data: ${JSON.stringify({ error: err.message })}\n\n`);
res.end();
});
});
Browser Frontend (Vanilla JS):
async function streamChat(message) {
const output = document.getElementById("output");
output.textContent = "";
const response = await fetch("/api/stream", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message }),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
const lines = decoder.decode(value).split("\n");
for (const line of lines) {
if (line.startsWith("data: ")) {
const data = JSON.parse(line.slice(6));
if (data.text) {
output.textContent += data.text;
}
if (data.done) {
console.log("Tokens used:", data.usage);
}
}
}
}
}
Tip UX quan trọng: Thêm cursor nhấp nháy để báo hiệu AI đang viết:
.streaming-cursor::after {
content: "▋";
animation: blink 1s step-end infinite;
}
@keyframes blink {
50% { opacity: 0; }
}
[INTERNAL-LINK: Claude và Slack bot integration → bài B-F5 về xây dựng Slack bot với Claude]
SSE Hay WebSocket — Khi Nào Nên Chọn Cái Nào Cho AI App Của Bạn?
Theo phân tích kỹ thuật năm 2025, SSE phù hợp cho 95% AI chat application khi luồng là một chiều server → client (DEV.to, 2025). Nhưng khi AI app phức tạp hơn, ranh giới này thay đổi.

So sánh kỹ thuật chi tiết:
| Tiêu chí | SSE | WebSocket |
|---|---|---|
| Giao thức | HTTP/HTTPS (HTTP/2 ready) | WS/WSS (upgrade từ HTTP) |
| Chiều truyền | Server → Client (1 chiều) | Bidirectional (2 chiều) |
| Reconnect | Tự động (built-in) | Manual code |
| Load balancer | Compatible (stateless) | Sticky session cần |
| Firewall/Proxy | Ít vấn đề hơn | Thường bị block |
| Dùng khi | Chat, summarization, content gen | Agent workflow, tool call approval |
Chọn SSE khi: - User gửi prompt → server stream response (pattern phổ biến nhất) - Dùng Claude cho content generation, summarization, Q&A - Cần deploy đơn giản với CDN và reverse proxy
Chọn WebSocket khi: - User cần gửi signal trong khi AI đang stream (hủy, steer agent) - Multi-turn agent workflow cần real-time bidirectional control - Claude Code-style IDE integration với live tool call approval
Theo nghiên cứu của CallSphere (callsphere.ai, 2025), hầu hết AI chatbot và content generation app không cần WebSocket — SSE đủ dùng và đơn giản hơn nhiều trong deployment.
[INTERNAL-LINK: Claude API batch processing → bài B-F10 về Claude Batch API cho xử lý hàng loạt]
Những Lỗi Phổ Biến Khi Dùng Claude Streaming Và Cách Fix Hiệu Quả?
Theo báo cáo từ GitHub Issues của Claude Code (tháng 1/2026), một số user gặp streaming delays 59-138 giây sau khi nhận chunk đầu (GitHub anthropics/claude-code #18028, 2026). Đây là danh sách lỗi phổ biến và cách xử lý:
Lỗi 1: Stream đột ngột dừng (silent timeout)
# ❌ Sai: không có timeout handling
with client.messages.stream(...) as stream:
for text in stream.text_stream:
process(text)
# ✓ Đúng: thêm timeout và error handling
import anthropic
try:
with client.messages.stream(
model="claude-opus-4-7",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
) as stream:
for text in stream.text_stream:
yield text
except anthropic.APITimeoutError:
yield "\n[Lỗi: Request timeout, vui lòng thử lại]"
except anthropic.APIStatusError as e:
yield f"\n[Lỗi API: {e.status_code}]"
Lỗi 2: Buffer không flush — user không thấy gì dù stream đang chạy
# ✓ Luôn dùng flush=True khi print stream
print(text, end="", flush=True)
# Với web framework, disable buffering
import os
os.environ["PYTHONUNBUFFERED"] = "1" # Cho gunicorn/uvicorn
Lỗi 3: Không handle overloaded_error khi Claude bận
import time
import anthropic
def stream_with_retry(prompt, max_retries=3):
for attempt in range(max_retries):
try:
with client.messages.stream(
model="claude-opus-4-7",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
) as stream:
for text in stream.text_stream:
yield text
return # Thành công, thoát retry loop
except anthropic.APIStatusError as e:
if e.status_code == 529 and attempt < max_retries - 1:
# overloaded_error: chờ theo exponential backoff
wait = 2 ** attempt
print(f"Claude bận, thử lại sau {wait}s...")
time.sleep(wait)
else:
raise
Lỗi 4: Nginx/proxy cắt stream sớm
Nếu dùng Nginx làm reverse proxy, cần tắt buffering:
location /api/stream {
proxy_pass http://backend;
proxy_buffering off; # Tắt buffer — bắt buộc với SSE
proxy_cache off;
proxy_read_timeout 300s; # Tăng timeout cho long generation
add_header X-Accel-Buffering no;
}
FAQ: Claude Streaming Responses
Claude streaming có tốn nhiều token hơn non-streaming không?
Không — số token input/output hoàn toàn giống nhau. Streaming chỉ thay đổi cách delivery, không ảnh hưởng đến cách model tính toán hay giá tiền. Sự khác biệt duy nhất là bạn nhận thêm usage metadata qua message_delta event cuối stream (Anthropic Docs, 2026).
Tôi có thể dừng streaming giữa chừng không?
Có. Với Python SDK, thoát khỏi context manager (with block) sẽ cancel stream tự động. Với TypeScript, gọi stream.abort(). Điều này tiết kiệm token vì Claude ngừng generate ngay lập tức. Đây là một trong những lợi thế lớn của streaming — không phải chờ toàn bộ response mới có thể interrupt.
TTFT của Claude là bao nhiêu so với GPT-4 hay Gemini?
Claude Sonnet 4 đạt TTFT 1.23s (non-reasoning), Claude 4.5 Sonnet là 1.64s — cả hai đều cạnh tranh với các model cùng tier về giá (Artificial Analysis, 2026). Claude Opus 4.6 với extended thinking có TTFT cao hơn (~20s) do phải suy nghĩ trước khi trả lời.
Streaming có hoạt động với extended thinking (Opus 4.6) không?
Có, nhưng thinking blocks cũng được stream theo. Anthropic cho phép bạn omit thinking content khỏi stream để tăng tốc độ bằng cách dùng display field. Với Opus 4.6 reasoning mode, tokens/giây có thể chậm hơn vì model phức tạp hơn.
Tôi nên dùng stream=True raw hay SDK helper?
Dùng SDK helper (client.messages.stream()) trong hầu hết trường hợp — nó handle reconnection, error, và final message accumulation tự động. Chỉ dùng raw stream=True với manual SSE parsing khi bạn cần control hoàn toàn (ví dụ: forward SSE events sang client mà không parse ở server).
[INTERNAL-LINK: so sánh Claude vs Gemini API → bài Hub A về so sánh các AI API phổ biến]
Kết Luận: Streaming Không Phải Optional — Đây Là Baseline Cho AI App 2026
Với 53% người dùng bỏ trang sau 3 giây chờ đợi và streaming giảm được 75% perceived latency, đây không còn là "nice to have" mà là yêu cầu cơ bản của bất kỳ AI application nghiêm túc nào. Claude đã làm cho việc implement streaming trở nên đơn giản — chỉ cần thêm .stream() vào Python SDK hoặc .on("text") vào TypeScript SDK.
Tóm tắt những gì bạn đã học:
- SSE là nền tảng: Hiểu event types (
message_start→content_block_delta→message_stop) giúp bạn debug dễ hơn. - Python & JS đều đơn giản: SDK handle phần phức tạp, bạn chỉ cần xử lý text chunks.
- SSE đủ cho 95% use case: Chỉ cần WebSocket khi cần bidirectional communication thực sự.
- Production checklist: Tắt Nginx buffering, implement retry với backoff, luôn dùng
flush=True.
Bước tiếp theo: nếu bạn đang build AI app phức tạp với nhiều bước xử lý, hãy xem thêm về cách dùng Batch API để xử lý song song hàng nghìn requests.
[INTERNAL-LINK: Claude Batch API cho production workloads → bài B-F10 về Claude Batch API xử lý hàng loạt]
[INTERNAL-LINK: xây dựng chatbot Claude trên Slack → bài B-F5 về tích hợp Claude Slack bot thực tế]