Bạn có thể xây dựng một AI chatbot có khả năng truyền phát (stream) phản hồi chỉ trong một buổi chiều. Việc duy trì nó hoạt động sau lỗi 429 đầu tiên mới là công việc thực sự. Chúng ta sẽ xây dựng một dịch vụ FastAPI nhỏ với tính năng truyền phát SSE, bộ nhớ hội thoại phía máy chủ và danh sách cho phép (allowlist) các model, tất cả được bảo mật sau một khóa TokenLab duy nhất. Mục tiêu là tạo ra một backend mà bạn có thể đặt phía sau một giao diện sản phẩm thực tế và thay đổi các model mà không cần viết lại phần tích hợp. Nếu bạn chưa thực hiện việc chuyển đổi base URL, hãy đọc hướng dẫn di chuyển trước. Nếu mối quan tâm chính của bạn là việc xử lý quá tải (backoff), hãy kết hợp hướng dẫn này với hướng dẫn giới hạn tốc độ AI API.
Những điểm chính
- Một chatbot sẵn sàng cho sản xuất cần sáu thành phần: endpoint đồng bộ, endpoint truyền phát, trạng thái hội thoại phía máy chủ, danh sách cho phép model, xử lý lỗi thực tế và lộ trình nâng cấp lưu trữ.
- Tải khóa từ
TOKENLAB_API_KEYtrong mọi tiến trình và giữ nó trên máy chủ. Tài liệu xác thực (ghi nhận ngày 03/10/2026) khuyến cáo không bao giờ được để lộ khóa trong mã nguồn phía client hoặc commit chúng. GET /v1/modelslà công khai, vì vậy phản hồi thành công không chứng minh được khóa của bạn hoạt động. Hãy chứng minh bằng một yêu cầu chat thực tế (Quickstart, ghi nhận ngày 03/10/2026).- SSE phù hợp với hầu hết các sản phẩm chat. Hãy coi một luồng dữ liệu bị ngắt sau khi đã có đầu ra là chưa hoàn thành, không phải là một câu trả lời đã kết thúc.
- Danh sách cho phép trong hướng dẫn này ánh xạ các cấp độ (tier) tới
deepseek-v4-flash,gpt-5.5,claude-sonnet-5vàgemini-3.5-flash. Mỗi mục đều liệt kêopenai_chat_completionslà định dạng được chấp nhận (ghi nhận ngày 03/10/2026). - Theo ước tính từ giá bên dưới, một lượt chat với 2.000 token đầu vào và 500 token đầu ra có chi phí khoảng $0.0006 đến $0.0075, tùy thuộc vào model.
Xây dựng AI Chatbot: Thiết lập và Dịch vụ cốt lõi
Dịch vụ hoàn thiện có sáu thành phần chính:
- Endpoint
/chatđồng bộ để kiểm tra nhanh (smoke test). - Endpoint
/chat/streamtruyền phát cho giao diện người dùng thực tế. - Trạng thái hội thoại được khóa bởi
conversation_id. - Danh sách cho phép model, để frontend không thể yêu cầu các ID tùy ý.
- Xử lý lỗi không bị sụp đổ ngay ở lỗi 429 đầu tiên.
- Lộ trình từ bản mẫu trong bộ nhớ sang Redis hoặc PostgreSQL.
Đó là đủ cho một bot hỗ trợ, trợ lý nội bộ hoặc phiên bản đầu tiên của widget chat nhúng. Mỗi khối mã bên dưới được thêm vào một tệp, main.py, theo thứ tự hiển thị.
Bước 1: Thiết lập khóa và cài đặt stack
Tạo một khóa trong mục API Keys trong workspace của bạn. Giá trị đầy đủ chỉ được hiển thị một lần và tiền tố trong danh sách khóa không thể xác thực các yêu cầu. Tất cả các khóa TokenLab đều bắt đầu bằng sk-. Kiểm tra số dư của bạn trong mục Billing, vì yêu cầu đầu tiên sẽ được tính phí theo giá hiện tại của model (Quickstart, ghi nhận ngày 03/10/2026).
Lưu khóa trong biến môi trường, không bao giờ lưu trong mã nguồn. Lệnh dưới đây giữ nó không xuất hiện trong dòng lệnh và lịch sử shell, và nó chỉ áp dụng cho terminal hiện tại cùng các tiến trình con. Hãy sử dụng trình quản lý bí mật (secret manager) cho các ứng dụng đã triển khai. Quickstart cũng có phiên bản cho PowerShell.
printf 'TokenLab API key: '
read -r -s TOKENLAB_API_KEY
printf '\n'
export TOKENLAB_API_KEY
pip install fastapi uvicorn openai pydantic
Bạn không cần SDK riêng của TokenLab. SDK chính thức của OpenAI hoạt động ngay khi bạn thay đổi khóa và base URL (Hướng dẫn SDK, ghi nhận ngày 03/10/2026).
Bước 2: Xác định schema, danh sách cho phép và bộ nhớ
Chúng ta bắt đầu với các phần dùng chung. Schema yêu cầu sử dụng tier, không phải chuỗi model thô, và mọi ví dụ curl sau này trong hướng dẫn này đều gửi các trường tương tự.
Bản demo chatbot đầu tiên thường giữ bản ghi trong trình duyệt và gửi lại mỗi lượt. Ví dụ, điều đó hiệu quả cho một bản mẫu. Nó sẽ trở nên lộn xộn khi bạn cần thử lại, các phiên có thể tiếp tục hoặc các công cụ phía máy chủ. Vì vậy, máy chủ sẽ sở hữu lịch sử ngay từ đầu.
import json
import logging
import os
import time
import uuid
from collections import defaultdict
from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from openai import APIConnectionError, APIStatusError, OpenAI, RateLimitError
from pydantic import BaseModel, Field
log = logging.getLogger("chatbot")
app = FastAPI()
client = OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
timeout=30.0,
max_retries=0,
)
ALLOWED_MODELS = {
"default": "deepseek-v4-flash",
"flagship": "gpt-5.5",
"balanced": "claude-sonnet-5",
"budget": "gemini-3.5-flash",
}
SYSTEM_PROMPT = "You are a helpful assistant. Be concise and direct."
MAX_HISTORY = 20 # số tin nhắn giữ lại mỗi hội thoại (tùy chọn)
MAX_REPLY_TOKENS = 1000 # giới hạn đầu ra mỗi lượt (tùy chọn)
class ChatRequest(BaseModel):
message: str = Field(min_length=1, max_length=4000)
tier: str = "default"
conversation_id: str | None = None
def resolve_model(tier: str) -> str:
model = ALLOWED_MODELS.get(tier)
if model is None:
raise HTTPException(status_code=400, detail=f"unknown tier: {tier}")
return model
conversations: dict[str, list[dict]] = defaultdict(list)
def build_messages(conv_id: str, user_msg: str) -> list[dict]:
history = conversations[conv_id][-MAX_HISTORY:]
return [
{"role": "system", "content": SYSTEM_PROMPT},
*history,
{"role": "user", "content": user_msg},
]
def remember(conv_id: str, user_msg: str, reply: str) -> None:
conversations[conv_id].append({"role": "user", "content": user_msg})
conversations[conv_id].append({"role": "assistant", "content": reply})
Client đặt max_retries=0 và thời gian chờ 30 giây, khớp với Quickstart. Chúng ta tự đưa ra quyết định thử lại, vì việc SDK tự động thử lại trên một lượt truyền phát có thể dẫn đến việc bị tính phí hai lần.
Bước 3: Ánh xạ lỗi và thêm endpoint đồng bộ
Chúng ta giữ một hàm nhỏ để chuyển đổi bất kỳ ngoại lệ SDK nào thành mã trạng thái và thông báo an toàn cho người dùng. Chi tiết lỗi sẽ được ghi vào log của bạn, không gửi cho người dùng.
def describe_error(exc: Exception) -> tuple[int, str]:
if isinstance(exc, RateLimitError): # kiểm tra trước: nó là lớp con của APIStatusError
return 429, "The assistant is busy. Please retry shortly."
if isinstance(exc, APIConnectionError):
return 502, "Connection problem. Please retry."
if isinstance(exc, APIStatusError):
log.error("upstream status=%s code=%s", exc.status_code, exc.code)
if exc.status_code in (401, 402, 403): # khóa, số dư hoặc quyền: vấn đề của người vận hành
return 503, "The assistant is unavailable right now."
if exc.status_code in (400, 404): # trường hoặc ID model sai: lỗi của chúng ta
return 500, "Something went wrong on our side."
return 502, "The assistant is unavailable right now."
return 500, "Something went wrong."
def retry_delay(exc: RateLimitError) -> float:
try:
return min(float(exc.response.headers.get("retry-after", "2")), 10.0)
except ValueError:
return 2.0
def create_with_one_retry(**kwargs):
try:
return client.chat.completions.create(**kwargs)
except RateLimitError as exc:
time.sleep(retry_delay(exc))
return client.chat.completions.create(**kwargs)
@app.post("/chat")
def chat(req: ChatRequest):
model = resolve_model(req.tier)
conv_id = req.conversation_id or str(uuid.uuid4())
messages = build_messages(conv_id, req.message)
try:
response = create_with_one_retry(
model=model, messages=messages, max_tokens=MAX_REPLY_TOKENS
)
except (APIConnectionError, APIStatusError) as exc:
status, message = describe_error(exc)
raise HTTPException(status_code=status, detail=message)
reply = response.choices[0].message.content or ""
log.info("model=%s usage=%s", model, response.usage)
remember(conv_id, req.message, reply)
return {"conversation_id": conv_id, "reply": reply}
Hãy chạy một yêu cầu trước khi xây dựng bất cứ thứ gì khác. Nếu bước này thất bại, đừng tiếp tục xây dựng trên đó.
Truyền phát phản hồi và giữ cho AI Chatbot phản hồi nhanh
Hầu hết các sản phẩm chatbot cảm thấy chậm không phải vì model chậm, mà vì giao diện người dùng vẫn trống cho đến khi toàn bộ phản hồi được tải xong. Chúng ta thêm SSE vì nó bao phủ hầu hết các sản phẩm chat và mang ít gánh nặng vận hành hơn websockets. Nếu sản phẩm của bạn đã chạy một client trình duyệt qua HTTP thuần túy, SSE giúp kiến trúc đơn giản hơn.
Bước 4: Thêm endpoint truyền phát
Tài liệu truyền phát (Hướng dẫn truyền phát, ghi nhận ngày 03/10/2026) giữ cho Chat Completions streaming hợp lệ với các framework đã mong đợi các đoạn SSE từ /v1/chat/completions. Chúng ta tuân theo lộ trình đó vì cả bốn model trong danh sách cho phép đều chấp nhận nó. Chúng ta mã hóa mỗi sự kiện dưới dạng JSON, vì vậy các ký tự xuống dòng bên trong phản hồi không thể phá vỡ khung dữ liệu.
def sse(payload: dict) -> str:
return f"data: {json.dumps(payload)}\n\n"
@app.post("/chat/stream")
def chat_stream(req: ChatRequest):
model = resolve_model(req.tier)
conv_id = req.conversation_id or str(uuid.uuid4())
messages = build_messages(conv_id, req.message)
def generate():
parts: list[str] = []
finish_reason = None
try:
with client.chat.completions.create(
model=model,
messages=messages,
max_tokens=MAX_REPLY_TOKENS,
stream=True,
stream_options={"include_usage": True},
) as stream:
for chunk in stream:
if chunk.usage:
log.info("model=%s usage=%s", model, chunk.usage)
if not chunk.choices:
continue
choice = chunk.choices[0]
if choice.delta.content:
parts.append(choice.delta.content)
yield sse({"delta": choice.delta.content})
if choice.finish_reason:
finish_reason = choice.finish_reason
except (APIConnectionError, APIStatusError) as exc:
_, message = describe_error(exc)
yield sse({"error": message})
return
if finish_reason != "stop":
yield sse({"error": f"Reply incomplete ({finish_reason})."})
return
remember(conv_id, req.message, "".join(parts))
yield sse({"done": True, "conversation_id": conv_id})
return StreamingResponse(generate(), media_type="text/event-stream")
Hai chi tiết quan trọng ở đây. Thứ nhất, include_usage: true thêm mức sử dụng token vào các đoạn stream, và đoạn sử dụng cuối cùng không có choices, vì vậy vòng lặp sẽ bỏ qua nó. Thứ hai, chúng ta chỉ lưu lượt chat khi finish_reason là stop. Kết thúc kiểu length có nghĩa là giới hạn token đã cắt ngang phản hồi, và tài liệu liệt kê cả hai là điều kiện kết thúc bình thường.
Vì các lỗi xuất hiện bên trong trình tạo (generator), trạng thái HTTP đã là 200 vào thời điểm xảy ra lỗi. Client phải đọc sự kiện error. Khối with đóng luồng upstream khi trình tạo thoát. Tài liệu khuyên nên hủy upstream khi người dùng dừng hoặc ngắt kết nối, vì vậy hãy kiểm tra xem máy chủ ASGI của bạn có thực sự dừng trình tạo khi ngắt kết nối hay không.
Bước 5: Đọc luồng trong trình duyệt
Trình duyệt nên gọi backend đã xác thực của riêng bạn và không bao giờ gọi trực tiếp TokenLab. Hãy phân tích theo ranh giới sự kiện, vì một đoạn mạng không nhất thiết là một sự kiện SSE hoàn chỉnh.
async function sendMessage(payload, onDelta) {
const response = await fetch('/chat/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload), // { message, tier, conversation_id }
});
if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const events = buffer.split('\n\n');
buffer = events.pop();
for (const raw of events) {
if (!raw.startsWith('data: ')) continue;
const event = JSON.parse(raw.slice(6));
if (event.error) throw new Error(event.error);
if (event.delta) onDelta(event.delta);
}
}
}
Nâng cấp bộ nhớ lên Redis khi bạn cần
Từ điển trong bộ nhớ là ổn cho một tiến trình. Hãy sử dụng Redis khi các hội thoại cần TTL, khả năng tiếp tục hoặc nhiều instance. Hãy sử dụng PostgreSQL khi bản ghi là dữ liệu sản phẩm mà bạn phải truy vấn, kiểm toán hoặc báo cáo. Thay đổi Redis là về hệ thống lưu trữ, không phải logic. Chạy pip install redis, sau đó để build_messages và remember đọc và ghi thông qua các hàm hỗ trợ này.
import redis
redis_client = redis.Redis(
host=os.environ.get("REDIS_HOST", "127.0.0.1"), port=6379, decode_responses=True
)
def load_history(conv_id: str) -> list:
raw = redis_client.get(f"chat:{conv_id}")
return json.loads(raw) if raw else []
def save_history(conv_id: str, history: list) -> None:
redis_client.setex(f"chat:{conv_id}", 60 * 60 * 24, json.dumps(history))
Xử lý lỗi khi xây dựng AI Chatbot cho sản xuất
Người dùng cuối không quan tâm liệu lỗi đến từ việc giới hạn tốc độ, số dư trống hay sự cố upstream. Họ quan tâm liệu giao diện người dùng có bị đóng băng hay không. Hãy tưởng tượng một widget hỗ trợ có luồng bị chết sau bốn mươi từ. Tài liệu TokenLab cho biết một luồng bị gián đoạn sau khi đã có đầu ra không phải là một phản hồi hoàn chỉnh, và việc lặp lại nó có thể tạo ra đầu ra khác và tính phí lần thứ hai (Xử lý lỗi API, ghi nhận ngày 03/10/2026). Đó là lý do tại sao Bước 4 không bao giờ lưu một lượt chat chưa hoàn thành.
Bước 6: Áp dụng các quy tắc thử lại
Xử lý lỗi theo trạng thái HTTP và code, không phải theo message có thể đọc được bởi con người, vì nó có thể thay đổi mà không cần báo trước. Bảng dưới đây kết hợp tài liệu với những gì dịch vụ của chúng ta thực hiện.
| Trạng thái | Ý nghĩa (theo tài liệu) | Dịch vụ này thực hiện |
|---|---|---|
400 |
Trường, ID model hoặc đầu vào không hợp lệ | Trả về 500, ghi log, không bao giờ thử lại |
401 |
Khóa bị thiếu, không hợp lệ, hết hạn hoặc bị thu hồi | Trả về 503, cảnh báo người vận hành |
402 |
Số dư hoặc giới hạn API-key quá thấp (insufficient_balance, quota_exceeded) |
Trả về 503, cảnh báo người vận hành |
403 |
Khóa không thể sử dụng model này | Trả về 503, kiểm tra danh sách model của khóa |
404 |
Tài nguyên hoặc model không tìm thấy | Trả về 500, kiểm tra ID so với /v1/models |
429 |
Đã đạt giới hạn yêu cầu | Đợi Retry-After, thử lại một lần (chỉ đồng bộ) |
500–504 |
Dịch vụ không khả dụng hoặc lỗi mạng | Chỉ thử lại khi retryable là true; tôn trọng retry_after |
Hai lưu ý từ tài liệu. Một lỗi 503 all_channels_failed không phải lúc nào cũng có nghĩa là sự cố tạm thời. Nếu retryable là false, đừng lặp lại cùng một yêu cầu và hãy kiểm tra GET /v1/models trước khi chọn model khác. Ngoài ra, Chat Completions và Responses sử dụng đối tượng error kiểu OpenAI, nhưng Anthropic Messages và Gemini giữ các định dạng riêng. Dịch vụ của chúng ta gọi Chat Completions trên mọi model trong danh sách cho phép, vì vậy một trình phân tích cú pháp là đủ. Nếu bạn chuyển một tier sang định dạng gốc, bạn cần trình phân tích cú pháp thứ hai.
Lưu ID Yêu cầu từ các header phản hồi cùng với endpoint, model, thời gian và ID người dùng hoặc công việc của riêng bạn. Tài liệu không đặt tên header trên các trang chúng ta đã sử dụng, vì vậy hãy xác nhận nó dựa trên lộ trình khắc phục sự cố trước khi bạn kết nối các log của mình.
Phần còn lại của danh sách kiểm tra sản xuất rất ngắn:
- Thêm ID yêu cầu của riêng bạn để bạn có thể kết nối lỗi frontend với log backend.
- Giới hạn đồng thời mỗi người dùng và kích thước yêu cầu.
max_length=4000trênmessagelà điểm khởi đầu bạn nên điều chỉnh. - Giới hạn lịch sử theo số lượng tin nhắn, như
MAX_HISTORYthực hiện. Tài liệu thanh toán cho biết việc token hóa khác nhau tùy theo model, ngôn ngữ và đầu vào phương tiện, vì vậy công thức đếm ký tự không phải là ước tính thanh toán đáng tin cậy. - Ghi log model, độ trễ, mức sử dụng và lý do kết thúc trong mỗi lượt.
- Giữ các thông báo hiển thị cho người dùng tách biệt với chi tiết lỗi nội bộ.
- Gửi một lượt kiểm tra đến tier thay thế, để bạn biết dự phòng hoạt động trước khi sự cố thực sự đầu tiên xảy ra.
Chọn Model và Ước tính chi phí cho AI Chatbot
Không bao giờ để frontend truyền một chuỗi model tùy ý vào API. Một trường văn bản tự do dễ dẫn đến lỗi chính tả, các model đã nghỉ hưu và các model bạn không bao giờ có ý định công khai. Bản đồ ALLOWED_MODELS cung cấp cho bạn một nơi để thay đổi model khi nhà cung cấp ngừng hỗ trợ một model nào đó, mà không cần cập nhật client. ID model không xác định sẽ trả về model_not_found, và tài liệu hướng dẫn bạn đến /v1/models để lấy ID hiện tại. Kiểm tra danh mục model trước khi bạn khóa danh sách cho phép.
Giá hiện tại trên mỗi 1 triệu token
Đây là giá TokenLab bằng USD từ mục API trực tiếp của mỗi model, ghi nhận ngày 03/10/2026. Giá được cập nhật lần cuối vào 2026-10-02T16:53:30.068Z. Cả bốn model đều hiển thị giai đoạn vòng đời là active.
| Tier | Model ID | Đầu vào / 1M | Đầu ra / 1M | Cache read / 1M | Nguồn |
|---|---|---|---|---|---|
| default | deepseek-v4-flash (ngoài giờ cao điểm) |
$0.15 | $0.60 | $0.003 | model API |
| default | deepseek-v4-flash (giờ cao điểm) |
$0.30 | $1.20 | $0.006 | model API |
| flagship | gpt-5.5 |
$1.50 | $9.00 | $0.15 | model API |
| balanced | claude-sonnet-5 |
$0.90 | $4.50 | $0.09 | model API |
| budget | gemini-3.5-flash |
$0.75 | $4.50 | $0.075 | model API |
Đối với deepseek-v4-flash, mục nhập liệt kê các cửa sổ cao điểm vào các ngày trong tuần, không bao gồm các ngày lễ của Trung Quốc. Ngoài giờ cao điểm bao gồm cuối tuần và các ngày lễ đó. claude-sonnet-5 cũng liệt kê giá ghi cache là $1.125 mỗi 1 triệu token.
Một yêu cầu đã thực hiện
Giả sử một lượt với 2.000 token đầu vào và 500 token đầu ra. Kích thước đó là giả định của chúng tôi, không phải phép đo. Đối với gpt-5.5:
- Đầu vào: 2.000 × $1.50 / 1.000.000 = $0.0030
- Đầu ra: 500 × $9.00 / 1.000.000 = $0.0045
- Tổng cộng: $0.0075 mỗi lượt (ước tính, không có cache hit)
Sử dụng cùng phép tính số học, chúng ta có các ước tính sau: deepseek-v4-flash tốn $0.0006 ngoài giờ cao điểm và $0.0012 vào giờ cao điểm, claude-sonnet-5 tốn $0.00405, và gemini-3.5-flash tốn $0.00375. Qua 1.000 lượt như vậy, đó là khoảng $7.50 trên gpt-5.5 so với $0.60 đến $1.20 trên deepseek-v4-flash. Các lượt thực tế tăng lên khi lịch sử tích lũy, vì vậy hãy đọc usage từ phản hồi hoặc đoạn stream cuối cùng thay vì tin tưởng vào ước tính này.
Trong thực tế, chúng ta định tuyến hầu hết các hội thoại đến tier mặc định và dành riêng gpt-5.5 hoặc claude-sonnet-5 cho các lượt cần chúng. Tài liệu thanh toán cảnh báo rằng giá thấp nhất trên mỗi token không phải lúc nào cũng là chi phí thấp nhất cho mỗi tác vụ hoàn thành. Hãy chạy cùng các prompt qua hai tier và so sánh chi phí cuối cùng trong mục Usage.
Hai kiểm tra trước khi bạn xuất xưởng. Tài liệu thanh toán cho biết không nên hard-code bảng giá đã sao chép, vì vậy hãy đọc GET /v1/models/:model/pricing nếu ứng dụng của bạn hiển thị chi phí (Thanh toán và định giá, ghi nhận ngày 03/10/2026). Ngoài ra, mục deepseek-v4-flash liệt kê json-mode, prompt-cache và tool-use nhưng không có vision. Nếu chatbot của bạn chấp nhận hình ảnh, hãy định tuyến các lượt đó đến một tier có liệt kê vision.
Kiểm tra nhanh và xuất xưởng AI Chatbot
Bước 7: Chạy từ đầu đến cuối
Khởi động máy chủ trong terminal nơi TOKENLAB_API_KEY đã được export. Các payload curl sử dụng cùng các trường message, tier và conversation_id như ChatRequest.
uvicorn main:app --reload --port 8000
# 1. Endpoint đồng bộ: mong đợi một phản hồi ngắn và một conversation_id
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "Reply only with OK.", "tier": "default"}'
# 2. Truyền phát: mong đợi các dòng data: {"delta": ...}, sau đó là {"done": true, ...}
curl -N -X POST http://localhost:8000/chat/stream \
-H "Content-Type: application/json" \
-d '{"message": "Hello!", "tier": "default"}'
# 3. Bộ nhớ: sử dụng lại conversation_id đã trả về ở trên
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "What did I just say?", "tier": "default", "conversation_id": "PASTE_ID_FROM_STEP_1"}'
# 4. Lỗi cưỡng bức: mong đợi HTTP 400 và "unknown tier: nope"
curl -i -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "Hello!", "tier": "nope"}'
Để buộc lỗi upstream, hãy khởi động lại máy chủ với TOKENLAB_API_KEY sai. Client của bạn sẽ nhận được thông báo 503 sạch sẽ, và log của bạn sẽ hiển thị lỗi 401 từ upstream với invalid_api_key. Sau đó mở Requests để xác nhận model, trạng thái và mức sử dụng của các cuộc gọi thành công. Nếu bạn có thể truyền phát một lượt, bảo tồn một hội thoại và trả về lỗi sạch sẽ khi có lỗi cưỡng bức, bạn đã có nền tảng đúng đắn.
Sau đó, các nâng cấp tiếp theo là cơ sở hạ tầng nhàm chán thay vì AI:
- Xác thực, để một người dùng không thể đọc hội thoại của người dùng khác.
- Tính bền vững, để các phiên làm việc tồn tại sau khi triển khai.
- Giới hạn tốc độ mỗi người dùng, để một người dùng ồn ào không thể đốt cháy hạn ngạch của bạn.
- Gán mức sử dụng, nếu chatbot hướng tới khách hàng.
- Tóm tắt nền, nếu các hội thoại cần bộ nhớ dài hạn.
Khi việc di chuyển base URL đã hoàn tất, thay đổi model chỉ là một chỉnh sửa cấu hình cho ALLOWED_MODELS.
Câu hỏi thường gặp
Tôi có cần websockets để xây dựng AI chatbot không?
Không. SSE, được hiển thị trong Bước 4, bao phủ hầu hết các sản phẩm chat. Websockets thêm giá trị khi bạn cần đẩy hai chiều bên ngoài yêu cầu và phản hồi, chẳng hạn như cộng tác trực tiếp hoặc các sự kiện do máy chủ khởi tạo. Đối với giao diện chat tiêu chuẩn, SSE đơn giản hơn để triển khai và gỡ lỗi.
Chatbot của tôi nên sử dụng model nào theo mặc định?
Bắt đầu với tier giá thấp như deepseek-v4-flash và thêm claude-sonnet-5 hoặc gpt-5.5 phía sau danh sách cho phép cho các lượt khó hơn. Giá ở trên, ghi nhận ngày 03/10/2026, cho thấy khoảng cách. Sau đó, kiểm tra các prompt của riêng bạn trên hai tier và so sánh chi phí cuối cùng trong Usage. Kiểm tra danh mục model trước khi bạn cam kết, vì các dòng sản phẩm thay đổi.
Tại sao chatbot của tôi trả về 402 hoặc 429?
Lỗi 402 có nghĩa là số dư tài khoản hoặc giới hạn sử dụng của khóa quá thấp. Các mã lỗi là insufficient_balance và quota_exceeded, và việc lặp lại yêu cầu sẽ không giúp ích gì. Lỗi 429 có nghĩa là bạn đã đạt giới hạn yêu cầu, vì vậy hãy đợi Retry-After rồi thử lại. Endpoint đồng bộ của chúng tôi thử lại một lần và endpoint truyền phát báo cáo lỗi cho client.
Trình duyệt có thể gọi trực tiếp TokenLab không?
Không an toàn. Tài liệu xác thực cho biết không bao giờ được để lộ API key trong mã phía client. Hãy giữ khóa trong môi trường máy chủ và để trình duyệt gọi backend đã xác thực của riêng bạn, như mẫu frontend đã làm.
Tạo API key tại tokenlab.sh, thêm tiền và chạy kiểm tra nhanh ở trên.
Nguồn
Giá quan sát ngày 2026-10-03
- TokenLab Docs: QuickstartQuan sát ngày 2026-10-03
- TokenLab Docs: AuthenticationQuan sát ngày 2026-10-03
- TokenLab Docs: StreamingQuan sát ngày 2026-10-03
- TokenLab Docs: Handle API errorsQuan sát ngày 2026-10-03
- TokenLab Docs: Billing and pricingQuan sát ngày 2026-10-03
- TokenLab Docs: SDKs & LibrariesQuan sát ngày 2026-10-03
- TokenLab Docs: Create Chat CompletionQuan sát ngày 2026-10-03
- TokenLab Docs: List ModelsQuan sát ngày 2026-10-03



