Context Window, Tokens และใบเรียกเก็บเงิน: วัดให้เห็นจริง
บทสนทนา 40 turn ใช้ input token ถูกคิดเงิน 22 เท่าของความยาวจริง caching ลดได้ 68% แต่ timestamp ผิดที่เพิ่ม 20%
ในหน้านี้
นี่คือบทสนทนาซัพพอร์ตสี่สิบ turn คิดเงินทีละ turn ไม่มีอะไรแปลก: นักพัฒนาถามเรื่อง API, assistant ตอบหนึ่งหรือสองย่อหน้า การแลกเปลี่ยนทั้งหมดมีข้อความ 5,090 token — ประมาณแปดหน้า
| turn | prompt tokens | ข้อความใหม่ | output | cost ของ turn นี้ | ยอดสะสม |
|---|---|---|---|---|---|
| 1 | 213 | 18 | 183 | $0.002622 | $0.002622 |
| 5 | 892 | 14 | 123 | $0.003260 | $0.014354 |
| 10 | 1,656 | 19 | 114 | $0.004680 | $0.035530 |
| 20 | 2,868 | 18 | 103 | $0.006972 | $0.094426 |
| 30 | 3,941 | 20 | 99 | $0.009070 | $0.174170 |
| 40 | 4,947 | 17 | 142 | $0.011598 | $0.274386 |
อ่านคอลัมน์ที่สองและสามคู่กัน ที่ turn 40 ผู้ใช้พิมพ์สิบเจ็ด token แต่ถูกคิดเงินสำหรับ 4,947 token คำถามไม่ได้ยากกว่าคำถามแรก มันสั้นกว่าด้วยซ้ำ สิ่งที่เปลี่ยนคือ request นั้นแบกบทสนทนาทั้งหมดมากับมันอีกครั้ง เป็นครั้งที่สี่สิบ
input token ทั้งหมดที่ถูกคิดเงินตลอดสี่สิบ call นี้: 112,617 บทสนทนายาว 5,090 token คุณจ่ายให้มันไป ยี่สิบสองเท่า
บทนี้ว่าด้วยเหตุผลที่สิ่งนี้เกิดขึ้น แต่ละ provider เรียกมันว่าอะไรบนใบแจ้งหนี้ และในห้าสิ่งที่คุณกำลังถูกคิดเงิน มีอะไรบ้างที่คุณทำอะไรกับมันได้
แสดงรายละเอียด
บทนี้ต้องใช้อะไรจาก Part II
- บทที่ 7 สร้าง tokenizer ขึ้นมา token คือหน่วยที่ใช้ที่นี่เช่นกัน — หน่วยเดียวกัน เพียงแต่ตอนนี้มีราคา
- บทที่ 9 อนุมาน self-attention และ cost ของมันในกล่อง asymptotic notation cost นั้นคือเหตุผลว่าทำไมจึงต้องมี limit และจะลิงก์มาที่นี่แทนการอธิบายซ้ำ
- บทที่ 13 วัด prefill เทียบกับ decode และคำนวณว่า KV cache ใช้พื้นที่เท่าไร สอง phase นั้นคือสิ่งที่คอลัมน์ input และ output ข้างบนกำลังซื้อจริง ๆ
ที่เหลือทั้งหมดเป็น TypeScript เพราะนี่คือการทำบัญชีสำหรับ remote call ไม่ใช่คณิตศาสตร์ของ model
The window is not memory
ลิงก์ไปยังส่วน: The window is not memoryความเข้าใจผิดที่แพงที่สุดอย่างหนึ่งในธุรกิจนี้คือการคิดว่า model จำบทสนทนาได้
มันจำไม่ได้ และกลไกจากบทที่ 13 บอกเหตุผลอย่างชัดเจน state ของ transformer ระหว่าง generation คือ KV cache: keys และ values ที่คำนวณสำหรับทุก token ใน sequence cache นั้นมีชีวิตอยู่ตลอดหนึ่ง request เมื่อ request จบ process ที่ถือมันอยู่ก็ว่างไปให้บริการคนอื่น และ cache ก็หายไป ไม่มี store รายผู้ใช้อยู่ฝั่งโน้น และไม่มี session
ดังนั้น request ถัดไปต้องมาพร้อมทุกสิ่งที่ model ควรรู้ และ model สร้าง state นั้นขึ้นใหม่โดยรัน forward pass บน prompt ทั้งหมดก่อนจะปล่อย token ใหม่แม้แต่ตัวเดียว บทที่ 15 เรียก prompt ว่า “state ทั้งหมด” นี่คือเหตุผลทางกายภาพ: prompt คือ state ที่สมบูรณ์ เพราะไม่มีอะไรอื่นรอดจาก call
context window คือความยาวสูงสุดของ prompt นั้นรวมกับคำตอบของมัน มันเป็นเพดานว่าคุณ rebuild state ได้มากแค่ไหน ไม่ใช่ภาชนะที่เก็บอะไรไว้ระหว่าง request การเรียกมันว่า “memory ของ model” ทำให้ทิศทางของเหตุและผลกลับหัว — คุณไม่ได้กำลังเติม memory คุณกำลังจ่ายเงินเพื่อสร้างมันขึ้นใหม่
นี่คือที่มาของยี่สิบสอง Turn แบก turn ก่อนหน้าทั้งหมด มาด้วย ดังนั้น input รวมตลอดบทสนทนาที่มี turn จึงเป็นผลรวมของอนุกรมที่โตขึ้น ซึ่งเป็นกำลังสอง:
โดยที่ คือ system prompt และ คือ history ที่ turn เมื่อนำ input สะสมที่วัดได้ไป fit กับ ตลอดสี่สิบ turn จะได้ ซึ่งทำนาย 113,645 token ที่ turn 40 เทียบกับค่าที่วัดได้ 112,617 term กำลังสองครอบงำ และ term เชิงเส้นคือสิ่งที่ผู้ใช้พิมพ์จริง
ผลลัพธ์คือประโยคที่ควรจำจากบทนี้: บิลของคุณโตตามกำลังสองของบทสนทนา ไม่ใช่ตามคำถามล่าสุด คำถามสี่สิบข้อเดียวกัน ถามโดยไม่มี history เลย มี cost $0.066036 การเก็บ history มี cost $0.274386 History คูณบิลขึ้น 4.2 เท่า และมันจะคูณต่อไป เพราะตัวคูณคือความยาวของบทสนทนา
ทำไมจึงต้องมี limit
ลิงก์ไปยังส่วน: ทำไมจึงต้องมี limitwindow มีขนาดจำกัดด้วยสองเหตุผลที่ดึงไปทางเดียวกัน เหตุผลแรกคือของบทที่ 9: attention เปรียบเทียบทุก token กับทุก token อื่น ดังนั้นงานของ layer นั้นโตตามกำลังสองของความยาว sequence เหตุผลที่สองคือ memory: KV cache โตแบบ เชิงเส้น ตามความยาว sequence และบทที่ 13 ทำเลขนั้นไปแล้ว — ที่ sequence ยาว ๆ มันใหญ่กว่า weights
ทั้งสอง limit ถูกโจมตีแล้ว และยังไม่มีอันไหนถูกกำจัด FlashAttention1 จัดระเบียบ computation ใหม่เพื่อให้อ่านและเขียน high-bandwidth memory น้อยลงมาก ทำให้ sequence ยาว ๆ ใช้งานได้จริงโดยไม่เปลี่ยน asymptotic cost Position Interpolation2 และ YaRN3 ขยาย window ที่ใช้ได้ของ model ที่ train แล้วโดย rescale positional encodings จากบทที่ 9 แทนการ retrain รวมกันแล้วนี่คือเหตุผลที่ windows โตจาก 2K เป็น 1M ในห้าปี
สิ่งที่พวกมันไม่ได้ทำคือทำให้ context ยาว ๆ ฟรี มันทำให้เพดานสูงขึ้นและความชันนุ่มลง ความชันยังอยู่ และมันคือสิ่งที่ price tiers ต่อไปในบทนี้กำลังวัด
ห้า bucket ไม่ใช่สอง
ลิงก์ไปยังส่วน: ห้า bucket ไม่ใช่สองcost calculator เกือบทั้งหมดบนอินเทอร์เน็ต model API call เป็น input tokens คูณ input price บวก output tokens คูณ output price นั่นเคยถูกต้องในปี 2023 ตอนนี้มันผิดในแบบที่ทำให้บิลคลาดเคลื่อนได้สองเท่าหรือมากกว่านั้นทั้งสองทิศทาง
มีหมวด token ที่คิดเงินได้ ห้า ประเภท:
| bucket | คืออะไร | ราคาทั่วไป เทียบกับ input |
|---|---|---|
| uncached input | prompt tokens ที่ model ต้อง process ใหม่ | 1× |
| cache read | prompt tokens ที่เสิร์ฟจาก prefix ที่เก็บไว้ | 0.1× |
| cache write | prompt tokens ที่ถูกเก็บเข้า cache ใน call นี้ | 1.25× ถึง 2× |
| output | tokens ที่ model generate และส่งให้คุณ | 5× ถึง 6× |
| reasoning | tokens ที่ model generate และ ไม่ได้ ส่งให้คุณ | อัตรา output |
สามในห้าประเภทนี้ไม่ได้มีอยู่เป็นบรรทัดแยกเมื่อสองปีก่อน และ cache สองบรรทัดคือสิ่งที่คนมักเข้าใจผิด เพราะ cache write แพงกว่า input ปกติ ไม่ใช่ถูกกว่า คุณจ่าย premium เพื่อเก็บบางอย่าง เพื่อให้คุณจ่ายส่วนลดตอนอ่านกลับมา และดีลนี้คุ้มหรือไม่ขึ้นอยู่กับว่าคุณอ่านมันกี่ครั้งล้วน ๆ
reasoning bucket เป็นของ บทที่ 12 ตอนนี้มีราคาแล้ว และมีรายละเอียดหนึ่งที่ควรพูดตรง ๆ: เอกสารของ Google บอกว่า pricing “is based on the full thought tokens the model needs to generate, despite only the summary being output from the API.”4 คุณถูกคิดเงินสำหรับ tokens ที่ไม่เคยถูกส่งมาถึงคุณ นี่เป็น bucket เดียวที่คุณนับ ตรวจดู หรือยืนยันเนื้อหาของมันไม่ได้
call เดียวกัน สาม dialect
ลิงก์ไปยังส่วน: call เดียวกัน สาม dialectตอนนี้มาถึงส่วนที่ทำให้เรื่องนี้เป็นปัญหา normalisation แทนที่จะเป็นปัญหาการคูณ provider ทุกเจ้ารายงาน bucket เหล่านี้ด้วยชื่อต่างกัน และ — นี่คือกับดัก — สองเจ้าใช้คำเดียวกันกับปริมาณคนละอย่าง
สมมติ call หนึ่ง: 4,837 token อ่านจาก cache, 110 token สด, visible output 142 token, reasoning 300 token
// OpenAI-compatible
{ "usage": { "prompt_tokens": 4947,
"prompt_tokens_details": { "cached_tokens": 4837 },
"completion_tokens": 442,
"completion_tokens_details": { "reasoning_tokens": 300 } } }
// Anthropic
{ "usage": { "input_tokens": 110,
"cache_read_input_tokens": 4837,
"cache_creation_input_tokens": 0,
"output_tokens": 442 } }
// Gemini
{ "usageMetadata": { "promptTokenCount": 4947,
"cachedContentTokenCount": 4837,
"candidatesTokenCount": 142,
"thoughtsTokenCount": 300 } }ดู prompt_tokens: 4947 และ input_tokens: 110 ทั้งสอง field คือจำนวน input token สำหรับ prompt เดียวกัน ของ OpenAI รวม cached tokens; ของ Anthropic ไม่รวม — เอกสารของมันระบุ identity อย่างชัดเจนว่า total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens5 input_tokens ของ Anthropic หมายถึง “tokens หลัง cache breakpoint สุดท้ายของคุณ”
แล้วดู output OpenAI และ Anthropic รายงาน 442 ทั้งคู่ ซึ่งมี reasoning tokens 300 อยู่ในนั้นแล้ว Gemini รายงาน 142 และใส่ 300 ไว้ใน field แยกของมันเอง บทที่ 12 ชี้เรื่องนี้ว่าเป็น incompatibility ระหว่างวิธีนับงานเดียวกันสองแบบ นี่คือสิ่งที่มันทำกับ cost
normaliser มีแค่สามสิบบรรทัด และไม่ใช่ optional:
export interface Usage {
promptTokens?: number; // input, NOT cached
cachedInputTokens?: number; // read from cache
cacheWriteTokens?: number; // written to cache on this call
completionTokens?: number; // output
reasoningTokens?: number; // billed apart from output (Gemini only)
}
const num = (v: unknown) => (typeof v === "number" && isFinite(v) ? v : 0);
export const fromOpenAI = (raw: any): Usage => {
const u = raw.usage ?? {}, d = u.prompt_tokens_details ?? {};
const cached = num(d.cached_tokens), write = num(d.cache_write_tokens);
return {
promptTokens: Math.max(0, num(u.prompt_tokens) - cached - write),
cachedInputTokens: cached,
cacheWriteTokens: write,
completionTokens: num(u.completion_tokens), // reasoning already inside
reasoningTokens: 0,
};
};
export const fromAnthropic = (raw: any): Usage => {
const u = raw.usage ?? {};
return {
promptTokens: num(u.input_tokens), // already excludes cache
cachedInputTokens: num(u.cache_read_input_tokens),
cacheWriteTokens: num(u.cache_creation_input_tokens),
completionTokens: num(u.output_tokens),
reasoningTokens: 0,
};
};
export const fromGemini = (raw: any): Usage => {
const m = raw.usageMetadata ?? {}, cached = num(m.cachedContentTokenCount);
return {
promptTokens: Math.max(0, num(m.promptTokenCount) - cached),
cachedInputTokens: cached,
cacheWriteTokens: 0,
completionTokens: num(m.candidatesTokenCount), // EXCLUDES thinking
reasoningTokens: num(m.thoughtsTokenCount), // billed at output rate
};
};รัน payload ทั้งสามข้างบนผ่าน reader ทั้งสาม แล้วทั้งสามให้ Usage เดียวกัน และดังนั้นได้ตัวเลขเดียวกัน: $0.006491 ความสอดคล้องนี้คือประเด็นทั้งหมดของการเขียน layer นี้
ถ้าทำผิด นี่คือ cost ของความผิด บน call เดียวกัน:
| mistake | billed | error |
|---|---|---|
ปฏิบัติกับ cached_tokens ว่าเป็น ส่วนเพิ่ม จาก prompt_tokens | $0.016165 | 2.49× — คุณคิดเงิน prompt ซ้ำสองรอบ |
| ปฏิบัติกับ cache reads ว่าฟรีแทนที่จะเป็น 0.1× | $0.005524 | 0.85× — คุณกลืนต้นทุน 15 % |
อ่าน candidatesTokenCount แล้วมองข้าม thoughtsTokenCount | $0.002891 | 55 % ของ call หายไป |
ข้อที่สามอันตราย เพราะมันล้มเหลวแบบเงียบ ๆ ไปในทิศทางของข่าวดี dashboard ของคุณแสดงว่า reasoning model มี cost น้อยกว่าครึ่งของ cost จริง และไม่มีอะไรที่ไหน raise error
การคำนวณ cost
ลิงก์ไปยังส่วน: การคำนวณ costเมื่อ normalise bucket แล้ว cost function ก็สั้น ส่วนเดียวที่ไม่ obvious คือ tier lookup ซึ่ง section ถัดไปจะอธิบาย:
export interface Tier { maxPromptTokens: number | null; price: number }
export interface Pricing {
input: Tier[]; output: Tier[];
cachedInput?: Tier[]; cacheWrite?: Tier[]; reasoning?: Tier[];
}
const tierPrice = (tiers: Tier[] | undefined, contextSize: number, fallback?: Tier[]) => {
const table = tiers ?? fallback;
if (!table?.length) return 0;
const sorted = [...table].sort(
(a, b) => (a.maxPromptTokens ?? Infinity) - (b.maxPromptTokens ?? Infinity));
for (const t of sorted)
if (t.maxPromptTokens === null || contextSize <= t.maxPromptTokens) return t.price;
return sorted[sorted.length - 1].price;
};
export function computeCost(pricing: Pricing, usage: Usage): number {
const fresh = usage.promptTokens ?? 0;
const read = usage.cachedInputTokens ?? 0;
const write = usage.cacheWriteTokens ?? 0;
const out = usage.completionTokens ?? 0;
const think = usage.reasoningTokens ?? 0;
const contextSize = fresh + read + write; // the tier depends on the WHOLE prompt
return fresh * tierPrice(pricing.input, contextSize)
+ read * tierPrice(pricing.cachedInput, contextSize, pricing.input)
+ write * tierPrice(pricing.cacheWrite, contextSize, pricing.input)
+ out * tierPrice(pricing.output, contextSize)
+ think * tierPrice(pricing.reasoning, contextSize, pricing.output);
}มี design decisions สองอย่างในนั้นที่ควรโต้แย้งสนับสนุน fallbacks — cache prices fallback เป็น input, reasoning เป็น output — encode ความหมายของ table ที่หายไป: reasoning tokens บน Gemini ถูกคิดเงินที่อัตรา output ดังนั้นราคา reasoning ที่ไม่มีอยู่ไม่ใช่ศูนย์ แต่คือราคา output และ contextSize รวม input bucket ทั้งสาม ไม่ใช่เฉพาะ token สด เพราะ tier ถูกเลือกตามความยาวของ prompt ไม่ใช่ตามว่าคุณถูกคิดเต็มราคากับมันมากแค่ไหน
Prompt caching และ cost ของการเขียนมัน
ลิงก์ไปยังส่วน: Prompt caching และ cost ของการเขียนมันprompt cache เก็บ computed state ของ model สำหรับ prefix ของ prompt ของคุณ เพื่อให้ request ภายหลังที่มี prefix เดียวกันข้ามการ recompute มันได้ คุณสมบัติสี่อย่างตามมาจากคำว่า “prefix” และทั้งสี่อย่างทำให้คนประหลาดใจ
มันคือ prefix ไม่ใช่ set
ลิงก์ไปยังส่วน: มันคือ prefix ไม่ใช่ setcache match จากจุดเริ่มต้นของ rendered prompt ไปข้างหน้า และหยุดที่ byte แรกที่ต่างกัน ไม่มีคะแนนบางส่วนให้กับ content ที่ปรากฏทีหลังในลำดับอื่น OpenAI พูดตรง ๆ ว่า: “cache reuse requires the entire rendered prefix to match.”6
มีความยาวขั้นต่ำ
ลิงก์ไปยังส่วน: มีความยาวขั้นต่ำต่ำกว่านั้น จะไม่มีอะไรถูก cache และไม่มี error ส่งกลับมา บน OpenAI ขั้นต่ำคือ 1,024 token สำหรับ GPT-5.6 และใหม่กว่า และ 2,048 สำหรับ model เก่ากว่า บน Anthropic อยู่ระหว่าง 512 ถึง 4,096 ขึ้นกับ model — 1,024 สำหรับ Claude Sonnet 4.5, 4,096 สำหรับ Claude Haiku 4.5 ถ้า cache fields ทั้งสองกลับมาเป็นศูนย์ นั่นมักเป็นเหตุผล
การเขียนแพงกว่าการอ่าน และแพงกว่าการไม่ caching
ลิงก์ไปยังส่วน: การเขียนแพงกว่าการอ่าน และแพงกว่าการไม่ cachingบน OpenAI และ Anthropic cache write คือ 1.25× ของอัตรา uncached input สำหรับ cache อายุสั้น และ cache หนึ่งชั่วโมงของ Anthropic คือ 2× read คือ 0.1× Google ไม่คิดค่าเขียน แต่ให้เช่า storage: $4.50 ต่อหนึ่งล้าน token ต่อชั่วโมงบน Gemini 2.5 Pro
มันหมดอายุ และมันอยู่บนเครื่องเดียว
ลิงก์ไปยังส่วน: มันหมดอายุ และมันอยู่บนเครื่องเดียวentry เริ่มต้นของ Anthropic มีชีวิตห้านาที refresh ฟรีเมื่อ hit แต่ละครั้ง ของ OpenAI อย่างน้อยสามสิบนาทีหลัง write หรือ reuse ล่าสุด และ OpenAI ระบุว่า cached states อยู่บนเครื่องแต่ละเครื่อง ดังนั้น request จะ hit ก็ต่อเมื่อถูก route ไปยังเครื่องที่ถือ entry นั้น — ซึ่งเป็นสิ่งที่ prompt_cache_key มีอิทธิพล แต่ไม่รับประกัน
break-even เล็กพอจะจำไว้ในหัว และเอกสารของ OpenAI ทำเลขให้: เขียน prefix ครั้งเดียวและ reuse หนึ่งครั้งมี cost 1.35× ของ input cost ปกติ เทียบกับ 2× สำหรับการ process มันสองครั้งแบบ uncached; ตลอดสิบ request, write หนึ่งครั้งและ read เก้าครั้งมี cost 2.15× เทียบกับ 10× reuse หนึ่งครั้งก็จ่ายคืนค่า write แล้ว Anthropic ลงเอยที่เดียวกัน: read หนึ่งครั้งสำหรับ cache ห้านาที สองครั้งสำหรับ cache หนึ่งชั่วโมง
กลับมาที่บทสนทนาสี่สิบ turn อีกครั้ง โดยเปิด caching และ prefix เสถียร:
| uncached input | cache reads | cache writes | total | |
|---|---|---|---|---|
| no cache | 112,617 | — | — | $0.274386 |
| caching | 2,887 | 104,783 | 4,947 | $0.088250 |
ถูกลงหกสิบแปดเปอร์เซ็นต์ และตัวเลขสามตัวในตารางนั้นควรค่าแก่การสนใจ
cache ยังไม่ทำงานจนถึง turn 6 prompt ยังไม่ถึง 1,024 token จนกว่าจะถึงตอนนั้น ดังนั้นห้า turn แรกถูกคิดเงินเหมือนเดิมทุกประการ — และ turn ที่หกถูกคิด แย่กว่าเดิม ที่ write premium 1.25× เพราะเป็น turn ที่เติม cache read แรกมาถึงที่ turn 7 token uncached 2,887 ในตารางคือเลขคณิต: มูลค่าของห้า turn ไม่ใช่หก Caching คือส่วนลดบน prompt ยาว และบทสนทนาสั้นไม่ได้อะไรจากมัน
write premium คือ $0.002474 ซึ่งเป็น 2.8 % ของบิลแบบ cached ทุก turn เขียน tail ใหม่ของมัน สี่สิบครั้ง และ write premium ทั้งหมดเป็นเศษปัดเมื่อเทียบกับสิ่งที่ reads ประหยัดได้ write charge ควรเข้าใจให้แม่นยำเพื่อให้คุณเลิกกังวลกับมัน
มีเพียง 2,887 token เท่านั้นที่ถูกคิดที่ราคา input เต็ม จาก 112,617 นี่คือรูปทรงของ cache ที่ทำงาน: แทบทุกอย่างเป็น read
ลำดับของ prompt ตัดสินว่าสิ่งนี้จะเกิดขึ้นหรือไม่
ลิงก์ไปยังส่วน: ลำดับของ prompt ตัดสินว่าสิ่งนี้จะเกิดขึ้นหรือไม่นี่คือ failure ที่ทำให้เสียเงินจริง และมันเป็น bug บรรทัดเดียว
เอาบางอย่างที่เปลี่ยนทุก call ไปไว้ใกล้ต้น prompt — timestamp, request id, ชื่อผู้ใช้, บรรทัด “today is”, เอกสารที่เพิ่ง retrieve มา — แล้ว prefix จะต่างตั้งแต่ byte แรก ไม่มีอะไร match ทุก call เป็น miss และเพราะทุก call เสนอ prefix ใหม่ ทุก call จึง เขียน ด้วย
บทสนทนาเดิม สี่สิบ turn เดิม เปิด caching โดยมี timestamp ราย call อยู่ด้านบนของ system prompt:
| total | เทียบกับ | |
|---|---|---|
| no caching at all | $0.274386 | — |
| caching, stable prefix | $0.088250 | −67.8 % |
| caching, volatile prefix | $0.329251 | +20.0 % |
การเปิด prompt caching ทำให้บทสนทนาแพงขึ้นยี่สิบเปอร์เซ็นต์ มากกว่า การไม่เปิดมัน คุณจ่าย write premium 1.25× บน 109,730 token และอ่านกลับมาเป็นศูนย์ ไม่มี error ไม่มี warning และ feature ก็เปิดอยู่
ดังนั้น rule — และนี่คือ prompt caching ทั้งหมดในบรรทัดเดียว: content ที่เสถียรไว้ข้างหน้า content ที่เปลี่ยนได้ไว้ข้างหลัง system instructions, tool definitions และ reference material มาก่อน; timestamps, user identity และคำถามปัจจุบันมาทีหลัง Anthropic ทำ hierarchy ให้ชัดเจน — cache ตาม tools → system → messages และการเปลี่ยนแปลงที่ level ใด ๆ จะ invalidate level นั้นและทุกอย่างหลังจากนั้น ดังนั้นการแก้คำอธิบาย tool เพียงรายการเดียว invalidate cache ทั้งหมด5
มีผลตามมาสองอย่างที่คนสะดุด การเปลี่ยนว่า tool ไหน enabled เปลี่ยน tool definitions ดังนั้น feature flag ที่เพิ่ม tool ให้ผู้ใช้บางคนจะแยก cache ของคุณเป็นสองส่วน และบน Anthropic การ toggle web search หรือ citations จะ modify system prompt ซึ่ง invalidate system และ message caches โดยที่คุณไม่ได้แตะข้อความของตัวเองสักบรรทัด
การตัด history ไม่ใช่วิธีแก้
ลิงก์ไปยังส่วน: การตัด history ไม่ใช่วิธีแก้ปฏิกิริยาที่ obvious ต่อบิลกำลังสองคือหยุดส่ง history ทั้งหมด: เก็บข้อความล่าสุดสักโหลแล้วทิ้งที่เหลือ มันลดบิลจริง และมักเป็นการทำผิดทาง และการวัดบอกว่าทำไม
| strategy | total | เทียบกับ full history + cache |
|---|---|---|
| full history, no cache | $0.274386 | +211 % |
| full history, caching | $0.088250 | — |
| last 12 messages, no cache | $0.118712 | +35 % |
| last 12 messages, caching on | $0.122546 | +39 % |
การตัดให้เหลือ window สิบสอง message ถูกกว่าการส่งทุกอย่างแบบ uncached 57 % — นี่คือ comparison ที่ทุกคนทำ และเป็นเหตุผลที่ technique นี้นิยม แต่แพงกว่า 39 % เมื่อเทียบกับการส่งทุกอย่างพร้อม cache ที่ทำงาน และการเปิด caching ควบคู่กับ truncation ทำให้มันแย่ลงเล็กน้อยแทนที่จะดีขึ้น
กลไกคือ prefix อีกครั้ง sliding window ทิ้งข้อความเก่าสุดทุก turn ดังนั้น prompt จึงไม่ได้เริ่มจากจุดเดิมที่เริ่มครั้งก่อนอีกต่อไป และทุก turn เสนอ prefix ใหม่ guidance ของ OpenAI พูดตรงนี้ว่า: “summarisation, compaction, or context truncation can change the prefix and reset cache reuse.”6 เมื่อถึง turn 40 prompt แบบ window มี 813 token ต่ำกว่าขั้นต่ำ 1,024-token จึง cache ไม่ได้เลย
และเงินเป็น cost ครึ่งที่ถูกกว่า สิ่งที่คุณทิ้งคือ instruction ที่ผู้ใช้ให้ไว้ตอน turn 2 ซึ่ง model ต้องใช้ตอน turn 40 Truncation แลกบิลที่คุณเห็นกับ failure ที่คุณมองไม่เห็น และการทำมันให้ถูกต้อง — compaction, structured notes ที่ถือไว้นอก window, retrieving history on demand — คือหัวข้อของ บทที่ 24
การข้าม tier ทำให้ทั้ง request ถูก reprice
ลิงก์ไปยังส่วน: การข้าม tier ทำให้ทั้ง request ถูก repricecontexts ยาว ๆ ไม่ได้แพงขึ้นเพียงเพราะมันยาวกว่า หลัง threshold หนึ่ง มันแพงขึ้น ต่อ token และ threshold นั้นนำไปใช้ย้อนหลังกับ prompt ทั้งหมด
หน้า model ของ OpenAI สำหรับ gpt-5.6-terra ระบุไว้ในประโยคเดียว: “Prompts with >272K input tokens are priced at 2x input and 1.5x output for the full request.”7 ไม่ใช่เฉพาะส่วนเกิน สำหรับทั้งก้อน
prompt 271,999 + 500 output -> $0.5500
prompt 272,000 + 500 output -> $0.5500
prompt 272,001 + 500 output -> $1.0970หนึ่ง token ห้าสิบห้าเซนต์ ถ้า service ของคุณสร้าง prompts จากเอกสารที่ retrieve มา ซึ่งคุณควบคุมขนาดไม่ได้ คุณมีหน้าผาใน cost model ที่ขอบเขตซึ่งไม่มีใครในทีมเขียนไว้
pricing ของ Google ทำงานแบบเดียวกันกับ threshold 200,000-token: Gemini 2.5 Pro คือ $1.25 ต่อหนึ่งล้าน input token สำหรับ prompts ถึง 200K และ $2.50 เมื่อสูงกว่านั้น โดย output จาก $10.00 ไป $15.008 Anthropic ไปอีกทาง — ณ 6 กันยายน 2026 เอกสารระบุว่า Claude 4.6 และใหม่กว่ารวม one-million-token window ทั้งหมดไว้ที่ standard pricing ดังนั้น “a 900k-token request is billed at the same per-token rate as a 9k-token request.”9 model ก่อนหน้านั้นยังมี surcharge
นี่คือเหตุผลที่ price ไม่ใช่ตัวเลข Price คือ table ของ tiers ที่ key ด้วยความยาว prompt ซึ่งเป็นหน้าที่ของ Tier[] ใน cost function และเป็นเหตุผลที่ computeCost เลือก tier โดยใช้ prompt ทั้งหมดแทนที่จะแยกตาม bucket
Prefill, decode และทำไม output จึงแพงกว่า input หกเท่า
ลิงก์ไปยังส่วน: Prefill, decode และทำไม output จึงแพงกว่า input หกเท่าbucket ทั้งห้า map เข้ากับสอง phase ของบทที่ 13 และเมื่อคุณเห็น mapping แล้ว ratio ราคาจะเลิกดูเหมือน arbitrary
Input tokens คือ prefill prompt ทั้งหมดผ่าน model ใน pass เดียว ประมวลผลแบบ parallel — matrix multiplications ขนาดใหญ่, compute-bound cost ต่อ token ต่ำ และนี่คือ phase ที่กำหนด time to first token: prompt 4,947-token มี prefill 4,947 token ต้องทำก่อนคำแรกจะปรากฏ
Output tokens คือ decode มันถูกผลิตทีละตัว แต่ละตัวเป็น forward pass เต็มที่อ่าน KV cache ทั้งหมด โดย GPU ส่วนใหญ่รอ memory แทนที่จะ compute นี่คือ phase ที่กำหนด tokens per second มัน parallelise ภายใน response เดียวไม่ได้ และนี่คือเหตุผลที่ output มี cost ประมาณหกเท่าของ input บน model ที่ตั้งราคาที่นี่: $12.00 เทียบกับ $2.00 ต่อหนึ่งล้าน token
มีผลตามมาตรง ๆ สามอย่าง cache read แทนที่ prefill work ดังนั้นมันซื้อทั้ง latency และเงินพร้อมกัน — ส่วนลดเดียวกันปรากฏเป็นบิลที่ต่ำลงและการรอสั้นลงสำหรับ token แรก Reasoning tokens คือ decode ที่คุณไม่เห็น ซึ่งเป็นเหตุผลที่ reasoning model ไม่ stream อะไรหลายวินาทีแล้วตอบอย่างรวดเร็ว: บทที่ 12 เตือนเรื่องผลต่อ interface และนี่คือผลบน invoice และ การ abort stream ไม่ได้หยุด generation — บทที่ 14 สร้าง cancellation และทิ้งราคาไว้ให้บทนี้ และราคาคือ output count เต็ม เพราะ tokens ถูกผลิตและถูกคิดเงินไม่ว่าจะมีใครฟังอยู่หรือไม่ก็ตาม เช่นเดียวกับคำตอบที่ไม่มีใครเก็บไว้: regenerate คำตอบ turn 40 ห้าครั้งมี cost $0.057990 สำหรับคำตอบเดียวที่เหลือบนหน้าจอ
นับ tokens ก่อนส่ง
ลิงก์ไปยังส่วน: นับ tokens ก่อนส่งtokenizer ของบทที่ 7 เป็น Python และอยู่ตรงนั้น Budgeting เกิดใน server ที่สร้าง request ดังนั้นมันต้องเกิดที่นี่ และมีความแม่นยำให้เลือกแค่สาม level
Level one: count locally js-tiktoken มาพร้อม BPE merge tables เดียวกับ Python tiktoken จึงนับได้เหมือนกัน byte-for-byte สำหรับ OpenAI encodings โดยไม่ต้อง network call:
import { getEncoding } from "js-tiktoken";
const enc = getEncoding("o200k_base");
const PER_MESSAGE = 4; // role and delimiters added by the chat template
const PER_REPLY = 3; // priming for the assistant turn
export function promptTokens(messages: { role: string; content: string }[]) {
return messages.reduce(
(sum, m) => sum + enc.encode(m.content).length + PER_MESSAGE, PER_REPLY);
}constant สองตัวนั้นสำคัญ และเป็นจุดที่ local counts drift ข้อความของคุณไม่ใช่สิ่งที่ถูก tokenize — chat template ของ บทที่ 11 ห่อทุก message ด้วย role markers ก่อน และสิ่งเหล่านั้นคือ tokens ที่คุณจ่าย สี่ต่อ message และสามสำหรับ reply priming คือ approximation ตามธรรมเนียมสำหรับ OpenAI chat models; ตลอดข้อความแปดสิบเอ็ดข้อความของบทสนทนาข้างบน มันรวมเป็น 324 token หรือ 6.4 % ของความยาว counts ที่นี่ cross-check กับ Python tiktoken จากบทที่ 7 บน string ทั้งแปดสิบเอ็ดแล้วและตรงกัน
Level two: ask the provider Anthropic เปิด /v1/messages/count_tokens และ Google เปิด count_tokens ทั้งคู่รับ request shape เดียวกับ call จริงและคืน input token count ฟรี ใช้มันเมื่อคุณ count locally ไม่ได้ — และคุณ count locally สำหรับ Anthropic ไม่ได้ เพราะ tokenizer ของมันไม่ได้เผยแพร่ เอกสารของ Anthropic ระวังมากว่าสิ่งที่ให้คุณคืออะไร: count นั้น “is an estimate” และ “may include tokens added automatically by Anthropic for system optimizations” ซึ่ง “you are not billed”10
Level three: read usage in the response นั่นคือความจริง และมันมาถึงหลังจากเงินถูกใช้ไปแล้ว นี่คือเหตุผลที่มีสอง level แรก — เพื่อ decide ว่าจะส่ง request หรือไม่ ไม่ใช่เพื่อ bill มัน
สิ่งที่คุณจ่ายแต่ไม่มีใครแสดงให้เห็น
ลิงก์ไปยังส่วน: สิ่งที่คุณจ่ายแต่ไม่มีใครแสดงให้เห็นสี่ line item ที่ไม่ปรากฏเป็น line item
system prompt ถูกจ่ายทุก call ตัวข้างบนคือ 192 token พร้อม template overhead ตลอดสี่สิบ call นั่นคือ 7,680 token — 5.6 % ของบิลทั้งหมดของบทสนทนานี้ สำหรับแปดบรรทัดที่เขียนครั้งเดียว มันยังเป็น cache candidate ที่ดีที่สุด เพราะทั้งเสถียรและอยู่ก่อน
Tool definitions ชื่อ คำอธิบาย และ JSON schema ของทุก tool ถูกส่งออกไปทุก request และ providers เพิ่ม scaffolding ข้างบน Anthropic เผยแพร่ตัวเลข: การเปิด tools แค่อย่างเดียวเพิ่ม system prompt ที่ซ่อนอยู่ 496 token บน Claude Sonnet 4.5 โดยตั้ง tool_choice เป็น auto หรือ 588 เมื่อมี any หรือ named tool9 นั่นคือก่อน schemas ของคุณเอง บทที่ 18 สร้าง catalogue; บทที่ 24 วัดว่ามันกินอะไร
ทุก generation รวมถึงอันที่คุณทิ้ง regenerate ห้าครั้งมี cost ห้าเท่า chat แสดงหนึ่งครั้ง
Thoughts ที่คุณไม่ถูกแสดง billing อิง full thought tokens แม้จะคืนแค่ summary และ accounting ของคุณตรวจ audit ตัวเลขนั้นไม่ได้
การมี 200K tokens ไม่ใช่การใช้มัน
ลิงก์ไปยังส่วน: การมี 200K tokens ไม่ใช่การใช้มันขอปิดด้วยคำเตือนหนึ่งข้อ เพราะมันคือความคิดถัดไปตามธรรมชาติ และคำตอบไม่ใช่สิ่งที่ obvious
million-token window ไม่ได้แปลว่า usable tokens หนึ่งล้าน Retrieval accuracy เสื่อมตามตำแหน่ง: Liu et al. พบว่า models หา information ได้อย่างน่าเชื่อถือที่ต้นและท้ายของ input ยาว ๆ และน่าเชื่อถือน้อยกว่ามากตรงกลาง11 window ที่ใหญ่ขึ้นซื้อความสามารถในการส่งมากขึ้น ไม่ใช่ความแน่นอนว่าจะถูกอ่าน
ปรากฏการณ์นี้ถูกวัดครั้งเดียวใน course นี้ — retrieval rate ที่เก้าตำแหน่งใน prompt 853-token เดียวกัน — และมันอยู่ในบทที่ 24 ซึ่งมันเปลี่ยนสิ่งที่ agent ทำ มันถูก cite ที่นี่เพราะมันเปลี่ยนสิ่งที่คุณควร ซื้อ: token ที่ถูกที่สุดคือ token ที่คุณไม่ได้ส่ง
ต่อไปจะไปไหน
ลิงก์ไปยังส่วน: ต่อไปจะไปไหนตอนนี้คุณทำนายได้แล้วว่า call หนึ่งจะมี cost เท่าไรก่อนทำมัน อ่านได้ว่าหลังจากนั้นมันมี cost เท่าไรจริง และแยกความต่างระหว่างสองอย่างนี้ได้ นั่นครอบคลุมทุกอย่างเกี่ยวกับ request ยกเว้นส่วนที่คุณยังไม่ได้แตะ: knobs
บทที่ 17 คือ sampling — temperature, top-p, top-k, penalties และ determinism ที่คุณไม่มี มันเริ่มด้วยการรื้อข้อผิดพลาดที่แพร่หลายที่สุดใน field ว่า temperature เป็นปุ่มปรับ creativity มันไม่ใช่: temperature หาร logits จาก บทที่ 4 ก่อน softmax และการเพิ่มมันไม่ได้ทำให้ model มีจินตนาการ แต่มันเพิ่มความน่าจะเป็นของ tokens ที่ model เองให้คะแนนว่าแย่กว่า จากนั้นคือเหตุผลที่ greedy decoding ผลิต text ที่วัดได้ว่า แย่กว่า sampling, ทำไม top-k และ top-p ล้มเหลวบน distribution shapes คนละแบบ และ experiment ที่จบบท: forward pass เหมือนกันยี่สิบครั้งที่ temperature 0 กลับมาเหมือนกัน bit-for-bit เมื่อ model รันลำพัง และการใส่ prompt เดียวกันลงใน batch เคียงข้าง request ของคนอื่นทำให้ logits ของมัน 97 % ขยับ
มันไม่ได้ match ทั้งหมด เหตุผลเริ่มจากกล่อง floating-point ใน บทที่ 2
Sources and method
ลิงก์ไปยังส่วน: Sources and methodprices, thresholds และ multipliers ทั้งหมดในบทนี้อ่านจากหน้าของ providers เองเมื่อ 6 กันยายน 2026 และระบุพร้อมวันที่นั้นเพราะมันจะเปลี่ยน method สำคัญกว่าตัวเลข: buckets, prefix rule และ tier arithmetic เสถียรมาสองปี ขณะที่ทุกตัวเลขในนั้นขยับไปแล้ว
Stanford CS336 lecture 2, Resource accounting, คือ academic treatment ที่ใกล้ที่สุดของ material นี้ และเป็นสิ่งที่ควรอ่านต่อ: มันทำเลขแบบเดียวกันบนฝั่ง training ที่บทนี้ทำบนฝั่ง inference token counts ที่นี่ผลิตด้วย js-tiktoken 1.0.21 โดยใช้ encodings o200k_base และ cl100k_base บนบทสนทนาสี่สิบ turn ที่มี 5,090 token; per-message template overhead คือ approximation ตามธรรมเนียมแบบ four-plus-three และระบุไว้ทุกที่ที่รวมมัน figures ของ cache, tier และ truncation คือ documented pricing rules ที่ apply กับ measured token counts เหล่านั้น ไม่ใช่ observations จาก live API responses — ไม่มี paid call ถูกทำเพื่อผลิตบทนี้ ซึ่งเป็นเหตุผลซื่อสัตย์เช่นกันว่าทำไม latency claims จึง qualitative และ cost claims จึงไม่ใช่
รายการอ้างอิง
ลิงก์ไปยังส่วน: รายการอ้างอิง-
Dao, T., Fu, D. Y., Ermon, S., Rudra, A. and Ré, C. FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. arXiv:2205.14135 (2022). เหตุผลที่ ceiling ขยับโดย asymptotic cost ไม่เปลี่ยน ↩
-
Chen, S., Wong, S., Chen, L. and Tian, Y. Extending Context Window of Large Language Models via Positional Interpolation. arXiv:2306.15595 (2023). ↩
-
Peng, B., Quesnelle, J., Fan, H. and Shippole, E. YaRN: Efficient Context Window Extension of Large Language Models. arXiv:2309.00071 (2023). ↩
-
Google, Thinking,
ai.google.dev/gemini-api/docs/thinking, and Token counting,ai.google.dev/gemini-api/docs/tokens, both accessed 2026-09-06. “Pricing is based on the full thought tokens the model needs to generate, despite only the summary being output from the API.” usage object reportstotal_input_tokens,total_output_tokens,total_thought_tokens,total_cached_tokens,total_tool_use_tokensandtotal_tokens— six buckets, with thoughts and tool use outside the output count. field name ก่อนหน้าสำหรับ quantity เดียวกัน ซึ่งยัง return โดย generateContent surface คือthoughtsTokenCount, documented on a third page,ai.google.dev/gemini-api/docs/generate-content/thinking. ↩ -
Anthropic, Prompt caching,
docs.anthropic.com/en/docs/build-with-claude/prompt-caching, accessed 2026-09-06. Source of thetools→system→messagesinvalidation hierarchy and its table; per-model minimum cacheable lengths; identitytotal_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens; และ five-minute default lifetime ที่ refresh ฟรีเมื่อ hit แต่ละครั้ง ↩ ↩2 -
OpenAI, Prompt caching,
platform.openai.com/docs/guides/prompt-caching, accessed 2026-09-06. Source of: entire-rendered-prefix rule; minimum cacheable prefix (1,024 visible input tokens บน GPT-5.6 และใหม่กว่า, 2,048 ก่อนหน้า); multipliers write 1.25× และ read 0.1× และการไม่มี write charge บน GPT-5.5 และก่อนหน้า; lifetime 30 นาที; limits สี่ writes ต่อ request และห้าสิบ breakpoint; machine-affinity note และprompt_cache_key; worked examples break-even 1.35×, 2.15× และ 10×; และ statement ว่า summarisation, compaction หรือ truncation reset cache reuse ↩ ↩2 -
OpenAI, Pricing (
platform.openai.com/docs/pricing) และหน้า model สำหรับgpt-5.6-terra, both accessed 2026-09-06.gpt-5.6-terra, standard service tier, ต่อหนึ่งล้าน tokens: input $2.00, cached input $0.20, cache writes $2.50, output $12.00; long context input $4.00, cached $0.40, writes $5.00, output $18.00; “prompts with >272K input tokens are priced at 2x input and 1.5x output for the full request”; context window 1,050,000 tokens พร้อม maximum 922,000 input tokens ตารางเดียวกัน listgpt-6-astraที่ $10.00/$1.00/$12.50/$50.00 และgpt-5.6-lunaที่ $0.20/$0.02/$0.25/$1.20 worked cost ทุกตัวในบทนี้ใช้ rates standard short-context ของgpt-5.6-terra↩ -
Google, Gemini Developer API pricing,
ai.google.dev/gemini-api/docs/pricing, accessed 2026-09-06. Gemini 2.5 Pro, ต่อหนึ่งล้าน tokens: input $1.25 สำหรับ prompts ถึง 200K และ $2.50 เมื่อสูงกว่า; output $10.00 และ $15.00 ทั้งสองกรณี labeled “including thinking tokens”; context caching $0.125 และ $0.25, plus storage charge $4.50 ต่อหนึ่งล้าน tokens ต่อชั่วโมง Gemini 3.1 Pro Preview ใช้ threshold 200K เดียวกันที่ input $2.00/$4.00 และ output $12.00/$18.00 ↩ -
Anthropic, Pricing,
docs.anthropic.com/en/docs/about-claude/pricing, accessed 2026-09-06. ต่อหนึ่งล้าน tokens, base input / 5-minute cache write / 1-hour cache write / cache read / output: Claude Sonnet 4.5 $3 / $3.75 / $6 / $0.30 / $15; Claude Haiku 4.5 $1 / $1.25 / $2 / $0.10 / $5; Claude Opus 5 $5 / $6.25 / $10 / $0.50 / $25. Multipliers: 1.25× สำหรับ five-minute write, 2× สำหรับ one-hour write, 0.1× สำหรับ read. Also source of long-context statement (“Claude 4.6 and later models... include the full 1M token context window at standard pricing”), tool-use system prompt token counts (496 tokens บน Claude Sonnet 4.5 withtool_choiceofautoornone, 588 withanyor a named tool), and note that Claude 4.7 and later use a newer tokenizer producing “approximately 30 % more tokens for the same text”. ↩ ↩2 ↩3 -
Anthropic, Token counting,
docs.anthropic.com/en/docs/build-with-claude/token-counting, accessed 2026-09-06. endpoint/v1/messages/count_tokenstakes the same inputs as a message and returns an input token count; documentation states that the count is an estimate, that it may include tokens Anthropic adds for system optimisations, and that those are not billed. ↩ -
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 (2023). cite ที่นี่, วัดในบทที่ 24 ↩