Quay lại blog

AI cho người mớiHướng dẫn

Tại sao câu trả lời AI dừng giữa câu? Giải thích max_tokens, cửa sổ ngữ cảnh và finish_reason

Câu trả lời AI dừng giữa câu đã chạm một giới hạn. Đọc finish_reason, phân biệt max_tokens với cửa sổ ngữ cảnh đầy và kiểm tra giới hạn đầu ra của từng mô hình.

Bởi CheapTokens TeamĐã xuất bản Đã cập nhật 8 phút đọc

Ảnh bìa tối với logo cheaptokens: các từ max_tokens, finish_reason và length trong khung, cùng tiêu đề “Answer cut off mid-sentence?”
Trên trang này
  1. Ba giới hạn cắt ngắn câu trả lời
  2. Đọc finish_reason trước
  3. Cách sửa 1: tăng max_tokens
  4. Cách sửa 2: khi cửa sổ ngữ cảnh đầy
  5. Cách sửa 3: nhận câu trả lời dài theo phần
  6. Khi finish_reason là stop
  7. Chọn mô hình theo độ dài câu trả lời

Một câu trả lời dừng giữa câu gần như luôn bị cắt bởi một giới hạn. Mô hình không mất mạch suy nghĩ; yêu cầu đã hết chỗ. Phản hồi cho biết đó là giới hạn nào: đọc finish_reason. length nghĩa là đã chạm giới hạn. stop nghĩa là mô hình tự kết thúc.

Ba yếu tố quyết định độ dài tối đa của câu trả lời. Khi bạn biết yếu tố nào gây ra, việc sửa chỉ mất một phút.

Ba giới hạn cắt ngắn câu trả lời#

  • max_tokens là giới hạn bạn đặt trong yêu cầu. Khi câu trả lời đạt đến đó, quá trình sinh dừng lại, kể cả giữa từ. Tài liệu tham khảo API của DeepSeek định nghĩa nó là số token tối đa được sinh trong phần hoàn thành.
  • Giới hạn đầu ra của mô hình là mức trần cho một câu trả lời từ mô hình đó. Nó thuộc về mô hình, nên hãy giữ max_tokens bằng hoặc thấp hơn nó. Số liệu cho các mô hình của chúng tôi có trong bảng dưới đây.
  • Cửa sổ ngữ cảnh là chỗ cho toàn bộ cuộc trao đổi: đầu vào và câu trả lời cộng lại. Tài liệu tham khảo của DeepSeek nói rằng token đầu vào và token được sinh cộng lại bị giới hạn bởi độ dài ngữ cảnh của mô hình.

Nếu token vẫn còn là khái niệm mơ hồ, hãy bắt đầu với What is a token in AI?. Trang này giả định bạn đã biết token là gì.

Đọc finish_reason trước#

Mỗi lựa chọn trong phản hồi chat đều mang một finish_reason. Bên cạnh đó, usage cho bạn biết có bao nhiêu token vào và ra.

Đây là hình ảnh của một câu trả lời bị cắt bên cạnh một câu trả lời đã hoàn thành. Yêu cầu hỏi "In one short sentence, what is a token?", lần đầu với max_tokens đặt là 16, sau đó là 200.

Ví dụ minh họa, không phải phản hồi được ghi lại: số đếm và các trường bổ sung của bạn sẽ khác.

json
{
  "choices": [
    {
      "message": { "role": "assistant", "content": "A token is a small piece of text that a model reads and writes, usu" },
      "finish_reason": "length"
    }
  ],
  "usage": { "prompt_tokens": 21, "completion_tokens": 16, "total_tokens": 37 }
}
json
{
  "choices": [
    {
      "message": { "role": "assistant", "content": "A token is a small piece of text that a model reads and writes, usually a word or part of a word." },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 21, "completion_tokens": 24, "total_tokens": 45 }
}

Trong phản hồi đầu tiên completion_tokens là 16, đúng bằng giới hạn, và văn bản kết thúc bằng "usu". Trong phản hồi thứ hai, mô hình dừng ở 24 token, thấp hơn nhiều so với giới hạn 200, vì nó đã xong.

Để xác định chuyện gì đã xảy ra, hãy làm theo thứ tự sau:

  1. Xem finish_reason. Nếu là stop, câu trả lời đã hoàn chỉnh. Mô hình chọn kết thúc, hoặc chạm chuỗi dừng bạn đặt. Nếu là length, một giới hạn đã cắt câu trả lời, và bước tiếp theo sẽ cho biết là giới hạn nào. Có các giá trị khác, như content_filter và tool_calls; cả hai đều không phải vấn đề độ dài.
  2. So sánh completion_tokens với max_tokens bạn đã gửi. Nếu chúng bằng nhau hoặc gần bằng, bạn đã chạm max_tokens.
  3. Nếu completion_tokens thấp hơn nhiều so với max_tokens và prompt_tokens lớn, cửa sổ ngữ cảnh có lẽ đã đầy. Tài liệu tham khảo của DeepSeek dùng length cho cả hai trường hợp, nên chỉ trường này không thể phân biệt chúng; phép so sánh ở bước 2 là cách để biết. Tài liệu của Anthropic và Z.ai gọi trường hợp thứ hai riêng là model_context_window_exceeded. Hãy xem trường trong phản hồi của chính bạn để biết bạn nhận được tên nào.

Cả ba mô tả đều từ tài liệu của nhà phát triển, kiểm tra ngày 20 tháng 10 năm 2026: DeepSeek, Anthropic và Z.ai.

Những gì bạn thấy finish_reason Nguyên nhân Việc cần làm
Bị cắt, completion_tokens bằng max_tokens length Đã đạt max_tokens Tăng max_tokens, hoặc yêu cầu trả lời theo phần
Bị cắt, completion_tokens thấp hơn nhiều so với max_tokens, đầu vào dài length (hoặc model_context_window_exceeded trong một số API) Cửa sổ ngữ cảnh đầy Rút ngắn đầu vào hoặc lịch sử, hoặc chọn mô hình có cửa sổ lớn hơn
Hoàn chỉnh nhưng ngắn hơn bạn muốn stop Mô hình đã xong, hoặc chuỗi dừng đã kích hoạt Yêu cầu chi tiết hơn; kiểm tra tham số stop
Không có văn bản, hoặc bị từ chối content_filter Phản hồi đã bị lọc Diễn đạt lại yêu cầu
Không có văn bản, thay vào đó là yêu cầu công cụ tool_calls Mô hình muốn gọi công cụ Chạy công cụ và gửi kết quả trả lại

Cách sửa 1: tăng max_tokens#

Nguyên nhân phổ biến nhất cũng là dễ sửa nhất. Hãy tự đặt max_tokens, để bạn biết giới hạn nào áp dụng. Đừng trông cậy vào giá trị mặc định.

python
from openai import OpenAI

client = OpenAI(base_url="https://api.cheaptokens.dev/v1", api_key="YOUR_API_KEY")

resp = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    messages=[{"role": "user", "content": "Write a detailed guide to setting up a home network."}],
    max_tokens=16000,  # example value: stay at or below the model's output limit
)

choice = resp.choices[0]
print(choice.finish_reason, resp.usage.completion_tokens)

Đoạn này cho thấy hình dạng của lệnh gọi. Khóa được đưa vào dưới dạng YOUR_API_KEY ở đây; đừng để khóa thật trong mã bạn chia sẻ.

Tăng số chỉ có ích đến giới hạn của chính mô hình. Dưới đây là các giới hạn trên CheapTokens, kiểm tra ngày 20 tháng 10 năm 2026 dựa trên danh sách mô hình CheapTokens:

Mô hình ID mô hình Đầu ra tối đa mỗi câu trả lời Ngữ cảnh
DeepSeek-V4-Flash (0731) deepseek-ai/DeepSeek-V4-Flash-0731 32.768 token 380K
GLM-5.3-Flash zai-org/GLM-5.3-Flash 8.192 token 390K
MiniMax-M2.7 MiniMaxAI/MiniMax-M2.7 8.192 token 200K

Các nhà phát triển công bố giới hạn riêng cho API của họ. Ví dụ, Z.ai liệt kê tới 128K đầu ra cho dòng GLM-5. Đó không phải giới hạn của GLM-5.3-Flash trên CheapTokens, vốn là 8.192 token mỗi câu trả lời. Với bất cứ thứ gì bạn xây dựng trên CheapTokens, hãy dùng bảng trên.

Cách sửa 2: khi cửa sổ ngữ cảnh đầy#

Trong một cuộc chat, toàn bộ lịch sử lại được gửi đi với mỗi tin nhắn, nên đầu vào tăng theo từng lượt. Cửa sổ ngữ cảnh phải chứa lịch sử đó cộng với câu trả lời mới. Nếu chỉ riêng lịch sử đã chiếm gần hết, gần như không còn chỗ cho phản hồi.

Các cửa sổ này lớn, và một câu hỏi ngắn sẽ không làm đầy chúng. Những cuộc chat dài và tài liệu dán vào sẽ làm được. Những gì hữu ích:

  • Bắt đầu cuộc chat mới cho nhiệm vụ mới thay vì tiếp tục cuộc cũ.
  • Yêu cầu mô hình tóm tắt cuộc trò chuyện đến nay, và chỉ mang theo bản tóm tắt.
  • Cắt bỏ tin nhắn và tệp đính kèm cũ không còn cần.
  • Nếu bản thân đầu vào quá lớn, chọn mô hình có cửa sổ lớn nhất. Trong số của chúng tôi, đó là GLM-5.3-Flash với 390K. Giới hạn đầu ra của nó là 8.192 token, nên hãy yêu cầu kết quả dài theo từng phần.

Cách sửa 3: nhận câu trả lời dài theo phần#

Khi câu trả lời bạn muốn dài hơn giới hạn đầu ra của mô hình, không cài đặt nào có thể nhét nó vào một phản hồi. Hãy yêu cầu theo từng mảnh: khi finish_reason là length, gửi phần trả lời một phần trở lại và yêu cầu mô hình tiếp tục.

python
messages = [{"role": "user", "content": "Write a detailed guide to setting up a home network."}]
parts = []

for _ in range(4):  # example limit: at most 4 rounds
    resp = client.chat.completions.create(
        model="zai-org/GLM-5.3-Flash",
        messages=messages,
        max_tokens=8000,  # example value: below this model's output limit
    )
    choice = resp.choices[0]
    parts.append(choice.message.content)
    if choice.finish_reason != "length":
        break
    messages.append({"role": "assistant", "content": choice.message.content})
    messages.append({"role": "user", "content": "Continue exactly where you stopped. Don't repeat anything."})

text = "".join(parts)

Lưu ý tương tự: một ví dụ về mẫu, không phải lần chạy được ghi lại. Hãy đọc các mối nối giữa các phần. Đôi khi mô hình lặp lại một câu hoặc bắt đầu lại danh sách ở chỗ nối.

Mỗi vòng gửi lại mọi thứ đến nay làm đầu vào, và mọi token đầu ra đều bị tính phí. Một câu trả lời dài tốn hơn câu trả lời ngắn. Đây là câu trả lời tám nghìn token với 2.000 token đầu vào, vừa với mọi mô hình trong bảng:

Mô hình Chi phí OpenRouter
GLM-5.3-Flash $0.00036 $0.00069
DeepSeek-V4-Flash $0.00083 $0.0013
MiniMax-M2.7 $0.0013 $0.0102

Tổng cộng 2.000 token đầu vào và 8.000 token đầu ra. Giá trên 1 triệu token là giá hiện hành và thay đổi cùng với biểu phí của chúng tôi; cột OpenRouter hiển thị giá tham chiếu cho cùng khối lượng công việc.

Khi finish_reason là stop#

stop nghĩa là mô hình đã kết thúc câu trả lời. Nếu nó kết thúc sớm hơn bạn muốn, đó không phải vấn đề giới hạn và max_tokens lớn hơn không thay đổi gì. Hãy nói rõ bạn muốn gì: độ dài, các phần, mức độ chi tiết. Cũng kiểm tra xem yêu cầu có đặt tham số stop không; một chuỗi dừng xuất hiện trong câu trả lời sẽ kết thúc nó ngay lập tức.

Chọn mô hình theo độ dài câu trả lời#

Bắt đầu từ câu trả lời bạn cần. Nếu nó vừa trong 8.192 token, MiniMax-M2.7 (MiniMaxAI/MiniMax-M2.7) xử lý được trên CheapTokens. Đầu ra của nó rẻ hơn giá tham khảo của OpenRouter, và được tính ở $0.15 (mỗi 1M token đầu ra). Giá như nhau mọi giờ, không có giờ cao điểm, và API tương thích với OpenAI và Anthropic.

Đây là bảng giá hiện tại cho mọi mô hình:

Mô hình Ngữ cảnh Đầu vào $/1M Đầu ra $/1M OpenRouter vào OpenRouter ra
GLM-5.3-Flash reasoning 390K $0.02 $0.04 $0.0413 $0.0758
DeepSeek-V4-Flash 380K $0.015 $0.10 $0.0475 $0.1568
MiniMax-M2.7 180K $0.05 $0.15 $0.30 $1.20

Giá tính bằng USD cho mỗi 1 triệu token. Các số liệu của OpenRouter được hiển thị để so sánh và có thể thay đổi.

Nếu bạn chưa gửi yêu cầu nào, Your first API request with CheapTokens hướng dẫn từng bước. Về token là gì và tại sao đầu vào và đầu ra được tính riêng, xem What is a token in AI?.

Endpoint, ID mô hình và giới hạn cho mọi mô hình có trong CheapTokens docs.