Chọn Auto, TokenLab Verified hoặc Official cho mỗi yêu cầu, với giá được hiển thị ngay từ đầu. Xem có gì mới

Thử lại các phản hồi LLM dạng streaming mà không làm trùng lặp đầu ra

CryptoCrypto
·28 tháng 9, 2026·19 phút đọc·Cập nhật 28 tháng 9, 2026·34 lượt xem
#phát trực tuyến#API phản hồi#độ tin cậy#websocket
Thử lại các phản hồi LLM dạng streaming mà không làm trùng lặp đầu ra

Một yêu cầu streaming chỉ an toàn để phát lại khi đồng thời thỏa mãn ba điều kiện. Chưa có gì đến được client của bạn. Chưa có gì có thể quan sát được bị tính phí. Và yêu cầu không mang trạng thái phía server. Sau sự kiện đầu ra đầu tiên, hành động đúng đắn là báo cáo lỗi thay vì phát lại nó.

TokenLab áp dụng quy tắc đó trên gateway của mình cho việc streaming Responses API, qua cả HTTP và WebSocket. Đường dẫn WebSocket đã được thay đổi vào ngày 2026-09-28 để khớp với HTTP.

Tại sao một stream lại khác với một yêu cầu thông thường

Một cuộc gọi không streaming sẽ trả về một body hoặc một lỗi. Bạn có thể thử lại lỗi đó vì bạn chưa nhận được gì cả.

Một stream cung cấp cho bạn đầu ra trước khi yêu cầu kết thúc. Sự kiện đầu ra đầu tiên là điểm không thể quay đầu. Nếu kết nối bị ngắt sau đó, bạn sẽ giữ một phần văn bản. Việc phát lại yêu cầu đồng nghĩa với việc tạo ra cùng một câu trả lời lần nữa và phải trả phí cho nó hai lần. Bạn cũng có thể làm trùng lặp một lệnh gọi công cụ (tool call) mà agent của bạn đã thực hiện.

Hướng dẫn TokenLab streaming guide nêu rõ điều này:

Sau khi sự kiện đầu tiên đến, một stream bị gián đoạn là chưa hoàn chỉnh và không được khởi động lại tự động.

Vì vậy, client của bạn cần một bit trạng thái cục bộ: saw_output. Nó chuyển sang true ngay khi bất kỳ đầu ra nào đến được code của bạn. Mọi quyết định thử lại đều đọc bit đó trước tiên.

Một stream kết thúc mà không có response.completed là một thất bại. Đừng cho rằng văn bản bạn có là hoàn chỉnh. Hãy xử lý các sự kiện response.failed, response.incomplete và error.

Quyết định phát lại, từng điểm một

TokenLab phát lại một yêu cầu một lần trên một route khả dụng khác khi tất cả các điều kiện sau được thỏa mãn. Yêu cầu là không trạng thái (stateless). Chưa có gì đến được client. Không có kết quả hoặc mức sử dụng nào được quan sát cho lần thử thất bại đó. Và lỗi là một sự kiện tiền đầu ra có thể thử lại hoặc lỗi đọc upstream trước sự kiện đầu tiên. Tối đa một lần phát lại xảy ra cho mỗi yêu cầu. Nếu lần thay thế cũng thất bại trước khi có đầu ra, lỗi đó sẽ không được phát lại nữa.

Nguồn: Hướng dẫn streaming và hành vi gateway của TokenLab, quan sát ngày 2026-09-28.

Điểm thất bại Được TokenLab phát lại? Lý do
Sự kiện tiền đầu ra có thể thử lại (response.failed, hoặc một sự kiện error được đánh dấu có thể thử lại, chẳng hạn như lỗi upstream nội bộ hoặc quá tải) Có, một lần, nếu yêu cầu là stateless Không có gì đến được client và không có mức sử dụng nào được quan sát, vì vậy lần thực thi thứ hai là vô hình.
Stream upstream bị ngắt (lỗi đọc) trước sự kiện đầu tiên Có, một lần, nếu yêu cầu là stateless Cùng một cửa sổ. Client không giữ đầu ra và không bị tính phí.
Lần thất bại thứ hai trước khi có đầu ra, sau một lần phát lại Không Ngân sách là một lần phát lại cho mỗi yêu cầu.
Bất kỳ lỗi nào sau khi đầu ra đã đến được client Không Client đã giữ một phần văn bản. Việc phát lại sẽ làm trùng lặp đầu ra và chi phí.
Phản hồi được lưu trữ (store), tiếp nối (previous_response_id), hoặc yêu cầu gắn với origin Không Lần thực thi thứ hai có thể tạo ra phản hồi được lưu trữ thứ hai hoặc làm lệch trạng thái hội thoại.
Timeout sự kiện đầu tiên Không Upstream có thể vẫn đang tạo. Việc phát lại có thể chạy cùng một công việc hai lần trong khi lần thử đầu tiên vẫn tiếp tục.
Tràn bộ đệm tiền đầu ra Không Giới hạn là cục bộ đối với gateway. Tiền tố quá khổ tương tự rất có thể sẽ lại chạm giới hạn đó trên route tiếp theo.
Client bị ngắt kết nối Không Client đã ngừng lắng nghe.
Lỗi tất định, ví dụ: yêu cầu không hợp lệ Không Thử lại không thể thay đổi kết quả. Được gửi đi mà không thay đổi.
Lỗi đã mang theo mức sử dụng Không Lần thử đã được đo lường. Được gửi đi mà không thay đổi.
Không còn route nào khác Không Không còn nơi nào để gửi nó. Client nhận lỗi với mã lỗi của riêng nó.

Khi một lỗi không được phát lại, hoặc không còn route nào khác, bạn nhận được nó với mã lỗi riêng. Các ví dụ công khai: stream_read_error khi stream upstream bị ngắt, và upstream_stream_buffer_limit khi tràn bộ đệm. Nếu việc chọn route tự thất bại sau một quyết định phát lại, lượt WebSocket kết thúc với websocket_response_failed (trạng thái 500) và khoản phí đã đặt trước được hoàn lại.

Việc thanh toán tuân theo cùng một quy tắc. Bạn chỉ trả tiền cho lần thử thành công. Một yêu cầu được phát lại có thể đã thực thi upstream hai lần, và chi phí upstream bổ sung đó là của TokenLab, vì không có gì đến được bạn từ lần thử đầu tiên. Một lượt thất bại không mang lại kết quả gì sẽ được hoàn tiền.

Một chi tiết về thời gian quan trọng đối với việc xử lý lỗi của bạn. Trước khi đầu ra bắt đầu, gateway giữ response.created và response.in_progress cho đến khi sự kiện đầu ra đầu tiên hoặc lỗi đến, trong tối đa 10 giây. Những sự kiện được giữ đó sau đó đến với bạn cùng với đầu ra đầu tiên, hoặc với sự kiện kết thúc. Thứ tự và nội dung không thay đổi. Bạn chỉ thấy chúng muộn hơn một chút. 10 giây đó là mức tối đa, không phải độ trễ điển hình.

Những gì đã thay đổi đối với WebSocket vào ngày 2026-09-28

TokenLab phục vụ Responses API qua HTTP streaming ("stream": true, server-sent events) và qua WebSocket tại wss://api.tokenlab.sh/v1/responses, nơi client gửi các sự kiện response.create. Các phản hồi WebSocket luôn được stream. Chúng không hỗ trợ background hoặc response.cancel. Mỗi kết nối xử lý một phản hồi hoạt động tại một thời điểm trong tối đa 60 phút.

Trước khi thay đổi, hai đường dẫn này không thống nhất. HTTP giữ các sự kiện vòng đời và phát lại các lỗi tiền đầu ra stateless. WebSocket chuyển tiếp response.created ngay lập tức và gửi các lỗi tiền đầu ra đến client, hoàn tiền cho chúng. Cùng một sự cố upstream tạo ra câu trả lời sạch trên HTTP và lỗi trên WebSocket.

Đường dẫn WebSocket hiện tuân theo quy tắc HTTP, bao gồm việc phát lại một stream bị ngắt trước khi bất kỳ sự kiện nào đến. Về nội bộ, hầu hết các lỗi upstream thấy trên các lượt WebSocket xảy ra trước bất kỳ đầu ra nào. Đó chính xác là cửa sổ mà việc phát lại là an toàn.

Gateway cải thiện trường hợp lỗi tiền đầu ra. Nó không đảm bảo rằng một stream sẽ hoàn tất.

Cách thay đổi được triển khai mà không làm hỏng hành vi khác

Công việc tuân theo một quy trình được xây dựng để phát hiện các thay đổi hành vi âm thầm.

  • Khóa hành vi. Trước khi thay đổi, mọi kịch bản lượt WebSocket đều được ghi lại dưới dạng fixture: các khung hình mà client nhận được, các cuộc gọi upstream được thực hiện và kết quả thanh toán. Bộ này đã tăng lên 63 kịch bản được ghi lại trong quá trình này. Một thay đổi hành vi phải được khai báo trước. Chỉ các fixture được nêu tên trong khai báo đó mới có thể thay đổi. Mọi fixture khác phải giữ nguyên byte-identical.
  • Kiểm tra đột biến. Mỗi quy tắc quyết định mới đều được kiểm tra bằng cách cố tình đảo ngược nó, chẳng hạn như phát lại timeout sự kiện đầu tiên hoặc không phát lại lỗi đọc, và xác nhận khóa thất bại.
  • Một sự kiểm tra lại. Phiên bản đầu tiên cũng làm cho trường hợp tràn bộ đệm có thể phát lại, với tuyên bố tương đương với HTTP. Việc xem xét cho thấy HTTP không bao giờ phát lại trường hợp đó, vì lý do trong bảng. Một bản cập nhật tiếp theo đã khôi phục hành vi cũ và thêm các kịch bản biên: lỗi đọc thứ hai không được phát lại, không còn route, lỗi sau khi giữ response.created, và một stream thay thế sau đó bị ngắt.

Nhật ký yêu cầu của một lượt thành công sau khi phát lại hiện cũng ghi lại lần thử thất bại trước đó, như HTTP đã làm.

Code client sở hữu quyết định thử lại

Đặt các lần thử lại tự động của SDK thành 0 cho các cuộc gọi streaming. Điều đó giữ cho quyết định nằm trong code của bạn. Giữ quyết định phát lại ở một nơi, không rải rác qua các trình xử lý. Đối với lỗi HTTP, hãy tôn trọng retryable và retry_after như được mô tả trong hướng dẫn xử lý lỗi, và giữ các ID yêu cầu.

SSE qua HTTP

import os

from openai import OpenAI

with OpenAI(
    api_key=os.environ["TOKENLAB_API_KEY"],
    base_url="https://api.tokenlab.sh/v1",
    timeout=30.0,
    max_retries=0,  # sở hữu quyết định thử lại thay vì gửi lại một stream đã đọc một nửa
) as client:
    completed, saw_output = False, False
    with client.responses.create(
        model="gpt-5.6-terra",
        input="Reply with one short sentence about retries.",
        stream=True,
    ) as stream:
        for event in stream:
            if event.type == "response.output_text.delta":
                saw_output = True
                print(event.delta, end="", flush=True)
            elif event.type == "response.completed":
                completed = True
            elif event.type in {"response.failed", "response.incomplete", "error"}:
                raise RuntimeError(f"{event.type} after_output={saw_output}")
    if not completed:
        raise RuntimeError(f"stream closed before response.completed, after_output={saw_output}")
    print()

Ví dụ sử dụng OpenAI SDK 2.15.0 với https://api.tokenlab.sh/v1 và max_retries=0. Nó theo dõi saw_output, và raise lỗi trên các sự kiện response.failed, response.incomplete và error, và trên một stream đóng trước response.completed. Đã xác minh với môi trường production vào ngày 2026-09-28 với gpt-5.6-terra.

Nếu lỗi đến với saw_output == false và yêu cầu đủ điều kiện (stateless, với lỗi có thể phát lại), TokenLab đã phát lại nó một lần; các phản hồi được lưu trữ, tiếp nối và timeout sự kiện đầu tiên không được phát lại chút nào. Quyết định ở cấp ứng dụng xem một yêu cầu mới có chấp nhận được không, vì một yêu cầu mới là một lần tạo mới. Nếu saw_output == true, hãy báo cáo lỗi và hiển thị những gì bạn có, hoặc cố tình loại bỏ phần văn bản đó.

WebSocket

import asyncio
import json
import os

import websockets

URL = "wss://api.tokenlab.sh/v1/responses"
TERMINAL = {"response.completed", "response.failed", "response.incomplete", "error"}


async def run_turn(prompt: str) -> str:
    headers = {"Authorization": f"Bearer {os.environ['TOKENLAB_API_KEY']}"}
    async with websockets.connect(URL, additional_headers=headers, max_size=None) as ws:
        await ws.send(json.dumps({
            "type": "response.create",
            "model": "gpt-5.6-terra",
            "input": prompt,
            "store": False,
        }))
        text, saw_output = [], False
        async for raw in ws:
            event = json.loads(raw)
            kind = event.get("type")
            if kind == "response.output_text.delta":
                saw_output = True
                text.append(event["delta"])
            elif kind in TERMINAL:
                if kind != "response.completed":
                    # Sau khi đầu ra đã bắt đầu, một lỗi là cuối cùng cho lượt này.
                    # Chỉ gửi lại nếu ứng dụng của bạn có thể loại bỏ phần văn bản đó.
                    raise RuntimeError(f"{kind} after_output={saw_output}: {json.dumps(event)[:300]}")
                return "".join(text)
        raise RuntimeError(f"socket closed before a terminal event, after_output={saw_output}")


print(asyncio.run(run_turn("Reply with one short sentence about retries.")))

Ví dụ sử dụng websockets 16.0, kết nối tới wss://api.tokenlab.sh/v1/responses với header Bearer, gửi một response.create với store: false, và thu thập response.output_text.delta. Nó raise lỗi với after_output trên bất kỳ sự kiện terminal nào không phải completed hoặc đóng sớm. Đã xác minh với môi trường production vào ngày 2026-09-28 với gpt-5.6-terra.

Cờ after_output có cùng ý tưởng với saw_output. Nó cho code gọi của bạn biết liệu một lượt mới có khả thi mà không làm trùng lặp các tác dụng phụ hay không.

Danh sách kiểm tra cho logic thử lại của riêng bạn

  • Luôn coi một stream kết thúc mà không có response.completed là một thất bại.
  • Theo dõi một boolean về việc đầu ra đã đến được code của bạn hay chưa. Chuyển nó sang true ở sự kiện đầu ra đầu tiên, không phải ở sự kiện vòng đời đầu tiên.
  • Một lỗi tiền đầu ra trên một yêu cầu đủ điều kiện đã được gateway phát lại một lần; một lần thử tiếp theo là quyết định của bạn.
  • Sau khi có đầu ra một phần, chỉ gửi lại nếu ứng dụng của bạn có thể loại bỏ phần văn bản đó và chấp nhận trả phí cho hai lần tạo.
  • Trong các vòng lặp agent, kiểm tra xem stream một phần đã chứa lệnh gọi công cụ mà code của bạn đã thực hiện hay chưa. Đừng phát lại một lượt mà các tác dụng phụ của nó bạn không thể hoàn tác.
  • Đối với các phản hồi được lưu trữ và tiếp nối previous_response_id, hãy kiểm tra trạng thái hiện có trước khi gửi lại bất cứ thứ gì.
  • Đặt số lần thử lại streaming thành 0 trong SDK của bạn và giữ quyết định phát lại trong một hàm.
  • Ghi nhật ký ID yêu cầu để bạn có thể khớp câu trả lời đã nhận với các lần thử đằng sau nó.

Câu hỏi thường gặp

TokenLab có khởi động lại stream sau khi có đầu ra một phần không?

Không. Khi đầu ra đã đến được client của bạn, lỗi được báo cáo và không bao giờ được phát lại. Bạn giữ phần văn bản đó, vì vậy việc khởi động lại sẽ làm trùng lặp đầu ra và chi phí. Ứng dụng của bạn quyết định xem có hiển thị, cắt bớt hoặc loại bỏ những gì nó có hay không.

Tôi có bị tính phí hai lần nếu gateway phát lại yêu cầu của tôi không?

Không. Bạn chỉ trả tiền cho lần thử thành công. Một yêu cầu được phát lại có thể đã thực thi upstream hai lần, nhưng không có gì đến được bạn từ lần thử đầu tiên, và chi phí upstream bổ sung đó là của TokenLab. Một lượt thất bại không mang lại kết quả gì sẽ được hoàn tiền.

Tại sao timeout sự kiện đầu tiên không được thử lại?

Vì upstream có thể vẫn đang tạo. Việc phát lại có thể chạy cùng một công việc hai lần trong khi lần thử đầu tiên vẫn tiếp tục. Timeout sự kiện đầu tiên được xử lý khác với lỗi đọc làm ngắt stream trước sự kiện đầu tiên.

Tôi có thể thử lại một phản hồi được lưu trữ hoặc tiếp nối previous_response_id không?

Không tự động. TokenLab không bao giờ phát lại các phản hồi được lưu trữ, tiếp nối hoặc các yêu cầu gắn với origin, vì lần thực thi thứ hai có thể tạo ra phản hồi được lưu trữ thứ hai hoặc làm lệch trạng thái hội thoại. Kiểm tra trạng thái hiện có trước khi bạn gửi lại bất cứ thứ gì, và chỉ gửi lại nếu ứng dụng của bạn có thể hòa giải trạng thái đó.

Nếu bạn muốn tự theo dõi luồng sự kiện thô, hãy tạo API key và ghi nhật ký mọi loại sự kiện mà client của bạn nhận được. Hướng dẫn streaming và hướng dẫn xử lý lỗi bao gồm toàn bộ tập sự kiện. Để biết thông tin cơ bản về cách gateway định tuyến và phục hồi, hãy xem TokenLab AI API reliability infrastructure và Responses API vs Chat Completions for agents.

Nguồn

Mô hình liên quan

Mô hình mới phát hành

Xây dựng với các mô hình trong hướng dẫn này

So sánh giá, thử route và biến nghiên cứu thành một lệnh gọi API chạy được.