Context rot: ยิ่งใส่ข้อมูลให้ AI ยาว ยิ่งตอบพลาด แล้วควรใส่อะไรใน context
งานวิจัยของ Chroma ทดสอบ 18 โมเดล พบว่าคุณภาพคำตอบลดลงเมื่อ input ยาวขึ้น และข้อมูลที่คล้ายแต่ผิดทำให้แย่ลงอีก พร้อมกฎใช้งานจริงว่าควรใส่อะไรใน context
- context window ใหญ่ไม่ได้แปลว่าโมเดลใช้ทุก token ได้ดีเท่ากัน คุณภาพลดลงเมื่อ input ยาวขึ้น แม้งานจะง่าย
- ข้อความที่คล้ายคำตอบแต่ไม่ใช่ (distractor) ทำร้ายมากกว่าข้อความที่ไม่เกี่ยวเลย ตัดออกก่อนส่ง
- ในการทดลอง LongMemEval prompt ที่คัดแล้ว ~300 token ให้ผลดีกว่า prompt เต็ม ~113,000 token ในทุกโมเดล
- ตั้งงบ token ต่อคำถาม คัดเฉพาะส่วนที่เกี่ยว และเริ่ม session ใหม่เมื่อประวัติแชตยาวเกินงาน
Context rot คืออาการที่โมเดลภาษาตอบแย่ลงเมื่อ input ยาวขึ้น แม้ข้อมูลที่ต้องใช้จะอยู่ใน context ครบ เรื่องนี้สำคัญกับทุกระบบที่โยนเอกสาร ประวัติแชต หรือผลจากเครื่องมือให้ AI เพราะความเชื่อว่า “ใส่ไปเยอะ ๆ เดี๋ยวมันหาเอง” ใช้ไม่ได้จริง
ตรวจข้อเท็จจริงกับรายงาน Context Rot ของ Chroma (กรกฎาคม 2025) และ repo ของงาน ณ ตุลาคม 2026 โค้ดตัวอย่างทดสอบบน Python 3.12
งานวิจัยนี้วัดอะไร
Chroma ออกรายงาน Context Rot: How Increasing Input Tokens Impacts LLM Performance เมื่อ 14 กรกฎาคม 2025 ทดสอบ 18 โมเดล จาก 4 ค่าย: Anthropic 5 ตัว (Claude Opus 4, Sonnet 4, Sonnet 3.7, Sonnet 3.5, Haiku 3.5), OpenAI 7 ตัว (o3, GPT-4.1 และรุ่น mini/nano, GPT-4o, GPT-4 Turbo, GPT-3.5 Turbo), Google 3 ตัว (Gemini 2.5 Pro, 2.5 Flash, 2.0 Flash) และ Qwen3 3 ขนาด
จุดที่ต่างจาก benchmark “needle in a haystack” แบบเดิม คือเขาไม่ได้ซ่อนคำตอบที่ตรงตัวอักษรกับคำถาม แต่ทำให้งานใกล้ของจริงขึ้น แล้วคุมให้ความยากของงานคงที่ เปลี่ยนแค่ความยาว input ถ้าผลตก แปลว่าตกเพราะความยาว ไม่ใช่เพราะงานยากขึ้น
ผลหลัก 5 ข้อ
| การทดลอง | สิ่งที่เปลี่ยน | ผล |
|---|---|---|
| ความคล้ายคำถามกับคำตอบ | คำถามที่ใช้คำต่างจากคำตอบมาก (similarity ต่ำ) | ยิ่ง input ยาว ผลยิ่งตกเร็วกว่าคู่ที่คล้ายกัน |
| Distractor | ใส่ข้อความที่คล้ายคำตอบแต่ไม่ใช่ 0, 1, 4 ชิ้น | 1 ชิ้นก็ทำให้แย่ลง 4 ชิ้นแย่ลงอีก และแต่ละชิ้นทำร้ายไม่เท่ากัน |
| โครงสร้างกองข้อมูล | เรียงเป็นเรื่องต่อเนื่อง vs สลับประโยคมั่ว | แบบสลับมั่วกลับได้ผลดีกว่า ในทั้ง 18 โมเดล |
| LongMemEval | prompt คัดแล้ว ~300 token vs เต็ม ~113,000 token | ทุกโมเดลทำได้ดีกว่ามากเมื่อได้ prompt ที่คัดแล้ว |
| คัดลอกคำซ้ำ | ให้พิมพ์ลำดับคำซ้ำ ยาว 25 ถึง 10,000 คำ | ผลตกต่อเนื่องเมื่อยาวขึ้น ในทุกโมเดล |
ข้อที่สามขัดสัญชาตญาณ ผู้วิจัยเองก็บอกว่ายังอธิบายกลไกไม่ได้ เราจึงไม่ควรตีความเกินว่า “ต้องสลับข้อมูลก่อนส่ง” สิ่งที่ควรจำคือ โมเดลไม่ได้อ่าน context แบบสม่ำเสมอทุก token
พฤติกรรมต่างกันตามค่ายด้วย เมื่อเจอ distractor โมเดล Claude มีอัตราตอบมั่ว (hallucination) ต่ำสุด และมักเลือกไม่ตอบเมื่อไม่แน่ใจ ส่วน GPT มีอัตราตอบมั่วสูงสุด มักตอบผิดอย่างมั่นใจ ระบบที่ต้องการความแม่นจึงต้องรับมือได้ทั้งสองแบบ คือคำตอบ “ไม่ทราบ” และคำตอบผิดที่ฟังดูถูก
context window บอกว่าใส่ได้เท่าไร ไม่ได้บอกว่าโมเดลใช้ได้ดีแค่ไหน
ตัวอย่างคำนวณ: แชตยาว 40 รอบ
ข้อมูลจาก LongMemEval ก่อน: prompt เต็มเฉลี่ย ~113,000 token ส่วนที่คัดแล้วเฉลี่ย ~300 token
- 113,000 ÷ 300 ≈ 377 เท่า
- แปลว่า prompt เต็มมีส่วนที่เกี่ยวกับคำถามประมาณ 300 ÷ 113,000 ≈ 0.27% ที่เหลือคือสิ่งที่โมเดลต้องฝ่าไปเอง
ลองเทียบกับงานจริงแบบสมมติ ผู้ช่วย AI ที่คุยกับลูกค้ามา 40 รอบ รอบละประมาณ 2,500 token (รวมคำตอบและผลจากเครื่องมือ)
- ประวัติทั้งหมด = 40 × 2,500 = 100,000 token
- คำถามล่าสุดต้องใช้ข้อมูลจริงแค่ 2 รอบ = 2 × 2,500 = 5,000 token
- ส่วนที่เกี่ยวคิดเป็น 5,000 ÷ 100,000 = 5% อีก 95% เป็นเสียงรบกวน และบางส่วนเป็น distractor ตัวจริง เช่น ที่อยู่จัดส่งเก่าที่ลูกค้าแก้ไปแล้ว
ถ้าส่งเฉพาะสรุปประวัติ 500 token บวก 2 รอบที่เกี่ยว รวม 5,500 token input ลดลง 1 − 5,500 ÷ 100,000 = 94.5% ได้ทั้งคำตอบที่แม่นขึ้น ถูกลง และเร็วขึ้น (ตัวเลขชุดนี้เป็นตัวอย่างสมมติเพื่อคิดตาม ไม่ได้มาจากงานวิจัย)
กฎใช้งาน: ควรใส่อะไรใน context
1. ตั้งงบ token ต่อคำถาม แล้วคัดให้พอดีงบ แทนที่จะใส่จนเต็ม window Anthropic สรุปหลักนี้ไว้ในบทความ context engineering ว่าให้หา “ชุด token ที่เล็กที่สุดแต่มีสัญญาณสูงสุด”
2. ตัด distractor ก่อนตัดสิ่งที่ไม่เกี่ยว ของที่อันตรายที่สุดคือของที่ดูเหมือนคำตอบ: เอกสารฉบับเก่า, record ของลูกค้าชื่อคล้ายกัน, config ของ staging ปนกับ production ถ้าระบบมี version ให้ส่งเฉพาะฉบับล่าสุด
3. คัดก่อนส่ง ไม่ใช่ส่งทั้งกอง ระบบ RAG ควรตั้งเกณฑ์คะแนนขั้นต่ำ ไม่ใช่ดึง top-k แล้วส่งทั้งหมดเสมอ
4. แยกงานยาวออกเป็นห้อง ให้ subagent ไปอ่านของเยอะในห้องตัวเอง แล้วส่งกลับแค่สรุป Anthropic บอกว่าสรุปแบบนี้มัก 1,000-2,000 token ทั้งที่ subagent อาจใช้ไปหลายหมื่น
5. เริ่ม session ใหม่เมื่องานเปลี่ยน แทนการลากแชตเดิมยาวไปเรื่อย ๆ จดสิ่งที่ต้องส่งต่อลงไฟล์ แบบใน บทเรื่องไฟล์ส่งงานข้าม session
6. ล้างผลจากเครื่องมือที่ใช้เสร็จแล้ว ผล grep หรือ log ยาว ๆ จากสิบรอบก่อนแทบไม่มีใครกลับไปใช้ แต่ยังกิน context อยู่
โค้ด: คัด chunk ให้พอดีงบ
ตัวอย่างนี้ใช้ stdlib ล้วน รับ chunk ที่ได้จากการค้นพร้อมคะแนน ตัดตัวที่คะแนนต่ำ ตัดตัวที่เกือบซ้ำกัน (มักเป็นเอกสารฉบับเก่าของเรื่องเดียวกัน) แล้วหยิบจนเต็มงบ
from difflib import SequenceMatcher
def pick_context(chunks, count, budget=2000, min_score=0.5, dup_ratio=0.9):
"""chunks: (score, text) pairs from retrieval; count: your tokenizer's counter."""
picked, used = [], 0
for score, text in sorted(chunks, reverse=True):
if score < min_score:
break # the rest are noise and distractors
if any(SequenceMatcher(None, text, p).ratio() > dup_ratio for p in picked):
continue # near-copy, e.g. an old version of the same doc
cost = count(text)
if used + cost > budget:
continue # over budget: leave it out
picked.append(text)
used += cost
return picked, used
chunks = [
(0.91, "Refund policy v3 (2026): refunds within 14 days of delivery."),
(0.89, "Refund policy v3 (2026): refunds within 14 days of delivery!"),
(0.74, "Shipping takes 2-4 business days inside Thailand."),
(0.41, "Company picnic is on Friday."),
]
words = lambda t: len(t.split()) # stand-in counter for the demo
picked, used = pick_context(chunks, words, budget=50)
print(len(picked), used) # 2 17
ผลคือเหลือ 2 chunk: นโยบายคืนเงินหนึ่งชิ้น (ตัวซ้ำถูกตัด) กับเรื่องจัดส่ง ส่วนเรื่องปิกนิกคะแนนต่ำกว่าเกณฑ์ ในระบบจริงให้ส่งตัวนับ token ของโมเดลที่ใช้เข้าไปแทน words เพราะภาษาไทยใช้ token ต่อคำต่างจากภาษาอังกฤษมาก
difflib จับได้แค่ข้อความที่หน้าตาเกือบเหมือนกัน distractor ที่ความหมายคล้ายแต่คำต่างกัน ต้องแก้ที่ต้นทาง คือเก็บ metadata อย่าง version, วันที่, สถานะ แล้วกรองก่อนค้น
ข้อผิดพลาดที่เจอบ่อย
- เชื่อตัวเลข context window แล้วยัดเอกสารทั้งโฟลเดอร์ เพราะ “มันรับได้” รับได้ไม่เท่ากับใช้ได้ดี
- ทดสอบด้วยงานหาคำตรงตัว แล้วสรุปว่าโมเดลอ่าน context ยาวได้ดี งานจริงคำถามกับคำตอบมักใช้คำต่างกัน และงานแบบนั้นตกเร็วกว่า
- เก็บเอกสารทุกฉบับไว้ในคลังเดียว แล้วปล่อยให้ค้นเจอทั้งฉบับเก่าและใหม่ นี่คือการผลิต distractor เอง
- ลากแชตเดียวทั้งวัน ข้ามหลายงาน ทุกคำถามใหม่ต้องฝ่าประวัติทั้งหมด
- วัดผลที่ความยาวสั้น แล้วไปใช้ที่ความยาวจริง ถ้าระบบจริงส่ง 50,000 token ต้องทดสอบที่ 50,000 token repo ของ Chroma มีโค้ดให้รันซ้ำกับโมเดลของเราเอง
ก่อนเพิ่มข้อมูลเข้า context ถามว่า ชิ้นนี้ช่วยตอบคำถามนี้ หรือแค่ทำให้รู้สึกปลอดภัยว่าใส่ครบ
แหล่งอ้างอิง
คำถามที่พบบ่อย
โมเดลรองรับ 1 ล้าน token แล้ว ยังต้องคัดข้อมูลอยู่ไหม
ต้อง ตัวเลข context window บอกว่าใส่ได้เท่าไร ไม่ได้บอกว่าใช้ได้ดีแค่ไหน งานของ Chroma พบว่าทุกโมเดลที่ทดสอบตอบแย่ลงเมื่อ input ยาวขึ้น และ input ยาวยังแพงกับช้ากว่าด้วย
วางคำถามไว้ต้นหรือท้าย context ดีกว่า
งานนี้ไม่พบผลชัดเจนจากตำแหน่งของข้อมูลที่ต้องหา (needle position) สิ่งที่มีผลชัดกว่าคือความยาวรวม ความคล้ายระหว่างคำถามกับคำตอบ และ distractor จึงควรลงแรงกับการคัดข้อมูลก่อนเรื่องตำแหน่ง
ผลนี้ใช้กับโมเดลรุ่นใหม่กว่าที่ทดสอบได้ไหม
งานทดสอบโมเดลกลางปี 2025 รุ่นใหม่อาจดีขึ้น แต่ควรวัดกับงานของเราเองที่ความยาวจริง repo ของงานวิจัยเปิดให้รันซ้ำได้
คำสั่งและค่าตั้งในบทความทดสอบบนระบบตัวอย่าง ลองบนเครื่องทดสอบก่อนใช้กับระบบจริงเสมอ