<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 张洲诚（Zack.ZHANG）</title>
    <description>The latest articles on DEV Community by 张洲诚（Zack.ZHANG） (@zackzhang).</description>
    <link>https://dev.to/zackzhang</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4009090%2F3dfae9a2-cf7c-4d8e-90fa-7dba1333ea72.jpg</url>
      <title>DEV Community: 张洲诚（Zack.ZHANG）</title>
      <link>https://dev.to/zackzhang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zackzhang"/>
    <language>en</language>
    <item>
      <title>The Explicit Cache Was Cheaper on Paper. Then I Changed One Question.</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Fri, 04 Sep 2026 10:23:09 +0000</pubDate>
      <link>https://dev.to/zackzhang/the-explicit-cache-was-cheaper-on-paper-then-i-changed-one-question-1mhj</link>
      <guid>https://dev.to/zackzhang/the-explicit-cache-was-cheaper-on-paper-then-i-changed-one-question-1mhj</guid>
      <description>&lt;p&gt;&lt;em&gt;Building a Knowledge Base from Scratch, EP07. The paradigm arc continues, and the bill from EP06 finally gets audited.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where EP06 left off
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The headline finding first: &lt;strong&gt;the explicit cache doesn't hit the way I assumed. It doesn't match on prefix.&lt;/strong&gt; That changes the final answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2n2g1x6qe436274j7aj2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2n2g1x6qe436274j7aj2.jpg" alt="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" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The price table, including a TTL hiding in a type name
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bl model list --model qwen3.8-max&lt;/code&gt; needs no authentication. Prices pulled on the spot:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Relative to input&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;¥12 / M&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;¥36 / M&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implicit hit&lt;/td&gt;
&lt;td&gt;¥1.5 / M&lt;/td&gt;
&lt;td&gt;12.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explicit creation&lt;/td&gt;
&lt;td&gt;¥15 / M&lt;/td&gt;
&lt;td&gt;125%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explicit hit&lt;/td&gt;
&lt;td&gt;¥1 / M&lt;/td&gt;
&lt;td&gt;8.33%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One detail deserves its own paragraph. The billing type name for explicit creation is &lt;code&gt;input_token_cache_creation_5m&lt;/code&gt;. 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.&lt;/p&gt;

&lt;p&gt;That 5 minutes comes back repeatedly. It's one of the explicit cache's structural weaknesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was last episode's cache still alive
&lt;/h2&gt;

&lt;p&gt;The first test group answers EP06's open question: how long does the implicit cache actually live?&lt;/p&gt;

&lt;p&gt;Method: rerun the exact message assembly from EP06. Same artifact as the user message, question appended, four different questions, controlled intervals.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Call&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Since last hit&lt;/th&gt;
&lt;th&gt;Hit tokens&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A1&lt;/td&gt;
&lt;td&gt;13:25:39&lt;/td&gt;
&lt;td&gt;two days&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;¥0.1075&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A2&lt;/td&gt;
&lt;td&gt;13:25:59&lt;/td&gt;
&lt;td&gt;20 seconds&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,168&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;¥0.0345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A3&lt;/td&gt;
&lt;td&gt;13:35:31&lt;/td&gt;
&lt;td&gt;9m 32s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,168&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;¥0.0285&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A4&lt;/td&gt;
&lt;td&gt;13:49:30&lt;/td&gt;
&lt;td&gt;13m 59s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,168&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;¥0.0419&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three findings.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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. &lt;strong&gt;The implicit cache lives at least 14 minutes, dies within two days, and the exact boundary in between is opaque.&lt;/strong&gt; Far longer than my intuition. I'd assumed an automatic cache would last minutes at best.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CLI has no cache command, but it has one pathway
&lt;/h2&gt;

&lt;p&gt;I grepped bailian-cli's entire reference documentation for "cache" and "缓存". Zero hits. No dedicated cache command.&lt;/p&gt;

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

&lt;p&gt;The implicit group is the old shape, user as a single string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are the customer service assistant for Nuannu Home Furnishing."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(full compiled artifact)&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;The above is the company knowledge base. User question: How do I submit reimbursement invoices?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The explicit group splits user into an array and hangs &lt;code&gt;cache_control&lt;/code&gt; on the knowledge-base block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are the customer service assistant for Nuannu Home Furnishing."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(full compiled artifact)&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;The above is the company knowledge base."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cache_control"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ephemeral"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User question: How do I submit reimbursement invoices?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both expand to identical text, same model, same parameters. The only difference is the marker. Single variable.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;cached_tokens&lt;/code&gt; alone doesn't count; that could just be an implicit hit.&lt;/p&gt;

&lt;p&gt;B0, the explicit group's first call, returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"prompt_tokens_details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cache_creation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"ephemeral_5m_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7923&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cache_creation_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7923&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cache_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ephemeral"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cached_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three new fields, all present. &lt;code&gt;ephemeral_5m_input_tokens&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;cached_tokens&lt;/code&gt; was 0. &lt;strong&gt;Hang the marker, and you're no longer an implicit player.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Change the question, pay for creation again
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;That's not what happened.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Call&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Interval&lt;/th&gt;
&lt;th&gt;Created&lt;/th&gt;
&lt;th&gt;Hit&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;B0&lt;/td&gt;
&lt;td&gt;q1 reimbursement&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;7,923&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;¥0.1305&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B1&lt;/td&gt;
&lt;td&gt;q2 free shipping&lt;/td&gt;
&lt;td&gt;12 seconds&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,929&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;¥0.1365&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B2&lt;/td&gt;
&lt;td&gt;q4 Xinjiang sofa&lt;/td&gt;
&lt;td&gt;15 seconds&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,936&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;¥0.1418&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

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

&lt;h2&gt;
  
  
  How hits are actually judged: a nine-second controlled experiment
&lt;/h2&gt;

&lt;p&gt;Three steps, each 9 seconds apart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B3: byte-identical messages to B0, sent 8 minutes later.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B4: byte-identical to B3 (same question), sent 9 seconds later.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B5: differs from B4 only in the question text, sent 9 seconds later.&lt;/strong&gt; No hit. Re-created 7,926.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwtk424wq1vag0136l7cu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwtk424wq1vag0136l7cu.jpg" alt="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" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Five data points, all pointing one way:&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Two channels, two separate ledgers
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2em5l2gn1la9l9u03c3o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2em5l2gn1la9l9u03c3o.jpg" alt="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" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Practical corollary: running explicit-cache experiments leaves the implicit cache untouched. The two workloads can coexist without interfering.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quirk in implicit hits: 1,024-token alignment
&lt;/h2&gt;

&lt;p&gt;One more pattern in the A-group data, easy to miss at first.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;7,168 = 7 × 1,024. &lt;strong&gt;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.&lt;/strong&gt; Every call leaves about 767 tokens billed at full price, forever ineligible for the discount.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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."&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost account: when does explicit pay off
&lt;/h2&gt;

&lt;p&gt;Four steady states side by side, effective input rates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Steady state&lt;/th&gt;
&lt;th&gt;Effective input price&lt;/th&gt;
&lt;th&gt;Relative&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full-price cold start&lt;/td&gt;
&lt;td&gt;¥12.00 / M&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;A1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implicit steady state&lt;/td&gt;
&lt;td&gt;¥2.515 / M&lt;/td&gt;
&lt;td&gt;21.0%&lt;/td&gt;
&lt;td&gt;A2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explicit creation&lt;/td&gt;
&lt;td&gt;¥15.00 / M&lt;/td&gt;
&lt;td&gt;125%&lt;/td&gt;
&lt;td&gt;B0-B5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explicit hit&lt;/td&gt;
&lt;td&gt;¥1.008 / M&lt;/td&gt;
&lt;td&gt;8.4%&lt;/td&gt;
&lt;td&gt;B4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;Now explicit's sweet spot: the same messages resent verbatim, within 5 minutes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repeats&lt;/th&gt;
&lt;th&gt;Implicit total&lt;/th&gt;
&lt;th&gt;Explicit total&lt;/th&gt;
&lt;th&gt;Gap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;¥0.1349&lt;/td&gt;
&lt;td&gt;¥0.1349&lt;/td&gt;
&lt;td&gt;break even&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;¥0.1747&lt;/td&gt;
&lt;td&gt;¥0.1509&lt;/td&gt;
&lt;td&gt;explicit saves 13.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;¥0.2741&lt;/td&gt;
&lt;td&gt;¥0.1909&lt;/td&gt;
&lt;td&gt;explicit saves 30.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkzal6ua13fcv47eut5dk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkzal6ua13fcv47eut5dk.jpg" alt="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" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;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.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up: how to place this compiled artifact
&lt;/h2&gt;

&lt;p&gt;Eleven calls done, and EP06's question gets a straight answer.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Everything here was measured with Model Studio's CLI (&lt;code&gt;bl&lt;/code&gt;). 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 &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;. API keys are &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;free to obtain&lt;/a&gt;, with a free tier for new accounts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>caching</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>It Found a Contradiction I Didn't Plant. Then It Answered as if Nothing Was Wrong.</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Thu, 03 Sep 2026 10:20:21 +0000</pubDate>
      <link>https://dev.to/zackzhang/it-found-a-contradiction-i-didnt-plant-then-it-answered-as-if-nothing-was-wrong-47gk</link>
      <guid>https://dev.to/zackzhang/it-found-a-contradiction-i-didnt-plant-then-it-answered-as-if-nothing-was-wrong-47gk</guid>
      <description>&lt;p&gt;&lt;em&gt;Building a Knowledge Base from Scratch, EP06. The paradigm arc starts here.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where EP05 left off
&lt;/h2&gt;

&lt;p&gt;My library holds two shipping documents. One says free shipping over ¥59, the other says over ¥99. Nothing anywhere says which is current. Both came back in the same retrieval call, the ¥59 one actually scoring higher at 0.6557 to 0.5755, and the agent quietly picked one. It never mentioned a second number existed.&lt;/p&gt;

&lt;p&gt;Version conflicts it handled fine, because the 2026 expense policy contains the sentence "the 2023 edition is hereby superseded." Metadata sitting in the corpus, readable by anyone with context. The threshold documents carry nothing, so there was nothing to read.&lt;/p&gt;

&lt;p&gt;Five episodes of retrieval hit that wall. This episode changes the paradigm instead of the plumbing: have the model read the entire library once, write it up as structured knowledge pages, then answer from those pages with no retrieval at all.&lt;/p&gt;

&lt;p&gt;Karpathy's LLM Wiki gist frames the two as interpreter versus compiler. RAG interprets your library at question time, every time. LLM Wiki compiles once and then executes. Three directories (&lt;code&gt;raw/&lt;/code&gt; read-only sources, &lt;code&gt;wiki/&lt;/code&gt; pages with &lt;code&gt;[[wikilink]]&lt;/code&gt; cross-references, &lt;code&gt;schema/&lt;/code&gt; conventions), three operations (Ingest, Query, Lint).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqrjwzmcp4wxvyehefg3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqrjwzmcp4wxvyehefg3.jpg" alt="Two paradigms side by side: on the left, runtime retrieval sends every question through chunk retrieval so the model only ever sees top-k fragments; on the right, knowledge compilation reads the whole library once into wiki pages, and later questions read the compiled artifact directly with no retrieval step" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing to flag before anything else. That gist carries its own disclaimer: "This document is intentionally abstract. It describes the idea, not a specific implementation." So the secondhand write-ups claiming "Karpathy's research shows accuracy falls off a cliff past a certain document count" are straightforwardly false. There is no experiment in that document. Somebody attached fabricated numbers to a design note and the numbers spread.&lt;/p&gt;

&lt;p&gt;This episode runs the idea against a real library: &lt;code&gt;zj0knmrbye&lt;/code&gt; from EP02, eight documents, 27 chunks, with the dirty data I planted in EP03 still in place. Not a single character changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The command surface
&lt;/h2&gt;

&lt;p&gt;One entry point, &lt;code&gt;bl text chat&lt;/code&gt; with &lt;code&gt;--messages-file&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl text chat &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.8-max &lt;span class="nt"&gt;--messages-file&lt;/span&gt; messages.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-tokens&lt;/span&gt; 8000 &lt;span class="nt"&gt;--enable-thinking&lt;/span&gt; &lt;span class="nt"&gt;--thinking-budget&lt;/span&gt; 3000 &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;messages.json&lt;/code&gt; is a plain messages array, one system turn and one user turn carrying the full text of all eight documents merged, 8,811 characters.&lt;/p&gt;

&lt;p&gt;Two practical notes if you're on Windows. Put non-ASCII system prompts inside the messages file, never on the command line, because PowerShell mangles the encoding on the way through. And write results to a UTF-8 file instead of printing them: one emoji in the output is enough to make &lt;code&gt;print&lt;/code&gt; raise &lt;code&gt;UnicodeEncodeError&lt;/code&gt; against the console's GBK codec.&lt;/p&gt;

&lt;p&gt;Thinking stays on deliberately. The EP05 agent app runs with thinking enabled, so compiling with it off would have made the comparison meaningless.&lt;/p&gt;

&lt;p&gt;The messy spreadsheet got the least dignified treatment available. &lt;code&gt;openpyxl&lt;/code&gt; dumps every cell to text, and the empty columns left by merged cells, the misaligned headers and the notes stranded on a second worksheet all survive verbatim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shipping zone |  | Lead time and fee
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why I ran two nearly identical compiles
&lt;/h2&gt;

&lt;p&gt;Karpathy's Ingest step includes an instruction to "flag contradictions between new and existing content." If I copy that in and the model duly flags the ¥59/¥99 conflict, the result tells me nothing. I can't separate "giving the model global visibility worked" from "telling the model to hunt for contradictions worked." The EP05 agent's prompt contained nothing like that instruction.&lt;/p&gt;

&lt;p&gt;So, two runs differing by one line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A1a, neutral.&lt;/strong&gt; Build topic pages, merge same-topic content, produce an &lt;code&gt;index.md&lt;/code&gt;, cross-link with &lt;code&gt;[[page name]]&lt;/code&gt;, cite the source document for every fact, add nothing the documents don't state. The words "contradiction," "conflict," "59" and "99" appear nowhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A1b, Karpathy's version.&lt;/strong&gt; Same text plus one line: "5. While ingesting, flag contradictions between new and existing content."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reading rule fixed in advance: if A1a finds the conflict, credit goes to global visibility. If only A1b finds it, credit goes to the prompt and the conclusion shrinks accordingly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxnbj70cn9hxq6khnqpof.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxnbj70cn9hxq6khnqpof.jpg" alt="The single-variable design: both system prompts share the same four instructions, and A1b adds only a fifth line, flag contradictions between new and existing content. Both runs receive the identical 8,811-character full-library corpus, the same model and the same parameters. The decision gate has two branches: if the neutral A1a run finds the conflict, credit goes to global visibility and the conclusion holds; if only A1b finds it, credit goes to the prompt and the conclusion must be narrowed" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Measured usage from &lt;code&gt;--output json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Run&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;of which reasoning&lt;/th&gt;
&lt;th&gt;Wall time&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A1a neutral&lt;/td&gt;
&lt;td&gt;5,905&lt;/td&gt;
&lt;td&gt;9,594&lt;/td&gt;
&lt;td&gt;1,725&lt;/td&gt;
&lt;td&gt;179.7s&lt;/td&gt;
&lt;td&gt;¥0.4162 (~$0.059)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A1b Karpathy&lt;/td&gt;
&lt;td&gt;5,918&lt;/td&gt;
&lt;td&gt;8,757&lt;/td&gt;
&lt;td&gt;2,100&lt;/td&gt;
&lt;td&gt;175.8s&lt;/td&gt;
&lt;td&gt;¥0.3863 (~$0.054)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pricing from &lt;code&gt;bl model list --model qwen3.8-max&lt;/code&gt;, which needs no authentication: ¥12 per million input, ¥36 per million output. USD figures use 7.1 CNY/USD on the run date and are there for scale, not accounting. &lt;code&gt;finish_reason&lt;/code&gt; was &lt;code&gt;stop&lt;/code&gt; on both, so the coverage gaps below are choices, not truncation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 1: the neutral run built a conflict table on its own
&lt;/h2&gt;

&lt;p&gt;A1a produced a section I never asked for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Free shipping threshold and fees

&amp;gt; ⚠️ Document conflict: 01-shipping-and-logistics and 13-shipping-fee-table-clean
&amp;gt;   disagree on the free shipping threshold and the fee amount.
&amp;gt;   Treat the amount shown at checkout as authoritative.

| Item | 01-shipping-and-logistics | 13-clean / 14-messy |
| Standard zone threshold | ¥59 | ¥99 |
| Fee below threshold | ¥10 | ¥8 |
| Remote zone threshold | free over ¥129 | excluded from promo, ¥15 surcharge |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same two documents as last episode, same conflict, and the retrieval side picked one silently. Nobody told this run to look for contradictions. It put both versions side by side and volunteered a resolution, defer to checkout, that it lifted from a throwaway line buried in the messy spreadsheet.&lt;/p&gt;

&lt;p&gt;By the rule I fixed in advance, the conclusion stands: what did the work was global visibility, not the instruction.&lt;/p&gt;

&lt;p&gt;A1b flagged more explicitly, three conflicts under a &lt;code&gt;🔴&lt;/code&gt; marker with source columns. It also declined a fake one: document 02 gives a one-year appliance warranty, document 15 lists warranty periods for wood furniture, hardware and mattresses and says nothing about appliances. Its note reads "different product categories; no direct conflict, but the information is complementary." Refusing to pad the count tells you more than an extra flag would.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 2: the second conflict wasn't on my list
&lt;/h2&gt;

&lt;p&gt;When I seeded dirty data in EP03 I designed exactly two conflicts: the shipping threshold and the policy version pair. The third one A1b found by itself, in how the two documents define remote zones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;01-shipping-and-logistics&lt;/code&gt;: Xinjiang, Tibet, Inner Mongolia, Qinghai, Ningxia, &lt;strong&gt;Hainan&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;13-shipping-fee-table-clean&lt;/code&gt;: Xinjiang, Tibet, &lt;strong&gt;Gansu&lt;/strong&gt;, Qinghai, Inner Mongolia, Ningxia&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One list has Hainan and no Gansu. The other has Gansu and no Hainan. I checked the sources. The model was right.&lt;/p&gt;

&lt;p&gt;I built this library and ran five episodes of experiments against it. That mismatch sat there the whole time and I never noticed. This carries more weight than finding the traps I set on purpose: with a planted trap you can reasonably suspect I hinted at it somewhere. I couldn't have hinted at this one.&lt;/p&gt;

&lt;p&gt;The academic name is inter-context conflict, one of three categories in the EMNLP 2024 survey &lt;a href="https://arxiv.org/abs/2403.08319" rel="noopener noreferrer"&gt;&lt;em&gt;Knowledge Conflicts for LLMs&lt;/em&gt;&lt;/a&gt;. Retrieval rarely surfaces this class, because two chunks that disagree usually don't get read together and nothing in the pipeline is responsible for comparing them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 3: the sharpest objection didn't reproduce
&lt;/h2&gt;

&lt;p&gt;The strongest critique I found is about overwrite behavior: during compilation the new revision silently replaces the old one, even though somebody may still be running the old value. The example is a hardware datasheet where Rev 6 specifies 100nF and Rev 7 changes it to 220nF, so the engineer holding a Rev 6 board can no longer look up the value that applies to them.&lt;/p&gt;

&lt;p&gt;My two expense policies are structurally the same case. Neither run overwrote anything. A1a kept both editions as separate index entries, gave each a standalone page with its full allowances, put a banner on the old one (&lt;code&gt;⚠️ Superseded. Effective 2023-04-01, replaced 2026-02-01. Historical reference only.&lt;/code&gt;), and &lt;code&gt;[[link]]&lt;/code&gt;ed the two pages to each other.&lt;/p&gt;

&lt;p&gt;The boundary matters though: both documents state their effective dates and supersession relationship in their own body text. The metadata was there to read. EP05's line about a decision layer rescuing conflicts that carry evidence holds in this paradigm too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 4: what verification caught
&lt;/h2&gt;

&lt;p&gt;Fabricated values, orphaned pages and dropped information can't be assessed by reading output and nodding. I scripted it: pull every numeric token out of the artifact and look for it in the source corpus, then check every &lt;code&gt;[[link]]&lt;/code&gt; against the set of headings.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;A1a neutral&lt;/th&gt;
&lt;th&gt;A1b Karpathy&lt;/th&gt;
&lt;th&gt;Objection tested&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Artifact size&lt;/td&gt;
&lt;td&gt;12,089 chars / 578 lines&lt;/td&gt;
&lt;td&gt;10,089 chars / 433 lines&lt;/td&gt;
&lt;td&gt;reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Numbers traced to source&lt;/td&gt;
&lt;td&gt;73 distinct, &lt;strong&gt;0 unfound&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;71 distinct, &lt;strong&gt;0 unfound&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;fabrication: didn't occur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;[[link]]&lt;/code&gt; integrity&lt;/td&gt;
&lt;td&gt;42 links / 11 targets, &lt;strong&gt;0 broken&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;7 links / 7 targets, &lt;strong&gt;0 broken&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;orphans: didn't occur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18 key facts spot-checked&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18/18&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17/18&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;dropped info: &lt;strong&gt;partly occurred&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both runs lost the same thing: the timestamp on the messy spreadsheet's footer, "subject to change, latest support response governs (updated 2024.6)."&lt;/p&gt;

&lt;p&gt;That is not decoration. Document &lt;code&gt;01&lt;/code&gt;, the other side of the threshold conflict, carries no date at all. So "which of these two is more recent" has exactly one clue in the entire library, and compilation dropped it. &lt;strong&gt;What compilation loses is precisely the metadata arbitration depends on.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A1b dropped three more things A1a kept, including the manual's own note that "version v3.1 (March 2026) is superseded." Sit with that one: A1b flagged the expense policy's version relationship more explicitly and simultaneously lost the manual's. It also came in 2,000 characters shorter with &lt;code&gt;[[link]]&lt;/code&gt; count down from 42 to 7. Attention is zero-sum inside a fixed output budget. Copying Karpathy's instruction in isn't a free upgrade, it's a trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cold water: the artifact says "conflict," the answer doesn't
&lt;/h2&gt;

&lt;p&gt;A compiled artifact isn't for human reading, it's context for the model. So the real test is loading it as context and asking the exact EP05 questions, side by side. Terms: the compiled side's system prompt is one sentence naming its role, matching the EP05 app. The artifact used is A1a, the neutral one, because using the more explicitly annotated A1b would be stacking the deck.&lt;/p&gt;

&lt;p&gt;Shipping threshold question, compiled side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Standard zones: free over ¥99 / ¥8 fee below ¥99
Remote zones (Xinjiang, Tibet, Gansu, Qinghai, Inner Mongolia, Ningxia):
  excluded from promotional free shipping, ¥15 surcharge applies

&amp;gt; Note: our rules changed recently, so please treat the shipping
&amp;gt;   amount shown at checkout as authoritative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieval side: "Orders over ¥99 ship free (remote zones excepted). Below ¥99, an ¥8 fee applies."&lt;/p&gt;

&lt;p&gt;Both report one number. Neither mentions ¥59. And the compiled side does something slightly worse: the artifact it's reading says "document conflict" in plain text, and the answer rephrases that as "our rules changed recently." Nothing in the library says the rules ever changed. That framing is invented, and it converts uncertainty into false confidence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqo1oam7k4uw2t2ywwodk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqo1oam7k4uw2t2ywwodk.jpg" alt="Discovery and disclosure are separate steps: the compiled artifact contains an explicit warning that two documents disagree on the ¥59 vs ¥99 threshold, but the answer given to the user reports only ¥99, rephrases " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the accurate claim isn't that compilation surfaces conflicts to users. It's that &lt;strong&gt;compilation solves finding the conflict and does nothing about telling anyone.&lt;/strong&gt; Two separate jobs, and that gist only covers the first.&lt;/p&gt;

&lt;p&gt;I reran the version questions too, formal and colloquial phrasing. Both sides answered correctly from the 2026 edition, 30 days to submit, five business days to review. Switching paradigms bought nothing there, for the same reason as before: the metadata was intact and retrieval was already handling it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where they actually diverge
&lt;/h2&gt;

&lt;p&gt;The fourth question got it out: a customer in Xinjiang wants a sofa, can you ship it, is cash on delivery available, how is shipping calculated? Answering forces the remote-zone rules from both documents into one answer.&lt;/p&gt;

&lt;p&gt;Retrieval side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Remote zone orders ship free over ¥129; below that, an ¥8 remote surcharge
  applies on top of the standard fee.
- Remote zones are excluded from promotional free shipping and require
  a ¥15 fee adjustment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those two bullets are mutually exclusive. Presented as a numbered list, they read like two rules that both apply. A support agent reading this to a customer contradicts themselves mid-sentence.&lt;/p&gt;

&lt;p&gt;Compiled side, same question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Source document | Rule |
| 01-shipping-and-logistics | Free over ¥129 in remote zones, ¥8 surcharge below |
| 13/14-shipping-fee-table | Remote zones excluded from promo, ¥15 adjustment |

&amp;gt; ⚠️ The two documents conflict. Treat the checkout amount as authoritative.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Split by source, with a warning. Cleanest divergence in the episode, and the mechanism is easy to state: the compiled side put those two rules next to each other once, at compile time, judged them incompatible, and that judgment persisted in the artifact. The retrieval side never saw the library whole. It got two chunks and had no basis for deciding they were incompatible.&lt;/p&gt;

&lt;p&gt;Put that with the previous section and the gain is narrow and specific: &lt;strong&gt;compilation pays off when two conflicting rules have to enter the same answer, because it knows they conflict.&lt;/strong&gt; On simple single-fact questions it picks one silently, exactly like retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost went the opposite way from what I expected
&lt;/h2&gt;

&lt;p&gt;I assumed this section would be where compilation died. Stuffing an entire library into context on every question has to cost more than retrieving a few chunks. It doesn't.&lt;/p&gt;

&lt;p&gt;The four compiled-side questions ran back to back, and from the second one on &lt;code&gt;prompt_tokens_details.cached_tokens&lt;/code&gt; had a value:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Call&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Cached&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Q1&lt;/td&gt;
&lt;td&gt;7,929&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;370&lt;/td&gt;
&lt;td&gt;¥0.1085&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q2&lt;/td&gt;
&lt;td&gt;7,935&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,168&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;388&lt;/td&gt;
&lt;td&gt;¥0.0339&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q3&lt;/td&gt;
&lt;td&gt;7,932&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,168&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;287&lt;/td&gt;
&lt;td&gt;¥0.0303&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q4&lt;/td&gt;
&lt;td&gt;7,942&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,168&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;665&lt;/td&gt;
&lt;td&gt;¥0.0440&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The artifact prefix bills at the cache-hit rate, ¥1.5 per million, 12.5% of standard input. Only the few hundred tokens that change pay full price. Input cost per question fell from ¥0.0951 to ¥0.0200, and I configured nothing. The server matched the prefix implicitly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2e1os1pvkppi4y497rkd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2e1os1pvkppi4y497rkd.jpg" alt="How implicit prefix caching bills: the input of one call splits into two segments, the unchanged compiled-artifact prefix of 7,168 tokens charged at ¥1.5 per million, and the changing question portion of roughly 760 tokens charged at the full ¥12 per million. Three cases: the first question is a cold start with no prefix to match, so everything pays full price; appending the human arbitration log to the end of the artifact leaves the prefix unchanged and the cache still hits; editing any source document and recompiling rewrites the prefix, so the whole cached segment is lost" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Head to head:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Same question&lt;/th&gt;
&lt;th&gt;Compiled (cache hit)&lt;/th&gt;
&lt;th&gt;Retrieval&lt;/th&gt;
&lt;th&gt;Delta&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shipping threshold&lt;/td&gt;
&lt;td&gt;¥0.0339&lt;/td&gt;
&lt;td&gt;¥0.0753&lt;/td&gt;
&lt;td&gt;compiled 55% cheaper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Xinjiang sofa (cross-document)&lt;/td&gt;
&lt;td&gt;¥0.0440&lt;/td&gt;
&lt;td&gt;¥0.1431&lt;/td&gt;
&lt;td&gt;compiled 69% cheaper&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two reasons. The retrieval side's 4,739 input tokens get no discount at all, everything at ¥12. And retrieval generates more output, because it emits tool calls and restates what it retrieved: 1,510 tokens against 665 on the cross-document question, and output is the expensive line item at ¥36 per million.&lt;/p&gt;

&lt;p&gt;Compiling costs ¥0.4162 once. Steady-state savings averaged ¥0.0703 per question, so ¥0.4162 ÷ ¥0.0703 ≈ 6. Roughly six questions in, the compile pays for itself.&lt;/p&gt;

&lt;p&gt;That 6 rests entirely on the cache. Look at Q1 again: with no cache hit, compiled costs ¥0.1085 against retrieval's ¥0.0753, which is 44% &lt;em&gt;more&lt;/em&gt;. Heavy consecutive use breaks even at six and then runs half to two thirds cheaper. Sparse occasional use pays a cold start every single time and never breaks even.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm6mqsf2l40xybnictjb4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm6mqsf2l40xybnictjb4.jpg" alt="Two cost curves: with high-frequency consecutive questions the compiled side hits the cache and recovers its ¥0.4162 compile cost in about six questions, saving half to two thirds per question afterward; with low-frequency scattered questions every call is a cold start that re-sends the whole library at full price, ¥0.1085 per question against retrieval's ¥0.0753, and never breaks even" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My four questions ran inside a minute of each other, so the second one hit. How long the prefix survives is a server-side policy question, and you'll want to measure it against your own traffic. Which lines up with the boundary Karpathy drew himself: if you just need to look one thing up, RAG is fine. He offered that as intuition. This is the invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a human decision back in
&lt;/h2&gt;

&lt;p&gt;The other RAG criticism in that gist is the absence of accumulation. Every question starts from zero, so yesterday's conclusion doesn't carry. A compiled artifact is a file, so you can write conclusions into it.&lt;/p&gt;

&lt;p&gt;I simulated what an operations team would do, appended to the end of A1a: operations confirms ¥99 and ¥8 are current, the ¥59 and ¥10 figures are pre-2024 and must not be used, agents must also tell the customer the threshold changed, and the remote-zone conflict is &lt;strong&gt;not&lt;/strong&gt; yet decided so escalate to a team lead. That last line is deliberate: one conflict resolved, one explicitly left open, so I could see whether it distinguishes them.&lt;/p&gt;

&lt;p&gt;Same shipping question, new answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current rule: free shipping over ¥99 / ¥8 fee below ¥99

⚠️ Heads up: this threshold changed recently. If you've seen "free over ¥59"
   somewhere, that's the old rule and no longer applies. ¥99 is current.

One more thing: remote zones have their own rules. If your delivery address
   is in one of them, let me know and I'll confirm the details for you.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¥59 appears in a customer-facing answer for the first time, with the correct disposition attached. On the conflict I left undecided it didn't force an answer, it asked for the delivery address and offered human follow-up. And the internal instruction to escalate didn't leak.&lt;/p&gt;

&lt;p&gt;There's a practical finding buried in this call. &lt;code&gt;cached_tokens&lt;/code&gt; was still 7,168. &lt;strong&gt;The arbitration log went at the end, so the prefix didn't change and the cache survived.&lt;/strong&gt; Append rather than insert, and incremental maintenance stays cheap.&lt;/p&gt;

&lt;p&gt;The cost of all this is that a human made the call. Compilation didn't tell me whether ¥59 or ¥99 was right. It put the disagreement in front of me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;p&gt;The context window is not the constraint here. &lt;code&gt;bl model list --model qwen3.8-max&lt;/code&gt; reports a 1,000,000 window and 991,808 max input tokens. Those 8,811 characters compile to 5,905 input tokens, so 991,808 ÷ 5,905 ≈ 168. The library can grow 168× before the window matters.&lt;/p&gt;

&lt;p&gt;One asymmetry belongs on the table. The compiled side read the full text dump of the messy spreadsheet; the retrieval side read the 11 chunks it was split into. The compiled side genuinely saw a more complete table. That follows from the definitions, compilation eats documents and retrieval eats chunks, but part of the divergence on the cross-document question comes from seeing more, and that part shouldn't be credited to the paradigm.&lt;/p&gt;

&lt;p&gt;Update costs are lopsided. Change one document and the compiled side re-runs the whole compile, ¥0.4162 and about three minutes, and the new artifact invalidates the cache prefix so the next question pays ¥0.1085 instead of ¥0.0339. The retrieval side re-uploads and re-indexes that one document at effectively zero cost.&lt;/p&gt;

&lt;p&gt;Cases where I'd skip compilation, in priority order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Libraries needing per-user access control. A compiled artifact merges cross-department knowledge onto shared pages, which breaks permission boundaries by construction. A retrieval pipeline can pin &lt;code&gt;tenant_id&lt;/code&gt; and filter before search. I haven't seen this objection raised publicly.&lt;/li&gt;
&lt;li&gt;Libraries that change often, per the update cost above.&lt;/li&gt;
&lt;li&gt;Sparse, occasional questions, where no cache hit means every question costs more.&lt;/li&gt;
&lt;li&gt;One-off lookups. Karpathy's own boundary.&lt;/li&gt;
&lt;li&gt;Personal knowledge management, which inverts the enterprise case. Organizing material is itself how learning happens. A company library exists for other people to query, so automating it costs nothing. Automate your own study notes and the learning stops.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; bailian-cli
bl auth login &lt;span class="nt"&gt;--api-key&lt;/span&gt; sk-xxxxx
bl model list &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.8-max
bl text chat &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.8-max &lt;span class="nt"&gt;--messages-file&lt;/span&gt; messages.json &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten billed calls, ¥1.27 total, about eighteen cents.&lt;/p&gt;

&lt;p&gt;My read: this isn't a choice between two options. On this library, compilation delivered visible conflicts and durable decisions. It did not deliver disclosure to the end user, so you'll need prompt or process work to cover that gap. And the economics rest entirely on cache hits, so a change in question cadence flips the math.&lt;/p&gt;

&lt;p&gt;Last episode I said the curation debt comes due no matter what. Switching paradigms changes one word: compilation will itemize the debt for you, but you still pay it. It put a mismatch that had been sitting in my library for five episodes right in front of me, and in the same pass it threw away the timestamp needed to settle it.&lt;/p&gt;

&lt;p&gt;Next episode continues the paradigm arc. I fed the artifact in as one block and got implicit cache hits from the second question on, which saved real money, but I have no idea when that prefix expires because it's entirely server-side. Model Studio also offers explicit caching, ¥15 per million to create and ¥1 per million on hits, with rules that work nothing like the implicit path. EP07 runs both against this same artifact.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Everything here was measured with Model Studio's CLI (&lt;code&gt;bl&lt;/code&gt;). Full usage figures, reasoning traces and compiled artifacts from all 10 calls are archived in the project repo. Command signatures may shift between versions, so check the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;. API keys are &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;free to obtain&lt;/a&gt;, with a free tier for new accounts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>llm</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Retrieval Ranked the Expired Policy First. The Agent Ignored It.</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Thu, 03 Sep 2026 09:49:36 +0000</pubDate>
      <link>https://dev.to/zackzhang/retrieval-ranked-the-expired-policy-first-the-agent-ignored-it-3jof</link>
      <guid>https://dev.to/zackzhang/retrieval-ranked-the-expired-policy-first-the-agent-ignored-it-3jof</guid>
      <description>&lt;p&gt;&lt;em&gt;Building a Knowledge Base from Scratch, EP05. The advanced arc begins.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Last episode closed the fundamentals arc with a cliffhanger: my knowledge base holds an expired 2023 expense policy, the 2026 rewrite that contradicts it, and eleven broken-spreadsheet fragments. Fixed retrieval ranks the expired policy first, 0.7226 to 0.7055, and no reranker setting moves it. The colloquial version of the invoice question drops the current policy out of the top five entirely.&lt;/p&gt;

&lt;p&gt;This episode wires the library into an agent and watches what it does. Eight controlled runs, same library both sides. The control group is EP04's fixed retrieval. The experiment group is a new agent application in the console: pick a model (qwen3.8-max, chosen from &lt;code&gt;bl model list --capability TG&lt;/code&gt;, which lays out fifty-four conversational models with context windows and prices), associate the knowledge base, switch on the thinking process, publish.&lt;/p&gt;

&lt;p&gt;One command, and the agent's whole reasoning comes out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl app call &lt;span class="nt"&gt;--app-id&lt;/span&gt; 32d619eeea744a31ad892e3873afe487 &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"报销发票怎么提交"&lt;/span&gt; &lt;span class="nt"&gt;--has-thoughts&lt;/span&gt; &lt;span class="nt"&gt;--stream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--stream&lt;/code&gt; flag matters. &lt;code&gt;--has-thoughts&lt;/code&gt; alone returns only the answer. With both flags, the output arrives in three marked segments: &lt;code&gt;[Thinking]&lt;/code&gt; (the judgment), &lt;code&gt;[Action]&lt;/code&gt; (the tool call, &lt;code&gt;search_knowledgebase&lt;/code&gt; in every run this episode), and &lt;code&gt;[Observation]&lt;/code&gt; (the tool's return, embedding the full retrieval JSON: scores, the rewrite field, the reranker name, total 27). Cheapest agent-audit mechanism I know of.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqcatqgusefo6wwzbdhl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqcatqgusefo6wwzbdhl.jpg" alt="Fixed retrieval versus Agentic RAG: on the left, question, retrieval, answer in a straight line, ranking decides; on the right, a yellow Agent-decides layer handles rewrite and arbitration, judgment decides" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A trap before the start
&lt;/h2&gt;

&lt;p&gt;My first instinct was to skip the console step and call the retrieval service that the platform auto-created back in EP02. Its ID was on file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl app call &lt;span class="nt"&gt;--app-id&lt;/span&gt; aid-4d6b2f2fddd047c794a5f88963ef397b &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"报销发票怎么提交"&lt;/span&gt; &lt;span class="nt"&gt;--has-thoughts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTTP 403, App.AccessDenied. The API key was fine. That resource is a retrieval pipeline built for &lt;code&gt;bl knowledge search --agent-id&lt;/code&gt;, not an agent application. The official docs seal it: the old RAG-augmented applications stopped accepting new instances in May 2024, and the official path is associating your knowledge base with an agent application. Two kinds of resources, two roads. The 403 is the border post.&lt;/p&gt;

&lt;p&gt;One design note on the application: the system prompt is a single sentence, "you are the customer-service assistant for Nuanwu Home Furnishing." Deliberately minimal. The exam questions are whether the agent notices the version conflict, rewrites colloquial questions, and admits ignorance on its own. Write those hints into the prompt and you're grading the prompt, not the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery 1: the expired policy still ranked first. The answer used the current one.
&lt;/h2&gt;

&lt;p&gt;The agent's retrieval results had the old policy first, 0.5655 to 0.4713. The answer was pure 2026 policy: electronic invoices, thirty days to upload through Enterprise WeChat, five working days for review. The old policy's fifteen-working-days-and-staple-paper routine never appeared.&lt;/p&gt;

&lt;p&gt;The thinking trace holds the pivot line: "我应该以现行版为准回答" (I should answer based on the version currently in force). The agent had read the current policy's opening sentence, "the 2023 version is hereby superseded." That sentence is metadata the document carries about itself. The retrieval layer sorts by textual relevance. The decision layer reads context and picks.&lt;/p&gt;

&lt;p&gt;The reverse check made it solid. Ask "住宿标准是多少" (what's the lodging cap) and this query's ordering flips, current policy first at 0.8512. Same answer either way. The ranking swings with the query. The answer doesn't swing with the ranking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy6zahdx0s5sbrx2md1jr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy6zahdx0s5sbrx2md1jr.jpg" alt="Version arbitration: retrieval ranked the 2023 old policy first (0.5655) over the 2026 current policy (0.4713), but the current policy carries " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery 2: the colloquial question got rescued, and the rescue is visible
&lt;/h2&gt;

&lt;p&gt;EP04's most painful result: ask "发票交了之后多久能报下来" (how long until the money lands) and the current policy falls out of the top five, replaced by the old policy at 0.4700 plus four spreadsheet fragments.&lt;/p&gt;

&lt;p&gt;The agent answered correctly. Five working days, citing the current policy. The interesting part was in the Observation metadata, a rewrite field:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"rewrite": "发票报销多久能报下来 报销周期"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The server had rewritten the colloquial question into document-style phrasing before retrieval. EP04 mentioned the enableRewrite config with no behavioral evidence. Now the evidence exists: control group direct search on the same question produces the 0.47-range fragment pileup; the agent channel has the current policy comfortably present. Two layers in relay, both on by default, zero code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery 3: the broken fragment got used as evidence
&lt;/h2&gt;

&lt;p&gt;Ask "新疆买沙发能发货吗" (can a sofa ship to Xinjiang). The agent's retrieval: the broken-spreadsheet fragment first at 0.8305, the clean document second at 0.7245. Then my favorite passage of the episode, the agent comparing sources:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The clean version provides general information about large-furniture delivery but doesn't specifically exclude remote areas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It noticed the granularity gap. The fragment has rotten formatting but is an explicit clause naming remote regions. The clean document is complete but vague on this specific question. The final answer is stitched: the no-large-items conclusion from the fragment, the 6-8 day window and 15 yuan surcharge context from the clean document. The eleven fragments went from retrieval killers to the decision layer's ugly-but-usable evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery 4: it decided not to search at all
&lt;/h2&gt;

&lt;p&gt;Second turn, with &lt;code&gt;--session-id&lt;/code&gt; attached, asking what the 2023 policy said. No tool call this turn. The trace: "based on the previously retrieved results, the 2023 policy states: review takes about 10 working days." It reused the previous turn's slices, answered correctly, and volunteered a comparison with the current process. "Who decides what to search" includes the third case: who decides not to. Sessions expire after one hour without requests, so pace your experiments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery 5: honest refusal
&lt;/h2&gt;

&lt;p&gt;Ask it to compare shipping policies with IKEA. The library holds nothing about IKEA. The trace: "I should not compare ourselves with other companies in a way that fabricates." The answer states its own policy in full (all library-sourced), declines to speak for IKEA, and redirects: tell me what you care about and I'll walk you through our terms. A move straight out of human customer-service training.&lt;/p&gt;

&lt;h2&gt;
  
  
  The turn: it rescued the version conflict, and silently fumbled the threshold conflict
&lt;/h2&gt;

&lt;p&gt;Then the cold water, from an unplanned discovery. The shipping answer contained "in-stock items ship within 48 hours of payment." Suspicious, since neither shipping document seemed to say that. I searched the library specifically. Real content, document 01, retrieval score 0.9456. False alarm on the hallucination, but the search surfaced the real problem: the library holds two free-shipping thresholds. Document 01 says free shipping over 59 yuan. Document 13, the clean table, says free over 99. Both were in the agent's retrieval results that turn, 01 scoring higher at 0.6557 to 0.5755.&lt;/p&gt;

&lt;p&gt;The agent took the 48-hour timeframe from 01 and the free-shipping-over-99 rule from 13. By topic, from each what it needed. Not one word about the 59-versus-99 conflict. It silently output one of two contradictory policies.&lt;/p&gt;

&lt;p&gt;Compare with the version conflict and the boundary is exact. The expense policies carry their arbitration basis in plain text: "the 2023 version is hereby superseded." The agent reads that and arbitrates. The shipping documents carry nothing. The agent can't arbitrate, and doesn't even register that arbitration is needed; from its seat these look like two topics, not one conflict.&lt;/p&gt;

&lt;p&gt;The deepest finding of the episode: &lt;strong&gt;the decision layer rescues conflicts that carry evidence chains and cannot rescue conflicts missing metadata.&lt;/strong&gt; Gartner's much-quoted number says 80% of enterprise agent projects never reach production over hallucination and consistency problems. This experiment is a micro-footnote: the agent's judgment filled real retrieval-layer holes, and the metadata debt in the library it cannot carry for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frufr9oldmqklbm0lie4v.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frufr9oldmqklbm0lie4v.jpg" alt="Two conflicts, two outcomes: a version conflict with metadata gets arbitrated (green check); a threshold conflict with no metadata gets one answer picked silently (red cross)" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Command surface notes
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;--pipeline-ids&lt;/code&gt;: with the application already associated to a library, passing the correct ID changes nothing (redundant), and passing a fake one (fakeid123) also changes nothing, silently, no error, still the mounted library. You think you switched. You didn't. Same pattern as EP04's &lt;code&gt;--rerank-mode&lt;/code&gt; accepting garbage values. A parameter's presence in the CLI table is not a promise the server validates it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--session-id&lt;/code&gt;: first call's response carries the ID, pass it back for automatic context, cloud storage expires after one idle hour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; bailian-cli
bl auth login &lt;span class="nt"&gt;--api-key&lt;/span&gt; sk-xxxxx
bl app call &lt;span class="nt"&gt;--app-id&lt;/span&gt; your-app-id &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"your policy question"&lt;/span&gt; &lt;span class="nt"&gt;--has-thoughts&lt;/span&gt; &lt;span class="nt"&gt;--stream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before you ship an agent on top of a real document library, take your most conflict-prone business rules, run them with &lt;code&gt;--has-thoughts --stream&lt;/code&gt;, and read the Observation sections. Two documents present, two different numbers, one silent answer: that's the failure you want to find before your users do.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All eight agent calls and retrieval controls ran for real on the Bailian CLI; full thinking traces and request IDs are kept in the project repo. CLI install: &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github" rel="noopener noreferrer"&gt;Bailian CLI docs&lt;/a&gt;. API key: &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;get one free&lt;/a&gt;, new accounts include free quota.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>tutorial</category>
      <category>agenticrag</category>
    </item>
    <item>
      <title>We Maxed Out the Reranker. The Expired Policy Still Won.</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Fri, 28 Aug 2026 10:07:32 +0000</pubDate>
      <link>https://dev.to/zackzhang/we-maxed-out-the-reranker-the-expired-policy-still-won-1p19</link>
      <guid>https://dev.to/zackzhang/we-maxed-out-the-reranker-the-expired-policy-still-won-1p19</guid>
      <description>&lt;p&gt;&lt;em&gt;Building a Knowledge Base from Scratch, EP04&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Last episode I polluted my knowledge base on purpose: an expired 2023 expense policy next to the 2026 rewrite, a broken spreadsheet that shattered into 11 of the library's 27 slices. The old policy outranked the current one at 0.7226 versus 0.7055. The standard advice for this failure mode is the same everywhere you read: turn on the reranker.&lt;/p&gt;

&lt;p&gt;This episode answers it with twenty-two commands, not opinions. Same library (&lt;code&gt;zj0knmrbye&lt;/code&gt;), baseline re-run first: five scores identical to the EP03 verification file, digit for digit. The library hadn't drifted. Everything below stands on that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery 1: reranking was already on
&lt;/h2&gt;

&lt;p&gt;Before touching any parameter, I read the library's configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl knowledge info &lt;span class="nt"&gt;--index-id&lt;/span&gt; zj0knmrbye
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There in the output: rerankModelName: qwen3-rerank. If you picked a ranking model when creating the library, it has been scoring every query since day one. So adding &lt;code&gt;--rerank&lt;/code&gt; to the command changed nothing, all five results identical. The flag confirms the status quo. It doesn't start anything.&lt;/p&gt;

&lt;p&gt;The proof that parameters do reach the server: swapping the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl knowledge retrieve &lt;span class="nt"&gt;--index-id&lt;/span&gt; zj0knmrbye &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"报销发票怎么提交"&lt;/span&gt; &lt;span class="nt"&gt;--rerank&lt;/span&gt; &lt;span class="nt"&gt;--rerank-model&lt;/span&gt; qwen3-rerank-hybrid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All five scores shifted, from the 0.72 range to the 0.68 range. Different model, different scoring scale. But note the ordering didn't budge. Hold that thought.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mode trap
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;--rerank-mode&lt;/code&gt; sits right there in the parameter table with three values: qa, similar, custom. I tried the aggressive version, custom mode with the instruction "优先返回2026年现行版制度", prefer the policy currently in force.&lt;/p&gt;

&lt;p&gt;Results identical to baseline. Then I filled the mode field with a value that doesn't exist, xxx. The command ran fine. No error. When an invalid value produces no complaint, the parameter isn't being consumed at query time at all. The official docs have the matching sentence: the ranking model's mode is selectable only at library creation and cannot be changed afterward.&lt;/p&gt;

&lt;p&gt;A flag being in the parameter table is not a promise you can set it at query time. This one is a library-level setting, locked at birth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The arithmetic constraint
&lt;/h2&gt;

&lt;p&gt;The recall parameters, &lt;code&gt;--dense-similarity-top-k&lt;/code&gt; and &lt;code&gt;--sparse-similarity-top-k&lt;/code&gt;, control how many candidates each lane feeds into the reranker's pool. Tested one lane at a time: nothing changes, this library's default is hybrid recall, both lanes or nothing. Both lanes at 5+5: the total field in the JSON drops from 27 (the whole library) to 6 (the merged candidate pool), and from third place down the cast changes.&lt;/p&gt;

&lt;p&gt;Then the extreme values. Recall lanes at 1 and 1, rerank window at 10. The server refused it outright, HTTP 400:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dense_similarity_top_k + sparse_similarity_top_k &amp;gt;= rerank_top_n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"api_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Index.InvalidParameter"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two recall lanes combined must be able to fill the rerank window. One plus one is two; two can't fill ten; rejected. The server states the pipeline's arithmetic constraint in the error itself. Honestly the best architecture documentation I've read all week.&lt;/p&gt;

&lt;p&gt;One more behavior worth knowing: rerank window at 100 with both recall lanes at 200 returned only 17 of the library's 27 slices. The missing ten were dropped by the rerankMinScore 0.2 gate. What you ask for is a ceiling. What clears the bar is what you get.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main event: rerank versus dirty data
&lt;/h2&gt;

&lt;p&gt;Four controlled runs against the EP03 failure cases. &lt;code&gt;--rerank&lt;/code&gt; alone. &lt;code&gt;--rerank-mode similar&lt;/code&gt;. Custom mode with the explicit instruction to prefer the 2026 policy. The fragment question with &lt;code&gt;--rerank&lt;/code&gt;. Every single one returned results identical to baseline, digit for digit. The expired policy held first at 0.7226. The broken-spreadsheet fragment held first at 0.7982.&lt;/p&gt;

&lt;p&gt;The mechanism is not mysterious. A reranker scores candidate chunks by textual relevance to the query. The old policy's line, "staple the reimbursement form with original invoices and hand them to the accountant", reads exactly like an answer to "how do I submit invoices". A reranker scoring accurately still operates inside the frame of "more relevant goes first". It doesn't know which policy was superseded. That was never its job.&lt;/p&gt;

&lt;p&gt;The model swap confirms it from the other side: qwen3-rerank-hybrid changed every score and moved zero rankings. Ordering is decided by textual relevance. The model choice sets the scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The phrasing gap
&lt;/h2&gt;

&lt;p&gt;Parameters can't rescue dirty data, and it turns out they can't rescue phrasing either. Same fact, two askings. Literal: "报销发票怎么提交", the two policy versions neck and neck. Colloquial: "发票交了之后多久能报下来", how long until the money lands. The top five collapsed to the old policy at 0.4700 followed by four broken-spreadsheet fragments. The current policy fell out of the top five entirely.&lt;/p&gt;

&lt;p&gt;Same pattern on the freight question. "新疆买沙发能发货吗" finds the answer fragment at 0.7982. Rephrase as "偏远地区买大件家具送不送", do you deliver big furniture to remote areas, and the winner becomes a fragment about free delivery in East China, 0.7420, purely on the literal match of the words for "large items". The actual answer fell to third.&lt;/p&gt;

&lt;p&gt;Retrieval matches wording, not intent. Your users don't phrase questions the way your documents phrase answers. The enableRewrite setting in the library config is the platform's library-level compensation for exactly this.&lt;/p&gt;

&lt;h2&gt;
  
  
  A correction I owe
&lt;/h2&gt;

&lt;p&gt;EP02 and EP03 both claimed knowledge base ingestion has no CLI command, console only. Wrong. &lt;code&gt;bl knowledge --help&lt;/code&gt; lists 31 knowledge subcommands, and &lt;code&gt;doc upload&lt;/code&gt; is one of them. I ran the full loop this episode: upload a temporary markdown file, watch the status come back PARSE_SUCCESS, clean up with file delete. With &lt;code&gt;--index-id&lt;/code&gt;, upload and ingest complete in one step.&lt;/p&gt;

&lt;p&gt;How the error happened: the local reference file with all 31 commands already existed at EP02 verification time. I checked the few I needed that day and treated "what I checked" as "what exists". A partial audit is not a full conclusion. Second episode in a row in that pit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two exhaust pipes, one engine
&lt;/h2&gt;

&lt;p&gt;retrieve is now marked deprecated, with a successor called search that routes through a deployed retrieval service. Same query on both paths: five scores identical digit for digit. The differences are all in shape. search returns text as structured blocks, document name, title, body, ready for an agent's context. Its metadata carries the rerank score, the query score, and a cost_time of 321ms. And its total field counts actual results (5) instead of the candidate pool (27).&lt;/p&gt;

&lt;p&gt;Tuning and experiments: retrieve, the full parameter surface is in your hands. Agent integration: search. Same engine underneath, so the twenty-two findings apply to both.&lt;/p&gt;

&lt;h2&gt;
  
  
  The triage order
&lt;/h2&gt;

&lt;p&gt;Twenty-two commands compressed into a checklist, walked from the top:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the evidence present at all? Correct answer missing from the top five means corpus or phrasing problems. No parameter helps.&lt;/li&gt;
&lt;li&gt;Run the two-phrasing check. Same fact, literal and colloquial. A drifting top result tells you more than any single high score.&lt;/li&gt;
&lt;li&gt;Only then, parameters. top-k sizes the pool, top-n sizes the plate, and the arithmetic constraint is enforced with a 400.&lt;/li&gt;
&lt;li&gt;Keep expectations for rerank level. It puts the more relevant first. It doesn't manufacture correct answers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; bailian-cli
bl auth login &lt;span class="nt"&gt;--api-key&lt;/span&gt; sk-xxxxx
bl knowledge info &lt;span class="nt"&gt;--index-id&lt;/span&gt; your-library-id
bl knowledge retrieve &lt;span class="nt"&gt;--index-id&lt;/span&gt; your-library-id &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"how do i submit invoices"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take the rule you trust least in your own documents, ask about its procedure in two different phrasings, and watch whether the top result survives the rephrase. That stability, or lack of it, is the honest health check of your library.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All twenty-two retrievals ran for real on the Bailian CLI against the &lt;code&gt;zj0knmrbye&lt;/code&gt; knowledge base; request IDs are kept in the project repo. CLI install: &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github" rel="noopener noreferrer"&gt;Bailian CLI docs&lt;/a&gt;. API key: &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;get one free&lt;/a&gt;, new accounts include free quota for 90 days.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>tutorial</category>
      <category>knowledgebase</category>
    </item>
    <item>
      <title>I Fed Two Versions of a Policy to My Knowledge Base. The Expired One Won.</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Fri, 28 Aug 2026 09:57:55 +0000</pubDate>
      <link>https://dev.to/zackzhang/i-fed-two-versions-of-a-policy-to-my-knowledge-base-the-expired-one-won-43il</link>
      <guid>https://dev.to/zackzhang/i-fed-two-versions-of-a-policy-to-my-knowledge-base-the-expired-one-won-43il</guid>
      <description>&lt;p&gt;&lt;em&gt;Building a Knowledge Base from Scratch, EP03&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;EP02 built a knowledge base with three clean, hand-written FAQ files and retrieval worked great. Real companies don't hand you clean files. So this time I ingested five documents of the kind that actually cross your desk: a 2023 expense policy nobody deleted, the 2026 rewrite that contradicts it in six places, the same freight table twice (once as sectioned Markdown, once as a merged-cell Excel with a note rotting in a corner of sheet two), and a 5,000-word support manual.&lt;/p&gt;

&lt;p&gt;All five went into the EP02 library (&lt;code&gt;zj0knmrbye&lt;/code&gt;), no fresh index, because a real library grows one file at a time. Eight documents, 27 slices, smart chunking. Then ten retrieval queries, three failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  One detour first
&lt;/h2&gt;

&lt;p&gt;The CLI has a &lt;code&gt;bl file upload&lt;/code&gt; command. It is not ingestion. It uploads to DashScope temporary storage, expires in 48 hours, and its mandatory &lt;code&gt;--model&lt;/code&gt; flag gives away the purpose: binding a file to a model for multimodal calls. A parcel locker, not a warehouse. Knowledge-base ingestion still lives in the web console, drag and drop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure 1: version blindness
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl knowledge retrieve &lt;span class="nt"&gt;--index-id&lt;/span&gt; zj0knmrbye &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"报销发票怎么提交"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(How do I submit invoices for reimbursement?) Top hit at 0.7226: the 2023 version, demanding paper invoices stapled and delivered to the accountant within 15 working days. The current 2026 version came second at 0.7055 with e-invoices uploaded within 30 days. The gap is 0.017.&lt;/p&gt;

&lt;p&gt;Three queries total, both versions neck and neck every time, largest gap 0.05. The winner is decided by whose phrasing matches the question, nothing else. I named the files "2023旧版" and "2026现行版"; filenames don't participate in retrieval at all. There is no version awareness in the retrieval layer. The old version doesn't just coexist, it occasionally wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure 2: the carrier matters more than the content
&lt;/h2&gt;

&lt;p&gt;Same freight information, two carriers. Ask "新疆买沙发能发货吗" (can a sofa ship to Xinjiang?) and a fragment from the raw Excel takes first at 0.7982:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;时效运费:6-8天不发大件（沙发床垫餐桌）差价15&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The word "sofa" is literally in there, so the score is fair. But the fragment can't answer anything: 15 RMB is the difference from what? The clean Markdown version came second at 0.6894 with a complete, forwardable rule.&lt;/p&gt;

&lt;p&gt;Worse numbers hide in the structure. The 6KB Excel expanded into 11 of the library's 27 slices (the console's slice view counts exactly that), 40%. Its stale corner note, "以上如有变动以客服最新答复为准（2024.6 更新）", crashed the top five in six of my ten queries on high-frequency words alone. One offhand spreadsheet became the library's permanent noise floor.&lt;/p&gt;

&lt;p&gt;And one accident I didn't plant: asking the free-shipping threshold returned both 满99包邮 from the new table and 满59包邮 from the EP02 FAQ. Two true-at-the-time documents, zero reconciliation. Retrieval serves both. It doesn't arbitrate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure 3: what smart chunking actually does
&lt;/h2&gt;

&lt;p&gt;The 5,000-word manual finally triggers chunking. Three findings. Titles ride along: each slice carries the headings of its sections, and they participate in matching, so heading hierarchy is load-bearing infrastructure, not typography. Cross-chapter slices exist (a slice starts with the tail of 4.5, then chapter five) but semantics survived. Tables keep their content and lose their syntax: a two-column Markdown table came back as pipe-joined rows, 场景|建议话术|, information intact, structure gone.&lt;/p&gt;

&lt;p&gt;Also: the manual's version line, "版本：v3.2 ｜ 更新日期：2026 年 6 月", landed in slice one. Retrieval ranking ignores it. The generation-stage model reads it. In a version conflict, that line is the only thing downstream that can tell old from new.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Delete expired versions before ingestion (the 2023 file outranked the 2026 one).&lt;/li&gt;
&lt;li&gt;One fact, one number, reconciled across files (59 and 99 both came back).&lt;/li&gt;
&lt;li&gt;Defuse Excel: unmerge, re-home corner notes, consolidate sheets, or convert to Markdown (6KB became 40% of all slices).&lt;/li&gt;
&lt;li&gt;Structure long documents with headings; they become the chunker's road signs.&lt;/li&gt;
&lt;li&gt;Write version and date in the body's first line, not the filename.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Zero code. The right people to execute it are the document owners, finance and ops and support leads, not the person holding the CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limits
&lt;/h2&gt;

&lt;p&gt;The messy Excel frequently outscores the clean document; if you audit data quality by score, it will fool you. Audit by whether the returned content can answer the question. Smart chunking boundaries aren't controllable from your side, though the console does offer manual slice editing. And my library is now polluted on purpose; production libraries shouldn't be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; bailian-cli
bl auth login &lt;span class="nt"&gt;--api-key&lt;/span&gt; sk-xxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Collect your real documents, run the five-item checklist, ingest in the console, then verify: pick the rule you trust least, ask about its procedure, and check whether the top two hits are two versions of the same document. If they are, the checklist wasn't finished.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All ten retrievals ran for real on the Bailian CLI against a Model Studio knowledge base; request IDs are kept in the project repo. CLI install: &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github" rel="noopener noreferrer"&gt;Bailian CLI docs&lt;/a&gt;. API key: &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;get one free&lt;/a&gt;, new accounts include free quota for 90 days.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>tutorial</category>
      <category>knowledgebase</category>
    </item>
    <item>
      <title>I Split One Fact Across Two Files to Test If RAG Retrieval Actually Works</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Mon, 24 Aug 2026 10:26:42 +0000</pubDate>
      <link>https://dev.to/zackzhang/i-split-one-fact-across-two-files-to-test-if-rag-retrieval-actually-works-cap</link>
      <guid>https://dev.to/zackzhang/i-split-one-fact-across-two-files-to-test-if-rag-retrieval-actually-works-cap</guid>
      <description>&lt;p&gt;&lt;em&gt;Building a Knowledge Base from Scratch, EP02&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The promise from last time
&lt;/h2&gt;

&lt;p&gt;Last episode I ran three experiments on the "paste a file into chat" habit: half a document and the model improvises, the wrong edition and it waves you through, a new window and it remembers nothing. I promised that next time we'd build a real knowledge base. This is that episode.&lt;/p&gt;

&lt;p&gt;The setup: a fictional small store ("Cozy Home," home goods and small appliances) with three Markdown support FAQs: shipping policy, returns &amp;amp; warranty, payments &amp;amp; invoices. Each file is 1-2KB. Plus one deliberate trap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap
&lt;/h2&gt;

&lt;p&gt;I split the cash-on-delivery information across two files. Which regions support COD lives in the shipping policy. The 3 RMB collection fee lives in the payments doc. This mirrors how real companies operate: ops writes shipping rules, finance writes payment rules, and nobody answers for the other document's completeness.&lt;/p&gt;

&lt;p&gt;If retrieval works the way RAG claims, asking "does COD cost extra?" should surface both files at once. If it's just fancy keyword matching, it won't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the library
&lt;/h2&gt;

&lt;p&gt;Honest disclosure first: there is no CLI command for creating the knowledge base. The &lt;code&gt;bl&lt;/code&gt; CLI's knowledge group currently has exactly one command, &lt;code&gt;retrieve&lt;/code&gt;. Ingesting documents happens in the web console: standard edition, file connector, drag in three files, wait for the status to flip to active. Ten minutes, zero code. The store owner could have done it.&lt;/p&gt;

&lt;p&gt;The console hands you a knowledge base ID (mine was &lt;code&gt;zj0knmrbye&lt;/code&gt;). Every retrieval command points at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The retrieval
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl knowledge retrieve &lt;span class="nt"&gt;--index-id&lt;/span&gt; zj0knmrbye &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"新疆可以货到付款吗"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query is Chinese because the documents are Chinese. The CLI doesn't care. What comes back is JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.8299149870872498&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"doc_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"01-发货与物流政策"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"……货到付款仅支持华东、华南主要城市。偏远地区（新疆、西藏等）不支持货到付款，请选择在线支付。货到付款订单需额外支付 3 元代收服务费。……"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.48945897817611694&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"doc_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"03-支付与发票说明"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;score&lt;/code&gt; is the relevance ranking. &lt;code&gt;doc_name&lt;/code&gt; tells you which file the answer came from. &lt;code&gt;content&lt;/code&gt; is the original passage, verbatim, not a processed conclusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five queries, three findings
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Hits&lt;/th&gt;
&lt;th&gt;What it proved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Does COD work for Xinjiang?&lt;/td&gt;
&lt;td&gt;Shipping 0.83 + Payments 0.49&lt;/td&gt;
&lt;td&gt;Cross-file recall&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free shipping threshold?&lt;/td&gt;
&lt;td&gt;Shipping 0.68&lt;/td&gt;
&lt;td&gt;Factual hit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;My humidifier arrived broken&lt;/td&gt;
&lt;td&gt;Returns 0.75&lt;/td&gt;
&lt;td&gt;Semantic match (zero keyword overlap)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can coupons stack?&lt;/td&gt;
&lt;td&gt;Payments 0.54 (only hit)&lt;/td&gt;
&lt;td&gt;Below-threshold filtering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does COD cost extra?&lt;/td&gt;
&lt;td&gt;Shipping 0.82 + Payments 0.77&lt;/td&gt;
&lt;td&gt;Both files, high scores&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The humidifier query had no keyword overlap with the docs at all.&lt;/strong&gt; It hit at 0.75, returning 「电器类商品（如加湿器、小夜灯）提供 1 年保修，非人为损坏免费维修或换新」, which translates to "electrical products (humidifiers, night lights) carry a 1-year warranty, free repair or replacement for non-artificial damage." The query says "broken"; the passage says "non-artificial damage." That's semantic retrieval, not string matching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The coupon query returned exactly one result&lt;/strong&gt; out of a three-document library: 「新人券：首单满 59 减 10，仅限新注册会员，不可与其他优惠叠加」, "the newcomer coupon cannot be combined with other offers." "Stack with" matched "combined." The other two docs weren't dragged in to pad the answer. If you're wiring this into a downstream system, don't assume the &lt;code&gt;nodes&lt;/code&gt; array has a fixed length.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The COD fee query was the payoff.&lt;/strong&gt; Shipping policy at 0.82, payments doc at 0.77. Shipping returned 「货到付款仅支持华东、华南主要城市（上海、杭州、南京、广州、深圳、厦门等）」, "COD is supported in major east and south China cities only." Payments returned 「需额外支付 3 元代收服务费」, "an extra 3 RMB collection fee applies." The retrieval assembled both without being told where anything lived. That's the entire value proposition of RAG in one response: &lt;em&gt;you don't need to know which file the answer lives in.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three small observations for builders
&lt;/h2&gt;

&lt;p&gt;Small documents don't get chunked. Each KB-scale FAQ came back as one whole slice. Bigger documents will get sliced, and where the cuts land determines retrieval quality. That's the next episode's topic.&lt;/p&gt;

&lt;p&gt;No model selection. The command has no &lt;code&gt;--model&lt;/code&gt; flag. The knowledge base ships with its retrieval model built in.&lt;/p&gt;

&lt;p&gt;The output is JSON by default. No extra flags. &lt;code&gt;jq&lt;/code&gt; one-liner gets you the top hit's content. Wiring retrieval into an existing system costs close to nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limits
&lt;/h2&gt;

&lt;p&gt;The library was tiny (three files), so chunking never came up, which is the best case. Retrieval returns raw passages, not phrased answers; don't paste them straight into customer replies. And &lt;code&gt;score&lt;/code&gt; is a within-query ranking signal, not a percentage: in my runs, 0.54 was the only correct answer to one question while 0.49 was a bystander in another. Never filter by absolute score.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; bailian-cli
bl auth login &lt;span class="nt"&gt;--api-key&lt;/span&gt; sk-xxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write three Markdown files from your own domain, build the library in the console (ten minutes), copy the index ID, and run one retrieval. Then ask a question whose answer spans two of your files. Watch what comes back.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All five retrievals ran for real on the Bailian CLI against a Model Studio knowledge base. Commands and full JSON responses are kept in the project repo. CLI install: &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github" rel="noopener noreferrer"&gt;Bailian CLI docs&lt;/a&gt;. API key: &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;get one free&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>tutorial</category>
      <category>knowledgebase</category>
    </item>
    <item>
      <title>I Handed My AI the Company Policy. It Never Asked If the File Was Still Valid</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Fri, 21 Aug 2026 10:32:38 +0000</pubDate>
      <link>https://dev.to/zackzhang/i-handed-my-ai-the-company-policy-it-never-asked-if-the-file-was-still-valid-2ne9</link>
      <guid>https://dev.to/zackzhang/i-handed-my-ai-the-company-policy-it-never-asked-if-the-file-was-still-valid-2ne9</guid>
      <description>&lt;p&gt;&lt;em&gt;Building a Knowledge Base from Scratch, EP01&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Everyone worries about LLM hallucinations on company-internal questions. In 2026, the standard move is pasting a policy file into the chat first, so hallucination feels handled. I ran three controlled experiments on that move itself, and found three failure modes that have nothing to do with hallucination: &lt;strong&gt;it improvises around missing files, it never questions the version you hand it, and it forgets everything by the next session.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This post walks through all three with reproducible commands. It's the first episode of a series that builds a knowledge base from zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tool: Bailian CLI (&lt;code&gt;bl&lt;/code&gt;), the CLI for Alibaba Cloud's Model Studio&lt;/li&gt;
&lt;li&gt;Model: Qwen3.8-Max&lt;/li&gt;
&lt;li&gt;Question domain: a company travel policy. A real reimbursement answer spans the policy PDF, an OA announcement, a shared-drive form template, and a group-chat notice. No one pastes all four every time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install and authenticate if you want to follow along:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; bailian-cli
bl auth login &lt;span class="nt"&gt;--api-key&lt;/span&gt; sk-xxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An API key with free quota is available in the &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;Model Studio console&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experiment 1: paste half the policy
&lt;/h2&gt;

&lt;p&gt;I pasted only the standards section (450 RMB cap for first-tier cities, over-cap not reimbursed, 30-day filing window) and left out the approval-process section, then asked whether a 550 booking could be reimbursed and what the process was.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl text chat &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.8-max &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"根据以下公司制度回答问题：《星野科技差旅报销制度（2026年3月修订）》（一）住宿标准：一线城市450元/晚，二线城市350元/晚，其他城市300元/晚。单人单晚超出标准部分原则上不予报销。（二）报销时限：出差返回后30日内在财务系统提交，逾期视为放弃。问题：我下周去上海出差，订了550元一晚的酒店，能报销吗？具体流程是什么？"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The standards half was flawless: 450 cap, 100 over, reimbursed at 450. The process half started leaking. It admitted the document "doesn't specify an exception process," then filled the gap with industry common sense: invoice, payment record, trip approval form. None of that was in the text I gave it. Its reasoning log said it plainly: the policy doesn't detail this, but suggestions can still be offered.&lt;/p&gt;

&lt;p&gt;The control run with the full policy produced the complete chain: OA approval form, two director signatures, four evidence types, 30-day deadline. One file of distance between the two answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experiment 2: paste the outdated version
&lt;/h2&gt;

&lt;p&gt;I swapped in the 2023 edition (cap 600, every other clause identical) and asked: booking at 600 gets fully reimbursed, right? If so I'll place the order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl text chat &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.8-max &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"根据以下公司制度回答问题：《星野科技差旅报销制度（2023年1月修订）》（一）住宿标准：一线城市600元/晚，二线城市450元/晚，其他城市350元/晚。单人单晚超出标准部分原则上不予报销；因会议安排等特殊情况超标的，须出差前在OA系统提交《超标住宿审批单》，经部门总监与财务总监双签后方可报销。（二）报销时限：出差返回后30日内在财务系统提交，逾期视为放弃。问题：我下周去上海出差，准备订600元一晚的酒店，按这个制度能全额报销吧？没问题我就下单了。"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It ran a diligent checklist: city tier, tax-inclusive pricing, single occupancy, the 30-day window. It even warned that 601 would tip it over. Then it waved me through: no over-cap, fully reimbursable, no approval form needed.&lt;/p&gt;

&lt;p&gt;Not once did it ask whether a 2023 policy is still in effect. The 2026 revision caps it at 450. Booking at 600 on the old file means paying 150 a night out of pocket. The AI said nothing wrong; the file was wrong, and the model has neither the duty nor the ability to suspect your file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experiment 3: new window, no file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl text chat &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.8-max &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"我们公司规定一线城市出差住宿费每晚报销上限是多少？"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reply: I don't have your company's internal policy. Everything you pasted yesterday is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this argues for a knowledge base
&lt;/h2&gt;

&lt;p&gt;The manual-paste move has a structural flaw beyond inconvenience: &lt;strong&gt;you usually don't know which file the answer lives in&lt;/strong&gt;. Reimbursement spans the policy, the OA notice, the group announcement. Support spans the product page, the after-sales doc, the latest Excel from ops. You can't paste what you can't find.&lt;/p&gt;

&lt;p&gt;RAG (retrieval-augmented generation) fixes all four gaps at once: ingest every source in every format once, retrieve across files at query time, serve everyone the same currently-effective version, retire old versions at the document layer. Store, retrieve, answer. That's the architecture behind most internal Q&amp;amp;A systems since 2023, and the frontier keeps moving: agents that decide what to retrieve (Agentic RAG), Karpathy's compile-don't-retrieve LLM Wiki proposal from this April, and PixelRAG's screenshot-based retrieval.&lt;/p&gt;

&lt;p&gt;The series continues with EP02, where we build an actual knowledge base in the Model Studio console, load the policy, announcements, and FAQ together, and query it with one command. Fifteen minutes, end to end.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All commands and raw responses are kept in the project repo for verification. CLI docs: &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github" rel="noopener noreferrer"&gt;Bailian CLI&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>rag</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>DeepSeek Harness Got 4,000+ Plugins in a Week — All Eyes, No Hands</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Wed, 19 Aug 2026 07:00:36 +0000</pubDate>
      <link>https://dev.to/zackzhang/deepseek-harness-got-4000-plugins-in-a-week-all-eyes-no-hands-48eh</link>
      <guid>https://dev.to/zackzhang/deepseek-harness-got-4000-plugins-in-a-week-all-eyes-no-hands-48eh</guid>
      <description>&lt;h2&gt;
  
  
  Everyone's reviewing the philosophy. I went through the plugin directory instead.
&lt;/h2&gt;

&lt;p&gt;DeepSeek open-sourced DeepSeek Harness (DSH) on August 13. A week later: 160k stars, 4,000+ third-party plugins. The coverage has mostly been about the "everything is a plugin" pitch: model, tools, session, sandbox, even the UI is swappable.&lt;/p&gt;

&lt;p&gt;Neat. But I had a more specific question: across those 4,000 plugins, what is everyone actually bolting onto this thing?&lt;/p&gt;

&lt;p&gt;So I spent an evening going through the plugin directories. Found a pattern, found a gap, and filled the gap with a single command. Details below.&lt;/p&gt;

&lt;h2&gt;
  
  
  What people are actually installing
&lt;/h2&gt;

&lt;p&gt;The category map as of mid-August:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Representatives&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Workbench&lt;/td&gt;
&lt;td&gt;VS Code-style sidebar workbench&lt;/td&gt;
&lt;td&gt;DSH-better-sidebar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-term memory&lt;/td&gt;
&lt;td&gt;Cross-session project memory&lt;/td&gt;
&lt;td&gt;dsh-memory-evolve, EverOS, dsh-persist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vision (image reading)&lt;/td&gt;
&lt;td&gt;Give the text-only model sight&lt;/td&gt;
&lt;td&gt;ModLens, dsh-tool-describe-image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File references&lt;/td&gt;
&lt;td&gt;@-mention files in the input&lt;/td&gt;
&lt;td&gt;dsh-at-file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-agent&lt;/td&gt;
&lt;td&gt;Spin up sub-agent teams&lt;/td&gt;
&lt;td&gt;dsh-agent-teams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code migration&lt;/td&gt;
&lt;td&gt;Move configs/skills/history over&lt;/td&gt;
&lt;td&gt;dsh-claude-move&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memes&lt;/td&gt;
&lt;td&gt;Virtual pets, mini-games&lt;/td&gt;
&lt;td&gt;deepseek-manners&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Memory is a three-way race, vision has two clear leaders, and the long tail is everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  An easy-to-miss detail: one provider keeps showing up
&lt;/h2&gt;

&lt;p&gt;DSH's chat models are text-only, so "reading images" was the first gap the ecosystem filled. The two leading vision plugins both default to Qwen-VL on Alibaba's Bailian (Model Studio) platform.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dsh-tool-describe-image has you set a &lt;code&gt;DASHSCOPE_API_KEY&lt;/code&gt; in setup, default model &lt;code&gt;qwen-vl-plus&lt;/code&gt; (one config line swaps to &lt;code&gt;qwen-vl-max&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;ModLens's OpenAI-compatible channel example uses Bailian's compatible-mode endpoint as the base URL, sample model &lt;code&gt;qwen3-vl-plus&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two authors who don't know each other, same default choice. Meanwhile in the memory lane, EverOS (local-first memory layer) configures its LLM in standard OpenAI-compatible form and defaults to OpenRouter, but points at Bailian's endpoint just fine. And DSH itself doesn't lock the main model; custom providers and base URLs are first-class settings.&lt;/p&gt;

&lt;p&gt;So inside DeepSeek's own ecosystem, the community has voted Bailian into three seats: the eyes, the memory engine, and the backup model slot. Nobody planned that. Config files did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap: eyes everywhere, no hands
&lt;/h2&gt;

&lt;p&gt;Here's what I did &lt;em&gt;not&lt;/em&gt; find in the directory: a single plugin that lets the agent produce image, video, or audio files.&lt;/p&gt;

&lt;p&gt;Every vision plugin solves "looking in." Nothing solves "making out."&lt;/p&gt;

&lt;p&gt;This bit me personally. I had an agent in DSH build me a small Pomodoro timer web app. Code came out clean. But when I wanted to share it with friends (a decent card image, a short demo clip), the agent could only suggest tools for &lt;em&gt;me&lt;/em&gt; to go use. The 90% was automated; the last 10% meant leaving the session, opening a design tool and a screen recorder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filling the spot without writing a plugin
&lt;/h2&gt;

&lt;p&gt;Writing a DSH plugin is real dev work. Filling the "generation" seat isn't, for three architectural reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;DSH's standard mode ships a shell tool: the agent can run commands, so any CLI is already its tool&lt;/li&gt;
&lt;li&gt;Bailian's CLI (&lt;code&gt;bl&lt;/code&gt;) wraps image generation, image editing, and video generation as subcommands; the agent discovers flags via &lt;code&gt;--help&lt;/code&gt; on its own&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bl&lt;/code&gt; reads the same &lt;code&gt;DASHSCOPE_API_KEY&lt;/code&gt; environment variable as the vision plugins. If you've configured one for reading images, you're already done&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you haven't: grab a &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;free API key&lt;/a&gt; from the Bailian console, install the CLI (&lt;code&gt;npm install -g bailian-cli&lt;/code&gt;, Node.js 18+; full &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;docs&lt;/a&gt;), and store the key once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl auth login &lt;span class="nt"&gt;--api-key&lt;/span&gt; sk-your-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then tell the agent the ground rules once: &lt;em&gt;every image, image edit, and video request in this project gets done with bl.&lt;/em&gt; That's the whole setup. Everything below: the prompts I typed were plain English; the commands are the agent's work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 1: A share card for the finished project
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What I said&lt;/strong&gt;: "Make a share card for FocusTick. Dark background, a clean tomato-timer glyph in the center, the tool name below. Restrained, lots of whitespace."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the agent ran&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl image generate &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"product share card, dark background, flat-style tomato timer glyph at center, tool name FocusTick in clean type below, generous whitespace, restrained minimal design"&lt;/span&gt; &lt;span class="nt"&gt;--size&lt;/span&gt; 1280&lt;span class="k"&gt;*&lt;/span&gt;720 &lt;span class="nt"&gt;--model&lt;/span&gt; qwen-image-3.0-pro &lt;span class="nt"&gt;--watermark&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--out-dir&lt;/span&gt; ./assets &lt;span class="nt"&gt;--out-prefix&lt;/span&gt; focustick-card
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 1280×720 PNG lands in &lt;code&gt;assets/&lt;/code&gt; as &lt;code&gt;focustick-card.png&lt;/code&gt;. Two things I learned: my first prompt omitted "generous whitespace" and the render came out cluttered; two words and a rerun fixed it, and iterating on a prompt is &lt;em&gt;much&lt;/em&gt; cheaper than iterating on a workflow. And &lt;code&gt;--out-prefix&lt;/code&gt; is a habit worth teaching the agent; without it, outputs default to the name &lt;code&gt;image&lt;/code&gt; and become indistinguishable after a few runs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fdsh-bl%2Fecosystem-map-1280x720-en.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fdsh-bl%2Fecosystem-map-1280x720-en.jpg" width="800" alt="Where Bailian sits in the DeepSeek Harness plugin ecosystem" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 2: Wrong vibe. Edit, don't regenerate.
&lt;/h2&gt;

&lt;p&gt;Friends pointed out the app is light-themed; a dark card felt disjointed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I said&lt;/strong&gt;: "Switch the card's background to off-white. Don't touch the glyph or layout."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the agent ran&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl image edit &lt;span class="nt"&gt;--image&lt;/span&gt; ./assets/focustick-card.png &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"replace the dark background with an off-white light background, keep the tomato glyph, text, and overall composition exactly unchanged"&lt;/span&gt; &lt;span class="nt"&gt;--out-dir&lt;/span&gt; ./assets &lt;span class="nt"&gt;--out-prefix&lt;/span&gt; focustick-card-light
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key distinction: &lt;code&gt;image edit&lt;/code&gt; is local modification. The composition you already liked survives. A fresh &lt;code&gt;generate&lt;/code&gt; washes it away and redraws everything. Both files sit side by side (&lt;code&gt;focustick-card&lt;/code&gt; / &lt;code&gt;focustick-card-light&lt;/code&gt;), which is exactly what the prefix naming buys you. Bonus: &lt;code&gt;--image&lt;/code&gt; can be passed multiple times for merges and composites, a rabbit hole for another post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 3: "A static image doesn't show me how it works"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What I said&lt;/strong&gt;: "Take the home screen screenshot and make a 5-second demo video. Slow push-in. Save it to assets."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the agent ran&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl video generate &lt;span class="nt"&gt;--image&lt;/span&gt; ./screenshots/home.png &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"camera slowly pushes toward the interface, cards gently appearing one by one, clean product-demo style, calm pacing"&lt;/span&gt; &lt;span class="nt"&gt;--duration&lt;/span&gt; 5 &lt;span class="nt"&gt;--download&lt;/span&gt; ./assets/focustick-demo.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three technical notes: passing &lt;code&gt;--image&lt;/code&gt; auto-switches to image-to-video mode; video is an async task, so the agent &lt;em&gt;waiting&lt;/em&gt; is not the agent &lt;em&gt;stuck&lt;/em&gt;; and &lt;code&gt;--download&lt;/code&gt; writes the mp4 on completion. Without it you get a task ID and need a separate download command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fdsh-bl%2Fscenarios-1280x720-en.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fdsh-bl%2Fscenarios-1280x720-en.jpg" width="800" alt="Three scenarios, one loop: plain-English request, agent-built command, output on disk" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The meter is running
&lt;/h2&gt;

&lt;p&gt;These are real billable calls, and the agent runs them automatically. Three images in a burst means three charges. Two commands to have in place before you start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl usage freetier &lt;span class="nt"&gt;--all&lt;/span&gt;   &lt;span class="c"&gt;# auto-stop when the free tier runs out&lt;/span&gt;
bl usage free &lt;span class="nt"&gt;--expiring&lt;/span&gt; 30  &lt;span class="c"&gt;# remaining quota per model (needs bl auth login --console once)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honest caveats on the free tier: it's valid for 90 days (not forever), and each model has its own quota. Running one dry doesn't auto-switch to another. Anyone claiming "permanent free quota" is misinformed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should bother
&lt;/h2&gt;

&lt;p&gt;If you build things inside DSH (small tools, personal sites, demos) and regularly hand something outward (cards, clips, illustrations), this closes the loop: code, assets, README, all in one session, no context-switching to a design app.&lt;/p&gt;

&lt;p&gt;If your output is just code and text, skip it: running a &lt;code&gt;bl&lt;/code&gt; command by hand when needed works fine; routing it through the agent isn't the point.&lt;/p&gt;

&lt;p&gt;The takeaway I'd keep: 4,000+ plugins voted on what the ecosystem wants, and the "seeing" vote went to Qwen-VL. The "generating" seat is still empty, and filling it didn't take a new plugin, just one command already on the PATH.&lt;/p&gt;

&lt;p&gt;To see what Bailian can do before committing, &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;start from the console&lt;/a&gt;, more informative than any roundup, mine included.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Stop Rerolling AI Video: Lock a Keyframe First, Generate Once</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Mon, 17 Aug 2026 09:21:04 +0000</pubDate>
      <link>https://dev.to/zackzhang/stop-rerolling-ai-video-lock-a-keyframe-first-generate-once-2l5f</link>
      <guid>https://dev.to/zackzhang/stop-rerolling-ai-video-lock-a-keyframe-first-generate-once-2l5f</guid>
      <description>&lt;h2&gt;
  
  
  The saved video you never manage to remake
&lt;/h2&gt;

&lt;p&gt;You scroll past a great short clip, tap Save, and think "I'll make something like this someday." When someday comes, every shot looks trivial in isolation — and together you have no idea where to start. What you saved is a finished product, not a structure.&lt;/p&gt;

&lt;p&gt;The bigger wall is what creators call rerolling: the same prompt can return a cinematic shot or a broken mess. Industry coverage keeps citing creators pulling 20 generations for 1 usable take. That's not one vendor's bug — it's the current mechanism of video models, and every reroll bills per second.&lt;/p&gt;

&lt;p&gt;(Quick scoping note: I've previously written about one-shot short-film pipelines, pre-production assets for short dramas, and prompt-writing methodology. This post is about &lt;strong&gt;single creative clips&lt;/strong&gt; — the entry point is deconstructing a reference video's technique. They stand alone. And the stance throughout: extract the technique, never the content — every example here is a generic technique description, nothing points at a specific creator's work.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: move the decisions to the cheap step
&lt;/h2&gt;

&lt;p&gt;Pure text-to-video hands composition, props, color grade, and background to the model's dice. Four random variables per generation, each one a potential reroll.&lt;/p&gt;

&lt;p&gt;The fix is decision-frontloading: lock the first frame with an image model (cheap, synchronous, seconds), pick the best of several candidates, and only then let the video model do the one thing left — motion.&lt;/p&gt;

&lt;p&gt;Step one costs nothing but attention: break the reference into a structure list — shot rhythm (cut length, push/pull), transitions (what bridges two shots), information density (one beat per shot or three). Manual scrubbing works fine; if you already run an agent, tools like Agent-Reach (70k+ stars on GitHub) can have it read YouTube videos and comment sections for a first draft. The comment section is free audience research: whatever moment the top comments celebrate is the hook — note the &lt;em&gt;placement technique&lt;/em&gt;, not the content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fviral-video-remake%2Fpipeline-en-1280x720.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fviral-video-remake%2Fpipeline-en-1280x720.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The command chain
&lt;/h2&gt;

&lt;p&gt;Keyframe, four candidates in one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl image generate &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"Product photography: a cup of latte on a dark walnut wood table, soft side lighting, delicate steam rising, shallow depth of field"&lt;/span&gt; &lt;span class="nt"&gt;--size&lt;/span&gt; 16:9 &lt;span class="nt"&gt;--n&lt;/span&gt; 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--n 4&lt;/code&gt; returns four images (max 6) — picking one moves your taste decisions to a near-free, seconds-fast step. &lt;code&gt;--size&lt;/code&gt; takes the ratio directly; keep it aligned with the video's &lt;code&gt;--ratio&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then image-to-video as an async job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl video generate &lt;span class="nt"&gt;--image&lt;/span&gt; &amp;lt;keyframe-url&amp;gt; &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"Camera slowly pushes in toward the cup, steam rising and curling softly"&lt;/span&gt; &lt;span class="nt"&gt;--ratio&lt;/span&gt; 16:9 &lt;span class="nt"&gt;--no-wait&lt;/span&gt;
bl video task get &lt;span class="nt"&gt;--task-id&lt;/span&gt; &amp;lt;task-id&amp;gt;
bl video download &lt;span class="nt"&gt;--task-id&lt;/span&gt; &amp;lt;task-id&amp;gt; &lt;span class="nt"&gt;--out&lt;/span&gt; demo.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;--image&lt;/code&gt; present, the model switches from text-to-video to image-to-video automatically. &lt;code&gt;--no-wait&lt;/code&gt; returns a task ID immediately; poll later, download when SUCCEEDED. (&lt;code&gt;--no-wait&lt;/code&gt; and &lt;code&gt;--async&lt;/code&gt; are equivalent; &lt;code&gt;--poll-interval&lt;/code&gt; only matters in blocking mode — 5s default for generate, 15s for edit.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Head-to-head test log (Aug 17)
&lt;/h2&gt;

&lt;p&gt;Same scene description, two paths, same day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image-to-video (keyframe-driven)&lt;/strong&gt;: 2m 03s submit-to-done. 5s 1080P. Smooth push-in, subject consistent start to finish — &lt;strong&gt;usable on the first pass&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text-to-video (no keyframe)&lt;/strong&gt;: 2m 21s. Decent quality, but the model improvised: a saucer, two table lamps, a café interior, wrong table color. For any brief with visual constraints, that's a rejected take and a paid reroll&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nearly identical wall-clock time. The difference isn't speed, it's &lt;strong&gt;hit certainty&lt;/strong&gt;: the keyframe route confines randomness to the image candidates (pickable, near-free), leaving the video step a single variable — whether the motion follows the prompt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fviral-video-remake%2Fcompare-en-1280x720.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fviral-video-remake%2Fcompare-en-1280x720.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Going vertical? &lt;code&gt;--ratio 9:16&lt;/code&gt;, and &lt;code&gt;--size 9:16&lt;/code&gt; on the keyframe. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest ledger
&lt;/h2&gt;

&lt;p&gt;Video calls are priced far above text, and a fresh account's free quota drains fast on video. Flip the guard before you start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl usage freetier &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With it on, generation auto-stops when the free quota is exhausted instead of silently billing. Check your real bill afterwards with &lt;code&gt;bl usage stats&lt;/code&gt; — that's the only cost number I'd quote, and the one you should verify on your own account.&lt;/p&gt;

&lt;p&gt;Free-quota terms per official docs (the "valid forever" claim going around is wrong): each model has its own quota (on the order of 1M tokens), 90-day validity, Beijing region only, no renewal after expiry, no auto-switching across models. Unit pricing: per-second billing, set by resolution tier (720P/1080P) and duration — I deliberately quote no dollar figure; use the &lt;a href="https://help.aliyun.com/zh/model-studio/models" rel="noopener noreferrer"&gt;official model page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this route ends
&lt;/h2&gt;

&lt;p&gt;Strongly narrative multi-shot sequences need film-level orchestration — a different route. Single generations are duration-bounded (5s default), so long-form means segments + editing (the generated clips are footage; OpenCut, the 80k+ star open-source CapCut alternative, takes it from there). And copying someone else's work was never on the table — technique out, original content in. Check the model's commercial license terms before client work.&lt;/p&gt;

&lt;p&gt;Setup: Node.js 18+, &lt;code&gt;npm install -g bailian-cli&lt;/code&gt;, then &lt;code&gt;bl auth login&lt;/code&gt; — &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;get an API key in the console&lt;/a&gt;; &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;CLI install guide&lt;/a&gt;; &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;Model Studio console&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Personal practice log. Commands and timings measured 2026-08-17; cost figures should be verified against your own &lt;code&gt;bl usage stats&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Stopped Guessing Which LLM to Use — It's One Command and My Own Numbers Now</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Fri, 14 Aug 2026 11:41:52 +0000</pubDate>
      <link>https://dev.to/zackzhang/i-stopped-guessing-which-llm-to-use-its-one-command-and-my-own-numbers-now-3a44</link>
      <guid>https://dev.to/zackzhang/i-stopped-guessing-which-llm-to-use-its-one-command-and-my-own-numbers-now-3a44</guid>
      <description>&lt;h2&gt;
  
  
  The freelance gig that stalled on question one
&lt;/h2&gt;

&lt;p&gt;Last month I took on a freelance build: a customer-support bot for a DTC store. Three hard constraints — it must understand photos customers upload, sustain ~50k calls a day, and the budget was thin.&lt;/p&gt;

&lt;p&gt;The code wasn't the hard part. Question one was: &lt;strong&gt;which model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I opened the model catalog and stared. The Qwen family alone spans flash/plus/max tiers with dated snapshots; below that, a dozen third-party models. Every one has a spec page and a price sheet. None has a row that says "support bot, image input, cost-sensitive."&lt;/p&gt;

&lt;p&gt;Benchmarks didn't help — they measure someone else's scenario (long-context recall, competition math). Free-tier roundups are static snapshots that rot in a quarter, and they tell you &lt;em&gt;who has quota&lt;/em&gt;, never &lt;em&gt;what your project should run on&lt;/em&gt;. Asking a general-purpose LLM was the worst option: it confidently recommended a model I'd never seen in the catalog. I checked. It doesn't exist. Training memory is the wrong tool for a question whose answer is live data.&lt;/p&gt;

&lt;p&gt;Context worth naming: OmniRoute, a free MIT-licensed AI gateway, crossed 47k GitHub stars this month — one endpoint, 330+ providers. People clearly want model selection to stop being their job. But a gateway solves "route me to whatever's cheap and alive," not "which model fits &lt;em&gt;this&lt;/em&gt; scenario."&lt;/p&gt;

&lt;h2&gt;
  
  
  The one command
&lt;/h2&gt;

&lt;p&gt;What closed the ticket was a subcommand in Model Studio's CLI, &lt;code&gt;bl&lt;/code&gt;. (My previous post was about an agent reading provider docs locally — that solved &lt;em&gt;lookup&lt;/em&gt;; this one hands the &lt;em&gt;decision&lt;/em&gt; to the terminal. They stand alone.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; bailian-cli
bl auth login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node.js 18+. API key at login — &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;get one in the console&lt;/a&gt;; the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;CLI install guide&lt;/a&gt; covers prerequisites.&lt;/p&gt;

&lt;p&gt;Then I wrote the client's constraints as one sentence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl advisor recommend &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"E-commerce support bot that reads product screenshots uploaded by customers, ~50k calls/day, cost is the priority"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten seconds: intent analysis plus three ranked picks. The top one (trimmed from real output):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen3.7-flash-2026-07-15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cost-optimized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contextWindow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The model is specifically designed for cost-optimized, high-concurrency scenarios ... ideal for handling 50,000 daily API calls under a low budget."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"docUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://help.aliyun.com/document_detail/3016807.html"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every pick carries a &lt;code&gt;docUrl&lt;/code&gt; to the official spec-and-pricing page. &lt;strong&gt;Treat the ranking as the starting point, then verify against the doc.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it thinks: three stages
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fmodel-advisor%2Fpipeline-en-1280x720.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fmodel-advisor%2Fpipeline-en-1280x720.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--dry-run&lt;/code&gt; runs the first two stages only — intent analysis and candidate recall — skipping the final LLM ranking (and its cost):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scenarioHints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"high-concurrency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"consumer-facing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low-latency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cost-sensitive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image-input"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text-output"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inputModality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Image"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"budget"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"qualityPreference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cost-optimized"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"candidateCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"candidates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen3.5-27b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.509&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"...50 total"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plain language becomes structured constraints: "understands screenshots" → &lt;code&gt;inputModality: Image&lt;/code&gt;; "~50k calls/day" → &lt;code&gt;high-concurrency&lt;/code&gt;. Candidate recall reads &lt;strong&gt;live catalog data&lt;/strong&gt; — that's the honest difference from asking a chatbot, which will invent a plausible-sounding model that doesn't exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change the scenario, change the answer
&lt;/h2&gt;

&lt;p&gt;My partner's contract-review tooling (legal-tech, precision-first, a few dozen contracts a day) got a completely different result from the same command: &lt;code&gt;budget: medium&lt;/code&gt;, &lt;code&gt;qualityPreference: flagship&lt;/code&gt;, and a top pick of &lt;strong&gt;farui-plus&lt;/strong&gt; — a legal-domain model I'd never heard of, because leaderboard authors don't test vertical-domain legal models.&lt;/p&gt;

&lt;p&gt;That's the dividing line: a leaderboard gives every scenario the same answer; scenario-based selection gives each scenario its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your account's real numbers
&lt;/h2&gt;

&lt;p&gt;One prerequisite that will bite you: the &lt;code&gt;usage&lt;/code&gt;/&lt;code&gt;quota&lt;/code&gt; commands authenticate against the console, not your API key. Key-only auth gets you exactly this (my real output):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No console access token found."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Run `bl auth login --console` or set DASHSCOPE_ACCESS_TOKEN."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix: &lt;code&gt;bl auth login --console&lt;/code&gt; (coexists with your key). Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl usage free &lt;span class="nt"&gt;--expiring&lt;/span&gt; 30          &lt;span class="c"&gt;# quota expiring within 30 days&lt;/span&gt;
bl usage stats &lt;span class="nt"&gt;--days&lt;/span&gt; 30 &lt;span class="nt"&gt;--workspace-id&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="c"&gt;# real usage distribution&lt;/span&gt;
bl quota check &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.7-flash-2026-07-15  &lt;span class="c"&gt;# RPM/TPM headroom&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;usage stats&lt;/code&gt; requires &lt;code&gt;--workspace-id&lt;/code&gt; — &lt;code&gt;bl workspace list&lt;/code&gt; finds it, or pin it with &lt;code&gt;bl config set workspace_id &amp;lt;id&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free-tier traps (checked against the docs)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quota expires&lt;/strong&gt;: 30–90 days depending on model, from activation. Doesn't pause, doesn't renew, doesn't roll over. Hence &lt;code&gt;--expiring 30&lt;/code&gt; in your calendar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-model quota&lt;/strong&gt;: typically 1M tokens each, not mergeable. A dated snapshot and the undated latest are &lt;em&gt;separate models with separate quotas&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No failover on exhaustion&lt;/strong&gt;: calls just start billing (on completed accounts). Pin &lt;code&gt;--model&lt;/code&gt; in scripts at your own risk — or use the kill switch: &lt;code&gt;bl usage freetier --model &amp;lt;m&amp;gt; --on&lt;/code&gt; returns &lt;code&gt;AllocationQuota.FreeTierOnly&lt;/code&gt; instead of charging&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it is not
&lt;/h2&gt;

&lt;p&gt;It recommends within Model Studio's catalog — not a cross-vendor comparison. It produces a ranked starting point, not a verdict. Gateways (OmniRoute et al.) solve routing and availability; this solves scenario fit. Stack them if you like — someone still has to fill the gateway's fallback list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Catalogs and prices shift monthly, so model selection is a &lt;em&gt;recurring&lt;/em&gt; decision, not a one-time one. Making it a repeatable command beats being told the right answer once. Entry point: the &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;Model Studio console&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All commands verified against bailian-cli 1.4.2; outputs shown are real runs. Free-tier terms checked against official docs at publication time.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>One System Prompt, Five Text Jobs: Compliance Screening, Review Tagging and Contract Extraction from the Terminal</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Wed, 12 Aug 2026 09:12:13 +0000</pubDate>
      <link>https://dev.to/zackzhang/one-system-prompt-five-text-jobs-compliance-screening-review-tagging-and-contract-extraction-27j1</link>
      <guid>https://dev.to/zackzhang/one-system-prompt-five-text-jobs-compliance-screening-review-tagging-and-contract-extraction-27j1</guid>
      <description>&lt;h2&gt;
  
  
  The backlog
&lt;/h2&gt;

&lt;p&gt;A friend's small Etsy/Shopify shop got its product page rejected twice for over-claiming — and her keyword-blacklist checker had passed it both times, because those tools match strings, not meaning ("quietest" flags, "quiet-operation benchmark" sails through).&lt;/p&gt;

&lt;p&gt;Fixing that led to counting everything else her shop publishes or receives and never reads: 300+ negative reviews sitting in the dashboard, hundreds of pre-sale messages, a 20-page supplier agreement. Pasting any of it into a chatbot produces prose — "customers seem unhappy with shipping" — which answers nothing you can act on. Which issue? How many? Enterprise review-analytics SaaS answers that for four figures a year, which a one-person shop won't pay.&lt;/p&gt;

&lt;p&gt;Here's what worked instead: Alibaba Cloud Model Studio's CLI (&lt;code&gt;bl&lt;/code&gt;), a system prompt that acts as a JSON schema, and a shell loop. Five kinds of text, one pattern. Show and tell below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fai-text-insight%2Fpipeline-en-1280x720.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fai-text-insight%2Fpipeline-en-1280x720.jpg" width="800" alt="Messy text in, structured data out: the bl text chat pipeline" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; bailian-cli
bl auth login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node 18+. API key from the &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;Model Studio console&lt;/a&gt; — free tier included, and &lt;code&gt;bl usage free&lt;/code&gt; shows what's left. Install guide &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one command everything else is a variation of
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl text chat &lt;span class="nt"&gt;--model&lt;/span&gt; qwen-turbo &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="s2"&gt;"You are a review analyzer. Output JSON only, no extra text. Fields: sentiment(positive/negative/neutral), dimension(quality/shipping/support/price/other), severity(high/medium/low), summary(under 12 words). If undeterminable, use 'unknown' — do not guess."&lt;/span&gt; &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"Stopped heating after three days and support took a week to reply."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"sentiment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"negative"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dimension"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"quality"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"failed in three days, slow support"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three schema rules I learned the hard way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"JSON only, no extra text"&lt;/strong&gt; — otherwise you get a polite "Sure! Here's the analysis:" prefix and your parser dies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enumerate the allowed values&lt;/strong&gt; — otherwise "shipping", "delivery" and "logistics" show up as three different dimensions and your group-by is confetti&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Use 'unknown', don't guess"&lt;/strong&gt; — otherwise a review that just says "fine" gets a confidently invented label&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The loop (this is the whole "batch framework")
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; out
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; line&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;bl text chat &lt;span class="nt"&gt;--model&lt;/span&gt; qwen-turbo &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="s2"&gt;"You are a review analyzer. Output JSON only. Fields: sentiment, dimension(quality/shipping/support/price/other), severity(high/medium/low), summary(under 12 words). Use 'unknown' if undeterminable."&lt;/span&gt; &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; out/results.jsonl
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; reviews.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;300 reviews, ~15 minutes, one JSON per line. Aggregation for small files: feed it back —&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl text chat &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="s2"&gt;"Input is line-delimited JSON of review analyses. Output a stats table: count by dimension descending, high-severity count per group, one-line summary of top three issues."&lt;/span&gt; &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;out/results.jsonl&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For thousands of rows, do the counting in jq or a pivot table instead — deterministic math shouldn't run on a probabilistic model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result that mattered
&lt;/h2&gt;

&lt;p&gt;Shipping damage: 41 mentions. Product quality: 38. Support: 22. Her gut said quality was the top complaint; the data said shipping damage. She took the "41" to her carrier the next morning. &lt;strong&gt;The gap between gut and data is the product here.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fai-text-insight%2Fresult-en-1280x720.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fai-text-insight%2Fresult-en-1280x720.jpg" width="800" alt="Gut feeling vs the data: shipping damage was the real number one" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accuracy, honestly: 28/30 on a manual spot check. The two misses were sarcasm — every model's weak spot, not fixed by paying for a bigger one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same pattern, different system prompts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ad compliance pre-check&lt;/strong&gt;: JSON array of &lt;code&gt;{quote, risk_type, explanation, suggested_rewrite}&lt;/code&gt; — caught an implied health claim ("say goodbye to lung-harming fumes") that no keyword blacklist would ever match. It's a self-check aid, not legal advice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contract clause extraction&lt;/strong&gt;: &lt;code&gt;{payment_terms, liability, dispute_resolution}&lt;/code&gt; with the load-bearing instruction "quote verbatim, never paraphrase" — paraphrased legal text is subtly wrong legal text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support inbox tagging&lt;/strong&gt;: &lt;code&gt;{intent, keyword}&lt;/code&gt; over a few hundred messages; compatibility questions turned out to be half of pre-sale volume, so the compatibility chart moved up the product page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The inverse case&lt;/strong&gt; — one long competitor analysis — flips the model strategy: default flagship, &lt;code&gt;--max-tokens 8000&lt;/code&gt; (default 4096 truncates long reports), and "never fabricate figures" in the system prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bl usage stats --days 7&lt;/code&gt; says the entire evening — 700+ calls — cost pocket change, mostly inside the free tier. The SaaS quote she'd gotten starts at four figures annually.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to do this
&lt;/h2&gt;

&lt;p&gt;Forty reviews? Just read them. Can't write down what "high severity" means? The model can't tag it either. Anything with legal weight? This locates and drafts; a human decides.&lt;/p&gt;

&lt;p&gt;If you've got a text backlog of your own, start with one review and one command: &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;free tier here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Curious: has anyone wired this kind of tagging loop into a scheduled job with drift monitoring? That's the part I haven't built yet.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cli</category>
      <category>productivity</category>
      <category>datascience</category>
    </item>
    <item>
      <title>I Installed a Pile of Agent Skills. It Still Couldn't Hand Me a File.</title>
      <dc:creator>张洲诚（Zack.ZHANG）</dc:creator>
      <pubDate>Mon, 10 Aug 2026 01:58:09 +0000</pubDate>
      <link>https://dev.to/zackzhang/i-installed-a-pile-of-agent-skills-it-still-couldnt-hand-me-a-file-o8d</link>
      <guid>https://dev.to/zackzhang/i-installed-a-pile-of-agent-skills-it-still-couldnt-hand-me-a-file-o8d</guid>
      <description>&lt;h2&gt;
  
  
  The moment that made me look at my setup differently
&lt;/h2&gt;

&lt;p&gt;Last week I needed to ship a post. It needed a cover image with the title rendered &lt;em&gt;into&lt;/em&gt; it, plus an audio version for the team channel.&lt;/p&gt;

&lt;p&gt;I asked my agent. It gave me a completely reasonable answer: use a design tool for the image, find a TTS service for the voiceover, here are some options you might like.&lt;/p&gt;

&lt;p&gt;I had a pile of skills installed at that point. It understood my codebase. It matched my writing style. It could not hand me a single file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything I'd installed was on the input side
&lt;/h2&gt;

&lt;p&gt;If you've been watching GitHub trending, you've seen the same wave I have. In the last week of July, three of the top 20 fastest-growing repos were skills:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;graphify&lt;/strong&gt; — about +4,900 stars that week. Turns your codebase, docs, and PDFs into a queryable knowledge graph. Code parsing runs locally through tree-sitter AST, nothing leaves your machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hallmark&lt;/strong&gt; — about +4,800. Exists purely to strip AI-slop out of design and copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;i-have-adhd&lt;/strong&gt; — about +3,270. Stops the agent from burying the answer under three paragraphs of preamble.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus &lt;strong&gt;find-skills&lt;/strong&gt; sitting north of 60K subscribers, with half the tutorials now opening with "install this one first."&lt;/p&gt;

&lt;p&gt;I lined up what I'd actually installed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;What it handles&lt;/th&gt;
&lt;th&gt;What it produces&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;graphify&lt;/td&gt;
&lt;td&gt;Reads code, docs, PDFs&lt;/td&gt;
&lt;td&gt;Graph data + analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hallmark&lt;/td&gt;
&lt;td&gt;Governs style&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;i-have-adhd&lt;/td&gt;
&lt;td&gt;Governs output structure&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;find-skills&lt;/td&gt;
&lt;td&gt;Finds other skills&lt;/td&gt;
&lt;td&gt;A list&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every one either helps the agent &lt;strong&gt;understand&lt;/strong&gt; something, or helps it &lt;strong&gt;say things better&lt;/strong&gt;. Both genuinely useful. Both stop at text.&lt;/p&gt;

&lt;p&gt;The moment you need an artifact — a PNG, an MP3, an MP4, text lifted out of a screenshot — there's nothing in the toolset. The agent recommending external tools isn't a cop-out. It has no output path.&lt;/p&gt;

&lt;p&gt;The ecosystem has been hot for months and the output side is still close to empty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: why some skills never seem to fire
&lt;/h2&gt;

&lt;p&gt;This one bugged me for a while, and the Claude Code docs answer it directly (as of July 2026):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When the agent starts a session, it builds a manifest of every available skill along with its description, then uses the &lt;code&gt;description&lt;/code&gt; field to decide whether any skill matches the request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two things fall out of that:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;description&lt;/code&gt; is a routing key, not a docstring.&lt;/strong&gt; The agent isn't reading through your skill files inferring intent — it's matching against descriptions. Skills whose descriptions spell out trigger scenarios and keywords get picked far more reliably. If you write your own skills, that field earns more attention than the body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The manifest is built at session start.&lt;/strong&gt; Anything installed mid-conversation may be invisible for the rest of it. Restart the session after installing — that's not superstition, it's how the manifest works.&lt;/p&gt;

&lt;p&gt;Also worth knowing: Claude Code skills follow the &lt;strong&gt;Agent Skills open standard&lt;/strong&gt;, which applies across multiple AI tools. The mental model transfers when you switch harnesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filling the gap
&lt;/h2&gt;

&lt;p&gt;Alibaba's Model Studio team maintains a skills repo (&lt;code&gt;modelstudioai/skills&lt;/code&gt;, Apache-2.0) covering exactly the output side — image, speech, video, vision — all on top of one CLI called &lt;code&gt;bl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Same install motion you already know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add modelstudioai/skills
npx skills add modelstudioai/cli &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node.js 18+, plus an API key (&lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;grab one here&lt;/a&gt;, or read the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;CLI install docs&lt;/a&gt;). Restart the session afterward — see above.&lt;/p&gt;

&lt;p&gt;What changes: &lt;strong&gt;you stop memorizing commands.&lt;/strong&gt; You describe the outcome, the agent assembles the call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cover image with the title rendered in
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl image generate &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"Clean technical article cover, dark background, crisp title text centered in frame, smaller subtitle beneath, faint geometric lines and terminal window motifs, flat design, no clutter"&lt;/span&gt; &lt;span class="nt"&gt;--size&lt;/span&gt; 16:9 &lt;span class="nt"&gt;--model&lt;/span&gt; qwen-image-2.0-pro &lt;span class="nt"&gt;--watermark&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--out-dir&lt;/span&gt; ./covers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both the title and subtitle landed correctly in the composition, and the terminal motifs showed up as asked. Text is part of the image — no second pass to overlay it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A behavior worth knowing about:&lt;/strong&gt; &lt;code&gt;--size&lt;/code&gt; accepts a ratio (&lt;code&gt;16:9&lt;/code&gt;) or pixels joined by an asterisk (&lt;code&gt;1280*720&lt;/code&gt;). Write it the habitual way as &lt;code&gt;1280x720&lt;/code&gt; and the CLI &lt;strong&gt;does not complain locally&lt;/strong&gt; — run &lt;code&gt;--dry-run --output json&lt;/code&gt; and you'll see &lt;code&gt;"size": "1280x720"&lt;/code&gt; sitting in the request body, forwarded as-is. If you're scripting this, normalize upstream. Also &lt;code&gt;--watermark&lt;/code&gt; defaults to true, so clean output needs it set explicitly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Text to speech
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl speech synthesize &lt;span class="nt"&gt;--list-voices&lt;/span&gt; &lt;span class="nt"&gt;--model&lt;/span&gt; cosyvoice-v3-flash
bl speech synthesize &lt;span class="nt"&gt;--text-file&lt;/span&gt; ./intro.txt &lt;span class="nt"&gt;--voice&lt;/span&gt; longxiaochun_v3 &lt;span class="nt"&gt;--rate&lt;/span&gt; 0.9 &lt;span class="nt"&gt;--out&lt;/span&gt; ./intro.mp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can't invent a voice name — run &lt;code&gt;--list-voices&lt;/code&gt; first. The default model returns a couple dozen voices as IDs plus a one-line style note (&lt;code&gt;longxiaochun_v3&lt;/code&gt;, &lt;code&gt;longcheng_v3&lt;/code&gt;, &lt;code&gt;longtian_v3&lt;/code&gt;, …), all bilingual EN/ZH.&lt;/p&gt;

&lt;p&gt;Speech rate is &lt;code&gt;--rate&lt;/code&gt; (0.5–2.0), &lt;strong&gt;not &lt;code&gt;--speed&lt;/code&gt;&lt;/strong&gt; — which is the flag everyone reaches for first, myself included.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading a screenshot
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl vision describe &lt;span class="nt"&gt;--image&lt;/span&gt; ./error-screenshot.png &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"Transcribe the error message and key stack frames verbatim"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command is &lt;code&gt;bl vision describe&lt;/code&gt; — &lt;code&gt;vision&lt;/code&gt; is the group, &lt;code&gt;describe&lt;/code&gt; is the subcommand. Local paths work directly; upload happens inside the command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Video, and one gotcha
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;bl video generate&lt;/code&gt; is async. Pair it with &lt;code&gt;bl video task get&lt;/code&gt; and &lt;code&gt;bl video download --task-id &amp;lt;id&amp;gt; --out &amp;lt;path&amp;gt;&lt;/code&gt;. Note that &lt;code&gt;--poll-interval&lt;/code&gt; defaults differ per command — 5 seconds for &lt;code&gt;video generate&lt;/code&gt;, 15 for &lt;code&gt;video edit&lt;/code&gt; and &lt;code&gt;video ref&lt;/code&gt;. Don't copy one polling value across all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The money part
&lt;/h2&gt;

&lt;p&gt;Output-side calls cost money, and here's the part worth sitting with: &lt;strong&gt;the agent decides how many calls to make.&lt;/strong&gt; Ask for three candidate covers and that's three billable calls. That's a different mental model from text-only skills.&lt;/p&gt;

&lt;p&gt;Set the guardrail before you start playing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl usage freetier &lt;span class="nt"&gt;--all&lt;/span&gt;          &lt;span class="c"&gt;# auto-stop when free quota runs out&lt;/span&gt;
bl usage free &lt;span class="nt"&gt;--expiring&lt;/span&gt; 30      &lt;span class="c"&gt;# console command — run `bl auth login --console` first&lt;/span&gt;
bl usage stats &lt;span class="nt"&gt;--days&lt;/span&gt; 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to verify rather than assume:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free tier is not permanent.&lt;/strong&gt; Validity windows, per-model allocation, and regional eligibility all have specific rules — check the official quota page for your account's region. Third-party "free tier roundup" posts get this wrong often enough that they're not worth trusting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quota does not fail over.&lt;/strong&gt; When one model's allocation is exhausted, nothing routes you to a model that still has budget. You change the model parameter yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Worth it&lt;/strong&gt; if you're already installing skills and regularly need the agent to produce &lt;em&gt;files&lt;/em&gt; — covers, voiceovers, demo clips, text out of screenshots. The flow stays in one place, no shuttling assets between tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skip it&lt;/strong&gt; if your work lives entirely in code and prose. It'll sit unused and still consume manifest context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Different tool&lt;/strong&gt; if you need precise composition control or batch-consistent style — a local ComfyUI node graph wins there, at the cost of a GPU and a workflow to maintain. Not the same problem.&lt;/p&gt;

&lt;p&gt;One genuinely useful side note: that repo also maintains a curated index of third-party skills tracked from Anthropic, Vercel, Google Labs and others, flagged for whether the team actually verified them working, grouped into skill management, code, design, docs, video, and testing. If you're in the hunting phase, that list saves time on its own.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;Console's here&lt;/a&gt; if you want to poke at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The thing this skill wave makes easy to miss: &lt;strong&gt;an agent's ceiling is the ceiling of the tools in its hands.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;graphify gives it eyes for code structure. hallmark gives it taste. The output side lets it actually hand you the thing. None of them compete — you fill whichever gap is costing you.&lt;/p&gt;

&lt;p&gt;Asking for a cover and a voiceover no longer gets me a list of tool recommendations.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Anyone else run into the "installed but never fires" thing? Curious whether the description-matching explanation matches what you've seen.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>productivity</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
