ข้าม​ไป​ยัง​เนื้อหา

งบประมาณ context window

สาม​บท​ที่​ผ่าน​มา​คุณ เพิ่ม ของ​เข้า context ตลอด — retrieval ใน​บท​ที่ 2 ดึง​ข้อความ​นโยบาย​เข้า​มา, memory ใน​บท​ที่ 3 อ่าน​โน้ต​เก่า​กลับ​เข้า​หน้าต่าง ทุก​ชั้น​ที่​ประกอบ​เข้าไป​มี​ต้นทุน​เป็น token และ​จนถึง​ตอน​นี้​เรา​ยัง​ไม่​เคย วัด ต้นทุน​นั้น​เลย บท​นี้​ไม่มี​ชั้น​ใหม่​ให้​สร้าง — เป็น​บท​ที่​เรา​ถอย​มา​หนึ่ง​ก้าว​แล้ว วัด ของ​ที่​มี​อยู่ เพราะ​ก่อน​จะ​ตัดสิน​ใจ​ได้​ว่า token ไหน​คุ้ม​ค่าที่​จะ​เก็บ คุณ​ต้อง​เห็น​ตัวเลข​จริง​ก่อน และ​วิธี​มอง Context WindowContext Windowเซต​ของ token ทั้งหมด​ที่ model อ้างอิง​ได้​ตอน sampling — มอง​ให้​ถูก​คือ 'งบประมาณ​จำกัด' ที่​ใช้​แล้ว​หมด ไม่ใช่ 'ถัง' ที่​เติม​ได้​เรื่อย ๆ ทุก token ที่​ใส่​เข้าไป​กิน​งบ​ก้อน​นี้​และ​ให้​ผล​ตอบแทน​ลด​ลง​เรื่อย ๆ (diminishing returns) สำหรับ agent ที่​วน​เป็น loop ข้อมูล​จะ​พอก​ขึ้น​ทุก​เทิร์น​จึง​ต้อง​คัด​สรร​ซ้ำ​ตลอดArchitecture ที่​ถูกต้อง​สำหรับ​งาน​นี้​ไม่ใช่ “ถัง​ที่​เติม​ได้” แต่​คือ Token BudgetToken Budgetการ​มอง context window เป็น​งบ token ที่​ใช้​แล้ว​หมด​และ​ต้อง​จัด​ลำดับ​ความ​สำคัญ​ว่า​จะ​ใส่​อะไร — เพดาน​หน้าต่าง​สูงสุด​คือ 'เพดาน ไม่ใช่​เป้า' วัด​การ​ใช้​จริง​จาก field `usage` ของ agent (#15) เทิร์น​ต่อ​เทิร์น​แล้ว​ตัด token สัญญาณ​ต่ำ (เช่น blob JSON ดิบ​เก่า ๆ) ทิ้ง​ก่อนProcess — งบ token ที่​จ่าย​ออก​แล้ว​หมด​ไป

📦 code ตัวอย่าง

บท​นี้​ต่อยอด agent Order ตัว​เดิม (repo kaen-food-ordering, กำลัง​จัด​ทำ) แต่ ไม่​เพิ่ม tool หรือ store ใหม่ — เรา​เอา agent จาก​คอร์ส​ที่ 15 ที่​รัน​อยู่​แล้ว​มา​ติด​เครื่อง​วัด: อ่าน​จำนวน token ที่​ใช้​จริง​ใน​แต่ละ​เทิร์น​จาก field usage แล้ว​ดู​งบ​ก้อน​นี้​โต​ขึ้น​ต่อ​เนื่อง ตัวเลข​ที่​วัด​ได้​ใน​บท​นี้​คือ​สิ่ง​ที่ compaction (บท​ที่ 5) กับ​การ​จัด​ระเบียบ​ผล​ของ tool (บท​ที่ 6) จะ​ไป​ลด​ให้​เห็น​ผล

หน้าต่าง​คือ​งบ​ที่​จ่าย​ออก ไม่ใช่​เป้า​ให้​เติม​เต็ม

หัวข้อ​ที่​มีชื่อ​ว่า “หน้าต่าง​คือ​งบ​ที่​จ่าย​ออก ไม่ใช่​เป้า​ให้​เติม​เต็ม”

บท​ที่ 1 วาง​กรอบ​นี้​ไว้​แล้ว บท​นี้​เอา​มา​ลงมือ​ใช้​จริง Anthropic พูด​ถึง context ใน​ฐานะ​ทรัพยากร​ที่​มี​ต้นทุน​ต่อ​หน่วย​ชัดเจน — “Every new token introduced depletes this budget by some amount, increasing the need to carefully curate the tokens available to the LLM.” และ​สรุป​หลัก​ไว้​ว่า “Context, therefore, must be treated as a finite resource with diminishing marginal returns.” คำ​สำคัญ​คือ diminishing marginal returns — ผล​ตอบแทน​ที่​ลด​ลง​ต่อ​เนื่อง ยิ่ง​อัด token เข้าไป​มาก ไม่​ได้​แปล​ว่า agent ทำงาน​ดี​ขึ้น​ตาม​ส่วน

ผล​ใน​ทาง​ปฏิบัติ​มี​หนึ่ง​ข้อ​ที่​ต้อง​ปัก​ไว้: ความ​จุ​สูงสุด​ของ​หน้าต่าง​คือ เพดาน ไม่ใช่ เป้า model รุ่น​ปัจจุบัน​มีหน้าต่าง​ใหญ่ (บาง​ตัว​ถึง​หลัก 1M token — ตัวเลข​นี้​เลื่อน​ไหว​เร็ว อย่า​ท่อง​เป็น​สัจธรรม) แต่ “ใส่​ได้” ไม่​เท่ากับ “ควร​ใส่” การ​มี​ที่​ว่าง​เหลือ​ไม่ใช่​เหตุผล​ให้​ยัด​คลัง​นโยบาย​ทั้ง​ก้อน​เข้า prompt (นี่​คือ​เหตุผล​เบื้องหลัง “เมื่อไร​ไม่​ต้อง​ทำ retrieval” ใน​บท​ที่ 2) เป้าหมาย​ไม่ใช่​เติม​งบ​ให้​เต็ม แต่​คือ​ใช้​งบ​ให้​น้อย​ที่สุด​เท่า​ที่​งาน​ต้องการ

ทำไม​การ​เติม​งบ​ให้​เต็ม​ถึง​เป็น​ความคิด​ที่​ผิด? มี​งาน​วิจัย​ที่​ลง​วัน​ที่​กำกับ​สอง​ชิ้น ชี้​ปัญหา​คนละ​มุม​แต่​บังคับ​ให้​เรา​ทำ​สิ่ง​เดียวกัน — รักษา​หน้าต่าง​ให้​กระชับ

หนึ่ง — token ยิ่ง​เยอะ ความ​น่า​เชื่อถือ​ยิ่ง​ตก งาน​ของ Chroma Research ท้า​สมมติฐาน​ที่​เรา​มัก​เชื่อ​โดย​ไม่รู้ตัว — “Large Language Models (LLMs) are typically presumed to process context uniformly—that is, the model should handle the 10,000th token just as reliably as the 100th. However, in practice, this assumption does not hold.” ข้อ​สรุป​คือ “models do not use their context uniformly; instead, their performance grows increasingly unreliable as input length grows.” อาการ​นี้​มีชื่อ​ว่า Context RotContext Rotปรากฏการณ์​ที่​ความ​แม่น​ใน​การ​เรียก​ใช้​ข้อมูล​ของ model ค่อย ๆ แย่​ลง​เมื่อ context ยาว​ขึ้น — model ไม่​ได้​ประมวล​ผล​ทุก​ตำแหน่ง​เท่า​กัน (Chroma Research) คอร์ส​สอน 'หลักการ' นี้ ไม่​อ้าง​ตัวเลข % เจาะจง (เลื่อน​ไหว​เร็ว) บทเรียน​ที่​ได้​คือ​รักษา​หน้าต่าง​ให้​กระชับ​ด้วย Compaction + Curation แทนที่​จะ​ปล่อย​ให้​เต็ม​จน​เสื่อมArchitecture — และ​มัน​เกิด​ขึ้น “Even on tasks as simple as non-lexical retrieval or text replication” คือ​แม้แต่​งาน​ที่​ง่าย​อย่าง​การ​ดึง​ข้อความตรงๆ ก็​ยัง​เสื่อม​เมื่อ input ยาว​ขึ้น

⚠️ หลักการ ไม่ใช่​ตัวเลข

context rot สอน​เป็น หลักการ: ยิ่ง​หน้าต่าง​เต็ม ความ​แม่น​ใน​การ​ดึง​ข้อมูล​ยิ่ง​เสื่อม ส่วน​ตัวเลข​เปอร์เซ็นต์​การ​เสื่อม​นั้น​ขึ้น​กับ model และ​ช่วง​เวลา และ​เปลี่ยน​เร็ว​มาก — บท​นี้​จึง​ไม่​อ้าง​ตัวเลข​เสื่อม​แบบ​เจาะจง จำหลักการ​ไว้ อย่า​จำเป็น​เปอร์เซ็นต์​ตายตัว

สอง — ที่​ที่ token อยู่ ก็​สำคัญ ไม่ใช่​แค่​จำนวน แต่ ตำแหน่ง ใน​หน้าต่าง​มี​ผล​ด้วย งาน​ของ Liu et al. (“Lost in the MiddleLost in the Middleผล​วิจัย (Liu et al., TACL 2024) ที่​ว่า model ใช้​ข้อมูล​ที่​อยู่ 'ต้น' หรือ 'ท้าย' context ได้​ดี​กว่า​ที่​อยู่ 'กลาง' อย่าง​มี​นัย​สำคัญ แม้​กับ model long-context — บทเรียน​เชิง​ปฏิบัติ: วางข้อเท็จจริง​ชี้ขาด (order id, เหตุผล) ไว้​ที่​หัว​หรือ​ท้าย ไม่​ฝัง​ไว้​กลาง promptArchitecture”) พบ​ว่า “performance can degrade significantly when changing the position of relevant information, indicating that current language models do not robustly make use of information in long input contexts.” และ​เจาะจง​ว่า “Performance is often highest when relevant information occurs at the beginning or end of the input context, and significantly degrades when models must access relevant information in the middle of long contexts, even for explicitly long-context models.” — model ใช้​ข้อมูล​ที่​ต้นๆ หรือ​ท้ายๆ ได้​ดี​กว่า​ข้อมูล​ที่​ถูก​ฝัง​กลาง​หน้าต่าง​ยาวๆ

สอง​ข้อ​นี้​รวม​กัน​ให้​กติกา​การ​วาง​ของ​ใน​งบ: เก็บ token ให้​น้อย​และ วาง ข้อเท็จจริง​ที่​ชี้ขาด (order id, เหตุผล​การ​คืน​เงิน) ไว้​ที่ ต้น​หรือ​ท้าย ไม่ใช่​ฝัง​กลาง​กอง​ประวัติ​ยาวๆ

flowchart TB
  SP["system prompt<br/>คงที่ · เล็ก"] --> USED
  TD["นิยาม tool 3 ตัว<br/>คงที่"] --> USED
  H["ประวัติบทสนทนา<br/>โตทุกเทิร์น ↑"] --> USED
  TR["ผลของ tool ที่สะสม<br/>โตเร็วสุด ↑↑"] --> USED
  USED["token ที่ใช้ไปแล้ว<br/>วัดได้จาก field usage"] --> CEIL{"เข้าใกล้เพดาน<br/>context window?"}
  CEIL -->|ยังห่าง| ANS["เหลืองบให้ model ตอบ"]
  CEIL -->|เกือบชน| ROT["สัญญาณเริ่มเสื่อม<br/>→ compaction บทที่ 5"]
  classDef grow fill:#ea580c,stroke:#7c2d12,color:#f8fafc;
  class H,TR grow;
  classDef fixed fill:#0891b2,stroke:#064e5b,color:#f8fafc;
  class SP,TD fixed;

คำ​บรรยาย​ภาพ: งบ​ก้อน​เดียว​ถูก​กิน​จาก​สี่​แหล่ง — system prompt กับ​นิยาม tool (สี​ฟ้า) คงที่​และ​เล็ก ส่วน​ประวัติ​บทสนทนา​กับ​ผล​ของ tool ที่​สะสม (สี​ส้ม) คือ​สอง​แหล่ง​ที่ โต ทุก​เทิร์น โดย​ผล​ของ tool โต​เร็ว​ที่สุด เมื่อยอด token ที่​ใช้​ไป​เข้า​ใกล้​เพดาน สัญญาณ​เริ่ม​เสื่อม — ซึ่ง​เป็น​เหตุผล​ที่​บท​ที่ 5 ต้อง compaction สอง​แหล่ง​สี​ส้ม​นั่น​คือ​คือ​ที่​ที่​เรา​ตัด​ได้​ก่อน

ทฤษฎี​พอแล้ว มา​ดู​ตัวเลข​จริง​ของ agent #15 กัน สิ่ง​ที่ วัด​ได้​จริง (ต่าง​จาก “attention budget” ที่​เป็น​แค่​สัญชาตญาณ​ตาม​บท​ที่ 1) คือ​จำนวน token จาก field usage ที่ model คืน​มา​ทุก​ครั้ง

ฝั่ง Microsoft.Extensions.AI (ที่​คอร์ส #15 พา​ย้าย​มา​ใช้​ใน​บท​ที่ 5) พับ block usage ของ Messages API ลงใน ChatResponse.Usage ให้ — อ่าน​ออก​มา​ต่อ​เทิร์นได้ตรงๆ:

// history = running List<ChatMessage> จาก #15
// chat = IChatClient เดิม (x-api-key, anthropic-version 2023-06-01,
// model "claude-opus-4-8", ไม่ส่ง temperature — invariant #1–4 ของ #15)
ChatResponse reply = await chat.GetResponseAsync(history, options);
UsageDetails? u = reply.Usage; // UsageDetails? — อาจเป็น null ถ้า connector ไม่ populate
if (u is not null)
Console.WriteLine(
$"เทิร์นนี้ → เข้า {u.InputTokenCount} · ออก {u.OutputTokenCount} · รวม {u.TotalTokenCount}");

เรียก​วน​ไป​ทุก​เทิร์น คุณ​จะ​เห็น InputTokenCount โต​ขึ้น​ต่อ​เนื่อง ทั้ง​ที่​คำถาม​ของ​ลูกค้า​ใน​แต่ละ​เทิร์นสั้นพอๆ กัน — เพราะ​ประวัติ​กับ​ผล​ของ tool ที่​สะสม (สอง​แหล่ง​สี​ส้ม​ใน​ไดอะแกรม) ถูก​ส่ง​ซ้ำ​เข้าไป​ทั้ง​กอง​ทุก​ครั้ง นั่น​คือ​งบ​ที่​ถูก​กิน​ขึ้น​ต่อ​เนื่อง ต่อ​หน้า​ต่อตา

⚠️ ChatResponse.Usage อาจ​เป็น null — ตรวจ​กับ connector ที่​ติดตั้ง​จริง

ChatResponse.Usage เป็น UsageDetails? — ค่า​มัน​จะ​มี​ก็​ต่อ​เมื่อ connector ของ Anthropic เป็น​คน populate ให้ (Claude ยัง​อยู่​หลัง NuGet Anthropic (beta) ผ่าน AsIChatClient ตาม invariant #6 ของ #15 ไม่มี connector first-party ของ Microsoft) ก่อน​พึ่ง​ตัวเลข​นี้ ให้​ยิง​จริง​หนึ่ง​ครั้ง​แล้ว​เช็กว่า Usage, InputTokenCount, OutputTokenCount ไม่​เป็น null กับ version package ที่​คุณ​ติดตั้ง​อยู่ ถ้า​เป็น null ให้​ถือ block usage ของ Messages API ดิบ​เป็น​แหล่ง​ความ​จริงinput_tokens (บวก cache_creation_input_tokens / cache_read_input_tokens ถ้า​ใช้ prompt caching) และ output_tokens ตาม​ที่ #15 บท​ที่ 2 สอน​อ่าน​มา​แล้ว

ถ้า​อยาก​รู้​ต้นทุน token ของ​ข้อความ ก่อน ยิง​จริง (เช่น​เพื่อ​เช็กว่า​จะ​ชน​เพดาน​ไหม​ก่อน​ส่ง) Messages API มี endpoint POST /v1/messages/count_tokens ให้​นับ​ล่วงหน้า​ได้ และ​ข้อ​ควร​ระวัง​จาก #15 ยัง​ใช้: อย่า​เดา token ของ Claude ด้วย tiktoken ของ OpenAI เพราะ​คนละ tokenizer นับ​ต่ำ​กว่า​จริง​ราว 15–20%

พอ​มี​ตัวเลข การ​จัด​งบ​ก็​กลาย​เป็นการ​ตัดสิน​ใจ​ที่​เห็น​ภาพ ไม่ใช่​เดา ลอง​ไล่​ดู​ว่า token ที่​ใช้​ไป​ประกอบ​ด้วย​อะไร แล้ว​ถาม​ว่า​อัน​ไหน สัญญาณ​ต่ำ — คือ​กิน​งบ​แต่​ไม่​ช่วย​เทิร์น​ถัด​ไป ตัว​ที่​มัก​โผล่​มา​อันดับ​แรก​คือ record ออเดอร์​ดิบๆ ก้อน​เต็ม​จาก getOrder เทิร์น​เก่าๆ ที่ agent อ่าน​ไป​แล้ว ตัดสิน​ใจ​ไป​แล้ว แต่​ยัง​ลอย​อยู่​ใน​ประวัติ กิน token ทุก​เทิร์น​ถัด​จาก​นั้น​โดย​ไม่​ให้​อะไร​กลับ​มา นั่น​คือ​เป้า​แรก​ที่​ตัด — งาน​ของ​บท​ที่ 6 (ปั้น​สิ่ง​ที่ tool คืน​กลับ​ให้​เหลือ​แต่ field ที่​ใช้) และ​บท​ที่ 5 (สรุป​ประวัติ​ที่​ยาว​แล้ว​เคลียร์​ผล​ของ tool ที่​จัดการ​ไป​แล้ว)

แต่​ระวัง​อย่า​แกว่ง​ไป​สุด​อีก​ทาง เป้าหมาย​ไม่ใช่ “ตัด​ให้​เหลือ​น้อย​ที่สุด” เฉยๆ — ดาวเหนือ​ของ​บท​ที่ 1 คือ token ที่ สัญญาณ​สูง ชุด​ที่​เล็ก​ที่สุด ไม่ใช่​แค่​เล็ก​ที่สุด Chip Huyen เตือน​อีก​ด้าน​ไว้​ว่า “The more relevant information there is in the context, the less the model has to rely on its internal knowledge, which can be unreliable…” และ “having access to relevant information in the context can help the model generate more detailed responses while reducing hallucinations.” — ข้อมูล ที่​เกี่ยวข้อง ใน​หน้าต่าง​ช่วย​ลด hallucination ได้ ฉะนั้น​สิ่ง​ที่​เรา​ไล่​ตัด​คือ token สัญญาณ​ต่ำ ไม่ใช่​ตัด​ข้อมูล​ที่​เกี่ยวข้อง​ทิ้ง​เพื่อ​ประหยัด​ตัวเลข

🔁 บท​นี้​เชื่อม​ไป​ไหน​ต่อ

งบ​ที่​คุณ​วัด​ได้​ใน​บท​นี้​คือ ตัว​กระตุ้น ของ​สอง​บท​ถัด​ไป — พอ InputTokenCount ข้าม​เกณฑ์​ที่​ตั้ง​ไว้ compaction (บท​ที่ 5) จะ​สรุป​ประวัติ​ให้​กระชับ​ลง และ การ​จัด​ระเบียบ​ผล​ของ tool (บท​ที่ 6) จะ​ลด​ขนาด​สิ่ง​ที่ tool คืน​กลับ​ตั้งแต่​ต้นทาง ทั้ง​คู่​วัดผล​ได้​จาก field usage ตัว​เดียว​กับ​ที่​บท​นี้​สอน​อ่าน

จบ​บท​นี้​คุณ​เปลี่ยน​จาก​คน “หวัง​ว่า​คง​ยัง​ไม่​ล้น” มา​เป็น​คน​ที่ เห็น​งบ​เป็น​ตัวเลข ทุก​เทิร์น รู้​ว่า token ก้อน​ไหน​โต​เร็ว ก้อน​ไหน​สัญญาณ​ต่ำ​ควร​ตัด​ก่อน และ​รู้​ว่า​เพดาน​คือ​ข้อ​จำกัด ไม่ใช่​เป้า บท​หน้า​เรา​เริ่ม​ลงมือ​ลด​งบ​ก้อนนั้นจริงๆ ด้วย compaction


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้

บท​นี้​อิง​ต้นทาง​ที่​ลง​วัน​ที่​กำกับ อ่าน​ต่อ​ได้​โดยตรง:

  • Anthropic, “Effective context engineering for AI agents” (2025-09-29) — context ใน​ฐานะ​ทรัพยากร​จำกัด​ที่​ผล​ตอบแทน​ลด​ลง (“Every new token introduced depletes this budget…” / “finite resource with diminishing marginal returns”)
  • Chroma Research, “Context Rot: How Increasing Input Tokens Impacts LLM Performance” (2025-07-14) — model ไม่​ได้​ประมวล​ผล context อย่าง​สม่ำเสมอ ความ​น่า​เชื่อถือ​เสื่อม​ลง​เมื่อ input ยาว​ขึ้น (สอน​เป็น​หลักการ ไม่​อ้าง​เปอร์เซ็นต์​เจาะจง)
  • Liu et al., “Lost in the Middle: How Language Models Use Long Contexts” (arXiv 2023-07-06; TACL 2024) — ประสิทธิภาพ​สูงสุด​เมื่อ​ข้อมูล​ที่​เกี่ยวข้อง​อยู่​ต้น​หรือ​ท้าย​หน้าต่าง และ​เสื่อม​เมื่อ​ถูก​ฝัง​กลาง
  • Chip Huyen, “Building A Generative AI Platform” (2024-07-25) — ข้อมูล​ที่​เกี่ยวข้อง​ใน​หน้าต่าง​ช่วย​ลด​การ​พึ่ง​ความ​รู้​ภายใน​ที่​ไม่​น่า​เชื่อถือ และ​ลด hallucination

เช็กความเข้าใจ — บทที่ 4

ข้อ 1 / 3

ทำไมจึงควรถือความจุสูงสุดของ context window เป็น 'เพดาน' ไม่ใช่ 'เป้าให้เติมเต็ม'?