Vấn đềVấn đề này thường xảy ra khi bạn bắt đầu kiểm thử ứng dụng của mình với dữ liệu thực tế. Bạn đưa một tệp PDF dài hoặc lịch sử trò chuyện cả tuần vào mô hình, và OpenAI API từ chối nó ngay lập tức. Lỗi context_length_exceeded này không phải là lỗi trong logic mã nguồn của bạn; đó là một giới hạn vật lý cố định. Mỗi mô hình đều có dung lượng 'bộ nhớ' cố định. Đối với GPT-4o, giới hạn đó là 128.000 token—khoảng 300 trang văn bản—bao gồm cả đầu vào của bạn và phản hồi được tạo ra từ mô hình.
openai.BadRequestError: Error code: 400 - {'error': {'message': "This model's maximum context length is 128000 tokens. However, your messages resulted in 145823 tokens.", 'type': 'invalid_request_error', 'param': 'messages', 'code': 'context_length_exceeded'}}
Tại sao API từ chối yêu cầu của bạnCác mô hình OpenAI như GPT-4o và GPT-4-turbo sử dụng cửa sổ ngữ cảnh 128k token. Nếu bạn gửi 145.823 token, API sẽ không cố gắng xử lý một phần phản hồi. Nó chỉ đơn giản là ngắt yêu cầu để ngăn chặn việc sử dụng tài nguyên tính toán không hiệu quả. Điều này thường xảy ra do 'phình ngữ cảnh' (context bloat)—giữ lại quá nhiều lượt trò chuyện trước đó hoặc đưa dữ liệu thô chưa được làm sạch trực tiếp vào prompt.
Bước 1: Đếm token cục bộ trước khi gửiCác ứng dụng LLM đáng tin cậy không bao giờ 'đoán' xem một prompt có vừa hay không. Thay vào đó, chúng xác thực số lượng token trước khi yêu cầu rời khỏi máy chủ. Sử dụng thư viện tiktoken, bạn có thể tính toán chính xác cách mô hình nhìn nhận văn bản của bạn. Lưu ý rằng GPT-4o sử dụng mã hóa o200k_base, trong khi các mô hình GPT-4 cũ hơn sử dụng cl100k_base.
import tiktoken
def num_tokens_from_messages(messages, model="gpt-4o"):
try:
encoding = tiktoken.encoding_for_model(model)
except KeyError:
encoding = tiktoken.get_encoding("cl100k_base")
num_tokens = 0
for message in messages:
# Mỗi tin nhắn tuân theo <im_start>{role/name}\n{content}<im_end>\n
num_tokens += 3
for key, value in message.items():
num_tokens += len(encoding.encode(value))
if key == "name":
num_tokens += 1
num_tokens += 3 # Mỗi câu trả lời được bắt đầu bằng <im_start>assistant
return num_tokens
Bước 2: Áp dụng chiến lược cửa sổ trượt (Sliding Window)Khi cuộc hội thoại trở nên quá dài, bạn phải quyết định nên lược bỏ phần nào. Cách tiếp cận 'cửa sổ trượt' (sliding window) giữ lại các hướng dẫn system thiết yếu nhưng loại bỏ các lượt trao đổi user/assistant cũ nhất. Điều này duy trì ngữ cảnh tức thời của cuộc hội thoại trong khi vẫn nằm dưới giới hạn 128k.
def trim_messages(messages, max_tokens=120000, model="gpt-4o"):
"""Loại bỏ các tin nhắn cũ nhất cho đến khi tổng số token nằm trong vùng đệm an toàn."""
system_message = [m for m in messages if m['role'] == 'system']
chat_history = [m for m in messages if m['role'] != 'system']
while num_tokens_from_messages(system_message + chat_history, model) > max_tokens:
if len(chat_history) > 1:
chat_history.pop(0) # Loại bỏ lượt trao đổi cũ nhất
else:
# Nếu một tin nhắn vẫn còn quá lớn, hãy cắt bớt chuỗi trực tiếp
chat_history[0]['content'] = chat_history[0]['content'][:5000]
break
return system_message + chat_history
Bước 3: Chuyển sang RAG cho các tập dữ liệu lớnViệc cắt bớt (truncation) hoạt động tốt cho hội thoại, nhưng sẽ thất bại nếu bạn cần phân tích một tài liệu kỹ thuật dài 1.000 trang. Trong kịch bản đó, đừng gửi toàn bộ tài liệu. Hãy sử dụng Retrieval Augmented Generation (RAG) để tìm 'kim đáy bể'. Chia dữ liệu của bạn thành các đoạn (chunk) 500 token, lưu trữ chúng trong một cơ sở dữ liệu vector như Pinecone hoặc ChromaDB, và chỉ đưa 5 đoạn văn bản liên quan nhất vào prompt của bạn. Điều này giúp số lượng token của bạn ở mức thấp—thường dưới 10.000—trong khi vẫn cung cấp cho mô hình những dữ kiện chính xác mà nó cần.
Xác thực và Vùng đệm an toànĐừng bao giờ nhắm tới giới hạn chính xác. Nếu một mô hình có giới hạn 128.000 token và bạn gửi 127.990 token, mô hình chỉ còn lại 10 token cho câu trả lời của nó. Điều này dẫn đến một phản hồi bị cắt ngang. Luôn để lại một vùng đệm an toàn ít nhất 10-20% cho đầu ra.
# Ví dụ: Thiết lập ngưỡng an toàn
MAX_ALLOWED = 115000
current_usage = num_tokens_from_messages(messages)
if current_usage > MAX_ALLOWED:
print(f"Đang cắt bớt: {current_usage} token vượt quá ngưỡng an toàn.")
messages = trim_messages(messages, max_tokens=MAX_ALLOWED)
Mẹo tối ưu hóa nhanh- Tóm tắt lịch sử: Thay vì xóa các tin nhắn cũ, hãy yêu cầu GPT tóm tắt 20 lượt trò chuyện đầu tiên thành một đoạn văn duy nhất và sử dụng đó làm điểm bắt đầu mới.- Kiểm tra phiên bản mô hình: Nếu bạn gặp giới hạn ở mức 8.000 token, có thể bạn đang sử dụng gpt-4 hoặc gpt-3.5-turbo cũ. Hãy chuyển sang gpt-4o để có toàn bộ cửa sổ 128k.- Làm sạch dữ liệu: Loại bỏ các khoảng trắng không cần thiết, thẻ HTML hoặc siêu dữ liệu khỏi đầu vào của bạn để tiết kiệm ngay lập tức 5-10% chi phí token.