Phản hồi dạng luồng

Phản hồi dạng luồng (Streaming) cho phép bạn nhận đầu ra theo thời gian thực trong quá trình mô hình tạo nội dung, giúp nâng cao trải nghiệm người dùng và tốc độ cảm nhận.

Nguyên lý hoạt động#

诺玛AI sử dụng giao thức Server-Sent Events (SSE) để triển khai phản hồi dạng luồng:

  • Khi gửi yêu cầu, client đặt `stream: true`
  • Máy chủ trả về từng đoạn nội dung được tạo (chunk) một cách tuần tự
  • Mỗi chunk được gửi qua SSE với tiền tố `data: `
  • Khi tạo xong sẽ gửi `data: [DONE]`

Luồng giao thức OpenAI#

cURL

curl https://as.apinoma.com/v1/chat/completions \
  -H "Authorization: Bearer $APINOMA_API_KEY" \
  -H "Content-Type: application/json" \
  -N \
  -d '{
    "model": "openai/gpt-4o",
    "messages": [{"role": "user", "content": "Viết một bài thơ về lập trình"}],
    "stream": true
  }'

Python

TypeScript

Luồng giao thức Anthropic#

Python

import anthropic
 
client = anthropic.Anthropic(
    base_url="https://as.apinoma.com/anthropic",
    api_key="<APINOMA_API_KEY của bạn>"
)

with client.messages.stream(
    model="anthropic/claude-sonnet-4.6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Viết một bài thơ về lập trình"}]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

TypeScript

Luồng + Function Calling#

Phản hồi dạng luồng cũng hỗ trợ tình huống gọi hàm. Mô hình sẽ trước tiên xuất theo luồng yêu cầu gọi công cụ, sau khi bạn xử lý xong thì tiếp tục cuộc hội thoại:

stream = client.chat.completions.create(
    model="openai/gpt-4o",
    messages=[{"role": "user", "content": "Hôm nay thời tiết Bắc Kinh thế nào?"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Lấy thời tiết của thành phố được chỉ định",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "Tên thành phố"}
                },
                "required": ["city"]
            }
        }
    }],
    stream=True
)
 
for chunk in stream:
    delta = chunk.choices[0].delta
    if delta.tool_calls:
        # Xử lý lệnh gọi công cụ
        print(f"Gọi công cụ: {delta.tool_calls[0].function}")
    elif delta.content:
        print(delta.content, end="", flush=True)

Xử lý lỗi và kết nối lại#

Kết nối dạng luồng có thể bị gián đoạn do sự cố mạng. Khuyến nghị triển khai logic kết nối lại.

import time
 
def stream_with_retry(client, max_retries=3, **kwargs):
    for attempt in range(max_retries):
        try:
            stream = client.chat.completions.create(stream=True, **kwargs)
            for chunk in stream:
                yield chunk
            return  # Hoàn tất thành công
        except Exception as e:
            if attempt < max_retries - 1:
                wait = 2 ** attempt  # Lùi theo cấp số nhân
                print(f"\nKết nối bị gián đoạn, thử lại sau {wait}s...")
                time.sleep(wait)
            else:
                raise e

Thực hành tốt nhất#

  • Luôn đặt thời gian chờ — Tránh chờ đợi vô hạn
  • Xử lý chunk không hoàn chỉnh — Một số chunk có thể không có content
  • Triển khai cơ chế kết nối lại — Sử dụng chiến lược lùi theo cấp số nhân
  • Phía frontend sử dụng `flush` — Đảm bảo nội dung hiển thị ngay lập tức

Cập nhật lần cuối vào 23 tháng 6, 2026