DEV Community

Cover image for The Explicit Cache Was Cheaper on Paper. Then I Changed One Question.
张洲诚(Zack.ZHANG)
张洲诚(Zack.ZHANG)

Posted on

The Explicit Cache Was Cheaper on Paper. Then I Changed One Question.

Building a Knowledge Base from Scratch, EP07. The paradigm arc continues, and the bill from EP06 finally gets audited.

Where EP06 left off

I ended EP06 with a confession. The "compile once, break even after about 6 questions" math only works if the cache keeps hitting. When it doesn't, the compiled side costs 44% more per question than retrieval. And I had no idea when the cache expired. That's entirely up to the server.

There was a loose thread too. Bailian offers an explicit cache. You mark the knowledge-base prefix yourself, pay ¥15 per million tokens to create it, ¥1 on a hit. On paper that hit price beats the implicit one, ¥1.5. But the rules are completely different, and I wanted to know how.

So this episode runs both caches against each other. Same assets as EP06: the compiled artifact (a 7,929-token prompt prefix), qwen3.8-max, the same system message, the same four questions. Eleven paid calls, ¥0.93 total.

The headline finding first: the explicit cache doesn't hit the way I assumed. It doesn't match on prefix. That changes the final answer.

Two caching mechanisms side by side: implicit caching automatically matches the shared prefix of the message with no configuration and bills hits at 1.5 yuan per million tokens; explicit caching requires a manual cache_control marker on the knowledge block, pays 15 yuan per million to create and 1 yuan on a hit

The price table, including a TTL hiding in a type name

bl model list --model qwen3.8-max needs no authentication. Prices pulled on the spot:

Tier Price Relative to input
Input ¥12 / M 100%
Output ¥36 / M n/a
Implicit hit ¥1.5 / M 12.5%
Explicit creation ¥15 / M 125%
Explicit hit ¥1 / M 8.33%

One detail deserves its own paragraph. The billing type name for explicit creation is input_token_cache_creation_5m. The 5-minute TTL is right there in the field name. Didn't have to guess from documentation; the price list confessed on its own.

That 5 minutes comes back repeatedly. It's one of the explicit cache's structural weaknesses.

Was last episode's cache still alive

The first test group answers EP06's open question: how long does the implicit cache actually live?

Method: rerun the exact message assembly from EP06. Same artifact as the user message, question appended, four different questions, controlled intervals.

Call Time Since last hit Hit tokens Cost
A1 13:25:39 two days 0 ¥0.1075
A2 13:25:59 20 seconds 7,168 ¥0.0345
A3 13:35:31 9m 32s 7,168 ¥0.0285
A4 13:49:30 13m 59s 7,168 ¥0.0419

Three findings.

After two days, the cache was gone. A1's 7,929 input tokens billed at full price. EP06's warning about low-frequency usage getting a cold start every time now has its own data.

Twenty seconds apart, it hit, and the hit count, 7,168, matches EP06's five calls digit for digit. The two episodes' data line up.

A3 and A4 were the surprise. A hit 9.5 minutes later, another 14 minutes after that. By A4, the cache had survived 23.5 minutes since first being hit. The implicit cache lives at least 14 minutes, dies within two days, and the exact boundary in between is opaque. Far longer than my intuition. I'd assumed an automatic cache would last minutes at best.

The CLI has no cache command, but it has one pathway

I grepped bailian-cli's entire reference documentation for "cache" and "缓存". Zero hits. No dedicated cache command.

But the official API's explicit cache works through structure markers inside the messages array, and bl text chat has a --messages-file flag that forwards whatever structure you give it. The pathway already exists. Pass it through.

The implicit group is the old shape, user as a single string:

[
 {"role": "system", "content": "You are the customer service assistant for Nuannu Home Furnishing."},
 {"role": "user", "content": "(full compiled artifact)\n\n---\nThe above is the company knowledge base. User question: How do I submit reimbursement invoices?"}
]
Enter fullscreen mode Exit fullscreen mode

The explicit group splits user into an array and hangs cache_control on the knowledge-base block:

[
 {"role": "system", "content": "You are the customer service assistant for Nuannu Home Furnishing."},
 {"role": "user", "content": [
   {"type": "text", "text": "(full compiled artifact)\n\n---\nThe above is the company knowledge base.",
    "cache_control": {"type": "ephemeral"}},
   {"type": "text", "text": "User question: How do I submit reimbursement invoices?"}
 ]}
]
Enter fullscreen mode Exit fullscreen mode

Both expand to identical text, same model, same parameters. The only difference is the marker. Single variable.

Whether the pass-through worked can't be left to vibes. I fixed the criterion in advance: the explicit group's usage must contain fields the implicit group never showed. A change in cached_tokens alone doesn't count; that could just be an implicit hit.

B0, the explicit group's first call, returned:

"prompt_tokens_details": {
  "cache_creation": {"ephemeral_5m_input_tokens": 7923},
  "cache_creation_input_tokens": 7923,
  "cache_type": "ephemeral",
  "cached_tokens": 0
}
Enter fullscreen mode Exit fullscreen mode

Three new fields, all present. ephemeral_5m_input_tokens even matches the billing type name from the price list. Pass-through confirmed: 7,923 tokens went through explicit creation. The answer itself was normal, 291 characters, correctly based on the 2026 reimbursement policy.

And a bonus observation running the other direction: B0 came 31 seconds after A4, which had just hit the implicit cache for 7,168. Yet B0's cached_tokens was 0. Hang the marker, and you're no longer an implicit player.

Change the question, pay for creation again

Next came what I expected to be the main event: B0 creates, B1 asks a different question immediately after, hits the knowledge prefix, pays the ¥1 hit price.

That's not what happened.

Call Question Interval Created Hit Cost
B0 q1 reimbursement n/a 7,923 0 ¥0.1305
B1 q2 free shipping 12 seconds 7,929 0 ¥0.1365
B2 q4 Xinjiang sofa 15 seconds 7,936 0 ¥0.1418

B1 came 12 seconds after B0, sharing a 7,900-token common prefix. No hit. Full-price creation all over again.

If the explicit cache matched on prefix, B1 would have hit the knowledge block B0 created. It didn't. For "one fixed knowledge prefix plus ever-changing questions," the most standard shape of knowledge-base Q&A, the explicit cache pays the 125% creation price every single time.

How hits are actually judged: a nine-second controlled experiment

Three steps, each 9 seconds apart.

B3: byte-identical messages to B0, sent 8 minutes later. B0's cache was created at 13:50:01. The official TTL is 5 minutes. B3 went out at 13:58:14, 8m 13s after creation. Result: re-created 7,923, no hit. The 5-minute TTL is real. When it expires, it's gone, and the 125% creation premium evaporates with it.

B4: byte-identical to B3 (same question), sent 9 seconds later. Hit. 7,923 tokens, zero creation, ¥0.0202 total. The cheapest single call of the entire episode, 29% below even the implicit group's best.

B5: differs from B4 only in the question text, sent 9 seconds later. No hit. Re-created 7,926.

Hit determination compared: implicit caching hits whenever the knowledge-base prefix matches, regardless of the question; explicit caching only hits when the entire request is byte-identical, changing the question fails, resending the identical request succeeds

Five data points, all pointing one way:

Bailian's explicit cache judges hits by "entire request identical," not "common prefix." The whole messages array must match byte for byte. Differ by one question and you miss.

This is a different semantic from Anthropic's prefix caching, where everything before the marker counts as long as the prefix matches. The standard pattern there is exactly "fixed knowledge base, varying questions." Port that intuition over and every question pays 125%, six times the implicit steady state.

The explicit cache's niche narrows to one thing: repeating an identical request. Multiple agents running the same prompt in parallel, batch retries, evaluation-set reruns. Outside those, it can't help.

Two channels, two separate ledgers

The experiment matrix planted a cross-check: after explicit caches had been created, send an unmarked request in the implicit style and see whether the two channels connect.

D1 ran 9 seconds after B0 and B1, two explicit creations. Implicit assembly, question q3. It hit, for 7,168, the implicit count, not the 7,923 that had just been explicitly created.

That number is itself the evidence. If the channels shared cache, D1 would have picked up the 7,923 entry, which covers more of the prompt. What it actually got was the implicit ledger's 1,024-aligned 7,168.

Two parallel ledgers separated by a brick wall labeled mutually non-sharing: the implicit ledger on the left in blue with 7168 tokens, the explicit ledger on the right in orange with 7923 tokens, unmarked requests routed left and marked requests routed right

Add B0's finding that marked requests can't ride the implicit cache, and all four directions are tested: explicit doesn't eat implicit, implicit doesn't eat explicit, explicit creation doesn't break the implicit chain, and the two channels keep entirely separate books.

Practical corollary: running explicit-cache experiments leaves the implicit cache untouched. The two workloads can coexist without interfering.

The quirk in implicit hits: 1,024-token alignment

One more pattern in the A-group data, easy to miss at first.

A2, A3, A4 and D1 all hit for exactly 7,168. Their prompt totals ranged from 7,929 to 7,942 depending on question length. The hit count never moved.

7,168 = 7 × 1,024. The implicit cache stores in 1,024-token blocks, and the tail of the common prefix that doesn't fill a block never enters the cache. Every call leaves about 767 tokens billed at full price, forever ineligible for the discount.

Priced out properly, the implicit discount isn't as deep as advertised. The effective input rate is ¥2.515 per million, 21% of list, not the headline 12.5%. That percentage only holds for the tokens that actually make it into cache.

The explicit cache has no such truncation. Its created and hit counts are 7,923, covering 99.9% of the prompt. Explicit hits are not only cheaper per token (¥1 vs ¥1.5), they cover more of the prompt. A genuine hidden advantage, locked away behind "change the question and you miss."

The cost account: when does explicit pay off

Four steady states side by side, effective input rates:

Steady state Effective input price Relative Source
Full-price cold start ¥12.00 / M 100% A1
Implicit steady state ¥2.515 / M 21.0% A2
Explicit creation ¥15.00 / M 125% B0-B5
Explicit hit ¥1.008 / M 8.4% B4

First, the standard shape of knowledge-base Q&A: one prefix, five different questions in a row. Implicit total ¥0.1750, explicit total ¥0.5946. That's 3.4×, and per-question steady state is worse: 0.1189 ÷ 0.0200 = 5.96×. In this scenario explicit is pure downside.

Now explicit's sweet spot: the same messages resent verbatim, within 5 minutes.

Repeats Implicit total Explicit total Gap
3 ¥0.1349 ¥0.1349 break even
5 ¥0.1747 ¥0.1509 explicit saves 13.6%
10 ¥0.2741 ¥0.1909 explicit saves 30.4%

A bar chart of four input-side costs per million tokens: explicit creation at 15 yuan is the tallest red bar, full price at 12 yuan is gray, implicit steady state at 2.5 yuan is a short blue bar, and explicit hit at 1 yuan is the shortest green bar

Ask the identical question 3 times within 5 minutes and explicit starts winning; the more repeats, the more it saves, up to 60% at the limit. Change the question even once and it snaps back to 6× more expensive per call. And the TTL doesn't forgive: if no hit comes within 5 minutes, the 125% creation premium evaporates entirely.

Wrapping up: how to place this compiled artifact

Eleven calls done, and EP06's question gets a straight answer.

Your workload How to place it
Customer-service Q&A: fixed prefix, ever-changing questions Implicit. Configure nothing.
Identical-request reruns: eval sets, batch retries, parallel agents on one prompt Explicit marker, breaks even at call 3
Low-frequency, a few queries a day Neither cache saves you; consider going back to retrieval
Getting more implicit hits Control the cadence: follow-ups within 14 minutes land

My own artifact lands in row one: configure nothing, rely on implicit. Customer-service questions always vary; the explicit 125% premium can't be dodged there. The one thing worth doing is pacing: keep questions inside the expiry boundary so that 7,168-token prefix stays warm.

A note for later: when batch evaluations or multi-agent runs show up, come back and hang the marker. That's the one arena where explicit wins.

What's next

One problem in the paradigms arc is still untouched. Everything through EP06 shared a default: everyone can read the whole library. Real companies don't work that way. Reimbursement policies are for finance and travelers; customer data belongs to the service team only. Compilation kneads the whole library into one artifact, and permissions are the first gate it has to survive. Next episode: cut the same library into two permission views and see what compiled artifacts, retrieval-side metadata filtering, and agent prompts can each defend.


Everything here was measured with Model Studio's CLI (bl). Full usage figures, reasoning traces and compiled artifacts from all 11 calls are archived in the project repo. Command signatures may shift between versions, so check the official docs. API keys are free to obtain, with a free tier for new accounts.

Top comments (0)