Context Engineering: ทำไม agent ของคุณโง่ลงที่เทิร์น 40
แค่ย้ายข้อเท็จจริงลงไปสามบรรทัดใน prompt ที่ใช้ 2.6% ของ context window การดึงข้อมูลร่วงจาก 84% เหลือ 19%
ในหน้านี้
นี่คือ prompt หนึ่งชุดที่ส่ง 288 ครั้งไปยังโมเดลเดียวกันด้วย greedy decoding ความยาว 853 token มีทะเบียน ticket ซัพพอร์ต 25 รายการ — เมือง, คิว, priority, เจ้าของ, extension — และคำถามหนึ่งข้อ: Marta Ferreira ต้องการให้โทรกลับเรื่อง ticket ของเธอ extension สายตรงของ ticket นั้นคืออะไร?
ทะเบียนเหมือนเดิมทุกครั้ง โมเดลเหมือนเดิมทุกครั้ง สิ่งเดียวที่เปลี่ยนคือบรรทัดใดใน 25 บรรทัดที่มีคำตอบ
| ตำแหน่งของคำตอบ | ตอบถูก | อัตราการดึงข้อมูล | ช่วง 95 % |
|---|---|---|---|
| 1 จาก 25 | 27/32 | 84 % | 68–93 % |
| 4 จาก 25 | 6/32 | 19 % | 9–35 % |
| 7 จาก 25 | 6/32 | 19 % | 9–35 % |
| 10 จาก 25 | 9/32 | 28 % | 16–45 % |
| 13 จาก 25 | 8/32 | 25 % | 13–42 % |
| 16 จาก 25 | 6/32 | 19 % | 9–35 % |
| 19 จาก 25 | 6/32 | 19 % | 9–35 % |
| 22 จาก 25 | 3/32 | 9 % | 3–24 % |
| 25 จาก 25 | 7/32 | 22 % | 11–39 % |
32 การทดลองต่อแถว ใช้ ticket ต่างกันในแต่ละการทดลอง และใช้ช่วง Wilson จาก บทที่ 4 เพราะ 17 จาก 20 ไม่ได้แยกอะไรออกจากอะไรได้เลย
ตำแหน่งที่หนึ่งถูกตอบ 84 % ของเวลา ตำแหน่งอื่นทั้งหมดอยู่ระหว่าง 9 % ถึง 28 % และช่วงของทั้งแปดตำแหน่งนั้นทับซ้อนกัน ดังนั้นการอ่านอย่างตรงไปตรงมาคือ อันดับแรก แล้วค่อยอย่างอื่นทั้งหมด Liu และคณะพบรูปตัว U — สูงที่ปลายทั้งสอง ต่ำตรงกลาง — แต่แขนด้าน recency ไม่ได้เห็นชัดที่นี่: 22 % ในตำแหน่งสุดท้ายยังอยู่ในช่วงกระจายเดียวกับตำแหน่งกลาง สิ่งที่ไม่อยู่ในช่วงไหนเลยคือการร่วงจากตำแหน่ง 1 ไปตำแหน่ง 4 สามบรรทัด
context window ของโมเดลนี้คือ 32,768 token prompt ใช้ไป 853 token หรือ 2.6 % ไม่มีอะไรล้น ไม่มีอะไรถูกตัด ไม่มีขีดจำกัดใดถูกแตะ ไม่มีคำเตือนปรากฏ โมเดลหยุดหาบรรทัดที่มันได้รับมาแล้วเจอ เพียงเพราะบรรทัดนั้นถูกย้ายลงไปสามตำแหน่งในรายการ 25 รายการ
บทที่ 16 คิดราคาของ context window และจบด้วยการเตือนว่า การ มี token หนึ่งล้านไม่ใช่การ ใช้ มัน พร้อมชี้มาที่นี่ นี่แหละคือสิ่งนั้น
แสดงรายละเอียด
บทนี้ต้องใช้สิ่งใดจากบทก่อนหน้า
- บทที่ 9 อนุมาน self-attention และต้นทุน ของมัน ทุก token attend ถึง token อื่นทุกตัว ดังนั้นจำนวนความสัมพันธ์แบบคู่จึงโตตามกำลังสองของความยาว ข้อเท็จจริงนี้จะถูกใช้ด้านล่าง ไม่ได้อนุมานซ้ำ
- บทที่ 16 นับ bucket token ที่คิดเงินได้ห้าประเภท และแสดงว่าบิลของบทสนทนาโตแบบกำลังสอง บทนี้คือสิ่งที่คุณทำกับมันโดยไม่ทำให้ agent พัง
- บทที่ 18 สร้างแค็ตตาล็อกเครื่องมือและวัดว่าเครื่องมือ 20 รายการไม่ได้ทำร้ายการเลือก แต่ทำให้ prompt ใหญ่ขึ้นหกเท่า นี่คือบิลของพวกมัน
- บทที่ 19 สร้างการดึงข้อมูล Just-in-time retrieval ด้านล่างคือบทนั้นที่นำมาใช้กับประวัติของ agent เอง; chunking จะไม่ถูกอธิบายซ้ำ
- บทที่ 23 สร้าง harness ทุกอย่างในบทนี้คือนโยบายที่รันอยู่ในลูปของมัน นั่นคือเหตุผลที่มันเป็น TypeScript: artefact คือบริการอายุยาวที่ถือ state ไม่ใช่ notebook ที่ถือ tensor
งานสองแบบที่ชื่อคล้ายกัน
ลิงก์ไปยังส่วน: งานสองแบบที่ชื่อคล้ายกันAnthropic ขีดเส้นแบ่งไว้ในเดือนกันยายน 2025 และสองประโยคนี้ควรวางคู่กัน Prompt engineering คือ “methods for writing and organizing LLM instructions for optimal outcomes” Context engineering คือ “the set of strategies for curating and maintaining the optimal set of tokens (information) during LLM inference, including all the other information that may land there outside of the prompts”.1
ความแตกต่างที่ใช้งานจริงคือ เมื่อไร และ โดยใคร prompt ถูกเขียนครั้งเดียวโดยมนุษย์ แล้วตรวจทาน context ถูกประกอบในทุก call โดยโค้ดที่ไม่มีใครกำลังมองอยู่ จากวัสดุที่ไม่มีใครเขียนด้วยมือ: ประวัติ 40 turn, ผลลัพธ์เครื่องมือ 6 ชุด, passage ที่ดึงมา 4 ชิ้น, โปรไฟล์ผู้ใช้, JSON schema 12 ชุด บทที่ 15 วัดว่าคำสั่งที่ดีขึ้นซื้ออะไรได้ บทนี้พูดถึง token อีก 90 เปอร์เซ็นต์ที่มาถึงเอง
เอกสารเดียวกันตั้งชื่อทรัพยากรที่ทุกอย่างใช้: โมเดล “have an ‘attention budget’ that they draw on when parsing large volumes of context. Every new token introduced depletes this budget by some amount” และตั้งชื่ออาการ: “as the number of tokens in the context window increases, the model’s ability to accurately recall information from that context decreases” — context rot.1
ประโยคสุดท้ายนั้นเป็นข้ออ้างเกี่ยวกับพฤติกรรม ซึ่งแปลว่าตรวจสอบได้ และตารางด้านบนของหน้านี้ก็คือการตรวจสอบ
ตารางนั้นสร้างขึ้นอย่างไร
ลิงก์ไปยังส่วน: ตารางนั้นสร้างขึ้นอย่างไร40 บรรทัดยิงไปที่ endpoint ในเครื่องจาก บทที่ 22 — เซิร์ฟเวอร์ Python ขนาดเล็กที่ถือ Qwen2.5-0.5B-Instruct บน CPU และพูดรูปแบบ chat-completions เพื่อให้ลูปอยู่ใน TypeScript และ tensor อยู่ฝั่งไกลของพอร์ต
const DEPTHS = [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1];
for (const d of DEPTHS) {
const slot = Math.round(d * (N - 1));
let hits = 0, other = 0;
for (let t = 0; t < TRIALS; t++) {
const recs = buildRecords(N, 1000 + t); // 25 unique tickets
const gold = recs[Math.floor(rng(7 + t)() * N)]; // a different one each trial
const rest = recs.filter((x) => x.ticket !== gold.ticket).slice(0, N - 1);
const lines = [...rest.slice(0, slot).map((x) => x.line),
gold.line,
...rest.slice(slot).map((x) => x.line)];
const r = await complete(prompt(lines, ask(gold.owner)), { maxTokens: 12 });
const said = /\d{4}/.exec(r.text)?.[0];
if (said === String(gold.ext)) hits++;
else if (said && recs.some((x) => String(x.ext) === said)) other++;
}
}ตัวนับ other คือสิ่งที่เปลี่ยนผลลัพธ์น่าผิดหวังให้เป็นผลลัพธ์ที่มีประโยชน์: เมื่อโมเดลผิด มัน หลงทาง หรือมัน มั่นใจ?
คำตอบคือมั่นใจ ในแปดตำแหน่งที่ไม่ใช่ตำแหน่งแรก 136 จาก 205 คำตอบผิดเป็น extension ของ ticket อื่น — ตัวเลขสี่หลักจริง รูปแบบถูกต้อง อ่านจากบรรทัดผิด ที่ตำแหน่ง 1 มีเพียงหนึ่งในห้าครั้งที่พลาดเป็นแบบนั้น; ที่ตำแหน่ง 7 เป็น 21 จาก 26
ความแตกต่างนี้คือสิ่งสำคัญใน production โมเดลที่พูดว่า ฉันหาไม่เจอ คือบั๊กที่คุณสังเกตเห็น; โมเดลที่ส่งเลขของแถวข้างเคียงกลับมา คือบั๊กที่คุณ ship เพราะบนหน้าจอทั้งสองดูเหมือนกัน นี่คือความล้มเหลวที่บทที่ 19 สร้าง citation ที่ตรวจสอบได้ไว้รับมือ แต่ครั้งนี้มันมาจากข้างใน prompt แทนที่จะมาจาก index
ไม่ใช่แค่ที่ไหน แต่คือมากแค่ไหนด้วย
ลิงก์ไปยังส่วน: ไม่ใช่แค่ที่ไหน แต่คือมากแค่ไหนด้วยตำแหน่งคือแกนหนึ่ง ความยาวคืออีกแกน และทดสอบง่ายกว่า: วางคำตอบไว้ตรงกลาง แล้วเพิ่มความยาวของรายการ
| records | prompt tokens | ตอบถูก | อัตรา | ช่วง 95 % | ผิดบรรทัด | ไม่ใช่ทั้งสอง |
|---|---|---|---|---|---|---|
| 1 | 97 | 18/20 | 90 % | 70–97 % | 0 | 2 |
| 3 | 159 | 11/20 | 55 % | 34–74 % | 9 | 0 |
| 8 | 315 | 3/20 | 15 % | 5–36 % | 17 | 0 |
| 20 | 695 | 2/20 | 10 % | 3–30 % | 16 | 2 |
| 40 | 1,324 | 3/20 | 15 % | 5–36 % | 15 | 2 |
| 80 | 2,587 | 1/20 | 5 % | 1–24 % | 18 | 1 |
| 140 | 4,477 | 2/20 | 10 % | 3–30 % | 18 | 0 |
หนึ่ง record และ 97 token: 90 % สาม record และ 159 token: 55 % แปด record และ 315 token: 15 % และจากนั้นแบนราบต่ำไปจนถึง 140 record และ 4,477 token การพังทั้งหมดเกิดขึ้นระหว่างบรรทัดแรกกับบรรทัดที่แปดของรายการ
คอลัมน์สุดท้ายคือทุกอย่างที่ไม่ใช่ extension ที่ถูกต้องและไม่ใช่ของ record อื่น ซึ่งเมื่อมี record เดียวบนหน้า ก็เป็นที่เดียวที่คำตอบผิดจะตกลงไปได้ สองครั้งที่พลาดในกรณี หนึ่ง record ควรรายงานแทนที่จะปัดทิ้ง เพราะทั้งสองไม่ใช่การปฏิเสธ: ครั้งหนึ่งตอบ 5806 ให้ทะเบียนที่มีบรรทัดเดียวบอกว่า 5805 ที่ 97 token กับผู้สมัครเพียงตัวเดียว โมเดลนี้ยังคัดลอกตัวเลขผิด 2 ครั้งจาก 20 และนั่นคือพื้นฐานที่ทุกอย่างอื่นถูกวัดเทียบ
มีข้อสรุปสองอย่าง context ที่ใหญ่ขึ้นซื้อสิทธิ์ในการส่งมากขึ้น ไม่ได้ซื้อความแน่นอนว่าจะถูกอ่าน: โมเดลนี้มี window 32,768 token และช่วงที่ใช้งานได้จริงในงานนี้คือไม่กี่ร้อย token และไม่มี threshold ไม่มีหน้าผา ไม่มีสถานะ “context full” — การเสื่อมเริ่มตั้งแต่ record ที่สามและสมบูรณ์ที่ record ที่แปด ที่หนึ่งเปอร์เซ็นต์ของ window ไม่ว่าขีดจำกัด context คืออะไร มันไม่ใช่สิ่งที่ควบคุมเรื่องนี้
มักมีการเสนอ 2 กลไก กลไกแรกคือเลขคณิตจากบทที่ 9 ซึ่ง Anthropic กล่าวด้วยถ้อยคำเดียวกับคอร์สนี้: โมเดล “are based on the transformer architecture, which enables every token to attend to every other token across the entire context. This results in n² pairwise relationships for n tokens”.1 Attention บน sequence ที่ยาวขึ้นไม่ใช่การทำ operation เดิมกับวัสดุมากขึ้น; มันคือ budget คงที่หนึ่งก้อนของมวลความน่าจะเป็นที่ถูกกระจายไปยังคู่แข่งมากขึ้น กลไกที่สองคือการฝึก: โมเดลเห็น sequence สั้นมากกว่ายาวมาก ดังนั้น pattern เชิงตำแหน่งระยะไกลจึงเป็นส่วนของ network ที่ฝึกน้อยที่สุด นั่นเป็นเหตุผลเชิงโต้แย้ง ไม่ใช่การวัด และบทนี้ตัดสินมันไม่ได้
สิ่งที่ ตัดสินแล้ว คือรูปทรง และเป็นเช่นนั้นมาตั้งแต่ 2023 Liu และคณะทดสอบการตอบคำถามหลายเอกสารและ key-value retrieval ข้ามตระกูลและขนาดของโมเดล และพบว่า “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”.2 บทที่ 15 เอากฎตำแหน่งจาก paper นั้น; บทที่ 19 เอาเหตุผลที่ chunk ที่ดึงมา 20 ชิ้นทำคะแนนแย่กว่า 4 ชิ้นจาก paper นั้น รูปแบบใช้งานจริงของข้อเท็จจริงนี้คือประโยคเดียวที่คุณควรลงมือทำ: เรื่องนี้ใช้เวลาห้านาทีในการวัดบนโมเดลของคุณเองด้วยข้อมูลของคุณเอง และ curve ที่ตีพิมพ์ใด ๆ ก็แทนของคุณไม่ได้
ไม่มีใครรู้ว่าอะไรอยู่ใน window ของตัวเอง
ลิงก์ไปยังส่วน: ไม่มีใครรู้ว่าอะไรอยู่ใน window ของตัวเองถามทีมหนึ่งว่าอะไรเติม context ของ agent ของพวกเขา แล้วคุณจะได้ค่าประมาณ เพราะไม่มี API ใดคืนคำตอบนี้: response ให้ prompt_tokens กับคุณ เป็นตัวเลขเดียวสำหรับทั้งหมด
คุณกู้ breakdown ได้ด้วยการนับ 4 ครั้งและลบ 3 ครั้ง — prompt ที่ render ทั้งหมด, ตัวเดียวกันแต่ไม่มีนิยามเครื่องมือ, system message อย่างเดียวทั้งมีและไม่มีนิยามเหล่านั้น, และทุกอย่างเมื่อเอาผลลัพธ์เครื่องมือออก:
async function buckets(messages: Msg[]) {
const sys = messages.slice(0, 1);
const withoutResults = messages.filter((m) => m.role !== "tool");
const [total, sysWithTools, sysNoTools, noResults] = await Promise.all([
countPrompt(messages, CATALOGUE), // everything
countPrompt(sys, CATALOGUE), // system + scaffolding + schemas
countPrompt(sys), // system + scaffolding
countPrompt(withoutResults, CATALOGUE), // everything but tool output
]);
return {
system: sysNoTools,
tools: sysWithTools - sysNoTools,
toolResults: total - noResults,
conversation: total - sysWithTools - (total - noResults),
total,
};
}countPrompt ใช้ chat template ของโมเดลเองก่อน tokenizing ซึ่งสำคัญกว่าที่ฟังดู: ข้อความของคุณไม่ใช่สิ่งที่ถูกนับ role marker, preamble ของ tool calling และการ render schema ล้วนเป็น token ที่คุณจ่ายเงินและไม่เคยพิมพ์ บทที่ 7 สร้าง tokenizer และบทที่ 16 นับด้วย js-tiktoken; ที่นี่การนับมาจากโมเดลเดียวกันที่จะอ่าน prompt ซึ่งเป็นการนับเดียวที่ถูกต้องเป๊ะ
ตอนนี้รัน agent จริงผ่านมัน: การสืบสวน incident 40 turn, เครื่องมือ 12 รายการ, สภาพแวดล้อม operations ปลอมที่คืน log dump และ series ของ metric ที่สมจริง
| turn | system | นิยามเครื่องมือ | บทสนทนา | ผลลัพธ์เครื่องมือ | prompt รวม | input ที่คิดเงินใน turn นี้ |
|---|---|---|---|---|---|---|
| 1 | 85 | 1,817 | 155 | 490 | 2,547 | 4,370 |
| 2 | 85 | 1,817 | 282 | 529 | 2,713 | 5,275 |
| 5 | 85 | 1,817 | 647 | 1,870 | 4,419 | 8,093 |
| 10 | 85 | 1,817 | 946 | 2,141 | 4,989 | 4,951 |
| 20 | 85 | 1,817 | 1,500 | 2,943 | 6,345 | 6,316 |
| 30 | 85 | 1,817 | 2,187 | 4,000 | 8,089 | 8,059 |
| 40 | 85 | 1,817 | 3,053 | 5,677 | 10,632 | 21,090 |
อ่านแถวแรกเทียบกับแถวสุดท้าย
ที่ turn 1 prompt มี 2,547 token และ 71 % ของมันคือนิยามเครื่องมือ system prompt คือ 3 % สิ่งที่ผู้ใช้พิมพ์คือ 6 % agent ยังไม่ได้ทำอะไรเลย แต่กำลังแบก JSON schema 1,817 token แล้ว
เมื่อถึง turn 40 prompt มี 10,632 token และสัดส่วนกลับด้าน: นิยาม 17 %, บทสนทนา 29 %, ผลลัพธ์เครื่องมือ 53 % output ของเครื่องมือแซงนิยามที่ turn 5; บทสนทนายังไม่แซงนิยามจนถึง turn 25 ดังนั้นตลอด 60 เปอร์เซ็นต์แรกของ session แค็ตตาล็อกเครื่องมือใหญ่กว่าทุกอย่างที่เคยพูดกัน
แล้วดูยอดรวม ตลอด 57 model call การรันนี้ถูกคิดเงิน 370,291 input token สำหรับ context สุดท้าย 10,632 — prompt สุดท้ายถูกจ่ายซ้ำประมาณ 35 เท่า ซึ่งคือ quadratic ของบทที่ 16 พร้อมตัวคูณของ agent ทับอยู่ด้านบน ใน 370,291 นั้น 103,569 หรือ 28 % ของทุกอย่างที่ถูกคิดเงิน คือนิยามเครื่องมือทั้ง 12 รายการ ที่ถูกส่งซ้ำแบบ byte-identical ในทุก call
นิยามเครื่องมือมีต้นทุนเท่าไร
ลิงก์ไปยังส่วน: นิยามเครื่องมือมีต้นทุนเท่าไรแค็ตตาล็อกเครื่องมือคือต้นทุนคงที่ที่ใหญ่ที่สุดใน agent และมองไม่เห็น เพราะคุณไม่เคยเห็นมัน: คุณส่ง array ของ object แล้ว provider render มันเข้า prompt ให้คุณ เมื่อนับบนเครื่องมือ 12 รายการเดิม:
system prompt + chat scaffolding, no tools: 85 tokens
all twelve definitions: 1,817 tokens
of which fixed tool-calling scaffolding: 126 tokens
three tools instead of twelve: 605 tokens
same twelve, one-sentence descriptions,
no parameter prose: 1,291 tokens (-29 %)ต่อตัว เครื่องมือมีต้นทุนเพิ่มตั้งแต่ 80 token สำหรับ get_current_time ซึ่งรับ string หนึ่งค่า ไปจนถึง 263 สำหรับ search_tickets ซึ่งรับ parameter 4 ตัวพร้อม enum และประโยคคำแนะนำตัวละหนึ่งประโยค นั่นคืออัตราแลกเปลี่ยนเบื้องหลังคำแนะนำหลักของบทที่ 18 ว่า description คือ API: description ที่ดีมีต้นทุนประมาณ 100 token ในทุก request ไปตลอดชีวิตของ agent มีผลตามมา 3 อย่าง
เครื่องมือที่คุณไม่ได้ใช้ก็ยังคิดเงิน agent เรียกใช้ 7 จาก 12 รายการ อีก 5 รายการมีต้นทุน 697 token ในแต่ละ request จาก 57 request — รวม 39,729 มากกว่าหนึ่งในสิบของทุกอย่างที่การรันถูกคิดเงิน สำหรับความสามารถที่ไม่เคยแตะ หนึ่งในห้ารายการมีรายละเอียดคมที่สุดใน trace: โมเดลพยายามเรียก read_log สามครั้ง ซึ่งไม่มีอยู่จริง เครื่องมือที่มันต้องการคือ search_logs นิยามแพงเป็นอันดับสองในแค็ตตาล็อกที่ 237 token มันจ่ายให้นิยามนั้น 57 ครั้ง ไม่เคยใช้ และไม่เคยหาชื่อเจอ
การตัด prose คือ optimisation ที่ถูกที่สุดที่มี และมันเป็นการแลกเปลี่ยน การตัด description เหลือหนึ่งประโยคและทิ้งเอกสาร parameter ประหยัดได้ 526 token ต่อ call หรือ 29 เปอร์เซ็นต์ โดยไม่แตะ logic สักบรรทัด — และทำให้โมเดลเรียกเครื่องมือแย่ลง ซึ่งเป็นสิ่งที่บทที่ 18 วัดไว้ ประเด็นคือทั้งสองด้านของการแลกเปลี่ยนนี้อยู่ในหน่วยเดียวกันแล้ว
เมื่อถึง scale หนึ่ง การส่งนิยามทั้งหมดก็ไม่สมเหตุสมผลอีกต่อไป Anthropic ใส่ตัวเลขให้เรื่องนี้ในเดือนพฤศจิกายน 2025: ชุด server ที่เชื่อมต่อจำนวนมากหมายถึงการประมวลผลนิยาม “hundreds of thousands of tokens” ก่อนที่ request จะถูกอ่าน และการแทนที่สิ่งนั้นด้วยการรันโค้ด — ให้ agent ค้นพบและโหลดเฉพาะนิยามที่ต้องการ — “reduces the token usage from 150,000 tokens to 2,000 tokens, a time and cost saving of 98.7%”.3 แนวคิดเดียวกับส่วนที่เหลือของบทนี้ แต่นำไปใช้กับ schema แทนประวัติ: เก็บ index ไว้ แล้ว resolve entry เมื่อจำเป็น
ทำให้พังโดยตั้งใจ
ลิงก์ไปยังส่วน: ทำให้พังโดยตั้งใจมีสิ่งสองอย่างถูกปลูกไว้ใน transcript 40 turn นั้น ที่ turn 2 ก่อนงานจริงใด ๆ ผู้ใช้ระบุกฎประจำว่า: ticket ใดก็ตามที่คุณเปิดต้อง filed ใต้ employee number ของฉัน 4417 ที่ turn 19 ระหว่าง incident มีข้อเท็จจริงหนึ่งข้อ: shard ที่ได้รับผลกระทบคือ pay-shard-7 ยืนยันโดยทีม payments ที่ turn 40 ผู้ใช้ขอให้ agent เปิด incident ticket ซึ่งต้องใช้ทั้งสองอย่าง แต่ละ probe ถูกถามด้วยถ้อยคำต่างกัน 6 แบบและให้คะแนนเต็ม 6 — greedy decoding เป็น deterministic ดังนั้น call เดียวให้ yes หรือ no ที่ทำซ้ำไม่ได้ และ 6 call ให้ rate
จากนั้น replay transcript ภายใต้ context policy 7 แบบ Replay แทนที่จะ re-run อย่างจงใจ: message, tool call และผลลัพธ์เครื่องมือเป็น byte-identical ในทั้ง 7 แบบ ดังนั้นตัวแปรเดียวคือ แต่ละ policy เลือกเก็บอะไรไว้ บทที่ 16 แสดงว่าทำไม sliding window เป็นการเคลื่อนไหวทาง เศรษฐศาสตร์ ที่แย่ เพราะมันทำลาย prefix ที่ cache ได้ นี่คือสิ่งที่มันทำกับพฤติกรรม:
| context policy | input token ตลอด 40 turn | prompt ที่ turn 40 | กฎจาก turn 2 | ข้อเท็จจริงจาก turn 19 |
|---|---|---|---|---|
| full history | 370,291 | 10,632 | 6/6 | 5/6 |
| sliding window, 12 message ล่าสุด | 157,578 | 2,922 | 5/6 | 0/6 |
| ละผลลัพธ์เครื่องมือที่เก่ากว่า 4 turn | 243,445 | 6,311 | 6/6 | 3/6 |
| compaction ทุก 6 turn | 195,515 | 3,220 | 6/6 | 0/6 |
| compaction พร้อม note ที่โมเดลเขียน | 200,849 | 3,286 | 6/6 | 0/6 |
| pin turn ของผู้ใช้เองไว้ด้านหน้า | 168,550 | 3,559 | 6/6 | 5/6 |
| pin turn ของผู้ใช้เองไว้ด้านหลัง | 168,835 | 3,564 | 6/6 | 6/6 |
| control: สอง turn นั้นเท่านั้นและไม่มีอะไรอีก | — | 1,981 | 6/6 | 6/6 |
แถว compaction รวมต้นทุนของการ compact: 18,581 input token สำหรับ summary 7 ชุด และอีก 3,392 สำหรับ note-taker แถว control อยู่ตรงนั้นเพื่อให้ศูนย์ถูกอ่านเป็นศูนย์ — เมื่อมี message สองข้อความนั้นอย่างเดียวใน prompt 1,981 token โมเดลนี้ตอบ probe ทั้งสองได้สมบูรณ์ ดังนั้นไม่มีแถวไหนที่งานยากเกินไป
Full history จำได้ และเป็นสิ่งที่แพงที่สุดในตาราง: 370,291 input token สำหรับ session ที่เนื้อหาถาวรมีสองประโยค
สิ่งนี้ตอบคำถามที่ตอนเปิดทิ้งไว้ ทำไม transcript 10,632 token ถึงถือข้อเท็จจริงที่ทะเบียน 853 token ทำหล่น? เพราะความยาวเป็นตัวแปรที่ผิด ทะเบียนมี extension สี่หลัก 25 ตัวในประโยคเหมือนกัน 25 ประโยค — decoy ที่เกือบสมบูรณ์ 24 ตัวสำหรับตัวเดียวที่คุณต้องการ transcript มี employee number หนึ่งค่าและ shard name หนึ่งชื่อเท่านั้น Context rot คือ interference ก่อนที่จะเป็น volume นั่นคือเหตุผลที่ 136 จาก 205 คำตอบผิดด้านบนเป็นค่าของเพื่อนบ้าน คำถามที่มีประโยชน์เกี่ยวกับ window ไม่ใช่ว่ามันยาวแค่ไหน แต่คือมีสิ่งกี่อย่างในนั้นที่ดูเหมือนคำตอบ
Sliding window ถูกกว่า 57 % และทำ incident หายไปแล้ว employee number รอดมาเพียงเพราะ agent พูดซ้ำมันเข้าไปใน turn ล่าสุด shard ซึ่งถูกระบุครั้งเดียวที่ turn 19 ไม่อยู่ใน 12 message ล่าสุด — และโมเดลไม่บอกเช่นนั้น เมื่อถูกถาม 6 ครั้ง มันตอบว่า “the affected payment shard is shard 4417” โดยคว้า employee number ซึ่งเป็น identifier อื่นเพียงตัวเดียวที่เหลือใน window และอีกสองครั้งตอบ “pool” ซึ่งยกมาจาก string pool_exhausted ใน log line
Compaction ถูกและทำข้อเท็จจริงเดียวกันหาย summary 7 ชุด เขียนโดยโมเดลภายใต้คำสั่งชัดเจนให้ เก็บ identifier, ตัวเลข, standing instruction และคำถามเปิดไว้ และ pay-shard-7 ไม่อยู่ใน summary ที่สำคัญเลย; การเดา 6 ครั้งคือ shard 1, pay_shard_1 และ pool Compaction ไม่ล้มเหลวเสียงดัง มันสร้าง session ที่ลื่นไหล น่าเชื่อ สั้นลงมาก และทำบรรทัดหนึ่งหล่นเงียบ ๆ
สามแถวได้ 0/6 บนข้อเท็จจริง turn 19 — sliding window, compaction และ compaction with notes รวมคำตอบผิด 18 ครั้ง และ ไม่มีสักครั้งที่เป็น “ฉันไม่รู้”
ต่อมาคือแถวที่ควรน่าอาย การเก็บ message ทั้ง 40 ข้อความของผู้ใช้แบบ verbatim พร้อม 4 turn ล่าสุดแบบเต็ม และไม่มีอะไรอีก มีต้นทุน 168,550 token — น้อยกว่า full history 54 % — และตอบ probe ทั้งสองได้ดีเท่า full history หรือดีกว่า ไม่มี summariser ไม่มี note-taker ไม่มีโมเดลตัวที่สอง: แค่ filter บน role === "user" คำพูดของผู้ใช้คือ token มูลค่าสูงที่ถูกที่สุดใน window ของ agent และ design ส่วนใหญ่ทิ้งมันไปพร้อมกับทุกอย่างอื่น
สองแถวสุดท้ายคือตารางเปิดบทอีกครั้ง ภายใน agent block ที่ pin ไว้ชุดเดียวกัน ย้ายจาก system message ไปไว้ท้าย prompt: 5/6 กลายเป็น 6/6 ด้วย 6 trials นั่นไม่ใช่ความแตกต่างที่มีนัยสำคัญและไม่ได้ถูกเสนอว่าเป็น — มันถูกเสนอเป็นเครื่องเตือนใจว่า ที่ไหน คือ parameter ที่คุณกำลังตั้งค่า ไม่ว่าคุณจะรู้หรือไม่
สี่วิธีใช้ window ให้น้อยลง
ลิงก์ไปยังส่วน: สี่วิธีใช้ window ให้น้อยลงกลยุทธ์ 4 ข้อด้านล่างเป็นของ Anthropic ตามลำดับของ Anthropic แม้มีเพียง 3 ข้อท้ายที่เป็นรายการ long-horizon ของเขา1 ทั้งสี่คือ variation ของคำสั่งเดียว: อย่าแบกสิ่งที่ fetch ได้ และอย่าแบกแบบ raw หากแบกแบบ compressed ได้
Just-in-time retrieval
ลิงก์ไปยังส่วน: Just-in-time retrievalอย่า pre-load content เก็บ identifier — file path, query, ticket number, tool name และ argument ของมัน — แล้ว resolve เมื่อจำเป็น bucket ที่ใหญ่ที่สุดใน agent ด้านบนคือ output ของเครื่องมือที่ถูกอ่านหนึ่งครั้ง ใช้หนึ่งครั้ง แล้วถูกแบกต่อไปอีก 30 turn การแทนที่ทุกผลลัพธ์ที่เก่ากว่า 4 turn ด้วย stub ที่บอกว่ามันคืออะไรและจะเอากลับมาได้อย่างไร ใช้ 6 บรรทัด:
const elide: Policy = (h) => [SYSTEM, ...h.flatMap((turn, ti) =>
turn.map((m) => (ti < h.length - 4 && m.role === "tool"
? { role: "tool", name: m.name,
content: `[${m.name} result from turn ${ti + 1}, ${m.content.length} chars, ` +
`elided; call ${m.name} again with the same arguments to re-read it]` }
: m)))];นี่คือบทที่ 19 โดยแทน corpus ด้วยอดีตของ agent เอง เครื่องจักร retrieval มีอยู่แล้ว — มันคือแค็ตตาล็อกเครื่องมือ
Compaction
ลิงก์ไปยังส่วน: Compactionเมื่อ transcript ผ่าน threshold ให้แทนที่ส่วนที่เก่าที่สุดด้วย summary ที่โมเดลเขียน แล้วทำต่อ prompt ที่เขียน summary คือ design ทั้งหมด และเป็นจุดที่ compaction ชนะหรือแพ้: เก็บ identifier, ตัวเลข, standing instruction และคำถามเปิด; ทิ้งคำทักทายและ output เครื่องมือที่ fetch ซ้ำได้
Compaction เป็น lossy โดยโครงสร้าง สิ่งที่มันทำหายถูกเลือกโดยโมเดลแทนคุณ และไม่มีอะไร error เมื่อมันเลือกผิด มันไม่ฟรีด้วย: ทุก compaction คือ call เพิ่มเติมที่ input คือสิ่งที่กำลังถูก compact
Structured note-taking
ลิงก์ไปยังส่วน: Structured note-takingรักษา store ขนาดเล็กนอก context และ re-inject ทั้งก้อนทุก turn ต่างจาก summary มันเป็น append-only และ addressable: กฎที่เขียนที่ turn 2 ยังอยู่แบบ verbatim ที่ turn 400 เวอร์ชันที่วัดที่นี่ถามโมเดลหลัง user message แต่ละข้อความว่ามีอะไร durable หรือไม่:
const r = await complete([
{ role: "system", content:
"You keep a durable note file for a support session. Given one user message, " +
"output one short note ONLY if it states a standing rule, an identifier or a fact " +
"that must survive the rest of the session. Otherwise output exactly NONE." },
{ role: "user", content: `Turn ${i + 1}: ${user}` },
], { maxTokens: 40 });
if (!/^none\b/i.test(r.text.trim())) notes.push(`turn ${i + 1}: ${r.text.trim()}`);นี่คือกลยุทธ์ที่มีเพดานสูงที่สุดตรงนี้ และเป็นกลยุทธ์ที่ล้มเหลวในการวัด ตลอด user message 40 ข้อความ note-taker เก็บ note 3 รายการและไม่มีสองรายการที่สำคัญ: บรรทัดคำแนะนำจาก runbook, ประกาศว่า session กำลังจบ และ Europe/Madrid is currently 13:45 — เวลา que มันแต่งขึ้น เพราะเครื่องมือที่มัน paraphrase คืนค่า 09:52 UTC note-taker เป็นโมเดล และทุกอย่างในบทนี้ใช้กับมันด้วย
Sub-agents
ลิงก์ไปยังส่วน: Sub-agentsให้งานที่โฟกัสมี window ของตัวเอง — system prompt ของตัวเอง, แค็ตตาล็อกขนาดเล็กของตัวเอง, ไม่มีประวัติของ parent — และคืนคำตอบสั้น ๆ แทน transcript บทที่ 23 วางตัวหนึ่งไว้หลัง tool schema และทิ้งบิลไว้ที่นี่; บิลคือคำตอบของ child เป็นส่วนเดียวของ window ของ child ที่ parent ต้องจ่าย
sub-agent ไม่อยู่ในตารางด้านบน เพราะมันไม่ได้รัน 40 turn: มันรันครั้งเดียว ใน window ที่ใครบางคน scope ไว้ให้ เมื่อให้ system prompt, turn 17 ถึง 19 และไม่มีอะไรอีก — 2,737 token — มันตอบ shard probe ได้ 6/6 ดีกว่า policy ทุกตัวในตาราง และตอบ employee probe ได้ 0/6 เพราะเลขนั้นไม่อยู่ใน 3 turn ที่มันได้รับ
นั่นคือ sub-agent ในตัวเลขสองตัว: window ที่สะอาดไม่ใช่ intelligence มันคือ scope และการ scope ถูกทำล่วงหน้าโดยโค้ดที่ต้องรู้อยู่แล้วว่า turn ไหนสำคัญ มีอีกสิ่งหนึ่งในคำตอบเหล่านั้นที่ควรเก็บไว้ นี่เป็น policy เดียวที่ตอบ “None available” แทนที่จะประดิษฐ์อะไรขึ้นมา โมเดลที่มี context เล็กและ coherent รู้ว่าตัวเองขาดอะไร; โมเดลที่มี context ใหญ่และ noisy ไม่รู้
ความจำสามแบบ
ลิงก์ไปยังส่วน: ความจำสามแบบแทบทุกบทสนทนาสับสนเรื่อง memory ของ agent คือกลไกสามแบบที่สวมคำเดียวกัน พวกมันมีอายุ เจ้าของ และ failure mode ต่างกัน และ system ที่เก็บทั้งหมดไว้ในที่เดียวกันมีปัญหาที่มันยังไม่สังเกตเห็น
| ประวัติบทสนทนา | retrieval | persistent user memory | |
|---|---|---|---|
| เก็บ | สิ่งที่พูดใน session นี้ | เอกสารที่คุณเป็นเจ้าของ | ข้อเท็จจริงเกี่ยวกับคนหนึ่งคน |
| อยู่ | หนึ่ง session | จนกว่าจะ re-index | ข้ามทุก session ตลอดไป |
| เขียนโดย | ลูป อัตโนมัติ | ingestion pipeline | โมเดล โดยตั้งใจ |
| เข้า prompt | เต็ม ๆ ทุก call | 4 passage เมื่อ query match | เต็ม ๆ ทุก call |
| ล้มเหลวด้วยการ | โตจน rot | ดึง chunk ผิด | จำบางอย่างเกี่ยวกับคุณผิด |
| สร้างใน | บทที่ 23 | บทที่ 19 | บทนี้ |
กรอบวิชาการคือ CoALA ซึ่งจัด language agent รอบ “modular memory components” และแยก working memory ออกจาก episodic, semantic และ procedural store4 MemGPT ใช้แนวคิดเดียวกันแบบตรงตัว โดยยืม virtual memory จาก operating system: tier เร็วใน window, tier ช้านอกมัน และโมเดลเองย้ายข้อมูลระหว่างกันด้วย function calls5 ทั้งสองบังคับคำถามที่ product ต้องตอบอยู่ดี — ไม่ใช่ ฉันเก็บได้มากแค่ไหน แต่คือ สิ่งนี้อยู่ใน store ไหน และหมดอายุเมื่อไร
การทดสอบเชิงปฏิบัติคือคำถามหนึ่งข้อสำหรับแต่ละข้อเท็จจริง: พรุ่งนี้อะไรควรยังเป็นจริงอยู่? ผลลัพธ์เครื่องมือจาก turn 12: ไม่มีอะไร Summary ของ session: จนกว่า session จะจบ ว่า employee number ของผู้ใช้คือ 4417: จนกว่าเขาจะเปลี่ยนงาน คำตอบสามแบบ store สามแบบ
ต่อจากนี้ไปไหน
ลิงก์ไปยังส่วน: ต่อจากนี้ไปไหนตอนนี้คุณวัดได้แล้วว่าอะไรอยู่ใน window ตัดสินใจได้ว่าอะไรควรอยู่ในนั้น และแยกความต่างระหว่าง agent ที่ลืมบางอย่าง กับ agent ที่แบกมันอยู่แต่ไม่ได้มอง
กลยุทธ์สุดท้ายในสี่ข้อคือข้อที่ไม่พอดีกับที่นี่ sub-agent ไม่ใช่ context policy มันคือ agent ตัวที่สอง และทันทีที่มีสองตัว คุณต้องตัดสินว่าอะไรส่งผ่านระหว่างกัน และตัวไหนเป็นหัวหน้า บทที่ 25 คือสิ่งนั้น: orchestration pattern 5 แบบและชื่อของแต่ละแบบมาจากไหนจริง ๆ, topology 2 แบบที่มักถูกปนกัน — การถาม sub-agent แล้วได้คำตอบกลับมา เทียบกับการส่ง conversation ให้มันและไม่ได้กลับมา — และผลการวัดที่พบว่าในงานที่มันคิดราคา วิธีที่เรียบง่ายกว่าชนะ ตามด้วยการทดสอบว่าเมื่อไรที่มันหยุดชนะ
มันยังรับมรดกทุกอย่างที่บทนี้เพิ่งวัดมาอย่างพอดี sub-agent คืน summary summary คือ compaction ที่คุณไม่ได้เขียน ผลิตโดยโมเดลที่คุณมองไม่เห็น window และ parent ไม่มีวิธีแยก summary ที่ดีออกจาก summary ผิดแต่มั่นใจ — ความต่างเดียวกับที่แยก 84 % ออกจาก 19 % ด้านบนของหน้านี้ และที่เปลี่ยนข้อเท็จจริงที่หาย 18 ข้อให้เป็นสิ่งประดิษฐ์ 18 อย่าง ดังนั้น: เมื่อ sub-agent ผิด parent ได้ดูอะไรกันแน่?
แหล่งที่มาและวิธีการ
ลิงก์ไปยังส่วน: แหล่งที่มาและวิธีการตัวเลขทุกตัวที่นี่ถูกผลิตบนเครื่องนี้และไม่มีตัวใดเป็นค่าประมาณ โมเดลคือ Qwen2.5-0.5B-Instruct ใน float32 บน CPU ด้วย greedy decoding เสิร์ฟผ่าน loopback โดย endpoint Python ขนาดเล็กที่พูดรูปแบบ chat-completions และเปิด token-count route — seam ของ บทที่ 14 อีกครั้ง tensor อยู่ฝั่ง Python และลูปอยู่ฝั่ง TypeScript — ดังนั้นทุก count คือ tokenizer ของโมเดลนั้นเองที่ apply กับ chat template ของมันเอง ตารางตำแหน่งคือ 288 call, 9 ตำแหน่งคูณ 32 trial โดยใช้ ticket ต่างกันในแต่ละ trial; ตารางความยาวคือ 140 call; agent run คือ 57 model call ตลอด 43 นาทีของ wall clock; ตาราง policy คือ transcript เดียวนั้น replay ภายใต้ 7 policy ช่วงเป็น Wilson จากบทที่ 4 ไม่มี paid API ถูกเรียก ซึ่งเป็นเหตุผลที่ไม่มีราคาสักรายการในบทนี้: token count ถูกต้องเป๊ะ และ rate ที่คุณจะเอาไปคูณคือของบทที่ 16
รายการอ้างอิง
ลิงก์ไปยังส่วน: รายการอ้างอิง-
Anthropic, Effective context engineering for AI agents, 29 September 2025,
anthropic.com/engineering/effective-context-engineering-for-ai-agents, อ่านเมื่อ 7 September 2026 แหล่งที่มาของสอง definition ที่ quote ด้านบน, ของ “attention budget” และ statement ว่า token ใหม่ทุกตัวทำให้มันพร่องลง, ของ description ของ context rot, ของ framing เรื่อง n² pairwise-relationships และของ strategy ที่ใช้เป็นกระดูกสันหลังของบทนี้ สามข้อในนั้นเป็นรายการ long-horizon ของเอกสารนั้น — compaction, structured note-taking และ multi-agent architectures; just-in-time retrieval มาก่อนใน article เดียวกัน ภายใต้ context retrieval และ agentic search และถูกจัดกลุ่มไว้กับพวกมันที่นี่ ↩ ↩2 ↩3 ↩4 -
Liu, N. F., Lin, K., Hewitt, J., Paranjape, A., Bevilacqua, M., Petroni, F. and Liang, P. Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172 (v1 July 2023, v3 November 2023). อ้างในบทที่ 15, 16 และ 19 และวัดที่นี่ ประโยคที่ quote มาจาก abstract; งานสองอย่างของ paper คือ multi-document question answering และ key-value retrieval และ finding ว่า effect ยังคงอยู่ในโมเดลที่เป็น long-context อย่างชัดเจนคือส่วนที่สำคัญต่อการตัดสินใจของ product ↩
-
Anthropic, Code execution with MCP: building more efficient agents, 4 November 2025,
anthropic.com/engineering/code-execution-with-mcp, อ่านเมื่อ 7 September 2026 แหล่งที่มาของการลดจาก 150,000 เป็น 2,000 token และตัวเลข 98.7 % รวมถึง observation ว่านิยามเครื่องมือที่โหลดไว้ล่วงหน้ากิน context ก่อนที่ request จะถูกอ่าน ↩ -
Sumers, T. R., Yao, S., Narasimhan, K. and Griffiths, T. L. Cognitive Architectures for Language Agents. arXiv:2309.02427 (2023). จัด language agent รอบ “modular memory components, a structured action space to interact with internal memory and external environments, and a generalized decision-making process to choose actions” และแบ่ง memory เป็น working, episodic, semantic และ procedural บทที่ 22 ใช้ taxonomy ของมันสำหรับ learning agent; ตารางสาม store ด้านบนคือเงาเชิงปฏิบัติของมัน ↩
-
Packer, C., Wooders, S., Lin, K., Fang, V., Patil, S. G., Stoica, I. and Gonzalez, J. E. MemGPT: Towards LLMs as Operating Systems. arXiv:2310.08560 (October 2023). เสนอ “virtual context management, a technique drawing inspiration from hierarchical memory systems in traditional operating systems” โดยให้โมเดลเองย้ายข้อมูลระหว่าง tier เร็วใน window กับ tier ช้านอก window ข้อความที่ชัดที่สุดเท่าที่มีว่าทำไม window จึงเป็น cache ไม่ใช่ memory ↩