Tính toán giới hạn trước
Ba con số quyết định mọi thứ:
- Tổng lượng ngữ cảnh 100.000 token, prompt và completion cộng lại.
max_tokensmặc định là 2048, tối đa mỗi yêu cầu là 16.000.- Thân yêu cầu không vượt quá 8 MB; giới hạn này hiếm khi chạm với văn bản thuần túy, thực tế token mới là thứ chạm giới hạn trước.
Công thức ngân sách rất đơn giản: prompt khả dụng = 100000 - max_tokens. Bạn muốn mô hình xuất 4000 token một lần, prompt tối đa chỉ còn 60.000. Khi vượt quá, API trả về 400, không tự động cắt ngắn cho bạn. Vì vậy, "nhét cả cuốn sách vào rồi hẵng tính" không phải là chiến lược, mà chỉ là may rủi.
Một hiểu lầm phổ biến khác: max_tokens càng lớn càng an toàn. Thực tế, đây là không gian dành trước cho đầu ra; nếu đặt 16.000, prompt chỉ còn 48.000. Hãy đặt theo nhu cầu, đừng tăng tối đa vô lý.
Ví dụ tính toán: giả sử bạn có bản ghi phỏng vấn 300.000 chữ cần tóm tắt. Ước lượng sơ bộ 1,3 token/chữ, khoảng 390.000 token, gấp hơn sáu lần giới hạn, dù sao cũng không thể gửi nguyên cả bản. Giả sử mỗi lần tóm tắt cần 800 token đầu ra, thì mỗi khối đầu vào tối đa 63.000, thực tế cần dành thêm 10% dự phòng, chỉ còn hơn 50.000. Nhưng đây chưa phải giải pháp tối ưu nhất: khi khối quá lớn, khả năng tập trung vào phần giữa của mô hình sẽ giảm, vì vậy cách làm dưới đây là chia nhỏ khối, thà gọi nhiều lần hơn.
Ước lượng token: thuật toán xấp xỉ
Khi không có bộ phân tích từ cục bộ, bạn có thể dùng các giá trị kinh nghiệm sau để ước lượng sơ bộ. Các giá trị dưới đây chỉ là xấp xỉ, không phải giá trị chính xác, khác biệt giữa các loại văn bản có thể đạt 10-20%:
| Loại văn bản | Quy đổi xấp xỉ |
|---|---|
| Văn bản tiếng Trung | Khoảng 1 đến 1,5 token mỗi chữ |
| Tiếng Anh | Khoảng 1 token cho 4 ký tự, hoặc khoảng 1,3 token mỗi từ |
| Mã nguồn, JSON, văn bản nhiều ký hiệu | Tốn nhiều token hơn văn bản thông thường, nên ước lượng theo giá trị cao hơn một chút |
Hàm ước lượng đủ dùng kèm tính toán ngân sách dự phòng như sau:
CONTEXT = 100000
def est_tokens(text: str) -> int:
"""粗估:中文按每字 1.3 token,其余字符按每 4 个字符 1 token。仅为近似。"""
zh = sum(1 for c in text if "\u4e00" <= c <= "\u9fff")
other = len(text) - zh
return int(zh * 1.3 + other / 4) + 1
def max_prompt_budget(max_tokens: int, margin: float = 0.1) -> int:
"""给定计划的 max_tokens,返回 prompt 最多能用多少 token(留 margin 余量)。"""
return int((CONTEXT - max_tokens) * (1 - margin))
# 例:计划让模型写 3000 token,prompt 预算
print(max_prompt_budget(3000)) # 54900Phương pháp hiệu chỉnh: gửi một đoạn đầu vào điển hình, đọc usage.prompt_tokens trong phản hồi, so sánh với ước lượng của bạn để tính hệ số nhân thực tế cho dữ liệu nghiệp vụ của bạn, sau đó thay thế hệ số bằng giá trị đo được. Ước lượng chỉ dùng để quyết định "có cần phân đoạn không", không dùng để đối chiếu, đối chiếu xem usage.
Tóm tắt tài liệu dài: phân đoạn và hợp nhất
Khi tài liệu vượt quá ngân sách, phương pháp chuẩn là map-reduce: phân đoạn để tóm tắt riêng lẻ, sau đó hợp nhất các tóm tắt thành bản tóm tắt tổng thể. Lưu ý các chi tiết sau:
- Cắt theo ranh giới đoạn văn, không cắt cứng theo số chữ cố định, nếu không sẽ làm đứt ngang câu.
- Dành đủ ngân sách cho mỗi khối; khuyến nghị không vượt 1/3 giới hạn để dành chỗ cho chỉ thị và đầu ra.
- Yêu cầu tóm tắt từng phần phải giữ lại tên người, số liệu và sự kiện then chốt, nếu không thông tin sẽ mất khi hợp nhất.
- Nếu bản tóm tắt của bản tóm tắt vẫn quá dài, hãy thực hiện thêm một lớp đệ quy.
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.wushenchaapi.com/v1", api_key=os.environ["API_KEY"])
def split_paragraphs(text, budget):
"""按段落切块,每块估算不超过 budget token。"""
chunks, cur, used = [], [], 0
for p in text.split("\n"):
t = int(len(p) * 1.3) + 1 # 中文粗估
if used + t > budget and cur:
chunks.append("\n".join(cur))
cur, used = [], 0
cur.append(p)
used += t
if cur:
chunks.append("\n".join(cur))
return chunks
def ask(prompt, max_tokens=800):
r = client.chat.completions.create(
model="uncensored",
temperature=0.3,
max_tokens=max_tokens,
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content
def summarize_long(text, budget=12000):
parts = split_paragraphs(text, budget)
partial = [ask("用不超过 200 字概括下面这段,保留人名和关键事件:\n\n" + p) for p in parts]
merged = "\n".join(f"第{i+1}部分:{s}" for i, s in enumerate(partial))
return ask("下面是分段摘要,请合并成一份 400 字以内的整体摘要:\n\n" + merged, max_tokens=900)Ở đây temperature lấy 0.3, nhiệm vụ tóm tắt cần sự ổn định. Các lần gọi đoạn độc lập với nhau, có thể chạy song song, nhưng lưu ý giới hạn 300 yêu cầu mỗi phút cho mỗi khóa API; tài liệu hàng chục đoạn sẽ không chạm giới hạn này.
Không có câu trả lời chuẩn cho việc xác định kích thước khối. Theo kinh nghiệm, mỗi khối 10.000 đến 15.000 token cân bằng giữa việc giữ chi tiết và số lần gọi. Chạy thử một mẫu với ba kích thước khối 5000, 12000 và 25000 token, so sánh số lượng sự kiện quan trọng bị thiếu trong bản tóm tắt để chọn giá trị phù hợp nhất. Với tài liệu có mật độ thông tin cao như hợp đồng hoặc tài liệu kỹ thuật, khối nên nhỏ hơn; với ghi chú hội thoại có nhiều thông tin dư thừa, khối có thể lớn hơn.
Tiếp tục tiểu thuyết: cửa sổ trượt và dàn ý động
Khi viết đến chương thứ mười mấy, toàn bộ nội dung đã không thể chứa hết và cũng không cần thiết phải chứa hết. Tư duy gồm hai lớp bộ nhớ:
- Nhìn gần: 2-3 chương gốc gần nhất, đảm bảo tính liên tục về văn phong, nhịp hội thoại và chi tiết bối cảnh.
- Nhìn xa: Các chương trước đó được nén thành outline, giữ lại mối quan hệ nhân vật, manh mối và các manh mối chưa được giải quyết.
Sau mỗi chương, hãy yêu cầu mô hình tóm tắt chương đó thành ba đến năm dòng và thêm vào dàn ý. Khi dàn ý vượt quá 3000 token, hãy nén toàn bộ dàn ý một lần nữa.
def continue_story(chapters, outline, new_hint, keep_last=3):
"""chapters: 已写章节列表。只带最近 keep_last 章原文,更早的用 outline(滚动大纲)代替。"""
recent = "\n\n".join(chapters[-keep_last:])
prompt = (
f"【全书大纲(早期章节摘要)】\n{outline}\n\n"
f"【最近章节原文】\n{recent}\n\n"
f"【下一章要求】\n{new_hint}\n\n请写下一章,约 2000 字。"
)
return ask(prompt, max_tokens=4000)Manh mối là thứ dễ bị mất nhất. Nên liệt kê riêng mục "manh mối chưa được giải quyết" trong outline, khi tiếp tục viết yêu cầu rõ ràng xử lý một trong số chúng. Tên nhân vật, địa danh cũng nên liệt kê thành danh sách cố định đặt trong system để tránh việc mô hình tự đổi tên khi trượt ra khỏi cửa sổ.
Một vấn đề dễ bị bỏ qua khi tiếp tục viết là sự trôi dạt phong cách. Mô hình có xu hướng viết ngày càng giống với vài chương gần nhất; nếu phong cách các chương trước khác biệt, sự chênh lệch này sẽ được khuếch đại. Giải pháp là thêm một “mẫu văn phong” vào system prompt, chọn một đoạn văn bản gốc mà bạn hài lòng nhất làm neo cố định, không bị trượt theo cửa sổ ngữ cảnh.
max_tokens và cắt ngắn
Có hai trường hợp đầu ra bị cắt ngắn mà bạn cần phân biệt:
finish_reasonlàlength: bạn đã chạm giới hạn max_tokens đã đặt. Giải pháp là tăng max_tokens, hoặc yêu cầu mô hình viết theo đoạn, "viết đến đây dừng lại, tôi sẽ ra lệnh tiếp tục".- Yêu cầu trả về 400 ngay lập tức: prompt cộng max_tokens đã vượt quá 100.000. Giải pháp là rút ngắn đầu vào hoặc giảm max_tokens.
Bảng đối chiếu ngân sách thực tế:
| Nhiệm vụ | max_tokens đề xuất | prompt khả dụng (khoảng) |
|---|---|---|
| tóm tắt, trích xuất | 800 | 63,200 |
| viết tiếp chương (khoảng 2000 từ) | 4000 | 60,000 |
| xuất văn bản dài hàng vạn từ | 16000 (giới hạn) | 48,000 |
Đầu ra dài chịu ảnh hưởng bởi "thời gian phản hồi", nên kết hợp với truyền phát (streaming) để hiển thị ngay khi mô hình sinh ra.
Khi phần tiếp nối bị cắt, đừng chỉ đơn giản đưa nửa đoạn đó cho mô hình và nói "tiếp tục". Cách ổn định hơn là đặt phần đã sinh ra vào messages với vai trò assistant, thêm một user message "viết tiếp sau câu cuối, không lặp lại", cách nối này tự nhiên nhất. Lưu ý bước này làm prompt dài hơn, cần kiểm tra lại ngân sách.
danh sách kiểm tra cho ngữ cảnh dài
- trước mỗi yêu cầu, hãy dùng hàm ước lượng để tính prompt; nếu vượt ngân sách thì chuyển sang nhánh phân đoạn.
- Ghi lại usage mỗi lần phản hồi để liên tục hiệu chỉnh hệ số ước lượng.
- đặt system và dàn ý ở đầu nội dung, nhắc lại các yêu cầu quan trọng trong lệnh ở cuối.
- kiểm tra finish_reason; nếu là length thì báo động hoặc viết tiếp.
- đặt giới hạn giữ lại cho hội thoại lịch sử; khi vượt quá, hãy loại bỏ các tin nhắn cũ nhất thay vì chờ API trả về lỗi 400.
Nội dung liên quan: cấu trúc prompt xem Cách viết Prompt, xử lý lỗi xem Sổ tay hướng dẫn xử lý lỗi API không kiểm duyệt, giá cả xem Trang giá.
nhắc lại một điểm: ngữ cảnh dài không đồng nghĩa với bộ nhớ dài. Mô hình đọc lại từ đầu mọi nội dung bạn cung cấp trong mỗi yêu cầu, không nhớ gì từ lần gọi trước. Nếu cần tính liên tục, bạn phải tự truyền lại lịch sử.