[{"data":1,"prerenderedAt":425},["ShallowReactive",2],{"tag-labels":3,"site-header":4,"article-what-a-prompt-cache-remembers-en":72},{},{"featuredPost":5,"navigations":29},{"id":6,"title":7,"title_id":8,"slug":9,"excerpt":10,"excerpt_id":11,"has_translation":12,"published_at":13,"reading_time":14,"hidden_from_listing":15,"tags":16,"thumbnail_image":21,"og_image":22,"series":23,"authors":28},110,"When You Pick a Framework Just to Look Different","Ketika Pilih Framework Cuma Biar Kelihatan Beda","ketika-pilih-framework-cuma-biar-kelihatan-beda","The last time you chose a new framework or library, was it because your problem needed that tool, or because it sounded cool and few people around you were using it? That question is uncomfortable. Technology decisions are often made for reasons that never appear in any technical document.","Terakhir kali kamu milih framework atau library baru, itu karena masalahmu butuh alat itu, atau karena alatnya kedengaran keren dan belum banyak yang pakai? Pertanyaan ini nggak enak dijawab. Soalnya keputusan teknologi sering diambil dengan alasan yang nggak pernah ditulis di dokumen teknis mana pun.",true,"2026-10-06T15:25:21.000Z",5,false,[17,18,19,20],"karier","framework","opini","pengalaman",null,"\u002Fog\u002Fketika-pilih-framework-cuma-biar-kelihatan-beda.png",{"id":24,"title":25,"title_id":26,"slug":27},9,"Articles","Artikel","all",[],[30,46,67],{"collection":31,"item":32},"series",{"__typename":31,"title":25,"title_id":26,"slug":27,"children_series":33},[34,38,42],{"title":35,"title_id":36,"slug":37},"Artificial Intelligence","Kecerdasan Artifisial","kecerdasan-artifisial",{"title":39,"title_id":40,"slug":41},"Programming Fundamentals","Konsep Dasar Pemrograman","konsep-dasar-pemrograman",{"title":43,"title_id":44,"slug":45},"Algorithms and Data Structures","Algoritma dan Struktur Data","algoritma-struktur-data",{"collection":31,"item":47},{"__typename":31,"title":48,"title_id":48,"slug":49,"children_series":50},"Tutorial","tutorial",[51,55,59,63],{"title":52,"title_id":53,"slug":54},"Learning Python","Belajar Python","belajar-python",{"title":56,"title_id":57,"slug":58},"Learning Go","Belajar Golang","belajar-golang",{"title":60,"title_id":61,"slug":62},"Learning Java","Belajar Java","belajar-java",{"title":64,"title_id":65,"slug":66},"Learning Pascal","Belajar Pascal","belajar-pascal",{"collection":31,"item":68},{"__typename":31,"title":69,"title_id":69,"slug":70,"children_series":71},"Lab","lab",[],{"article":73,"renderedBody":307,"renderedTakeaway":308,"latestArticles":309,"toc":409},{"id":74,"title":75,"title_id":76,"slug":77,"excerpt":78,"excerpt_id":79,"has_translation":12,"published_at":80,"reading_time":81,"hidden_from_listing":15,"tags":82,"thumbnail_image":21,"og_image":87,"series":88,"authors":299,"seoTitle":21,"seoDescription":21,"body_locale":302,"available_locales":303,"body":305,"takeway":306,"prerequisite":21},140,"What a Prompt Cache Actually Remembers","Apa yang Sebenarnya Diingat Prompt Cache","what-a-prompt-cache-remembers","Four providers cache the same bytes under four different rulebooks. The question that decides your bill: when the second request is longer than the first, does it reuse the whole prefix or only the part that was already written?","Empat provider meng-cache byte yang sama dengan empat buku aturan berbeda. Pertanyaan yang menentukan tagihanmu: saat permintaan kedua lebih panjang, apakah seluruh prefix dipakai ulang atau cuma bagian yang sudah pernah ditulis?","2026-11-08T01:10:00.000Z",6,[83,84,85,86],"llm","ai-agent","cache","api","\u002Fog\u002Fwhat-a-prompt-cache-remembers.png",{"id":89,"title":35,"title_id":36,"slug":37,"description":90,"description_id":21,"articles":91,"children_series":291,"parent_series":292},8,"Notes on AI that go past the demo: how the models work, where they break, and what changes once you put them in production.",[92,109,126,141,155,169,185,202,222,241,260,274,286],{"id":93,"title":94,"title_id":95,"slug":96,"excerpt":97,"excerpt_id":98,"has_translation":12,"published_at":99,"reading_time":100,"hidden_from_listing":15,"tags":101,"thumbnail_image":21,"og_image":106,"series":107,"authors":108},1,"Why Does AI Keep Hallucinating? Meet JEPA, the AI Brain With Common Sense","Kenapa AI Suka 'Halu'? Kenalan sama JEPA, Otak AI yang Punya Akal Sehat","kenapa-ai-suka-halu-kenalan-sama-jepa-otak-ai-yang-punya-akal-sehat","Generative AI hallucinates because all it does is guess word patterns. JEPA tries to change that by teaching machines how the real world works, the way a human brain does.","AI generatif sering 'halu' karena cuma jago nebak pola kata. JEPA coba ngerombak itu dengan ngajarin AI cara kerja dunia nyata kayak otak manusia.","2026-06-02T08:09:05.595Z",4,[102,103,104,105],"jepa","artificial-intelligence","yann-lecun","agi","\u002Fog\u002Fkenapa-ai-suka-halu-kenalan-sama-jepa-otak-ai-yang-punya-akal-sehat.png",{"id":89,"title":35,"title_id":36,"slug":37},[],{"id":110,"title":111,"title_id":112,"slug":113,"excerpt":114,"excerpt_id":115,"has_translation":12,"published_at":116,"reading_time":100,"hidden_from_listing":15,"tags":117,"thumbnail_image":21,"og_image":118,"series":119,"authors":120},2,"LLMs Are Going to Hit a Wall. What Then?","LLM Bakal Mentok, Terus Gimana Dong?","llm-bakal-mentok-terus-gimana-dong","LLMs like ChatGPT are great at stringing words together, but they never really grasp reality. Yann LeCun says this architecture will hit a wall and needs a new answer.","LLM kayak ChatGPT itu jago ngerangkai kata, tapi nggak beneran paham realitas. Yann LeCun bilang arsitektur ini bakal mentok dan butuh solusi baru.","2026-06-03T08:55:33.003Z",[103,83,104,102],"\u002Fog\u002Fllm-bakal-mentok-terus-gimana-dong.png",{"id":89,"title":35,"title_id":36,"slug":37},[121],{"authors_id":122},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},"Daffa Izzuddin","izzudd","Writer",{"id":127,"title":128,"title_id":129,"slug":130,"excerpt":131,"excerpt_id":132,"has_translation":12,"published_at":133,"reading_time":100,"hidden_from_listing":15,"tags":134,"thumbnail_image":21,"og_image":136,"series":137,"authors":138},3,"Meet JEPA: Where AI Starts Learning Common Sense","Mengenal JEPA: Saat AI Mulai Belajar Punya \"Akal Sehat\"","kenapa-ai-suka-halu-dan-gimana-jepa-beresin-masalah-ini","AI hallucinates because it only guesses data probabilities without understanding reality. The JEPA architecture tries to change that by building a world model in its head.","AI sering 'halu' karena cuma nebak probabilitas data tanpa paham realita. Arsitektur JEPA mencoba mengubah itu dengan membangun model dunia di kepalanya.","2026-06-03T09:32:50.446Z",[103,102,135,104],"world-models","\u002Fog\u002Fkenapa-ai-suka-halu-dan-gimana-jepa-beresin-masalah-ini.png",{"id":89,"title":35,"title_id":36,"slug":37},[139],{"authors_id":140},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":100,"title":142,"title_id":143,"slug":144,"excerpt":145,"excerpt_id":146,"has_translation":12,"published_at":147,"reading_time":100,"hidden_from_listing":15,"tags":148,"thumbnail_image":21,"og_image":150,"series":151,"authors":152},"Beyond Generative AI: JEPA Chases Human-Style Conceptual Understanding","Bukan Sekadar Generative AI, JEPA Coba Kejar Pemahaman Konsep ala Manusia","kenapa-ai-masih-gagal-paham-fisika-dasar-mengenal-arsitektur-jepa","AI usually only guesses pixels, while JEPA tries to learn from abstract concepts in latent space. That approach turns out to reach as far as reading brain waves.","AI biasanya cuma jago nebak piksel, tapi JEPA coba belajar lewat konsep abstrak di ruang laten. Ternyata, cara ini bisa sampai dipakai buat baca gelombang otak.","2026-06-03T09:34:14.840Z",[102,104,149,105],"machine-learning","\u002Fog\u002Fkenapa-ai-masih-gagal-paham-fisika-dasar-mengenal-arsitektur-jepa.png",{"id":89,"title":35,"title_id":36,"slug":37},[153],{"authors_id":154},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":14,"title":156,"title_id":157,"slug":158,"excerpt":159,"excerpt_id":160,"has_translation":12,"published_at":161,"reading_time":100,"hidden_from_listing":15,"tags":162,"thumbnail_image":21,"og_image":164,"series":165,"authors":166},"Inside JEPA's Kitchen: The Secrets of EMA, VICReg, and Compute Efficiency","Di Balik Dapur JEPA: Rahasia EMA, VICReg, dan Efisiensi Komputasi","di-balik-dapur-jepa-rahasia-ema-vicreg-dan-efisiensi-komputasi","Bored of AI that only guesses words or memorizes pixels? Meet JEPA, an architecture that teaches machines common sense very cheaply. Here is a look at its technical insides.","Bosen sama AI yang cuma jago nebak kata atau ngafalin pixel? Kenalan sama JEPA, arsitektur revolusioner yang ngajarin mesin punya \"akal sehat\" secara super efisien. Yuk intip jeroan teknisnya!","2026-06-07T16:14:13.723Z",[102,163],"artificial-intelligent","\u002Fog\u002Fdi-balik-dapur-jepa-rahasia-ema-vicreg-dan-efisiensi-komputasi.png",{"id":89,"title":35,"title_id":36,"slug":37},[167],{"authors_id":168},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":170,"title":171,"title_id":172,"slug":173,"excerpt":174,"excerpt_id":175,"has_translation":12,"published_at":176,"reading_time":89,"hidden_from_listing":15,"tags":177,"thumbnail_image":21,"og_image":182,"series":183,"authors":184},125,"MCP, Rebuilt: Good News If You're Tired of Writing Connectors","MCP dibongkar ulang: kabar baik buat kamu yang capek nulis konektor","mcp-dibongkar-ulang-kabar-baik-buat-kamu","You have a few internal APIs and one AI agent, but the code has to be rewritten every time the model changes. MCP ends that, and this year the protocol was rebuilt so servers can run like ordinary web servers. Here's what changed. 🔧","Kamu punya beberapa API internal dan satu agent AI, tapi kodenya harus ditulis ulang tiap kali modelnya diganti. MCP datang buat mengakhiri itu, dan tahun ini protokolnya dibongkar ulang supaya server-nya bisa jalan seperti web server biasa. Yuk lihat apa yang berubah. 🔧","2026-10-16T05:00:00.000Z",[178,179,84,83,180,181],"mcp","model-context-protocol","backend","python","\u002Fog\u002Fmcp-dibongkar-ulang-kabar-baik-buat-kamu.png",{"id":89,"title":35,"title_id":36,"slug":37},[],{"id":186,"title":187,"title_id":188,"slug":189,"excerpt":190,"excerpt_id":191,"has_translation":12,"published_at":192,"reading_time":193,"hidden_from_listing":15,"tags":194,"thumbnail_image":21,"og_image":197,"series":198,"authors":199},126,"AI Agent Memory: From Vector Store to Temporal Graph","Memori AI Agent: dari Vector Store ke Temporal Graph","memori-ai-agent-vector-store-ke-temporal-graph","Two contradictory facts make an agent answer nonsense. Here is how a vector store and a temporal knowledge graph differ at keeping long-term state.","Dua fakta yang saling bertentangan bikin agent menjawab ngawur. Ini bedanya vector store dan temporal knowledge graph dalam menjaga state jangka panjang.","2026-10-18T13:13:29.458Z",7,[195,83,180,196],"ai","system-design","\u002Fog\u002Fmemori-ai-agent-vector-store-ke-temporal-graph.png",{"id":89,"title":35,"title_id":36,"slug":37},[200],{"authors_id":201},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":203,"title":204,"title_id":205,"slug":206,"excerpt":207,"excerpt_id":208,"has_translation":12,"published_at":209,"reading_time":81,"hidden_from_listing":15,"tags":210,"thumbnail_image":21,"og_image":217,"series":218,"authors":219},127,"Why RAG That Leans on Cosine Similarity Alone Keeps Missing","Kenapa RAG yang cuma andalkan cosine similarity sering nyasar","kenapa-rag-cuma-cosine-similarity-sering-nyasar","RAG that leans only on embeddings returns chunks that look close in meaning, not the ones you need. Here is why, and the two layers that patch it.","RAG yang cuma andalkan embedding balikin chunk yang mirip maknanya, bukan yang kamu butuh. Ini penyebabnya, dan dua lapis penambalnya.","2026-10-19T02:04:06.718Z",[211,212,213,214,215,216],"rag","vector-search","bm25","hybrid-search","retrieval","embedding","\u002Fog\u002Fkenapa-rag-cuma-cosine-similarity-sering-nyasar.png",{"id":89,"title":35,"title_id":36,"slug":37},[220],{"authors_id":221},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":223,"title":224,"title_id":225,"slug":226,"excerpt":227,"excerpt_id":228,"has_translation":12,"published_at":229,"reading_time":81,"hidden_from_listing":15,"tags":230,"thumbnail_image":21,"og_image":236,"series":237,"authors":238},128,"Linear or Graph: How to Run Many Agents Without an Eternal Loop","Linear atau graf: cara mengatur banyak agent tanpa loop abadi","multi-agen-linear-atau-grafik","One agent with twenty tools gets messy fast. Once several agents revise each other, you need a graph that allows cycles and a number that makes it stop.","Satu agent dengan dua puluh tool gampang kacau. Begitu beberapa agent saling revisi, kamu butuh graf yang mendukung cycle dan angka yang membuatnya berhenti.","2026-10-21T16:05:00.000Z",[231,232,233,234,235,83],"multi-agent","langgraph","autogen","orkestrasi","checkpoint","\u002Fog\u002Fmulti-agen-linear-atau-grafik.png",{"id":89,"title":35,"title_id":36,"slug":37},[239],{"authors_id":240},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":242,"title":243,"title_id":244,"slug":245,"excerpt":246,"excerpt_id":247,"has_translation":12,"published_at":248,"reading_time":14,"hidden_from_listing":15,"tags":249,"thumbnail_image":21,"og_image":255,"series":256,"authors":257},129,"So Your RAG Stops Hitting 504: Split the Query Path From Ingestion","Biar RAG nggak kena 504: pisahin jalur query dan ingestion","rag-pisah-jalur-query-dan-ingestion","One RAG service that both parses uploads and answers queries will fall over the moment someone uploads a 200-page PDF. The cure is two paths and one queue.","Satu service RAG yang merangkap parsing dan menjawab kueri bakal tumbang pas ada yang upload PDF 200 halaman. Obatnya dua jalur dan satu queue.","2026-10-22T01:30:00.000Z",[211,250,251,252,253,254],"cloud-run","cloud-tasks","gcp","serverless","arsitektur","\u002Fog\u002Frag-pisah-jalur-query-dan-ingestion.png",{"id":89,"title":35,"title_id":36,"slug":37},[258],{"authors_id":259},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":261,"title":262,"title_id":21,"slug":263,"excerpt":264,"excerpt_id":21,"has_translation":15,"published_at":265,"reading_time":14,"hidden_from_listing":15,"tags":266,"thumbnail_image":21,"og_image":269,"series":270,"authors":271},134,"Your Stream Dies at 125 Seconds and Nothing in Your Code Says 125","your-stream-dies-at-125-seconds","Streaming moves the wait into a connection that outlives the request. Four layers sit between the model and the browser, their idle timeouts disagree, and the smallest one ends your stream.","2026-11-04T01:00:00.000Z",[267,268,84,86],"streaming","sse","\u002Fog\u002Fyour-stream-dies-at-125-seconds.png",{"id":89,"title":35,"title_id":36,"slug":37},[272],{"authors_id":273},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":275,"title":276,"title_id":21,"slug":277,"excerpt":278,"excerpt_id":21,"has_translation":15,"published_at":279,"reading_time":100,"hidden_from_listing":15,"tags":280,"thumbnail_image":21,"og_image":281,"series":282,"authors":283},135,"Caching That Never Hits Costs More Than No Caching","caching-that-never-hits","Same model, same conversation, same token count, and a bill five times higher. The cause is a prefix that changes, a write premium nobody reads about, and a cost curve that grows faster than the loop.","2026-11-04T01:10:00.000Z",[83,84,85,86],"\u002Fog\u002Fcaching-that-never-hits.png",{"id":89,"title":35,"title_id":36,"slug":37},[284],{"authors_id":285},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":74,"title":75,"title_id":76,"slug":77,"excerpt":78,"excerpt_id":79,"has_translation":12,"published_at":80,"reading_time":81,"hidden_from_listing":15,"tags":82,"thumbnail_image":21,"og_image":87,"series":287,"authors":288},{"id":89,"title":35,"title_id":36,"slug":37},[289],{"authors_id":290},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},[],{"id":24,"title":25,"title_id":26,"slug":27,"parent_series":21,"children_series":293},[294,295,297],{"id":89,"title":35,"title_id":36,"slug":37,"cover_image":21},{"id":296,"title":39,"title_id":40,"slug":41,"cover_image":21},12,{"id":298,"title":43,"title_id":44,"slug":45,"cover_image":21},15,[300],{"authors_id":301},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},"en",[302,304],"id","\nA prompt cache does not remember your prompt. It remembers the prefixes that earlier requests wrote, at the boundaries those requests marked, and it hands back the longest one that still matches. Every surprise about token caching follows from that sentence, including the invoice you did not expect.\n\nHere is the smallest version of the surprise. Your system prompt is 512 tokens. Turn one sends those 512. Turn two sends the same 512 plus a 512-token message. The provider reads 512 tokens from cache, charges full price for the other 512, and the second half stays outside the cache until some later request writes it. Turn three sends 1,536 tokens and reads 1,024.\n\nThe cache grows one turn behind you. That is not a bug in any provider; it is what a prefix cache is.\n\n## Four rulebooks, one mechanism\n\nEvery major provider caches the same three things: the model's key-value state for a sequence of tokens, a hash of the prefix that produced it, and an expiry. They differ in four places, and each difference shows up on the bill.\n\n**Implicit caching** is on by default and the provider chooses where the reusable prefix ends. OpenAI does this on every supported model, Gemini calls it implicit caching, and DeepSeek runs it on disk with no opt-in at all.\n\n**Explicit breakpoints** put you in charge of the boundary. Anthropic takes `cache_control` on a block, up to four per request. OpenAI supports the same idea on GPT-5.6 and later through `prompt_cache_options.mode: \"explicit\"`, also with four writes per request.\n\n**Named cache objects** are Gemini's explicit mode: you create a cache, reference it by name, and pay for storage per token-hour on top of the input tokens. TTL runs from one minute to no maximum, 60 minutes by default.\n\n**Router stickiness** is not a cache. OpenRouter tracks a conversation so the next request lands on the provider that still holds your prefix, and it derives the conversation key from a hash of the first system message and the first user message. A system prompt that changes every minute breaks that key too, and the conversation moves somewhere with an empty cache.\n\n```figure\ncache-prefix-hierarchy\n```\n\nThe numbers below come from the providers' own documentation in November 2026. Multipliers are similar across vendors; minimums and lifetimes are not, and those two are what decide whether a request hits.\n\n| | Anthropic | OpenAI (GPT-5.6+) | Gemini | DeepSeek |\n|---|---|---|---|---|\n| Write trigger | `cache_control` block, or automatic at the last cacheable block | explicit breakpoint, or implicit at the last eligible message | implicit by default, or a named cache | automatic at request boundaries |\n| Minimum prefix | 512 tokens on the newest models, 1,024 to 4,096 on older ones | 1,024 visible input tokens | 2,048 on Gemini 2, 4,096 on Gemini 3 and 4, 6,144 on some 3.x previews | none documented |\n| Lookup | prefix hash at each breakpoint, walking back 20 blocks | breakpoint hash, plus up to 20 earlier message endings | best effort | must fully match a stored prefix unit |\n| Default lifetime | 5 minutes, refreshed on use; 1 hour if you ask | 30 minutes, or 12 hours at a higher write price | up to 24 hours implicit; 60 minutes default explicit | hours to days, best effort |\n| Read price | 0.1x input, 0.05x on Sonnet 5.5 and Opus 5.5 | 0.1x input, 0.05x on GPT-6.1 Sol and Terra | 90% off | discounted hit price |\n| Write price | 1.25x for 5 minutes, 2x for 1 hour | 1.25x for 30 minutes, 2.5x for 12 hours | standard input price, plus storage if explicit | not a separate price |\n\nA cached token still counts toward your tokens-per-minute limit on OpenAI, so none of this buys throughput. It buys money.\n\n## The 512-token question\n\nTake a 512-token prefix, a 512-token message per turn, and the multipliers most models use: input at $2 per million tokens, a write at 1.25x, a read at 0.1x.\n\n```figure\ncache-prefix-growth\n```\n\nTurn one writes 512 tokens at $0.00128 and reads nothing. Turn two reads 512 at $0.00010 and writes the new 512 at $0.00128. Turn three reads 1,024 and writes the new 512. Over three turns that is $0.0041, against $0.0061 with no caching at all.\n\nTwo things go wrong here, and both are common enough that the documentation names them.\n\n**Extending a message instead of appending one.** OpenAI's guide is explicit. If request one ends with a user message containing `Content A`, and request two extends that same message to `Content A + Content B`, the old boundary now sits inside a message. The saved prefix is not reused there without an explicit breakpoint. DeepSeek states the same rule with different words. A request of `A + B` followed by `A + B + C` hits the unit `A + B`; a request of `A + B` followed by `A + C` hits nothing, though it does teach the system to persist the shared `A` for a third request.\n\n**Falling under the minimum.** On GPT-5.6 and later, 512 tokens cannot be cached at all, because the minimum is 1,024 visible input tokens; the first possible hit is turn two, and it covers the whole 1,024. On Gemini 2 the wait is 2,048 tokens, and on Gemini 3 and 4 it is 4,096. The newest Anthropic models take 512, which makes the example above possible there and nowhere else.\n\n## Two breakpoints that lie to you\n\nA breakpoint on the block that changes every request writes a fresh entry each time and never reads one. Anthropic's documentation calls this the common mistake in as many words. The lookup walks backward looking for entries that earlier requests *wrote*, not for stable content. If nobody wrote an entry at the end of your static block, there is nothing to find behind a timestamp.\n\nThe second trap is distance. Anthropic looks back 20 block positions from a breakpoint; OpenAI checks up to 20 earlier message endings in implicit mode. A turn that appends more than 20 blocks, or a conversation that grows past them, leaves the last written entry outside the window. That is what the second and third breakpoints are for.\n\n## Seven settings that decide the hit rate\n\n1. **Order by how often each part changes.** Tool definitions, then instructions, then reference material, then the conversation. Anything that changes per request belongs after everything that does not.\n2. **Put the breakpoint at the end of the last stable block.** Not on the last block. On a growing conversation the final block works, because the turn before it wrote there; on a prompt with a varying suffix it does not.\n3. **Append, never rewrite.** A summary is a new message at the end, not a smaller version of the transcript at the start. Compaction and truncation reset the prefix from the first changed token.\n4. **Keep the tools array byte-identical.** Disable a tool with `tool_choice: \"none\"` instead of removing its definition, and never put a session id or a snapshot timestamp in a description.\n5. **Match the lifetime to the pause.** A handbook reused all day wants the one-hour write at 2x. The conversation tail wants the default, because it expires between sessions anyway.\n6. **Send related requests to the same cache.** On OpenAI models before GPT-5.6 that means a stable `prompt_cache_key`, aimed at roughly 15 requests per minute per key. Through a router it means a prefix that does not change, so the routing key does not change with it.\n7. **Read the counter.** `cache_read_input_tokens`, `cached_tokens`, `prompt_cache_hit_tokens`, `cachedContentTokenCount`. Each provider reports the tokens that came from cache on that request. A hit rate near zero on a prompt that looks static means the prefix is changing somewhere you have not looked.\n\n## What caching does not buy\n\nCached input tokens still count toward rate limits on OpenAI, so caching will not rescue a loop that is throttled. A cache entry on Anthropic only becomes available after the first response starts, so parallel requests that all arrive at once cannot help each other. Hidden system content does not count toward the minimum prefix on either provider. Caches are isolated per organization and, on the Claude API, per workspace, so two copies of the same prompt in two workspaces are two writes. And Gemini charges storage for an explicit cache by the token-hour, which is a second meter that a 24-hour cache can run up.\n\n```figure\ncache-cost-curve\n```\n\nNone of this changes the shape of the cost curve. Caching divides the bill by five or so and leaves the growth alone, because the transcript still grows and you still resend it every turn. On the forty-turn loop priced in the previous article, caching moved $2.16 to $0.43 and both numbers kept climbing with the square of the turn count.\n\nOrder the prompt by how often each part changes, put the breakpoint at the end of the part that does not, and measure the counter on the next request. If the hit rate is near zero and the prompt looks static, the prefix is changing in the tools array, the first line of the system prompt, or somewhere above the block you meant to cache.\n","- **A cache entry is a hash of a prefix, written at a boundary you marked.** It never grows by itself: the longer request only reuses the part an earlier request already wrote.\n- **The 512-to-1,024 case has one answer.** The first 512 tokens come back at the read price, the new 512 are charged in full, and they become reusable only from the next request.\n- **The minimum length decides whether you get a hit at all.** 512 tokens is cacheable on the newest Anthropic models, 1,024 on OpenAI's GPT-5.6 and later, and 2,048 to 6,144 on Gemini depending on the model.\n- **Where the breakpoint sits is the whole game.** On the block that changes every request, every write is wasted; at the end of the stable block, a single write survives the whole conversation.\n- **Measure the reported counters, not the feature flag.** Every provider tells you how many tokens came from cache on that request, and that number is the only evidence that caching is working.","\u003Cp>A prompt cache does not remember your prompt. It remembers the prefixes that earlier requests wrote, at the boundaries those requests marked, and it hands back the longest one that still matches. Every surprise about token caching follows from that sentence, including the invoice you did not expect.\u003C\u002Fp>\n\u003Cp>Here is the smallest version of the surprise. Your system prompt is 512 tokens. Turn one sends those 512. Turn two sends the same 512 plus a 512-token message. The provider reads 512 tokens from cache, charges full price for the other 512, and the second half stays outside the cache until some later request writes it. Turn three sends 1,536 tokens and reads 1,024.\u003C\u002Fp>\n\u003Cp>The cache grows one turn behind you. That is not a bug in any provider; it is what a prefix cache is.\u003C\u002Fp>\n\u003Ch2 id=\"four-rulebooks-one-mechanism\">Four rulebooks, one mechanism\u003C\u002Fh2>\n\u003Cp>Every major provider caches the same three things: the model&#39;s key-value state for a sequence of tokens, a hash of the prefix that produced it, and an expiry. They differ in four places, and each difference shows up on the bill.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Implicit caching\u003C\u002Fstrong> is on by default and the provider chooses where the reusable prefix ends. OpenAI does this on every supported model, Gemini calls it implicit caching, and DeepSeek runs it on disk with no opt-in at all.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Explicit breakpoints\u003C\u002Fstrong> put you in charge of the boundary. Anthropic takes \u003Ccode>cache_control\u003C\u002Fcode> on a block, up to four per request. OpenAI supports the same idea on GPT-5.6 and later through \u003Ccode>prompt_cache_options.mode: &quot;explicit&quot;\u003C\u002Fcode>, also with four writes per request.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Named cache objects\u003C\u002Fstrong> are Gemini&#39;s explicit mode: you create a cache, reference it by name, and pay for storage per token-hour on top of the input tokens. TTL runs from one minute to no maximum, 60 minutes by default.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Router stickiness\u003C\u002Fstrong> is not a cache. OpenRouter tracks a conversation so the next request lands on the provider that still holds your prefix, and it derives the conversation key from a hash of the first system message and the first user message. A system prompt that changes every minute breaks that key too, and the conversation moves somewhere with an empty cache.\u003C\u002Fp>\n\u003Cfigure class=\"ds-figure not-prose\" data-ds-figure=\"cache-prefix-hierarchy\">\n  \u003Cdiv class=\"ds-figure-body\">\n    \u003Cdiv class=\"ds-panel\">\n      \u003Cdiv class=\"ds-panel-head\">\u003Cspan class=\"ds-panel-title\">Three levels, one order\u003C\u002Fspan>\u003C\u002Fdiv>\n      \u003Cdiv class=\"ds-stack\">\n        \u003Cdiv class=\"ds-tier\">\u003Cspan class=\"ds-tier-name\">tool definitions\u003C\u002Fspan>\u003Cspan class=\"ds-tier-tag\">5 tools · ~120 tokens · rarely change\u003C\u002Fspan>\u003C\u002Fdiv>\n        \u003Cdiv class=\"ds-tier\">\u003Cspan class=\"ds-tier-name\">system prompt\u003C\u002Fspan>\u003Cspan class=\"ds-tier-tag\">handbook · 6,050 tokens · never changes\u003C\u002Fspan>\u003C\u002Fdiv>\n        \u003Cdiv class=\"ds-tier is-open\">\u003Cspan class=\"ds-tier-name\">messages\u003C\u002Fspan>\u003Cspan class=\"ds-tier-tag\">transcript · +350 tokens per turn\u003C\u002Fspan>\u003C\u002Fdiv>\n        \u003Cdiv class=\"ds-bp\">\u003Cspan class=\"ds-bp-caret\" aria-hidden=\"true\">▲\u003C\u002Fspan>breakpoint goes here\u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n      \u003Cp class=\"ds-note\">A timestamp in the tool definitions invalidates all three levels. The same timestamp in the last message invalidates only what comes after it.\u003C\u002Fp>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n  \u003Cfigcaption>The cache is a hierarchy, not a pool: it is built from the top down, and each level depends on the ones above it.\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\u003Cp>The numbers below come from the providers&#39; own documentation in November 2026. Multipliers are similar across vendors; minimums and lifetimes are not, and those two are what decide whether a request hits.\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>Anthropic\u003C\u002Fth>\n\u003Cth>OpenAI (GPT-5.6+)\u003C\u002Fth>\n\u003Cth>Gemini\u003C\u002Fth>\n\u003Cth>DeepSeek\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>Write trigger\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>cache_control\u003C\u002Fcode> block, or automatic at the last cacheable block\u003C\u002Ftd>\n\u003Ctd>explicit breakpoint, or implicit at the last eligible message\u003C\u002Ftd>\n\u003Ctd>implicit by default, or a named cache\u003C\u002Ftd>\n\u003Ctd>automatic at request boundaries\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Minimum prefix\u003C\u002Ftd>\n\u003Ctd>512 tokens on the newest models, 1,024 to 4,096 on older ones\u003C\u002Ftd>\n\u003Ctd>1,024 visible input tokens\u003C\u002Ftd>\n\u003Ctd>2,048 on Gemini 2, 4,096 on Gemini 3 and 4, 6,144 on some 3.x previews\u003C\u002Ftd>\n\u003Ctd>none documented\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Lookup\u003C\u002Ftd>\n\u003Ctd>prefix hash at each breakpoint, walking back 20 blocks\u003C\u002Ftd>\n\u003Ctd>breakpoint hash, plus up to 20 earlier message endings\u003C\u002Ftd>\n\u003Ctd>best effort\u003C\u002Ftd>\n\u003Ctd>must fully match a stored prefix unit\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Default lifetime\u003C\u002Ftd>\n\u003Ctd>5 minutes, refreshed on use; 1 hour if you ask\u003C\u002Ftd>\n\u003Ctd>30 minutes, or 12 hours at a higher write price\u003C\u002Ftd>\n\u003Ctd>up to 24 hours implicit; 60 minutes default explicit\u003C\u002Ftd>\n\u003Ctd>hours to days, best effort\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Read price\u003C\u002Ftd>\n\u003Ctd>0.1x input, 0.05x on Sonnet 5.5 and Opus 5.5\u003C\u002Ftd>\n\u003Ctd>0.1x input, 0.05x on GPT-6.1 Sol and Terra\u003C\u002Ftd>\n\u003Ctd>90% off\u003C\u002Ftd>\n\u003Ctd>discounted hit price\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Write price\u003C\u002Ftd>\n\u003Ctd>1.25x for 5 minutes, 2x for 1 hour\u003C\u002Ftd>\n\u003Ctd>1.25x for 30 minutes, 2.5x for 12 hours\u003C\u002Ftd>\n\u003Ctd>standard input price, plus storage if explicit\u003C\u002Ftd>\n\u003Ctd>not a separate price\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>A cached token still counts toward your tokens-per-minute limit on OpenAI, so none of this buys throughput. It buys money.\u003C\u002Fp>\n\u003Ch2 id=\"the-512-token-question\">The 512-token question\u003C\u002Fh2>\n\u003Cp>Take a 512-token prefix, a 512-token message per turn, and the multipliers most models use: input at $2 per million tokens, a write at 1.25x, a read at 0.1x.\u003C\u002Fp>\n\u003Cfigure class=\"ds-figure not-prose\" data-ds-figure=\"cache-prefix-growth\">\n  \u003Cdiv class=\"ds-figure-body\">\n    \u003Cdiv class=\"ds-panel\">\n      \u003Cdiv class=\"ds-panel-head\">\u003Cspan class=\"ds-panel-title\">What the second request can reuse\u003C\u002Fspan>\u003C\u002Fdiv>\n      \u003Cdiv class=\"ds-legend\">\n        \u003Cspan>\u003Ci class=\"ds-swatch is-hit\">\u003C\u002Fi>read from cache\u003C\u002Fspan>\n        \u003Cspan>\u003Ci class=\"ds-swatch is-fresh\">\u003C\u002Fi>written this turn\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n      \u003Cdiv class=\"ds-scroll\">\u003Cdiv class=\"ds-seg-table\">\u003Cdiv class=\"ds-seg-row\">\n    \u003Cdiv class=\"ds-seg-name\">turn 1\u003Cspan>write 512 · read 0\u003C\u002Fspan>\u003C\u002Fdiv>\n    \u003Cdiv class=\"ds-seg-track\">\u003Cdiv class=\"ds-seg-bar\" style=\"width:33%\">\u003Cdiv class=\"ds-seg is-fresh\" style=\"flex:512 1 0\">\n    \u003Cspan class=\"ds-seg-tokens\">512\u003C\u002Fspan>\n    \u003Cspan class=\"ds-seg-label\">written this turn\u003C\u002Fspan>\n  \u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\u003Cdiv class=\"ds-seg-row\">\n    \u003Cdiv class=\"ds-seg-name\">turn 2\u003Cspan>read 512 · write 512\u003C\u002Fspan>\u003C\u002Fdiv>\n    \u003Cdiv class=\"ds-seg-track\">\u003Cdiv class=\"ds-seg-bar\" style=\"width:67%\">\u003Cdiv class=\"ds-seg is-hit\" style=\"flex:512 1 0\">\n    \u003Cspan class=\"ds-seg-tokens\">512\u003C\u002Fspan>\n    \u003Cspan class=\"ds-seg-label\">read from cache\u003C\u002Fspan>\n  \u003C\u002Fdiv>\u003Cdiv class=\"ds-seg is-fresh\" style=\"flex:512 1 0\">\n    \u003Cspan class=\"ds-seg-tokens\">512\u003C\u002Fspan>\n    \u003Cspan class=\"ds-seg-label\">written this turn\u003C\u002Fspan>\n  \u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\u003Cdiv class=\"ds-seg-row\">\n    \u003Cdiv class=\"ds-seg-name\">turn 3\u003Cspan>read 1,024 · write 512\u003C\u002Fspan>\u003C\u002Fdiv>\n    \u003Cdiv class=\"ds-seg-track\">\u003Cdiv class=\"ds-seg-bar\" style=\"width:100%\">\u003Cdiv class=\"ds-seg is-hit\" style=\"flex:1024 1 0\">\n    \u003Cspan class=\"ds-seg-tokens\">1,024\u003C\u002Fspan>\n    \u003Cspan class=\"ds-seg-label\">read from cache\u003C\u002Fspan>\n  \u003C\u002Fdiv>\u003Cdiv class=\"ds-seg is-fresh\" style=\"flex:512 1 0\">\n    \u003Cspan class=\"ds-seg-tokens\">512\u003C\u002Fspan>\n    \u003Cspan class=\"ds-seg-label\">written this turn\u003C\u002Fspan>\n  \u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n  \u003Cfigcaption>The prefix grows by 512 tokens a turn, and each turn reuses exactly what an earlier turn wrote — never more. The extra 512 is charged in full the first time it appears.\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\u003Cp>Turn one writes 512 tokens at $0.00128 and reads nothing. Turn two reads 512 at $0.00010 and writes the new 512 at $0.00128. Turn three reads 1,024 and writes the new 512. Over three turns that is $0.0041, against $0.0061 with no caching at all.\u003C\u002Fp>\n\u003Cp>Two things go wrong here, and both are common enough that the documentation names them.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Extending a message instead of appending one.\u003C\u002Fstrong> OpenAI&#39;s guide is explicit. If request one ends with a user message containing \u003Ccode>Content A\u003C\u002Fcode>, and request two extends that same message to \u003Ccode>Content A + Content B\u003C\u002Fcode>, the old boundary now sits inside a message. The saved prefix is not reused there without an explicit breakpoint. DeepSeek states the same rule with different words. A request of \u003Ccode>A + B\u003C\u002Fcode> followed by \u003Ccode>A + B + C\u003C\u002Fcode> hits the unit \u003Ccode>A + B\u003C\u002Fcode>; a request of \u003Ccode>A + B\u003C\u002Fcode> followed by \u003Ccode>A + C\u003C\u002Fcode> hits nothing, though it does teach the system to persist the shared \u003Ccode>A\u003C\u002Fcode> for a third request.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Falling under the minimum.\u003C\u002Fstrong> On GPT-5.6 and later, 512 tokens cannot be cached at all, because the minimum is 1,024 visible input tokens; the first possible hit is turn two, and it covers the whole 1,024. On Gemini 2 the wait is 2,048 tokens, and on Gemini 3 and 4 it is 4,096. The newest Anthropic models take 512, which makes the example above possible there and nowhere else.\u003C\u002Fp>\n\u003Ch2 id=\"two-breakpoints-that-lie-to-you\">Two breakpoints that lie to you\u003C\u002Fh2>\n\u003Cp>A breakpoint on the block that changes every request writes a fresh entry each time and never reads one. Anthropic&#39;s documentation calls this the common mistake in as many words. The lookup walks backward looking for entries that earlier requests \u003Cem>wrote\u003C\u002Fem>, not for stable content. If nobody wrote an entry at the end of your static block, there is nothing to find behind a timestamp.\u003C\u002Fp>\n\u003Cp>The second trap is distance. Anthropic looks back 20 block positions from a breakpoint; OpenAI checks up to 20 earlier message endings in implicit mode. A turn that appends more than 20 blocks, or a conversation that grows past them, leaves the last written entry outside the window. That is what the second and third breakpoints are for.\u003C\u002Fp>\n\u003Ch2 id=\"seven-settings-that-decide-the-hit-rate\">Seven settings that decide the hit rate\u003C\u002Fh2>\n\u003Col>\n\u003Cli>\u003Cstrong>Order by how often each part changes.\u003C\u002Fstrong> Tool definitions, then instructions, then reference material, then the conversation. Anything that changes per request belongs after everything that does not.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Put the breakpoint at the end of the last stable block.\u003C\u002Fstrong> Not on the last block. On a growing conversation the final block works, because the turn before it wrote there; on a prompt with a varying suffix it does not.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Append, never rewrite.\u003C\u002Fstrong> A summary is a new message at the end, not a smaller version of the transcript at the start. Compaction and truncation reset the prefix from the first changed token.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Keep the tools array byte-identical.\u003C\u002Fstrong> Disable a tool with \u003Ccode>tool_choice: &quot;none&quot;\u003C\u002Fcode> instead of removing its definition, and never put a session id or a snapshot timestamp in a description.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Match the lifetime to the pause.\u003C\u002Fstrong> A handbook reused all day wants the one-hour write at 2x. The conversation tail wants the default, because it expires between sessions anyway.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Send related requests to the same cache.\u003C\u002Fstrong> On OpenAI models before GPT-5.6 that means a stable \u003Ccode>prompt_cache_key\u003C\u002Fcode>, aimed at roughly 15 requests per minute per key. Through a router it means a prefix that does not change, so the routing key does not change with it.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Read the counter.\u003C\u002Fstrong> \u003Ccode>cache_read_input_tokens\u003C\u002Fcode>, \u003Ccode>cached_tokens\u003C\u002Fcode>, \u003Ccode>prompt_cache_hit_tokens\u003C\u002Fcode>, \u003Ccode>cachedContentTokenCount\u003C\u002Fcode>. Each provider reports the tokens that came from cache on that request. A hit rate near zero on a prompt that looks static means the prefix is changing somewhere you have not looked.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"what-caching-does-not-buy\">What caching does not buy\u003C\u002Fh2>\n\u003Cp>Cached input tokens still count toward rate limits on OpenAI, so caching will not rescue a loop that is throttled. A cache entry on Anthropic only becomes available after the first response starts, so parallel requests that all arrive at once cannot help each other. Hidden system content does not count toward the minimum prefix on either provider. Caches are isolated per organization and, on the Claude API, per workspace, so two copies of the same prompt in two workspaces are two writes. And Gemini charges storage for an explicit cache by the token-hour, which is a second meter that a 24-hour cache can run up.\u003C\u002Fp>\n\u003Cfigure class=\"ds-figure not-prose\" data-ds-figure=\"cache-cost-curve\">\n  \u003Cdiv class=\"ds-figure-body\">\n    \u003Cdiv class=\"ds-panel\">\n      \u003Cdiv class=\"ds-panel-head\">\u003Cspan class=\"ds-panel-title\">Forty turns, four lengths\u003C\u002Fspan>\u003C\u002Fdiv>\n      \u003Cdiv class=\"ds-legend\">\n        \u003Cspan>\u003Ci class=\"ds-swatch is-plain\">\u003C\u002Fi>no caching\u003C\u002Fspan>\n        \u003Cspan>\u003Ci class=\"ds-swatch is-cached\">\u003C\u002Fi>stable prefix\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n      \u003Cdiv class=\"ds-scroll\">\n        \u003Cdiv class=\"ds-cols\">\n          \u003Cdiv class=\"ds-col-group\">\n        \u003Cdiv class=\"ds-col-pair\">\n          \u003Cdiv class=\"ds-col\">\n    \u003Cdiv class=\"ds-col-bar is-plain\" style=\"height:3%\">\u003C\u002Fdiv>\n    \u003Cspan class=\"ds-col-value\">$0.24\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n          \u003Cdiv class=\"ds-col\">\n    \u003Cdiv class=\"ds-col-bar is-cached\" style=\"height:2%\">\u003C\u002Fdiv>\n    \u003Cspan class=\"ds-col-value\">$0.09\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cspan class=\"ds-col-label\">10\u003C\u002Fspan>\n      \u003C\u002Fdiv>\u003Cdiv class=\"ds-col-group\">\n        \u003Cdiv class=\"ds-col-pair\">\n          \u003Cdiv class=\"ds-col\">\n    \u003Cdiv class=\"ds-col-bar is-plain\" style=\"height:9%\">\u003C\u002Fdiv>\n    \u003Cspan class=\"ds-col-value\">$0.68\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n          \u003Cdiv class=\"ds-col\">\n    \u003Cdiv class=\"ds-col-bar is-cached\" style=\"height:2%\">\u003C\u002Fdiv>\n    \u003Cspan class=\"ds-col-value\">$0.18\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cspan class=\"ds-col-label\">20\u003C\u002Fspan>\n      \u003C\u002Fdiv>\u003Cdiv class=\"ds-col-group\">\n        \u003Cdiv class=\"ds-col-pair\">\n          \u003Cdiv class=\"ds-col\">\n    \u003Cdiv class=\"ds-col-bar is-plain\" style=\"height:29%\">\u003C\u002Fdiv>\n    \u003Cspan class=\"ds-col-value\">$2.16\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n          \u003Cdiv class=\"ds-col\">\n    \u003Cdiv class=\"ds-col-bar is-cached\" style=\"height:6%\">\u003C\u002Fdiv>\n    \u003Cspan class=\"ds-col-value\">$0.43\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cspan class=\"ds-col-label\">40\u003C\u002Fspan>\n      \u003C\u002Fdiv>\u003Cdiv class=\"ds-col-group\">\n        \u003Cdiv class=\"ds-col-pair\">\n          \u003Cdiv class=\"ds-col\">\n    \u003Cdiv class=\"ds-col-bar is-plain\" style=\"height:100%\">\u003C\u002Fdiv>\n    \u003Cspan class=\"ds-col-value\">$7.52\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n          \u003Cdiv class=\"ds-col\">\n    \u003Cdiv class=\"ds-col-bar is-cached\" style=\"height:15%\">\u003C\u002Fdiv>\n    \u003Cspan class=\"ds-col-value\">$1.16\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cspan class=\"ds-col-label\">80\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cp class=\"ds-axis\">turns in the loop\u003C\u002Fp>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n  \u003Cfigcaption>Same model, same output length, 6,000-token static block and 1,000 tokens appended per turn, at $2 per million input tokens. Doubling the loop multiplies the uncached bill by about three; caching divides that bill by five and leaves the growth alone.\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\u003Cp>None of this changes the shape of the cost curve. Caching divides the bill by five or so and leaves the growth alone, because the transcript still grows and you still resend it every turn. On the forty-turn loop priced in the previous article, caching moved $2.16 to $0.43 and both numbers kept climbing with the square of the turn count.\u003C\u002Fp>\n\u003Cp>Order the prompt by how often each part changes, put the breakpoint at the end of the part that does not, and measure the counter on the next request. If the hit rate is near zero and the prompt looks static, the prefix is changing in the tools array, the first line of the system prompt, or somewhere above the block you meant to cache.\u003C\u002Fp>\n","\u003Cul>\n\u003Cli>\u003Cstrong>A cache entry is a hash of a prefix, written at a boundary you marked.\u003C\u002Fstrong> It never grows by itself: the longer request only reuses the part an earlier request already wrote.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>The 512-to-1,024 case has one answer.\u003C\u002Fstrong> The first 512 tokens come back at the read price, the new 512 are charged in full, and they become reusable only from the next request.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>The minimum length decides whether you get a hit at all.\u003C\u002Fstrong> 512 tokens is cacheable on the newest Anthropic models, 1,024 on OpenAI&#39;s GPT-5.6 and later, and 2,048 to 6,144 on Gemini depending on the model.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Where the breakpoint sits is the whole game.\u003C\u002Fstrong> On the block that changes every request, every write is wasted; at the end of the stable block, a single write survives the whole conversation.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Measure the reported counters, not the feature flag.\u003C\u002Fstrong> Every provider tells you how many tokens came from cache on that request, and that number is the only evidence that caching is working.\u003C\u002Fli>\n\u003C\u002Ful>\n",[310,332,352,369,384,389],{"id":311,"title":312,"title_id":313,"slug":314,"excerpt":315,"excerpt_id":316,"has_translation":12,"published_at":317,"reading_time":100,"hidden_from_listing":15,"tags":318,"thumbnail_image":21,"og_image":323,"series":324,"authors":329},48,"What a Linked List Actually Is","Tentang Struktur Data LinkedList","tentang-struktur-data-linked-list","A linked list is a linear chain of nodes, each holding data and a pointer to the next node. Here is what that chain looks like, what it costs next to an array, and the same minimal implementation in Java, Python, and JavaScript.","Linked list adalah rantai linear berisi node, dan setiap node menyimpan data plus pointer ke node berikutnya. Ini bentuk rantainya, ongkosnya dibanding array, dan implementasi paling sederhana dalam Java, Python, serta JavaScript.","2026-11-21T01:00:00.000Z",[319,320,321,322],"struktur-data","linked-list","pointer","konsep","\u002Fog\u002Ftentang-struktur-data-linked-list.png",{"id":325,"title":326,"title_id":327,"slug":328},28,"Linear Data Structures","Struktur Data Linear","asd-struktur-linear",[330],{"authors_id":331},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":333,"title":334,"title_id":335,"slug":336,"excerpt":337,"excerpt_id":338,"has_translation":12,"published_at":339,"reading_time":193,"hidden_from_listing":15,"tags":340,"thumbnail_image":21,"og_image":343,"series":344,"authors":349},47,"Why Understanding Algorithms and Data Structures Actually Matters","Pentingnya Memahami Algoritma dan Struktur Data","pentingnya-memahami-algoritma-dan-struktur-data","An algorithm is an ordered list of steps, and that order decides how expensive a problem is. The examples are on the back of an instant noodle packet, in a factorial, and behind ctrl+z.","Sebagai konsep fundamental, algoritma menentukan seberapa mahal sebuah masalah diselesaikan. Contohnya ada di bungkus mie instan, di bilangan faktorial, dan di tombol undo.","2026-11-19T01:00:00.000Z",[341,319,322,342],"algoritma","dasar","\u002Fog\u002Fpentingnya-memahami-algoritma-dan-struktur-data.png",{"id":345,"title":346,"title_id":347,"slug":348},27,"About Algorithms and Data Structures","Tentang Algoritma dan Struktur Data","asd-intro",[350],{"authors_id":351},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":353,"title":354,"title_id":355,"slug":356,"excerpt":357,"excerpt_id":358,"has_translation":12,"published_at":359,"reading_time":14,"hidden_from_listing":15,"tags":360,"thumbnail_image":21,"og_image":364,"series":365,"authors":366},108,"At 11 a.m. the AI Quota Runs Out, and the Work Stops There","Jam 11 siang kuota AI-nya habis, dan kerjaan berhenti di situ","jam-11-kuota-ai-habis-kerjaan-berhenti","The panel says the usage limit is reached and the work stops, not because it is finished. Three guesses explain it, and all three are wrong: the limit is shaped like a cliff, and the day is built on top of one tool.","Panel di layar bilang batas pemakaian tercapai, dan kerja berhenti di situ. Bukan karena selesai. Ada tiga tebakan kenapa ini terjadi, dan ketiganya salah: batasnya berbentuk tebing, dan harinya dibangun di atas satu alat.","2026-11-12T01:00:00.000Z",[195,361,362,363],"tooling","rate-limit","produktivitas","\u002Fog\u002Fjam-11-kuota-ai-habis-kerjaan-berhenti.png",{"id":24,"title":25,"title_id":26,"slug":27},[367],{"authors_id":368},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":370,"title":371,"title_id":372,"slug":373,"excerpt":374,"excerpt_id":375,"has_translation":12,"published_at":376,"reading_time":127,"hidden_from_listing":15,"tags":377,"thumbnail_image":21,"og_image":378,"series":379,"authors":381},141,"A 6,000-Token Handbook and a 0% Cache Hit Rate","Buku Panduan 6.000 Token dan Hit Rate Cache 0%","cache-hit-rate-zero","Twenty-four turns, caching switched on, and a bill 21% higher than running with no cache at all. A support agent whose prompt looks static, and the three lines that quietly change it every turn.","Dua puluh empat giliran, caching menyala, dan tagihan 21% lebih tinggi daripada menjalankannya tanpa cache sama sekali. Agen support yang prompt-nya kelihatan statis, dan tiga baris yang diam-diam mengubahnya tiap giliran.","2026-11-08T01:20:00.000Z",[70,83,85,86],"\u002Fog\u002Fcache-hit-rate-zero.png",{"id":380,"title":69,"title_id":69,"slug":70},29,[382],{"authors_id":383},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":74,"title":75,"title_id":76,"slug":77,"excerpt":78,"excerpt_id":79,"has_translation":12,"published_at":80,"reading_time":81,"hidden_from_listing":15,"tags":82,"thumbnail_image":21,"og_image":87,"series":385,"authors":386},{"id":89,"title":35,"title_id":36,"slug":37},[387],{"authors_id":388},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},{"id":390,"title":391,"title_id":392,"slug":393,"excerpt":394,"excerpt_id":395,"has_translation":12,"published_at":396,"reading_time":14,"hidden_from_listing":15,"tags":397,"thumbnail_image":21,"og_image":400,"series":401,"authors":406},138,"Python venv: the Install That Went Into the Wrong Python","venv Python: Instalasi yang Masuk ke Python yang Salah","python-venv-pip","`pip install` printed Successfully installed, and the next run raised ModuleNotFoundError on the same machine. Two interpreters, one install, and the variable your script checks is the one the interpreter never sets.","`pip install` mencetak Successfully installed, lalu jalan berikutnya melempar ModuleNotFoundError di mesin yang sama. Dua interpreter, satu instalasi, dan variabel yang skripmu periksa justru tidak pernah dipasang oleh interpreter.","2026-11-07T12:00:00.000Z",[181,398,399],"venv","pip","\u002Fog\u002Fpython-venv-pip.png",{"id":402,"title":403,"title_id":404,"slug":405},18,"Working with Python Scripts","Menggunakan Script Python","python-pemrograman-dengan-script",[407],{"authors_id":408},{"id":93,"name":123,"slug":124,"role":125,"profile_picture":21},[410,413,416,419,422],{"text":411,"depth":110,"id":412},"Four rulebooks, one mechanism","four-rulebooks-one-mechanism",{"text":414,"depth":110,"id":415},"The 512-token question","the-512-token-question",{"text":417,"depth":110,"id":418},"Two breakpoints that lie to you","two-breakpoints-that-lie-to-you",{"text":420,"depth":110,"id":421},"Seven settings that decide the hit rate","seven-settings-that-decide-the-hit-rate",{"text":423,"depth":110,"id":424},"What caching does not buy","what-caching-does-not-buy",1794150713262]