กลับไปที่บล็อก

การแก้ไขปัญหาคู่มือ

คำตอบ AI ถูกตัด? แก้ finish_reason "length" และ max_tokens

คำตอบ API ของคุณหยุดกลางประโยคหรือคืน JSON ที่เสียหาย? ตรวจ finish_reason เพิ่ม max_tokens ภายในขีดจำกัด และเพิ่มขั้นตอนตอบต่อ

โดย CheapTokens Teamเผยแพร่เมื่อ ใช้เวลาอ่าน 6 นาที

ภาพปกสไตล์เทอร์มินัลแสดง finish_reason length ในคำตอบ API
ในหน้านี้
  1. คำตอบที่ถูกตัดจะหน้าตาอย่างไรบนหน้าจอ
  2. ขั้นตอนที่ 1: อ่านฟิลด์ที่บอกคุณ
  3. ขั้นตอนที่ 2: เพิ่ม max_tokens แต่ไม่เกินเพดาน
  4. ขั้นตอนที่ 3: JSON ที่หยุดกลางทาง
  5. ขั้นตอนที่ 4: ให้โมเดลตอบต่อ
  6. คำตอบยาวมีค่าใช้จ่ายเท่าไร
  7. ก่อนปล่อย: รายการตรวจสอบสั้น ๆ

สคริปต์ของคุณรันแล้ว โมเดลตอบกลับมา แล้วฟังก์ชันก็จบลงกลางบรรทัด หรือตัวแยกวิเคราะห์ JSON โยนข้อผิดพลาดใส่คำตอบที่ดูปกติสมบูรณ์ ส่วนใหญ่แล้วโมเดลไม่ได้ล้มเหลว: มันชนขีดจำกัด max_tokens ที่คุณตั้งไว้ (หรือค่าเริ่มต้นที่คุณไม่เคยเห็น) และ API ก็หยุดมันกลางคำ

วิธีแก้เร็ว: อ่าน finish_reason ในคำตอบ ถ้าเป็น "length" (API ที่เข้ากันได้กับ OpenAI) หรือ stop_reason เป็น "max_tokens" (Anthropic Messages) แสดงว่าคำตอบถูกตัด เพิ่ม max_tokens ขึ้นไปจนถึงขีดจำกัดเอาต์พุตของโมเดล หรือขอให้โมเดลตอบต่อ ส่วนที่เหลือของหน้านี้แสดงทั้งสองวิธีในโค้ดที่คุณคัดลอกไปวางได้

ถ้าคุณอยากได้คำอธิบายแบบภาษาง่ายว่าทำไมสิ่งนี้เกิดขึ้น เริ่มจาก ทำไมคำตอบ AI ถึงหยุดกลางประโยค หน้านี้เป็นฉบับสำหรับดีบัก

คำตอบที่ถูกตัดจะหน้าตาอย่างไรบนหน้าจอ#

อาการแตกต่างกันไปตามสิ่งที่คุณขอ แต่สาเหตุเดียวกัน ลองจับคู่สิ่งเหล่านี้กับสิ่งที่คุณเคยเห็น:

จับคู่

จับคู่อาการกับสาเหตุที่เป็นไปได้

  • ฟังก์ชันจบลงด้วย return resuคำตอบโค้ดชน max_tokens กลางบรรทัด
  • json.loads ล้มเหลวด้วย "Unterminated string" หรือ "Expecting ',' delimiter"JSON ถูกตัดกลางทาง
  • การแปลหยุดหลังข้อความครึ่งแรกขีดจำกัดเอาต์พุตเล็กกว่าข้อความที่แปล
  • คำตอบสมบูรณ์และฟิลด์ระบุว่า stopโมเดลจบเองตามธรรมชาติ มองหาที่อื่น

แถวสุดท้ายสำคัญ ถ้า finish_reason เป็น stop แสดงว่าขีดจำกัดไม่ใช่ปัญหาของคุณ และการเพิ่ม max_tokens จะไม่เปลี่ยนอะไร

ขั้นตอนที่ 1: อ่านฟิลด์ที่บอกคุณ#

ทุกคำตอบบอกว่าทำไมการสร้างจึงจบลง ในคำตอบที่เข้ากันได้กับ OpenAI ฟิลด์นั้นคือ choices[0].finish_reason: "stop" เมื่อโมเดลจบเอง, "length" เมื่อโทเค็นหมด ใน Anthropic Messages API ฟิลด์นั้นคือ stop_reason และค่าที่บ่งชี้ว่าถูกตัดคือ "max_tokens" คู่มือ stop-reason ของ Anthropic (ตรวจสอบเมื่อ 28 ตุลาคม 2026) แนะนำวิธีแก้สองข้อด้านล่างนี้พอดี: เพิ่ม max_tokens หรือตอบต่อจากคำตอบเดิม

นี่คือการตรวจสอบเดียวกันในสามรูปแบบ Base URL คือ https://api.cheaptokens.dev/v1 สำหรับ OpenAI SDK และ https://api.cheaptokens.dev (ไม่มี /v1) สำหรับ Anthropic SDK ค่า max_tokens=200 ตั้งใจให้เล็ก เพื่อให้คุณเห็นการตัดด้วยตัวเอง

การตรวจสอบเดียว สามวิธี

Python (OpenAI SDK)

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)

resp = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    max_tokens=200,
    messages=[{"role": "user", "content": "Write a Python class for a todo list with tests."}],
)

choice = resp.choices[0]
if choice.finish_reason == "length":
    print("Cut off at the token limit")
print(choice.message.content)
print(resp.usage.completion_tokens)

Python (Anthropic SDK)

python
import os
import anthropic

client = anthropic.Anthropic(
    base_url="https://api.cheaptokens.dev",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)

msg = client.messages.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    max_tokens=200,
    messages=[{"role": "user", "content": "Write a Python class for a todo list with tests."}],
)

if msg.stop_reason == "max_tokens":
    print("Cut off at the token limit")
print(msg.content[0].text)

curl

bash
curl https://api.cheaptokens.dev/v1/chat/completions \
  -H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-ai/DeepSeek-V4-Flash-0731",
    "max_tokens": 200,
    "messages": [{"role": "user", "content": "Write a Python class for a todo list with tests."}]
  }'

มองหา "finish_reason": "length" ใน JSON ที่ส่งกลับมา

เมื่อมันทำงานเป็นการวินิจฉัย คุณจะเห็นคู่นี้: ขีดจำกัดต่ำให้ length ขีดจำกัดกว้างให้ stop

คำขอเดียวกันด้วย max_tokens ต่ำและสูง: finish_reason เปลี่ยนจาก length เป็น stop
สองค่าของ finish_reason ที่คุณจะพบมากที่สุด

ขั้นตอนที่ 2: เพิ่ม max_tokens แต่ไม่เกินเพดาน#

วิธีแก้ที่ชัดเจนคือใส่ตัวเลขที่ใหญ่ขึ้น ข้อควรระวัง: ทุกโมเดลมีเพดานของตัวเองสำหรับคำตอบ อย่าตั้งค่าสูงเกินไป: ขีดจำกัดขึ้นอยู่กับแต่ละโมเดล ดังนั้นดูก่อนที่ตารางด้านล่าง นี่คือขีดจำกัดต่อคำตอบบน CheapTokens (ข้อเท็จจริงตรวจสอบเมื่อ 28 ตุลาคม 2026):

โมเดล เอาต์พุตสูงสุดต่อคำตอบ Context
DeepSeek-V4-Flash 32,768 โทเค็น 380K
GLM-5.3-Flash 8,192 โทเค็น 390K
MiniMax-M2.7 8,192 โทเค็น 180K

การแปล 9,000 โทเค็นต้องใช้โมเดลที่มีขีดจำกัดต่อคำตอบใหญ่กว่า — DeepSeek-V4-Flash รับได้ถึง 32,768 โทเค็นต่อคำตอบ สำหรับโค้ดยาว JSON ยาว หรือเอกสารทั้งฉบับ deepseek-ai/DeepSeek-V4-Flash-0731 มีพื้นที่มากที่สุด: ถึง 32,768 โทเค็นต่อคำตอบ ราคาเดียวทุกชั่วโมงของวัน ใช้ model id ที่แน่นอนจาก เอกสาร CheapTokens และ หน้ารายการโมเดล เนื่องจากผู้ให้บริการตั้งชื่อต่างกัน

กฎที่ใช้ได้จริง: ตั้ง max_tokens ให้เท่ากับคำตอบที่ยาวที่สุดที่คุณยอมรับ ไม่ใช่ค่าสูงสุด คุณจ่ายสำหรับโทเค็นที่โมเดลเขียนจริง ดังนั้นเพดานสูงจึงไม่มีค่าใช้จ่ายเมื่อคำตอบสั้น

ขั้นตอนที่ 3: JSON ที่หยุดกลางทาง#

คำตอบ JSON ที่ถูกตัดเป็นกรณีที่แย่ที่สุด เพราะข้อความดูถูกต้องจนกว่าตัวแยกวิเคราะห์จะเจอจุดสิ้นสุด สมมติว่าคุณดึงฟิลด์จากใบแจ้งหนี้ 40 ใบในการเรียกครั้งเดียวและขออาร์เรย์ โมเดลเขียน 31 ออบเจกต์ ชนขีดจำกัด และอาร์เรย์ไม่เคยปิด

ข้อผิดพลาด json.loads บนอาร์เรย์ที่ถูกตัดที่ขีดจำกัดโทเค็น
อาร์เรย์ JSON ที่ถูกตัดทอนหน้าตาอย่างไรในตัวแยกวิเคราะห์

อย่าพยายามซ่อมสตริงด้วยการเติม ]} คุณจะได้ JSON ที่ถูกต้องแต่ขาดระเบียน ซึ่งแย่กว่าข้อผิดพลาด ตรวจ finish_reason ก่อน; ถ้าเป็น length ให้เพิ่มขีดจำกัดแล้วรันการเรียกใหม่ หรือแบ่งอินพุตเป็นชุดเล็กลง สำหรับการบังคับรูปแบบเอง ดู JSON ที่ถูกต้องจาก LLM API

ขั้นตอนที่ 4: ให้โมเดลตอบต่อ#

บางครั้งคำตอบยาวเกินกว่าขีดจำกัดใด ๆ จริง ๆ: บทหนึ่งของหนังสือ การรีแฟกเตอร์ใหญ่ จากนั้นป้อนสิ่งที่คุณมีกลับไปและขอให้โมเดลทำต่อ ลูปด้านล่างเก็บข้อความที่มีไว้ ขอส่วนที่เหลือ และยอมแพ้หลังผ่านไปสองสามรอบเพื่อไม่ให้บั๊กวนไม่รู้จบ

python
def complete(base_messages, model, max_tokens=4096, rounds=4):
    text = ""
    for _ in range(rounds):
        messages = list(base_messages)
        if text:
            messages += [
                {"role": "assistant", "content": text},
                {"role": "user", "content": "Continue exactly where you stopped. Do not repeat anything."},
            ]
        r = client.chat.completions.create(model=model, max_tokens=max_tokens, messages=messages)
        choice = r.choices[0]
        text += choice.message.content or ""
        if choice.finish_reason != "length":
            return text, True   # finished on its own
    return text, False          # still cut after all rounds

นิสัยสองอย่างช่วยให้สิ่งนี้ซื่อตรง คืนค่าแฟล็ก True/False และแสดงคำเตือนให้ผู้ใช้เมื่อ False แทนที่จะนำเสนอเอกสารครึ่งหนึ่งราวกับว่าเสร็จแล้ว และจำไว้ว่าแต่ละรอบส่งข้อความก่อนหน้าเป็นอินพุตซ้ำ ดังนั้นคำตอบที่ต่อเนื่องมีค่าใช้จ่ายมากกว่าคำตอบใหญ่ครั้งเดียว สำหรับข้อมูลแบบมีโครงสร้าง ควรเลือกขีดจำกัดที่ใหญ่ขึ้นหรือชุดเล็กลงแทนการตอบต่อ

คำตอบยาวมีค่าใช้จ่ายเท่าไร#

สมมติว่าคำตอบยาวหนึ่งครั้งใช้อินพุต 2,000 โทเค็นและเอาต์พุต 8,000 โทเค็น บน DeepSeek-V4-Flash คำขอนั้นมีค่าใช้จ่าย $0.00083 ตามราคาวันนี้ และโดยรวมเมื่อนับอินพุตและเอาต์พุตรวมกัน ถูกกว่า OpenRouter เปลี่ยนตัวเลขเป็นโหลดของคุณเอง:

เครื่องคำนวณค่าใช้จ่าย

หนึ่งวันกับคำตอบยาว 50 ครั้ง

โทเค็นอินพุตต่อคำขอ: 2,000 · โทเค็นเอาต์พุตต่อคำขอ: 8,000 · คำขอต่อวัน: 50

โมเดลต่อวันต่อเดือน
GLM-5.3-Flash$0.018$0.54
DeepSeek-V4-Flash$0.0415$1.25
MiniMax-M2.7$0.065$1.95

ข้อสรุป: ฝั่งเอาต์พุตครองสัดส่วนในสถานการณ์นี้ ดังนั้นพื้นที่เผื่อใน max_tokens คือประกันราคาถูกต่อการรันซ้ำ คำตอบที่ถูกตัดซึ่งคุณต้องสร้างใหม่จะถูกจ่ายสองครั้ง ข้อมูลพื้นฐานเกี่ยวกับสองฝั่งของบิลอยู่ใน โทเค็นอินพุตเทียบเอาต์พุต และรายการโมเดลเต็มพร้อมราคาอยู่ที่ หน้าราคา

ก่อนปล่อย: รายการตรวจสอบสั้น ๆ#

เช็กลิสต์

รายการตรวจสอบการตัดทอนสำหรับโค้ดของคุณ