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

การลดต้นทุนคู่มือ

ตัดประวัติแชทเพื่อลดต้นทุน: sliding window หรือสรุป

จำกัดสิ่งที่คุณส่งซ้ำในแต่ละเทิร์น: เก็บไม่กี่ข้อความล่าสุดหรือสรุปสั้น ๆ การคำนวณโทเคนสำหรับแชท 50 เทิร์นและตัวช่วย Python

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

หน้าปก: ทำให้แชทยาวมีต้นทุนต่ำ ด้วยตัวช่วย trim_history
ในหน้านี้
  1. เทิร์นที่ 50 ส่งอะไรจริง ๆ
  2. ตัวเลือกที่ 1: sliding window
  3. ตัวเลือกที่ 2: สรุปเทิร์นเก่า
  4. สามรูปแบบมีต้นทุนเท่าไร
  5. เลือกอันไหน

บอทซัพพอร์ตของคุณแชทมาแล้ว 50 เทิร์น และทุกข้อความใหม่มีต้นทุนสูงกว่าข้อความก่อนหน้า API ไม่จดจำอะไรเลย โค้ดของคุณจึงส่งบทสนทนาทั้งหมดซ้ำทุกครั้ง วิธีแก้คือจำกัดสิ่งที่คุณส่ง: เก็บ system prompt กับไม่กี่เทิร์นล่าสุด (sliding window) หรือแทนที่เทิร์นเก่าด้วยสรุปสั้น ๆ ด้านล่างคือการคำนวณสำหรับแชท 50 เทิร์นและตัวช่วยที่คุณคัดลอกได้

คู่มือ multi-round chat ของ DeepSeek (api-docs.deepseek.com ตรวจสอบเมื่อ 1 พฤศจิกายน 2026) ระบุชัดเจนว่าเซิร์ฟเวอร์ไม่บันทึก context ของคำขอก่อนหน้า ไคลเอนต์จึงต้องส่งรายการ messages ทั้งหมดทุกครั้งที่เรียก รูปแบบที่เข้ากันได้กับ OpenAI บน CheapTokens ทำงานแบบเดียวกัน หากคุณเพิ่งรู้จักเรื่องนี้ เริ่มจาก ทำไมแชทยาวจึงมีต้นทุนสูงขึ้น และ อาร์เรย์ messages ทำงานอย่างไร

เทิร์นที่ 50 ส่งอะไรจริง ๆ#

สมมติคุณรันบอทซัพพอร์ตที่มี system prompt 400 โทเคน ในเทิร์นทั่วไปผู้ใช้เขียนราว 60 โทเคน และบอทตอบราว 190 โทเคน หนึ่งเทิร์นเต็มเพิ่ม 250 โทเคนเข้าประวัติ นี่คือสมมติฐาน: แทนที่ด้วยตัวเลขจากฟิลด์ usage ของคำตอบคุณเอง

ที่เทิร์น 50 คำขอมี system prompt, 49 เทิร์นก่อนหน้า และข้อความใหม่: 400 + 49 × 250 + 60 = 12,710 input tokens เพื่อให้ได้คำตอบ 190 โทเคน รวมทั้งแชท system prompt และข้อความใหม่ถูกส่งทุกครั้ง: 50 × 460 = 23,000 ประวัติที่ส่งซ้ำเพิ่ม 250 × 1,225 = 306,250 รวมกัน: 23,000 + 306,250 = 329,250 input tokens เทียบกับ 50 × 190 = 9,500 output tokens

ประวัติในแต่ละคำขอเพิ่มขึ้นหนึ่งเทิร์น ยอดรวมจึงโตตามกำลังสองของความยาวแชท จาก 50 เป็น 100 เทิร์น ส่วนประวัติที่ส่งซ้ำจะเพิ่มขึ้นราวสี่เท่า

ลองด้วยตัวเองลองดู: ถ้าคุณส่งเฉพาะ system prompt, 4 เทิร์นล่าสุด และข้อความใหม่ เทิร์นที่ 50 จะมี input tokens เท่าไร?แสดงคำตอบ

1,460 (400 + 4 × 250 + 60) เทียบกับ 12,710 เมื่อส่งประวัติเต็ม คำถามเดิม ความยาวคำตอบเดิม แต่ใช้ input เพียงเศษเสี้ยว

ตัวเลือกที่ 1: sliding window#

แนวคิด: ส่ง system prompt เสมอ ตัดทุกอย่างยกเว้น N เทิร์นล่าสุด คุณเก็บประวัติเต็มในที่จัดเก็บของคุณเองและตัดเฉพาะสำเนาที่ส่ง

python
def trim_history(messages, keep_pairs=4):
    """System prompt + the last keep_pairs turns + the new user message."""
    system = [m for m in messages if m["role"] == "system"]
    rest = [m for m in messages if m["role"] != "system"]
    return system + rest[-(keep_pairs * 2 + 1):]

เรียกมันหลังคุณต่อท้ายข้อความใหม่ของผู้ใช้ ด้วย keep_pairs=4 นั่นคือ 4 คู่ user/assistant บวกข้อความใหม่ รวม 9 ข้อความ และ slice จะเริ่มที่ข้อความของผู้ใช้เสมอ นี่คือการต่อเข้ากับการเรียกจริง:

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
MODEL = "deepseek-ai/DeepSeek-V4-Flash-0731"

history = [{"role": "system", "content": "You are a concise support assistant."}]

def ask(text):
    history.append({"role": "user", "content": text})
    resp = client.chat.completions.create(
        model=MODEL,
        messages=trim_history(history),
        max_tokens=500,
    )
    answer = resp.choices[0].message.content
    history.append({"role": "assistant", "content": answer})
    print(resp.usage.prompt_tokens, resp.usage.completion_tokens)
    return answer

บรรทัด print คือบรรทัดที่มีประโยชน์: จับตาดูตัวเลขแรก หากไม่ตัด มันจะเพิ่มขึ้นทุกเทิร์น ด้วย window มันจะหยุดโตเมื่อแชทผ่านเทิร์นที่ 4

ตัวช่วย Python trim_history: system prompt, 4 เทิร์นล่าสุด, ข้อความใหม่
ตัวช่วยตัดประวัติจากบทความนี้

ในเชิงตัวเลข: ตั้งแต่เทิร์นที่ 5 เป็นต้นไป แต่ละคำขอคือ 400 + 4 × 250 + 60 = 1,460 input tokens สี่เทิร์นแรกยังโตตามปกติและรวมกันได้ 3,340 ทั้งแชท: 3,340 + 46 × 1,460 = 70,500 input tokens ราวหนึ่งในห้าของ 329,250 ที่คุณส่งเมื่อใช้ประวัติเต็ม Output ยังคงเป็น 50 × 190 = 9,500

คำขอเทิร์นที่ 50: ประวัติเต็มเทียบกับ window 4 เทิร์น
ส่วนประกอบโทเคนของคำขอเทิร์นที่ 50 ตามสมมติฐานข้างต้น

ในโค้ด การเปลี่ยนแปลงทั้งหมดคืออาร์กิวเมนต์เดียวในการเรียก:

เปรียบเทียบ

การเปลี่ยนแปลงบรรทัดเดียว

ก่อน

python
messages=history

หลัง

python
messages=trim_history(history)

ข้อควรระวัง: window จะลืม หากผู้ใช้ถามในเทิร์นที่ 40 ว่า "หมายเลขคำสั่งซื้อของฉันคืออะไร?" และหมายเลขนั้นอยู่ในเทิร์นที่ 3 โมเดลจะไม่เคยเห็นมัน วิธีแก้ราคาถูกสองวิธี ดึงข้อเท็จจริงที่คงที่ (ชื่อ หมายเลขคำสั่งซื้อ แพ็กเกจ) ออกจากแชทในโค้ดของคุณเองและต่อท้ายเข้า system prompt ซึ่ง window จะไม่ตัดมัน หรือเพิ่มสรุป ซึ่งเป็นตัวเลือกถัดไป

ตัวเลือกที่ 2: สรุปเทิร์นเก่า#

แทนที่จะทิ้งเทิร์นเก่า คุณบีบอัดมัน ทุกไม่กี่เทิร์น ส่งเทิร์นที่เพิ่งหลุดออกจาก window พร้อมสรุปปัจจุบันไปยังโมเดลและขอสรุปใหม่ที่สั้น จากนั้นใส่สรุปลงใน system message

python
def summarize(summary, old_messages):
    transcript = "\n".join(f"{m['role']}: {m['content']}" for m in old_messages)
    prompt = (
        f"Summary so far:\n{summary}\n\nNew turns:\n{transcript}\n\n"
        "Rewrite the summary in under 200 words. "
        "Keep names, numbers, decisions and open questions."
    )
    resp = client.chat.completions.create(
        model=MODEL,
        messages=[{"role": "user", "content": prompt}],
        max_tokens=400,
    )
    return resp.choices[0].message.content

def build_messages(history, summary, keep_pairs=4):
    system = history[0]["content"]
    if summary:
        system += "\n\nEarlier in this chat: " + summary
    recent = history[1:][-(keep_pairs * 2 + 1):]
    return [{"role": "system", "content": system}] + recent

สรุปไม่ฟรี: มันคือการเรียกโมเดลเพิ่ม สมมติสรุปยาวราว 300 โทเคน รีเฟรชทุก 5 เทิร์น (9 ครั้งใน 50 เทิร์น) แต่ละครั้งอ่านราว 1,600 โทเคน (สรุปเก่าบวกห้าเทิร์นบวกคำสั่ง) และเขียน 300 เพื่อความปลอดภัย ให้นับสรุปในทุกคำขอตั้งแต่เทิร์นที่ 5 เป็นต้นไป (ในความเป็นจริงมันปรากฏหลังการรีเฟรชครั้งแรก นี่จึงเป็นการประเมินเกินเล็กน้อย): 400 + 300 + 4 × 250 + 60 = 1,760 input tokens

ยอดรวมสำหรับแชท: คำขอหลัก 3,340 + 46 × 1,760 = 84,300 input tokens, การเรียกสรุป 9 × 1,600 = 14,400 input และ 9 × 300 = 2,700 output ทั้งหมด 84,300 + 14,400 = 98,700 input และ 9,500 + 2,700 = 12,200 output tokens

สรุปมีต้นทุนมากกว่า window เปล่าเล็กน้อย และแลกมาด้วยโมเดลที่ยังรู้ว่าเกิดอะไรขึ้นในเทิร์นที่ 3

สามรูปแบบมีต้นทุนเท่าไร#

นี่คือแชท 50 เทิร์นเดียวกันบน DeepSeek-V4-Flash คำนวณสดจากยอดรวมข้างต้น:

หนึ่งแชท 50 เทิร์นบน DeepSeek-V4-Flash
  • ประวัติเต็ม (329,250 in, 9,500 out)$0.0059
  • Window บวกสรุป (98,700 in, 12,200 out)$0.0027
  • Sliding window 4 เทิร์น (70,500 in, 9,500 out)$0.002

ราคามาจากรายการราคาสด ตัวเลขจึงอัปเดตเมื่ออัตราเปลี่ยน ลำดับจะไม่เปลี่ยน: window ส่งน้อยที่สุด สรุปอยู่ตรงกลาง ประวัติเต็มแพงที่สุด

สำหรับแชทแบบนี้ โมเดล CheapTokens ที่ควรเริ่มด้วยคือ DeepSeek-V4-Flash มี context window 380K (แชท 50 เทิร์นใส่ได้แม้ไม่ตัด) output สูงสุด 32,768 โทเคนต่อคำตอบ ราคาคงที่เดียวทุกชั่วโมงของวัน และโดยรวมเมื่อนับ input กับ output รวมกัน ถูกกว่า OpenRouter model id คือ deepseek-ai/DeepSeek-V4-Flash-0731 หากคุณต้องการเปรียบเทียบโมเดลบนรูปแบบ window (เฉลี่ย 1,410 input tokens ต่อคำขอ, 190 output, 50 คำขอ) นี่คือตารางสด:

โมเดล ต่อคำขอ 50 คำขอ OpenRouter, 50 คำขอ
GLM-5.3-Flash $0.000036 $0.0018 $0.0032
DeepSeek-V4-Flash $0.00004 $0.002 $0.0063
MiniMax-M2.7 $0.000099 $0.005 $0.0594

1,410 โทเค็นอินพุตและ 190 โทเค็นเอาต์พุตต่อคำขอ ราคาต่อ 1M โทเค็นเป็นราคาปัจจุบันและเปลี่ยนแปลงไปพร้อมกับอัตราค่าบริการของเรา คอลัมน์ OpenRouter แสดงราคาอ้างอิงสำหรับปริมาณงานเดียวกัน

GLM-5.3-Flash มี context 390K และ MiniMax-M2.7 มี 180K ซึ่งเพียงพอสำหรับแชทที่ตัดแล้ว ราคาปัจจุบันอยู่ที่ หน้าราคา และ เอกสาร แสดงวิธีส่งคำขอแรก

เลือกอันไหน#

Window คือค่าเริ่มต้น: สามบรรทัดโค้ด ไม่มีการเรียกเพิ่ม ไม่มีโหมดล้มเหลวเพิ่ม ใช้กับบอทซัพพอร์ต ติวเตอร์ และ role-play ซึ่งไม่กี่เทิร์นล่าสุดแบกบทสนทนาและ system prompt แบกกฎ เพิ่มสรุปเมื่อผู้ใช้ย้อนอ้างถึงสิ่งที่พูดนานแล้ว เช่นผู้ช่วยส่วนตัวที่มีบทสนทนาไม่สิ้นสุด เก็บ window ให้เล็กพอที่โมเดลยังเห็น context ทันที และใหญ่พอที่จะเก็บการถาม-ตอบครบชุด สิ่งที่ใส่ได้ทั้งหมดเป็นขีดจำกัดแยก ครอบคลุมใน context window คืออะไร

เช็กด่วน

ผู้ใช้ถามในเทิร์นที่ 40 เกี่ยวกับหมายเลขคำสั่งซื้อที่ให้ไว้ในเทิร์นที่ 3 คุณใช้ window 4 เทิร์นโดยไม่มีสรุป เกิดอะไรขึ้น?

แสดงคำตอบ

คำตอบ: โมเดลมองไม่เห็น เพราะเทิร์นที่ 3 ถูกตัดออกจากคำขอ

API ไม่มีสถานะ โมเดลเห็นเพียงสิ่งที่อยู่ในคำขอ ข้อเท็จจริงที่คุณยังต้องการจึงต้องอยู่ใน system prompt หรือในสรุป