<?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: xiaoru chen</title>
    <description>The latest articles on DEV Community by xiaoru chen (@xiaoru_chen).</description>
    <link>https://dev.to/xiaoru_chen</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%2F4124148%2Fe77855fc-4278-477a-95a5-7f2d41f57e9c.png</url>
      <title>DEV Community: xiaoru chen</title>
      <link>https://dev.to/xiaoru_chen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xiaoru_chen"/>
    <language>en</language>
    <item>
      <title>Cloudflare's security-audit skill rejected my output 7 times</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Wed, 07 Oct 2026 14:21:35 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/cloudflares-security-audit-skill-rejected-my-output-7-times-da3</link>
      <guid>https://dev.to/xiaoru_chen/cloudflares-security-audit-skill-rejected-my-output-7-times-da3</guid>
      <description>&lt;p&gt;Security reports written by agents have a specific smell. The findings are confident, the file references are approximate, and some of them do not survive a ten-minute check. I have thrown away more of these than I have used, mostly because disproving a fabricated vulnerability costs more than finding a real one.&lt;/p&gt;

&lt;p&gt;So when cloudflare/security-audit-skill showed up third on a third-party GitHub weekly ranking the week I was looking at it (+9,047 stars, aggregator numbers rather than an official GitHub list), I read past the pitch. The README opens with "A coding-agent skill that turns your agent into a security auditor." Fine. The part I cared about was two scripts with no dependencies that validate the artifacts the agent writes: &lt;code&gt;validate-coverage-ledger.cjs&lt;/code&gt; and &lt;code&gt;validate-findings.cjs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I installed it, planted three vulnerabilities in a two-file demo repo of my own, and ran a restricted audit. The validators rejected my output seven times before anything counted. This is the show-and-tell version.&lt;/p&gt;

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

&lt;p&gt;The skill is agent-agnostic. Its one requirement is quoted here exactly as written: "A coding agent with a model that supports tool use and parallel sub-agents".&lt;/p&gt;

&lt;p&gt;Mine is Qwen Code with the model set to qwen3.8-max, baseUrl pointing at &lt;code&gt;https://dashscope.aliyuncs.com/compatible-mode/v1&lt;/code&gt; (Alibaba Cloud Model Studio, also known as Bailian). qwen3.8-max supports function calling, so tool use is covered. Keys come 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;console API key page&lt;/a&gt;, region China Beijing.&lt;/p&gt;

&lt;p&gt;Install, copied from the README:&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 https://github.com/cloudflare/security-audit-skill &lt;span class="nt"&gt;--skill&lt;/span&gt; security-audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ended up dropping the skill directory into my workspace's agent skills folder, which is the same thing on disk. Use the command above.&lt;/p&gt;

&lt;p&gt;The repo contains SKILL.md, thirteen attack-surface knowledge files, a report-schema.json, the two validators, and tests. Six phases run in order: Reconnaissance, Coverage-led hunting, Candidate validation, Structured output, Independent record verification, Target-neutral reporting. Three profiles control how much work each phase does. I picked quick, the restricted path for small targets: coarse-grained coverage units, one hunter wave, one critic pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  The target was mine, and the bugs were mine
&lt;/h2&gt;

&lt;p&gt;Two files. Three vulnerabilities I wrote in deliberately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a query parameter concatenated into &lt;code&gt;eval()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a hardcoded key with a live-looking prefix sitting in source&lt;/li&gt;
&lt;li&gt;an upload filename joined into a path with no sanitization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Worth saying out loud: nothing real was audited. The findings below describe my own throwaway code and nothing else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three rejections on the ledger
&lt;/h2&gt;

&lt;p&gt;Phase 1 writes &lt;code&gt;coverage-ledger.json&lt;/code&gt;. My first version was an object wrapping an array, because that is how I would design it. The validator disagreed immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: $: expected a top-level array
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second version, top-level array, and it listed fourteen required fields I had left out: &lt;code&gt;coverage_id&lt;/code&gt;, &lt;code&gt;canonical_refs&lt;/code&gt;, &lt;code&gt;subsystem&lt;/code&gt;, &lt;code&gt;starting_paths&lt;/code&gt;, &lt;code&gt;ordinary_attack_class_block&lt;/code&gt;, among others.&lt;/p&gt;

&lt;p&gt;Third version was rejected on semantics rather than missing fields. &lt;code&gt;coverage_id&lt;/code&gt; is not a label you make up. It is derived: take the unit's surface, boundary, subsystem, and attack class, percent-encode each, join with &lt;code&gt;::&lt;/code&gt;. &lt;code&gt;unresolved&lt;/code&gt; must be an array. And a unit still in &lt;code&gt;planned&lt;/code&gt; state cannot have an agent assigned, so &lt;code&gt;agent_id&lt;/code&gt; stays null until something is actually working on it.&lt;/p&gt;

&lt;p&gt;Fourth version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS: 3 coverage units valid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The derived ID makes sense once you notice that the README describes repeat runs as additive. The skill reads the previous ledger and findings, re-verifies units whose source changed, and carries unchanged evidence forward with its fingerprint. Identity that comes from the unit's coordinates survives a re-run. Identity that comes from whoever wrote the file does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four rejections on the findings
&lt;/h2&gt;

&lt;p&gt;Hunting and falsification went as you would hope, and all three planted bugs reproduced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. eval injection    input: ' || '1'=='1                            -&amp;gt; returned rows: 1 (expected 0)
2. path traversal    resolve('/tmp/uploads/' + '../../etc/passwd')  -&amp;gt; /etc/passwd
3. hardcoded key     grep -n sk-live src/server.js                  -&amp;gt; 3:const API_KEY = 'sk-live-...'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing them into &lt;code&gt;findings.json&lt;/code&gt; is where the skill got picky, across four failed versions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every trace entry needs a &lt;code&gt;kind&lt;/code&gt; of entrypoint, propagation, or sink, plus &lt;code&gt;file&lt;/code&gt;, &lt;code&gt;line&lt;/code&gt;, &lt;code&gt;scope&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;evidence anchors to file and line; quoting a chunk of code is not evidence&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;conditions&lt;/code&gt; is an array&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;execution&lt;/code&gt; block needs &lt;code&gt;attacker_perspective&lt;/code&gt;, &lt;code&gt;payloads&lt;/code&gt;, &lt;code&gt;instructions&lt;/code&gt;, &lt;code&gt;observed_result&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;severity&lt;/code&gt; and &lt;code&gt;confidence&lt;/code&gt; are objects with a score and a reason, and severity additionally needs an &lt;code&gt;overall_severity&lt;/code&gt; from a string enum&lt;/li&gt;
&lt;li&gt;the array sorts lexicographically by &lt;code&gt;fingerprint&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Version five passed: &lt;code&gt;PASS: 3 findings valid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Read that list as a set of questions instead of a schema and it is a decent audit methodology. Where does attacker input enter? How does it reach the sink? What did you run, with what payload, and what came back? How severe, and why do you say so? Prose answers let you skip any of them. Structured fields do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell you before installing it
&lt;/h2&gt;

&lt;p&gt;The seven rejections were never about the model being weak. Every one was container type, missing field, wrong enum, unsorted array, or an ID that had to be derived. That is the point of the design: fabricating a finding means fabricating a trace anchored to a real file and line, an execution block that reproduces, and a severity object that validates. Making things up gets more expensive than checking.&lt;/p&gt;

&lt;p&gt;Also be clear about what my run does not cover. Quick profile on a two-file repo, one time through. Phases 5 and 6 never ran, so there was no independent re-verification of my records and no target-neutral report. I ran hunter and verifier serially in a single agent, which means the parallel sub-agent orchestration the README asks for is untested by me. And the star count is a third-party aggregator's weekly figure.&lt;/p&gt;

&lt;p&gt;If you want the same experience, the loop is short: install the skill, plant two or three bugs in a repo you own, run the quick profile, and count your rejections. Mine was seven, and the number is the useful part, because each one points at a field you thought you could hand-wave.&lt;/p&gt;

&lt;p&gt;Model side needs nothing exotic. The requirement is a coding agent whose model supports tool use and parallel sub-agents; mine runs on qwen3.8-max through Bailian, and I drove the whole audit from one agent. The &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;Bailian CLI (bl)&lt;/a&gt; page covers the CLI and the model list on &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;Alibaba Cloud Model Studio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Has a schema ever talked you out of a finding you were sure about? I am curious whether other people's agents fail on the same fields, or whether &lt;code&gt;overall_severity&lt;/code&gt; being a string enum was just my week.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Missed a Watermark in My Own Photo. A Vision Model Read It Out Loud.</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Thu, 24 Sep 2026 02:52:50 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/i-missed-a-watermark-in-my-own-photo-a-vision-model-read-it-out-loud-2g9j</link>
      <guid>https://dev.to/xiaoru_chen/i-missed-a-watermark-in-my-own-photo-a-vision-model-read-it-out-loud-2g9j</guid>
      <description>&lt;h2&gt;
  
  
  The corner I never looked at
&lt;/h2&gt;

&lt;p&gt;I curated a batch of event photos for a colleague last week. Before sending them over I did my own pass: composition, exposure, stray bystanders, anything embarrassing in the background. All clear, so I hit send.&lt;/p&gt;

&lt;p&gt;The next morning: "what's that line of text in the bottom-right corner?"&lt;/p&gt;

&lt;p&gt;At 300% zoom there it was. A light-gray copyright line, small enough that I had looked straight at that corner several times and registered nothing. Human vision is blind to the unexpected periphery — you look at the subject, and the corners go dark. The annoying part wasn't the miss. It was realizing my "check" had never covered that class of thing at all.&lt;/p&gt;

&lt;p&gt;So I re-ran the batch through the vision describe command in the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;Bailian CLI (bl)&lt;/a&gt;, asking it what text was on screen. It read out every line, including the watermark I missed. Then I did the thing I should always do before trusting a model output that flatters me: I tried to make it fail.&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-vision-watermark%2Fdemo-watermark.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-vision-watermark%2Fdemo-watermark.jpg" width="800" alt="Test image with a hidden light-gray watermark in the bottom-right corner" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  One image proves nothing
&lt;/h2&gt;

&lt;p&gt;Here's the failure mode that actually matters with vision models. It isn't blur, and it isn't misreading a character. It's answering about an image it never really inspected — producing a plausible-sounding description because the question implied one.&lt;/p&gt;

&lt;p&gt;Ask "is there a watermark in this photo?" and a model that wants to be helpful has a cheap path to an answer. If I'd stopped at that one run, I'd have had a screenshot showing it found my watermark, and zero evidence it wasn't just agreeing with me.&lt;/p&gt;

&lt;p&gt;That's why the test needs a control. Same media, one variable changed, same question both times. If the model reads the mark when it's there and says "nothing in the corners" when it isn't, it's looking. If it says "yes, watermark" both times, it's guessing.&lt;/p&gt;

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

&lt;p&gt;I made my own media so I'd know the ground truth exactly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One 900×600 image with an 18px light-gray line tucked into the bottom-right corner, and the identical image with that line removed&lt;/li&gt;
&lt;li&gt;One four-second clip with a white label burned into the bottom-right corner, and the same clip rendered without it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing about these is subtle by accident. The point of a control isn't to find the model's breaking point — it's to establish that it reports presence and absence differently. Difficulty comes later.&lt;/p&gt;

&lt;p&gt;Four runs, four correct verdicts:&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-vision-watermark%2Ffig1-matrix-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%2Fai-vision-watermark%2Ffig1-matrix-en.jpg" width="800" alt="Single-variable test matrix: image with mark, image without, video with label, video without — 4 out of 4 correct" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the mark, it read the headline and the tiny corner line, character for character. Without it, the answer named only the headline and stated plainly that the corners held no watermark and no small text. Same split on video: label present, read aloud; label removed, "no text or watermark at all," followed by a straightforward description of what was on screen in each time range.&lt;/p&gt;

&lt;p&gt;Runs two and four are the ones carrying the weight. A model that fabricates agreement dies there, and this one didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  The command is one line
&lt;/h2&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; ./press-photo.jpg &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"What text is in the image, including tiny corner text?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Local paths go straight in; the CLI handles the upload, so there's no detour through a file host to get a public URL first. Video swaps one flag:&lt;br&gt;
&lt;/p&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;--video&lt;/span&gt; ./clip.mp4 &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"What text appears in the video?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fai-vision-watermark%2Ffig2-image-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%2Fai-vision-watermark%2Ffig2-image-en.jpg" width="800" alt="Real terminal output: the model reading the corner watermark out of the test image" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--image&lt;/code&gt; or &lt;code&gt;--video&lt;/code&gt; depending on the media type, and &lt;code&gt;--prompt&lt;/code&gt; has a working default if you omit it. I still write the question out every time. Vague questions get scene descriptions; specific questions get the inventory you actually wanted. "What text is on screen, including the corners" and "describe this image" return genuinely different documents.&lt;/p&gt;

&lt;p&gt;The default model is qwen3-vl-plus. You can point &lt;code&gt;--model&lt;/code&gt; at another vision model, but my controls only cover the default — if you switch, re-run the matrix. That's four commands, and it's the only way the result means anything for your setup.&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-vision-watermark%2Ffig3-video-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%2Fai-vision-watermark%2Ffig3-video-en.jpg" width="800" alt="Real terminal output: video label read correctly, and the control run reporting no text at all" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I use it for now
&lt;/h2&gt;

&lt;p&gt;Watermark checking was the entry point, not the use case. Once the pattern worked I started pointing it at other things where eyes skip details:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reuse clearance.&lt;/strong&gt; Before publishing someone else's image or clip, ask what text and marks are on screen. Corner watermarks, channel bugs, attribution lines, photographer handles — one pass surfaces all of them, and the answer is text you can keep next to the asset instead of a memory of having checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Archive mining.&lt;/strong&gt; A folder of training recordings or event replays becomes searchable by running "what text appears on screen" across it and filing by what comes back. Titles and lower-thirds get read out without scrubbing a single timeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Document scan double-check.&lt;/strong&gt; Stamps, page numbers, reference codes in the margins of scanned contracts and reports. Have them read aloud, then compare against the body text. The machine is good at "here is everything printed on this page," which is exactly the tedious part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility descriptions.&lt;/strong&gt; Describing what an infographic literally says for a colleague who can't see it. Not a summary of the vibe — the actual text, in place.&lt;/p&gt;

&lt;p&gt;All four are the same capability: marks on screen get missed by eyes and caught by a per-region scan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it stops helping
&lt;/h2&gt;

&lt;p&gt;My proof covers watermark-sized text. The line I hid was 18px light gray and it read it correctly. Smaller, fainter, or lower-contrast against the background — no promise. Run your own controls on your own media before you rely on it.&lt;/p&gt;

&lt;p&gt;Video is understood per frame and per time range, which the output makes obvious: it tells you what was on screen "from 0.2s to 3s." That's sampling, not a continuous stream. My label sat in the corner for the whole four seconds, so it was never at risk. A half-second flash is a different question, and I haven't tested it.&lt;/p&gt;

&lt;p&gt;And it reads what's there, not what's true. A typo in the image gets read aloud faithfully, and a copyright line being legible tells you nothing about whether the license is in order. First pass, not final pass — the last pair of eyes stays human.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it yourself
&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 vision describe &lt;span class="nt"&gt;--image&lt;/span&gt; your-photo.jpg &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"What text is in the image, including tiny corner text?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keys come 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;console API key page&lt;/a&gt; (region: China Beijing). Pick a photo you've looked at a dozen times without really inspecting, ask what text is on it, and see whether the answer includes something you missed. Mine did.&lt;/p&gt;

&lt;p&gt;What's the smallest text a vision model has correctly caught for you — and did you test the negative case, or take the first flattering answer?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Four AI Invoices a Month Taught Me to Audit a Subscription Before Paying for It</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Wed, 23 Sep 2026 01:43:31 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/four-ai-invoices-a-month-taught-me-to-audit-a-subscription-before-paying-for-it-1lmb</link>
      <guid>https://dev.to/xiaoru_chen/four-ai-invoices-a-month-taught-me-to-audit-a-subscription-before-paying-for-it-1lmb</guid>
      <description>&lt;h2&gt;
  
  
  The part of AI tooling nobody demos: the invoices
&lt;/h2&gt;

&lt;p&gt;When I moved from writing to video, the hard part wasn't the craft. It was the overhead. One coding agent for drafts and images, a separate voice service for narration, two more platforms for search and clip generation. Four dashboards, four API keys, four invoices — each metered in a different unit, so reconciling them at the end of the month meant converting tokens, characters, seconds and images into the same currency by hand.&lt;/p&gt;

&lt;p&gt;I started to suspect my side project had become a bookkeeping exercise.&lt;/p&gt;

&lt;p&gt;So when Alibaba Cloud Model Studio upgraded its Token Plan — one subscription, one Credits pool covering text, image, video, speech recognition and realtime voice, plus built-in tools like web search and code interpreter — my first reaction wasn't excitement. It was: does this actually consolidate the mess, or just add a fifth line to it?&lt;/p&gt;

&lt;p&gt;Standard and Pro tiers also bundle Harness perks (free monthly quota and discounts on AgentStudio tools), metered separately so they don't draw from Credits. That's the pitch. I have been burned by a plan that looked generous and went half-unused, so I run three audits before subscribing to anything. Here they are, with the actual commands.&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-token-plan%2Ffig1-plans-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%2Fai-token-plan%2Ffig1-plans-en.jpg" width="800" alt="Token Plan personal tiers: official list price and Credits" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit 1: the price, and a clock that starts when you do
&lt;/h2&gt;

&lt;p&gt;Official list prices for the personal edition: Lite at CNY 60/month with 2,500 Credits, Standard at CNY 180/month with 10,000, Pro at CNY 600/month with 40,000. Top-up packs are CNY 100 each, up to five held at once. Promo prices show up from time to time; whatever the subscription page charges you is the real number.&lt;/p&gt;

&lt;p&gt;The arithmetic on list price works out to roughly CNY 0.024 per Credit on Lite, 0.018 on Standard, 0.015 on Pro — bigger plans are cheaper per unit. That's the part marketing leads with, and it's the part I care about least, because the limit rules decide whether the cheap unit price is real or fictional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A fixed 7-day window&lt;/strong&gt;: the clock starts at your first call of each cycle; hit the cap mid-window and service pauses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No rollover&lt;/strong&gt;: unused Credits expire with the window&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four escape hatches at the cap&lt;/strong&gt;: buy a top-up pack, upgrade tiers, spend a reset charge, or wait out the seven days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency caps&lt;/strong&gt;: 1–2 on Lite, 3–4 on Standard, 6–8 on Pro&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a weekly-throughput subscription, not a monthly stockpile. The practical shift in how I read it: I stopped asking "how many Credits do I need per month" and started asking "how many do I burn per week". If your output is steady, the window is invisible. If you publish once a month and spend a whole week's quota in an afternoon, the window will fight you constantly, and no-rollover turns your leftover balance into a loss rather than an asset.&lt;/p&gt;

&lt;p&gt;My cadence is two to three videos a week — narration, cover art, the occasional stock clip. Judged against the official Credits deduction table, Standard's weekly allowance covers that with headroom for a sudden topic. That judgment, not the unit price, is what made me consider it.&lt;/p&gt;

&lt;p&gt;One scheduling lever worth knowing: a limited-time night window (22:00–08:00) halves Credits consumption on qwen3.8-max and three DeepSeek tiers. Anything that can be deferred effectively costs half, which for batch work is the same thing as doubling your quota. Promo windows change, so check the page rather than trusting my number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit 2: the roster, checked against the live catalog
&lt;/h2&gt;

&lt;p&gt;A plan page listing model names is marketing. A catalog query returning your model ID is evidence. I trust the second one.&lt;/p&gt;

&lt;p&gt;The roster spans five families: Qwen (qwen3.8-max, qwen3.8-flash, the 3.7 line, qwen3.6-flash, qwen-image-3.0-pro, and the qwen-audio-3.0 trio for TTS, ASR and realtime), Wan (wan2.7-image and pro), four DeepSeek v4 variants, Zhipu's glm-5.2/5.3, and HappyHorse 1.1 for image-to-video, text-to-video and reference-to-video.&lt;/p&gt;

&lt;p&gt;I verified eight of those IDs against the live catalog with the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;Bailian CLI (bl)&lt;/a&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 model list &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.8-max &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eight out of eight present, including the two I care about most: &lt;code&gt;qwen-audio-3.0-tts-plus&lt;/code&gt; for narration and &lt;code&gt;happyhorse-1.1-t2v&lt;/code&gt; for clips. This command needs no authentication, which means you can run the whole audit before you have an account, let alone a subscription.&lt;/p&gt;

&lt;p&gt;Then I pulled the plan's TTS voice list — also free information:&lt;br&gt;
&lt;/p&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;--config&lt;/span&gt; token-plan &lt;span class="nt"&gt;--model&lt;/span&gt; qwen-audio-3.0-tts-plus &lt;span class="nt"&gt;--list-voices&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two system voices came back, both bilingual Chinese/English. Whether narration is worth subscribing to is a style question, not a spec question, and this is the cheapest way to answer it before paying.&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-token-plan%2Ffig2-models-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%2Fai-token-plan%2Ffig2-models-en.jpg" width="800" alt="Plan model roster with verification notes" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit 3: the credential that had quietly expired
&lt;/h2&gt;

&lt;p&gt;Audits one and two passed, so I went to run a real 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 text chat &lt;span class="nt"&gt;--config&lt;/span&gt; token-plan &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"Introduce yourself in one sentence"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HTTP 401 InvalidApiKey.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cause was mundane: the subscription key stored on my machine was from a previous period and had long expired. Nothing warned me. The failure arrived as an auth error, which points at the credential while telling you nothing about which of your stored keys lost the argument.&lt;/p&gt;

&lt;p&gt;I also tried to read my Credits consumption directly:&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 token-plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one needs a console login session, and mine had expired too — so the consumption detail is something I'll read on the subscription page and in the console, not from the CLI on a machine that only holds an API key.&lt;/p&gt;

&lt;p&gt;Which means this post contains zero usage impressions. I won't invent them. How natural the TTS actually sounds, whether the image quota survives a real production week — that's the next post, after I refresh the key. Today's post is the audit trail only, and every number in it is either official documentation or something I ran and can show 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%2Fwan27.oss-cn-beijing.aliyuncs.com%2Fbailian_cli_operation%2Fai-token-plan%2Ffig3-verify-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%2Fai-token-plan%2Ffig3-verify-en.jpg" width="800" alt="Catalog verification and the 401 diagnosis, real terminal output" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reusable lesson is smaller than the plan and applies to any subscription: subscription keys and pay-as-you-go keys are different animals with different lifecycles, and expiry sends no notification. Before you pay for a new period, run one cheap call to validate the credential. A 401 means don't pay yet — fix the key, then decide. Keys live on 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;console API key page&lt;/a&gt;, and the region matters: pick China (Beijing).&lt;/p&gt;

&lt;h2&gt;
  
  
  What the three audits are worth
&lt;/h2&gt;

&lt;p&gt;Running them took maybe twenty minutes and one terminal. What they bought me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A per-Credit number I derived myself instead of one quoted at me, plus the limit rules that decide whether it's meaningful&lt;/li&gt;
&lt;li&gt;Proof that eight specific model IDs exist in the catalog today, rather than on a launch slide&lt;/li&gt;
&lt;li&gt;Knowledge that my stored credential was dead — discovered before payment instead of during a production run&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The third one is the reason I keep doing this. Every subscription I've regretted failed at the credential or the cadence, never at the feature list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this fits, who should skip it
&lt;/h2&gt;

&lt;p&gt;Subscribe if your output cadence is steady week to week, your toolchain is fragmented across voice/image/search vendors, and you work inside a coding agent — the plan plugs into mainstream coding agents and clients, so it's a key swap rather than a workflow migration, and the learning cost stays near zero.&lt;/p&gt;

&lt;p&gt;Skip it if you produce once a month (the weekly window plus no-rollover will waste the balance), if you're optimizing for one model's peak quality (a bundle optimizes for coverage), or if several people would share one seat and your parallel workload exceeds the concurrency cap for that tier. Credits are not the only capacity axis; concurrency is, and it's the one that silently queues your jobs.&lt;/p&gt;

&lt;p&gt;If you're on the fence, do what I did: run a shadow month. Take your existing invoices, convert the usage into Credits against the official deduction table, and see which week your real consumption lands in. It costs nothing and it kills the most common mistake — buying a plan that looks generous for the way you work on paper.&lt;/p&gt;

&lt;p&gt;Next post: the hands-on bill, in real Credits, once my key is live.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>cli</category>
    </item>
    <item>
      <title>My First LLM API Call Took 11 Minutes. I Now Hand This Exact Route to Everyone Who Asks</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Tue, 22 Sep 2026 02:04:34 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/my-first-llm-api-call-took-11-minutes-i-now-hand-this-exact-route-to-everyone-who-asks-h8f</link>
      <guid>https://dev.to/xiaoru_chen/my-first-llm-api-call-took-11-minutes-i-now-hand-this-exact-route-to-everyone-who-asks-h8f</guid>
      <description>&lt;p&gt;Every time someone asks me how to make their first LLM API call, I watch them hit the same wall. It is never the code.&lt;/p&gt;

&lt;p&gt;The wall is setup. One tutorial says put the key in an environment variable, another says edit a config file, a third uses a command that doesn't exist on your machine. Three tabs, three incompatible mechanisms, and eventually a 401 that doesn't tell you which of the three lost. Nobody can debug that, because the error points at the credential while the actual problem is that three documents disagreed and the reader had no way to arbitrate.&lt;/p&gt;

&lt;p&gt;So instead of pointing people at tutorials, I wrote down the route I use myself — then re-ran the whole thing end to end this week on my own machine, to be sure I wasn't handing out stale steps. Eleven minutes from minting a key to reading the token bill of a real call. Four steps, one CLI, no SDK.&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-first-call%2Ffig1-flow-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%2Fai-first-call%2Ffig1-flow-en.jpg" width="800" alt="Four steps: get a key, install the CLI, run one command, read the bill" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: mint a key, and pick the region on purpose
&lt;/h2&gt;

&lt;p&gt;Keys come 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;console API key page&lt;/a&gt;. The part almost every write-up skips: &lt;strong&gt;the region matters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Free quota on Alibaba Cloud Model Studio exists only in the China (Beijing) region, and keys are bound to a region. A key minted somewhere else doesn't throw an error, doesn't warn you, doesn't behave oddly — it authenticates fine and quietly bills you instead of drawing down free quota. I found this out the expensive way during my first week, which is why it's now step one of the route rather than a footnote.&lt;/p&gt;

&lt;p&gt;Copy the key when it appears. It's shown in full exactly once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: one install command
&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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;/div&gt;



&lt;p&gt;Mine reports &lt;code&gt;bl 1.27.0&lt;/code&gt; today. Version numbers drift; the command signatures below have been stable across the releases I've used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: the first call is a sentence, not a program
&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;--message&lt;/span&gt; &lt;span class="s2"&gt;"Introduce yourself in one sentence"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first run asks for your API key. If you'd rather not paste it every time, store it 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 the model answers. My actual output this week:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm Qwen, a large language model developed by Alibaba Group, here to help you think, write, and solve problems.&lt;/p&gt;
&lt;/blockquote&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-first-call%2Ffig2-terminal-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%2Fai-first-call%2Ffig2-terminal-en.jpg" width="800" alt="Real terminal output of the first call" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's the whole call. No client object, no request signing, no response parsing, no dependency pinned to a version that conflicts with the rest of your project. One sentence in, one sentence out. For a first contact with a model, that's the right amount of machinery: enough to prove the credential, the endpoint and the account all work, and nothing else in the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: the tier swap that doubles as an honesty test
&lt;/h2&gt;

&lt;p&gt;The default model is the most capable tier in the catalog, and most everyday questions don't need it. One flag moves you down a tier:&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-flash &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"Is today a good day to go outside in Berlin? Two sentences."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I like about the answer isn't the advice, it's the disclaimer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I can't access real-time weather or air quality for Berlin today, so I can't judge reliably. If it's mild and clear, go out; if there's wind, rain or smog, keep it short or protect yourself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No live data, and it says so, then gives you the conditional version. That's a useful first signal about whether a model fabricates. A hallucination-prone one would have cheerfully told me "sunny, 22°C, perfect day" and I'd have spent a week trusting it before noticing.&lt;/p&gt;

&lt;p&gt;My working rule since then: drafts, summaries, classification and quick questions go to the flash tier; hard reasoning and code go back to the default tier. Same key, same command, one flag — which means the switch is a policy decision you can write down, not a refactor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: read what one call costs
&lt;/h2&gt;

&lt;p&gt;Cost intuition is the thing beginners lack most, and they lack it because the bill is invisible by default. One flag fixes that:&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;--message&lt;/span&gt; &lt;span class="s2"&gt;"Answer in one sentence: what is 1+1?"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response carries a usage 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="nl"&gt;"prompt_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;71&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"completion_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;49&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_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;120&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;prompt_tokens&lt;/code&gt; is what my question cost, &lt;code&gt;completion_tokens&lt;/code&gt; what the answer cost. A one-sentence round trip is a little over a hundred tokens. Multiply by the published unit price on the model's page and you can price any call yourself — that 1+1 question landed four decimal places below one cent.&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-first-call%2Ffig3-usage-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%2Fai-first-call%2Ffig3-usage-en.jpg" width="800" alt="Real usage output of a single call" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Doing this on the first call, rather than the fiftieth, changes how the rest of the project gets scoped. You stop reasoning about "AI costs" in the abstract and start reasoning about tokens per request times requests per day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The free-quota rules, stated completely
&lt;/h2&gt;

&lt;p&gt;"Free credits" undersells how specific the rules are. As published:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Valid for &lt;strong&gt;90 days&lt;/strong&gt;, counted from whichever is later: account activation or model release&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per model&lt;/strong&gt;, typically 1M tokens each; quotas do not pool across models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beijing region only&lt;/strong&gt; — which is why step 1 insists on the region&lt;/li&gt;
&lt;li&gt;Unused quota still expires and is not reissued&lt;/li&gt;
&lt;li&gt;Once quota runs out: &lt;strong&gt;verified accounts switch to pay-as-you-go automatically&lt;/strong&gt;; unverified accounts simply stop&lt;/li&gt;
&lt;li&gt;There's a "stop when free quota is exhausted" switch in the console. With it on, an exhausted quota returns &lt;strong&gt;HTTP 403&lt;/strong&gt; instead of a bill&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That switch is the one I'd enable before letting anyone experiment on an account I'm paying for. A 403 is a clear signal at the moment it matters; a line item on next month's invoice is not.&lt;/p&gt;

&lt;p&gt;For your own remaining balance and expiry date, the console quota page is the source of truth. The CLI's quota self-check commands need a console login session, so they won't help you on a fresh machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this route is not
&lt;/h2&gt;

&lt;p&gt;An honest caveat, because I don't want anyone shipping a service this way: a CLI call is a probe, not a production architecture. A service needs typed errors it can match on, a retry and backoff policy it owns, connection reuse, and request shaping that lives in the same runtime as the rest of the code. That's what the SDK or the HTTP API is for, and moving to it should be a deliberate step rather than something you discover during an incident.&lt;/p&gt;

&lt;p&gt;The first call has a different job. It has to succeed quickly, cost nothing unexpected, and leave you able to explain what happened — and one command with a &lt;code&gt;--output json&lt;/code&gt; flag does all three better than a directory of boilerplate ever did for me.&lt;/p&gt;

&lt;p&gt;Install docs and the full command list: &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;Bailian CLI (bl)&lt;/a&gt;. Image, speech and video live in the same CLI under other command groups, with the same rhythm: read &lt;code&gt;--help&lt;/code&gt;, run one command, read the output.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>llm</category>
      <category>cli</category>
    </item>
    <item>
      <title>An Open-Source Code Review Tool, 7 Qwen Tiers, and the Real Cost of a Single PR</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Mon, 21 Sep 2026 01:45:39 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/an-open-source-code-review-tool-7-qwen-tiers-and-the-real-cost-of-a-single-pr-5gob</link>
      <guid>https://dev.to/xiaoru_chen/an-open-source-code-review-tool-7-qwen-tiers-and-the-real-cost-of-a-single-pr-5gob</guid>
      <description>&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%2Fw56fo0t7d2voyrljd1bs.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%2Fw56fo0t7d2voyrljd1bs.jpg" alt="An open-source code review tool with 26k+ stars reviewing a Python file" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The tool that made me curious
&lt;/h2&gt;

&lt;p&gt;This week, Alibaba open-sourced &lt;a href="https://github.com/alibaba/open-code-review" rel="noopener noreferrer"&gt;open-code-review&lt;/a&gt; (CLI name &lt;code&gt;ocr&lt;/code&gt;). It climbed to #1 on GitHub Trending with +15,028 stars in a week. Apache-2.0, Go binary, and — here is the part that caught my attention — it ships a built-in provider named &lt;code&gt;dashscope&lt;/code&gt; whose base URL is DashScope's OpenAI-compatible endpoint, with Qwen models pre-listed.&lt;/p&gt;

&lt;p&gt;No custom provider file to write. No YAML to assemble. Pick a provider, pick a model, and it works.&lt;/p&gt;

&lt;p&gt;So I set up a single-variable experiment: one buggy file, seven Qwen tiers, and a real cost table. Not a per-million-token sticker price — the actual bill for one review.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the tool does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ocr&lt;/code&gt; reads your code through tool calling (function calling). The model must natively support it; the project FAQ explicitly names &lt;code&gt;qwen3&lt;/code&gt; as working. Models that merely narrate tool calls in text output cannot drive it, regardless of how clever the prompt is.&lt;/p&gt;

&lt;p&gt;It has four modes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ocr review                                        &lt;span class="c"&gt;# review staged/unstaged/untracked changes&lt;/span&gt;
ocr review &lt;span class="nt"&gt;--from&lt;/span&gt; main &lt;span class="nt"&gt;--to&lt;/span&gt; feature-branch         &lt;span class="c"&gt;# review a branch range&lt;/span&gt;
ocr review &lt;span class="nt"&gt;--commit&lt;/span&gt; abc123                         &lt;span class="c"&gt;# review a single commit&lt;/span&gt;
ocr scan &lt;span class="nt"&gt;--path&lt;/span&gt; some/dir                          &lt;span class="c"&gt;# full-file scan, no git history needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Three things to know before you run it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;DASHSCOPE_API_KEY&lt;/code&gt; alone is not enough.&lt;/strong&gt; The resolver wants the full tuple of (URL, key, model). The env var supplies only the key. You still need &lt;code&gt;ocr config set provider dashscope&lt;/code&gt; and &lt;code&gt;ocr config set model &amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The provider is built-in.&lt;/strong&gt; &lt;code&gt;internal/llm/providers.go&lt;/code&gt; in the repo defines &lt;code&gt;dashscope&lt;/code&gt; with base URL &lt;code&gt;https://dashscope.aliyuncs.com/compatible-mode/v1&lt;/code&gt; and a model list that starts with five Qwen models. No need to register a custom provider.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Region matters.&lt;/strong&gt; API keys are region-bound. A Beijing key against a Virginia endpoint returns 401. If you migrated to a workspace domain, override the URL with &lt;code&gt;ocr config set providers.dashscope.url https://&amp;lt;WorkspaceId&amp;gt;.cn-beijing.maas.aliyuncs.com/compatible-mode/v1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The shortest path to a working setup:&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; @alibaba-group/open-code-review
ocr config &lt;span class="nb"&gt;set &lt;/span&gt;provider dashscope
ocr config &lt;span class="nb"&gt;set &lt;/span&gt;model qwen3.8-max
ocr config &lt;span class="nb"&gt;set &lt;/span&gt;providers.dashscope.api_key sk-your-key
ocr llm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://bailian.console.aliyun.com/?spm=content.ops.key_github#/api-key" rel="noopener noreferrer"&gt;Create an API key in the Bailian console&lt;/a&gt; before the fourth command.&lt;/p&gt;

&lt;h2&gt;
  
  
  The experiment
&lt;/h2&gt;

&lt;p&gt;One 64-line Python order service with four planted defects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;fetchone()&lt;/code&gt; result used without a null check (crashes on missing user)&lt;/li&gt;
&lt;li&gt;An f-string interpolated into SQL (textbook injection)&lt;/li&gt;
&lt;li&gt;A counter &lt;code&gt;+= 1&lt;/code&gt; without lock acquisition (concurrency bug)&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;open()&lt;/code&gt; without &lt;code&gt;with&lt;/code&gt; (handle leak)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus two decoys: an &lt;code&gt;@lru_cache&lt;/code&gt; on a pure function (correct usage) and a redundant &lt;code&gt;except Exception: raise&lt;/code&gt; (harmless, and the bare &lt;code&gt;raise&lt;/code&gt; correctly preserves the traceback).&lt;/p&gt;

&lt;p&gt;Same prompt, byte-identical input, seven Qwen tiers. Token counts from the API's own &lt;code&gt;usage&lt;/code&gt; block. Unit prices from &lt;code&gt;bl model list --model &amp;lt;name&amp;gt; --output json&lt;/code&gt;, a command that needs no account and no auth, so every price is checkable.&lt;/p&gt;

&lt;p&gt;Prices are in CNY.&lt;/p&gt;

&lt;h2&gt;
  
  
  The table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Thinking tokens&lt;/th&gt;
&lt;th&gt;Found /4&lt;/th&gt;
&lt;th&gt;Missed&lt;/th&gt;
&lt;th&gt;False +&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Wall time&lt;/th&gt;
&lt;th&gt;In tok&lt;/th&gt;
&lt;th&gt;Out tok&lt;/th&gt;
&lt;th&gt;Cost per review&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;qwen-turbo&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;-2.0&lt;/td&gt;
&lt;td&gt;7.4s&lt;/td&gt;
&lt;td&gt;468&lt;/td&gt;
&lt;td&gt;1,005&lt;/td&gt;
&lt;td&gt;¥0.0007&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3-coder-plus&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;10.1s&lt;/td&gt;
&lt;td&gt;464&lt;/td&gt;
&lt;td&gt;712&lt;/td&gt;
&lt;td&gt;tiered / see console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen-plus&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1.5&lt;/td&gt;
&lt;td&gt;39.3s&lt;/td&gt;
&lt;td&gt;464&lt;/td&gt;
&lt;td&gt;1,766&lt;/td&gt;
&lt;td&gt;¥0.0039&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3.7-plus&lt;/td&gt;
&lt;td&gt;1,182&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;23.4s&lt;/td&gt;
&lt;td&gt;502&lt;/td&gt;
&lt;td&gt;2,052&lt;/td&gt;
&lt;td&gt;not published / see console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3.6-flash&lt;/td&gt;
&lt;td&gt;4,258&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3.0&lt;/td&gt;
&lt;td&gt;42.6s&lt;/td&gt;
&lt;td&gt;502&lt;/td&gt;
&lt;td&gt;5,075&lt;/td&gt;
&lt;td&gt;not published / see console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3.8-flash&lt;/td&gt;
&lt;td&gt;9,517&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;241.0s&lt;/td&gt;
&lt;td&gt;540&lt;/td&gt;
&lt;td&gt;12,909&lt;/td&gt;
&lt;td&gt;¥0.0353&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3.8-max&lt;/td&gt;
&lt;td&gt;9,770&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;293.1s&lt;/td&gt;
&lt;td&gt;540&lt;/td&gt;
&lt;td&gt;11,064&lt;/td&gt;
&lt;td&gt;¥0.4048&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One run per configuration. Score is found minus false positives, out of 4. Three cells say "see console": &lt;code&gt;qwen3-coder&lt;/code&gt; is officially tiered-priced, and &lt;code&gt;qwen3.7-plus&lt;/code&gt; / &lt;code&gt;qwen3.6-flash&lt;/code&gt; return no unit price from the lookup command. I do not fill gaps with guesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things I did not expect
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The flagship bought nothing on this file.&lt;/strong&gt; qwen3.7-plus scored 4.0 in 23.4 seconds. qwen3.8-max also scored 4.0, in 293.1 seconds. The extra 8,588 thinking tokens and 270 seconds bought three genuine findings I had not planted (including that &lt;code&gt;amount &amp;lt;= 0&lt;/code&gt; does not reject &lt;code&gt;float('nan')&lt;/code&gt;), but no extra points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The divider is thinking, not price.&lt;/strong&gt; Three tiers that did not think all missed at least one bug. Five that thought missed nothing. Past roughly 1,200 thinking tokens the score stops moving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line numbers are only trustworthy on thinking tiers.&lt;/strong&gt; turbo was 0 of 9 correct, plus 0 of 4. qwen3.7-plus and above were accurate to within one line. Treat a non-thinking tier's output as a lead list and find the code yourself.&lt;/p&gt;

&lt;p&gt;One observation worth noting: qwen-plus missed the null deref, qwen3-coder-plus missed the race condition. Their blind spots did not overlap. Running both cross-reads the gaps, at the cost of stacked false positives and a human in the loop. Seven configurations measured once each — this is an observation, not a rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit price versus unit cost
&lt;/h2&gt;

&lt;p&gt;qwen-turbo costs ¥0.3 per million input tokens. qwen3.8-max costs ¥12. That is 40x on the pricing page. Per review, turbo spent ¥0.0007 and max spent ¥0.4048, which is 545x, because the expensive run wrote eleven thousand output tokens and most of them were reasoning.&lt;/p&gt;

&lt;p&gt;Budget from the pricing page and your invoice will not agree. Budget from a real review.&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-code-review-cost%2Funit-econ.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-code-review-cost%2Funit-econ.jpg" width="800" alt="Balance scale weighing a price tag against thinking tokens" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would pick
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Per review&lt;/th&gt;
&lt;th&gt;Reviews per ¥1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Coarse sieve on every commit, you read the code anyway&lt;/td&gt;
&lt;td&gt;qwen-turbo&lt;/td&gt;
&lt;td&gt;¥0.0007&lt;/td&gt;
&lt;td&gt;≈1,345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cheap pass with a published price&lt;/td&gt;
&lt;td&gt;qwen-plus&lt;/td&gt;
&lt;td&gt;¥0.0039&lt;/td&gt;
&lt;td&gt;≈256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default PR gate, line numbers you can jump to&lt;/td&gt;
&lt;td&gt;qwen3.7-plus&lt;/td&gt;
&lt;td&gt;≤¥0.0799 (bound)&lt;/td&gt;
&lt;td&gt;price unpublished&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hard diff, deep reasoning&lt;/td&gt;
&lt;td&gt;qwen3.8-flash&lt;/td&gt;
&lt;td&gt;¥0.0353&lt;/td&gt;
&lt;td&gt;≈28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The gnarliest file in the repo&lt;/td&gt;
&lt;td&gt;qwen3.8-max&lt;/td&gt;
&lt;td&gt;¥0.4048&lt;/td&gt;
&lt;td&gt;≈2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The ¥0.0799 is a bound, not an estimate: priced at flagship rates, that run's tokens come to ¥0.0799, one fifth of the flagship's ¥0.4048, and its real price cannot be higher. For tiers with no published price, check the console model pages rather than any reposted number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recalculate it yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;decimal&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROUND_HALF_UP&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in_tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price_in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price_out&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;prices in CNY per million tokens&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in_tok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price_in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out_tok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price_out&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ROUND_HALF_UP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;540&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11064&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;12&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;36&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# 0.4048: qwen3.8-max, one review
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;Decimal&lt;/code&gt; with &lt;code&gt;ROUND_HALF_UP&lt;/code&gt;, because Python's &lt;code&gt;round()&lt;/code&gt; does banker's rounding and your invoice will not explain the gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Boundaries and honest limits
&lt;/h2&gt;

&lt;p&gt;A community member reported 1,400 PRs taking over ten hours. A few-line PR still takes minutes. This is not a pre-commit tool. It does not replace SAST: a rules engine reports deterministic pattern hits, while &lt;code&gt;ocr&lt;/code&gt; reports what a model understood about your code. And if your team already pays for a hosted review product that works, this is not a pitch to switch.&lt;/p&gt;

&lt;p&gt;Sample size is one file, four bugs, one run per configuration. I ran two of the models twice and their scores moved. Read every number above as "this run", not as a benchmark.&lt;/p&gt;

&lt;p&gt;The new-user free quota is per model and independent (typically 1M tokens each), valid for 90 days, Beijing region only, with no reissue after expiry and no automatic failover to another model when it runs out. &lt;a href="https://bailian.console.aliyun.com/?spm=content.ops.key_github#/api-key" rel="noopener noreferrer"&gt;Create the key here&lt;/a&gt;. &lt;a href="https://bailian.console.aliyun.com/?spm=content.ops.cli_github" rel="noopener noreferrer"&gt;Install the Bailian CLI&lt;/a&gt; to look up prices on your own machine with no account required.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>llm</category>
    </item>
    <item>
      <title>Three Models, One Verdict: The Cheapest Code Review I Have Ever Run</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Sun, 20 Sep 2026 05:22:50 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/three-models-one-verdict-the-cheapest-code-review-i-have-ever-run-21ip</link>
      <guid>https://dev.to/xiaoru_chen/three-models-one-verdict-the-cheapest-code-review-i-have-ever-run-21ip</guid>
      <description>&lt;p&gt;TL;DR: every number below came out of my own terminal on 2026-09-18, three &lt;code&gt;bl text chat&lt;/code&gt; calls, total cost under two US cents of compute at marketplace unit prices (labeled estimate, usage fields not captured).&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap nobody prices
&lt;/h2&gt;

&lt;p&gt;One model telling you "no bugs found" is worth almost nothing. You have no idea whether it looked or just agreed with itself. Two models that never talked to each other, naming the same three lines independently, is a different kind of evidence entirely. That second kind used to cost you a colleague's afternoon. It now costs a few cents.&lt;/p&gt;

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

&lt;p&gt;A snippet with three real defects planted: skipped first element, divide-by-zero on empty input, and a discount function returning the discount amount instead of the discounted price. I did not tell the models what to look for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three commands
&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-flash &lt;span class="nt"&gt;--timeout&lt;/span&gt; 120 &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;code&amp;gt; List only real problems, one per line, no explanation"&lt;/span&gt;
bl text chat &lt;span class="nt"&gt;--model&lt;/span&gt; kimi-k3 &lt;span class="nt"&gt;--timeout&lt;/span&gt; 150 &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;code&amp;gt; List only real problems, one per line, no explanation"&lt;/span&gt;
bl text chat &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.8-max &lt;span class="nt"&gt;--timeout&lt;/span&gt; 150 &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;code + both reviews&amp;gt; False positives or misses? Which fix first? One line of fix code"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reviewer A took 98 seconds and returned three lines. Reviewer B took 18 seconds and returned the same three lines plus a fix for the third. The judge took 104 seconds, confirmed neither review had false positives or misses, picked the divide-by-zero as first-to-fix, and produced &lt;code&gt;return sum(prices) / len(prices) if prices else 0&lt;/code&gt;. I checked that line by hand: it fixes both the skipped element and the division by zero.&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%2Fk27hvryen3674alegn5s.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%2Fk27hvryen3674alegn5s.jpg" alt="Three models cross-reviewing" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agreement beats confidence
&lt;/h2&gt;

&lt;p&gt;The judge's most useful output was not the verdict. It was the ordering. "Fix this one first" is exactly the judgment that consumes human reviewer time; finding the lines is the cheap part. Getting that ordering from a third model that read both opinions is a workflow, not a demo.&lt;/p&gt;

&lt;p&gt;Two boundaries, stated plainly. This is a review workflow, not a safety mechanism; a human still reads the verdict. And review-style prompts make models think for one to two minutes, so keep &lt;code&gt;--timeout&lt;/code&gt; in the command or a non-streamed long-thinking request will look hung. I lost ten minutes to that one before I understood it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it, then argue with it
&lt;/h2&gt;

&lt;p&gt;The CLI is on the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;install page&lt;/a&gt;; from the second call you need an 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;console&lt;/a&gt;, and new accounts carry free quota. The platform overview is &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you want the wider picture first.&lt;/p&gt;

&lt;p&gt;If your roundtable catches something mine missed, or your judge overrules two agreeing reviewers, I want that case. Three opinions are only worth what the fourth one finds.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cli</category>
      <category>llm</category>
    </item>
    <item>
      <title>I Ran the Viral AI-Novel Workflow on a CLI: 800 Words for Half a Cent</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Sun, 20 Sep 2026 02:16:42 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/i-ran-the-viral-ai-novel-workflow-on-a-cli-800-words-for-half-a-cent-1l5e</link>
      <guid>https://dev.to/xiaoru_chen/i-ran-the-viral-ai-novel-workflow-on-a-cli-800-words-for-half-a-cent-1l5e</guid>
      <description>&lt;p&gt;TL;DR: on 2026-09-18 I reproduced a high-voted Chinese Q&amp;amp;A workflow for AI fiction on Alibaba Cloud Model Studio's CLI (&lt;code&gt;bl&lt;/code&gt;). Skeleton in one call (100 tokens in / 505 out), 798-character opening in a second (158 in / 7,728 out). Total ≈ ¥0.022, about $0.003, computed from measured usage × marketplace unit prices, not an invoice. The third step of the original workflow is human editing, and my attempt to delegate it timed out twice — which turned out to be the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow I copied
&lt;/h2&gt;

&lt;p&gt;The top answer under "Can AI actually write novels?" prescribes three steps: lift the skeleton from bestselling shorts, fill the flesh with a persona prompt, then hand-edit three spots (opening hook, reversal logic, emotional landing). No tool named. I used &lt;code&gt;bl text chat&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Step one asks for structure only:&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-flash &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;premise&amp;gt; output a skeleton under 100 words, structure only, no prose"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fourteen seconds: a four-beat skeleton — lowball offer, papers falling out of the piano lid, a recording hidden in the strings, the buyback. Skeletons are diffable and reviewable; prose is not. That is the first reason this pipeline survives contact with a production process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step two: persona in, opening out
&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-flash &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="s2"&gt;"web-serial author, six years: colloquial, fast, core conflict inside 300 chars, one reversal per 800 words"&lt;/span&gt; &lt;span class="nt"&gt;--messages-file&lt;/span&gt; msgs.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The opening came back at 798 Chinese characters: layoff in paragraph one, a haggling scene by paragraph three. 158 tokens in, 7,728 out.&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%2F0kffuh4ippp6m2z60qug.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%2F0kffuh4ippp6m2z60qug.jpg" alt="terminal capture of both calls with usage lines" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two traps worth your time. First, if you build the messages file with a quoted shell heredoc, &lt;code&gt;$(cat outline.txt)&lt;/code&gt; never expands; the model receives the literal string and invents its own premise. My first run produced a transmigration romance unrelated to my input, 7,537 output tokens of it. Assemble the JSON in a script. Second, I tried delegating the rewrite step: a reasoning model given the full draft timed out past two minutes twice, and the fast model timed out on 600 characters. Long input plus a thinking model plus a non-streamed CLI call is slow. The original workflow's third step is human for a reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the numbers do and do not say
&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Futg3idpceyqp1cu3k3wh.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%2Futg3idpceyqp1cu3k3wh.jpg" alt="cost card: step 1 ≈¥0.0014, step 2 ≈¥0.021, total ≈¥0.022" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this cost the generation step stops being a cost line at all; the human checkpoint and the premise selection are the budget. The answer's author claims six years of short-fiction income and a mentee paid on day ten; those are his self-reports. The durable lines are the other ones: platforms reject pure-AI slush, not AI-assisted drafts; community consensus puts plot coherence collapse past roughly 50k characters; and per-thousand-character rates are a market range, not a promise.&lt;/p&gt;

&lt;p&gt;The CLI is on the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;install page&lt;/a&gt;; step two needs an 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;console&lt;/a&gt;, and new accounts carry free quota — check your console for what remains. Platform entry point is &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If your serial-fiction workflow has a skeleton step I'm missing, or a checkpoint you'd put elsewhere, I want that diff. Half a cent buys you the right to argue with this pipeline from experience.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>writing</category>
      <category>cli</category>
    </item>
    <item>
      <title>Panel 2 Is Where AI Comics Die: Character Consistency, Measured</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Fri, 18 Sep 2026 03:14:49 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/panel-2-is-where-ai-comics-die-character-consistency-measured-4b2d</link>
      <guid>https://dev.to/xiaoru_chen/panel-2-is-where-ai-comics-die-character-consistency-measured-4b2d</guid>
      <description>&lt;p&gt;TL;DR: I drew a comic character with one command, asked for the same character in a second scene, and got a stranger back wearing her hoodie. This post is the run that fixed it: four CLI commands, four panels, one lip-synced clip, every step stamped with its price and wall-clock time on 2026-09-17. The panel pipeline cost about eleven US cents.&lt;/p&gt;

&lt;p&gt;The pitch you keep hearing is that AI comic drama is the next gold rush. The pitch is not wrong about demand, it is wrong about where the work actually is. Panel one is free-ish and easy; panel two is where your character dies, and almost no beginner tutorial says so out loud.&lt;/p&gt;

&lt;p&gt;I'm not here to adjudicate the gold-rush claim. I can't verify anyone's view counts, so I won't cite them. What I can hand you is a measured run on my own machine, with the failure included.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the gold rush looks like from inside
&lt;/h2&gt;

&lt;p&gt;Before the technique, one piece of context that changes what this topic is. I pulled Zhihu's site-wide hot list on 2026-09-17, thirty items: zero comic-drama entries. Of the seven existing Zhihu questions about AI comic drama, five ask "can I make money from this" and none ask about technique. This is a livelihood vertical, not a mass spectacle, and the people in it ask about income first.&lt;/p&gt;

&lt;p&gt;The stratification is blunt. Consumption is still growing: the August top-100 playback increment ran +44% month over month (second-hand, via DataEye). The supply-side dividend is closed: industry tracking puts active production studios at 1,216 falling to 698 within Q1, and revenue per 10k views down roughly tenfold (both second-hand). Tutorials are oversupplied and homogeneous. The empty niche is not "how to draw a panel"; it is what happens after you draw one, which is compliance and cost accounting. Both are below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap nobody's tutorial names
&lt;/h2&gt;

&lt;p&gt;Ask an image model for "a girl with round wire glasses, a bright yellow hoodie, black bob with straight bangs" and you get a perfectly good character sheet. Ask for that same girl on a rooftop at night and you get a different girl: glasses gone, hair a shade off, hoodie redesigned, face subtly someone else's. One broken panel and every panel after it is waste, because a comic is a sequence and a sequence needs one face.&lt;/p&gt;

&lt;p&gt;That gap, between one nice illustration and an actual comic, is the whole game.&lt;/p&gt;

&lt;p&gt;My setup, pinned so you can reproduce or distrust it: Alibaba Cloud Model Studio CLI &lt;code&gt;bl&lt;/code&gt; 1.22.0, image model &lt;code&gt;qwen-image-3.0&lt;/code&gt;, TTS &lt;code&gt;cosyvoice-v3-flash&lt;/code&gt;, video &lt;code&gt;wan3.0-video&lt;/code&gt;. Unit prices read from &lt;code&gt;bl model list --model &amp;lt;name&amp;gt; --output json&lt;/code&gt;, not estimated. Runs happened 2026-09-17, 20:41 to 20:48 local time.&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%2Fc1q9tj7i7c1cxaioa7pz.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%2Fc1q9tj7i7c1cxaioa7pz.jpg" alt="The four-step chain" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: write the identity down before you draw it
&lt;/h2&gt;

&lt;p&gt;Vague features cannot be locked later. Three identifiable traits go into the prompt, and two flags keep the output usable downstream:&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;"anime-style character sheet, a girl with round wire glasses, a bright yellow hoodie, black bob hair with straight bangs, half body, front view, calm slightly curious expression, clean lineart, cel shading, pure white background, no text"&lt;/span&gt; &lt;span class="nt"&gt;--model&lt;/span&gt; qwen-image-3.0 &lt;span class="nt"&gt;--size&lt;/span&gt; 3:4 &lt;span class="nt"&gt;--watermark&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--prompt-extend&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--out-dir&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--out-prefix&lt;/span&gt; char-sheet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--watermark false&lt;/code&gt;, or every downstream panel carries a mark. &lt;code&gt;--prompt-extend false&lt;/code&gt;, because prompt expansion invents details and a character sheet needs precision, not surprise.&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%2Ftrs0441an3l4x97m1h3d.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%2Ftrs0441an3l4x97m1h3d.jpg" alt="Character sheet" width="800" height="1096"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cost: ¥0.18, roughly two US cents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: the lock is a reference image, not a seed
&lt;/h2&gt;

&lt;p&gt;Here is my failure, in full, because it is the failure most tutorials hand you as advice. My first instinct was &lt;code&gt;--seed&lt;/code&gt;. Fix the seed, I reasoned, and the character stays fixed. It does not. A seed reproduces the same image from the same prompt; change the scene description and you get a different person wearing similar clothes. I burned two panels learning that the seed is a reproducibility switch, not an identity switch.&lt;/p&gt;

&lt;p&gt;The identity switch is reference-image editing. Feed the sheet from step 1 as &lt;code&gt;--image&lt;/code&gt;, and in the prompt change only scene and composition while stating that face, hair, glasses and clothing stay identical:&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; ./char-sheet.png &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"keep this character's face, hair, round glasses and bright yellow hoodie exactly unchanged; change only scene and composition: she stands at the edge of a rooftop at night, in profile, looking up; background is bokeh city lights and deep blue night sky; comic panel framing, cel shading"&lt;/span&gt; &lt;span class="nt"&gt;--model&lt;/span&gt; qwen-image-3.0 &lt;span class="nt"&gt;--size&lt;/span&gt; 3:4 &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; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--out-prefix&lt;/span&gt; panel-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;61 seconds later:&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%2Fqjfcgx38ilqvf6ah6stb.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%2Fqjfcgx38ilqvf6ah6stb.jpg" alt="Panel 1: rooftop at night" width="800" height="1096"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I ran the same sheet through two more scenes to check whether the lock holds or whether I got lucky once:&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%2F3d0yud4i32diio1nsj4u.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%2F3d0yud4i32diio1nsj4u.jpg" alt="Panel 2: rainy crossing" width="800" height="1096"&gt;&lt;/a&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foicmjettce33freyt2zt.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%2Foicmjettce33freyt2zt.jpg" alt="Panel 3: desk at night" width="800" height="1096"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Glasses, hoodie, bob, face shape: preserved across all three. The outputs even came back with comic panel borders. Per panel: ¥0.20 (¥0.18 edit plus ¥0.02 input), 61 to 70 seconds.&lt;/p&gt;

&lt;p&gt;Scope, stated plainly because overclaiming here would be worse than useless: tested is three scenes and four poses (half body, profile, head-down, three-quarter). Untested is cumulative drift past three scenes, full-body action, costume changes, extreme close-ups, multi-character frames. Treat the tested range as tested.&lt;/p&gt;

&lt;p&gt;One upgrade path and one trap from the wider community, both consistent with what I measured. The community consensus for consistency is layered stacking, and the turnaround or multi-angle character sheet is its accepted first step; my single sheet held three scenes, and a multi-angle sheet is the recommended upgrade once your storyboard multiplies angles beyond what one view can vouch for. The trap: accessories vanish unless you repeat them in the prompt. A first-hand overseas comparison of single-character references found exactly this, and it matches my own outputs, where the hoodie drawstrings survived only because the prompt kept naming the hoodie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: the voice line, and the alignment trap
&lt;/h2&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;--text&lt;/span&gt; &lt;span class="s2"&gt;"原来城市晚上，这么亮啊。"&lt;/span&gt; &lt;span class="nt"&gt;--voice&lt;/span&gt; longcheng_v3 &lt;span class="nt"&gt;--format&lt;/span&gt; mp3 &lt;span class="nt"&gt;--out&lt;/span&gt; voice-1.mp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Twelve Chinese characters, 3.264 seconds of audio, about ¥0.01. List voices with &lt;code&gt;--list-voices&lt;/code&gt; instead of guessing IDs.&lt;/p&gt;

&lt;p&gt;The trap: audio length and clip length are independent. My line is 3.26 seconds; in a 5-second clip the character spends the last 1.7 seconds with a closed mouth. Write dialogue that fills the duration, or take the silence as breathing room. Knowing which one you chose is the difference between direction and a bug you cannot find.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 (optional): make her talk
&lt;/h2&gt;

&lt;p&gt;Movement plus lip sync in one command, with &lt;code&gt;--image&lt;/code&gt; and &lt;code&gt;--image-voice&lt;/code&gt; paired by position and referenced in the prompt as &lt;code&gt;Image 1&lt;/code&gt; and &lt;code&gt;Audio 1&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 video ref &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"Image 1 stands on the rooftop at night, looking up; night wind moves her hair and hoodie drawstrings; she speaks: Audio 1. Camera slowly pushes in; city lights bokeh and flicker"&lt;/span&gt; &lt;span class="nt"&gt;--image&lt;/span&gt; ./panel-1.png &lt;span class="nt"&gt;--image-voice&lt;/span&gt; ./voice-1.mp3 &lt;span class="nt"&gt;--model&lt;/span&gt; wan3.0-video &lt;span class="nt"&gt;--duration&lt;/span&gt; 5 &lt;span class="nt"&gt;--resolution&lt;/span&gt; 720P &lt;span class="nt"&gt;--ratio&lt;/span&gt; 9:16 &lt;span class="nt"&gt;--watermark&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--download&lt;/span&gt; clip-1.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two flags will bite you. &lt;code&gt;--resolution&lt;/code&gt; defaults to 1080P, the expensive tier; write &lt;code&gt;720P&lt;/code&gt; on your first clip. And the CLI warns these jobs take 5 to 10 minutes; mine returned in 3 minutes 6 seconds, 720×1280, 150 frames, mouth moving with the audio from about the two-second mark.&lt;/p&gt;

&lt;p&gt;The reference budget has hard ceilings, and one number circulating in tutorials is wrong. Officially, &lt;code&gt;wan3.0-video&lt;/code&gt; accepts at most 10 reference images; reference video is capped at 5 clips and 15 seconds total; reference audio likewise 5 clips and 15 seconds total; and first-frame or last-frame inputs are mutually exclusive with any reference input in a single request. Images, video and audio are counted independently, which is why the prompt markers read Image 1, Video 1, Audio 1 as separate series. The circulating claim of "up to 20 reference images" is false: 10 is the image cap, and 20 is the total across all reference types.&lt;/p&gt;

&lt;p&gt;Treat lip sync as an optional advanced step, not a pipeline requirement, for three reasons. The platform's API layer has dedicated talking-head models (wan2.2-s2v, which handles cartoon subjects, and videoretalk), but the CLI at version 1.22.0 does not wrap them; I checked &lt;code&gt;bl --help&lt;/code&gt;, so I am not going to invent commands for models the CLI does not expose. Community measurement puts AI lip sync at draft quality that still needs manual calibration. And overseas sentiment toward AI-dubbed anime has been resistant since the December 2025 discussion cluster on Hacker News. The &lt;code&gt;--image-voice&lt;/code&gt; route above is the cheap way in; everything beyond it is deliberate extra work.&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%2Fo3m8t2tnsy77axkkrbhn.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%2Fo3m8t2tnsy77axkkrbhn.jpg" alt="Extracted frame from the clip" width="720" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where the money goes: 5 seconds at ¥0.6 per second is ¥3.00. Which is why my advice runs opposite to most tutorials. Lock the panels cheap first; render video only once the sequence is right. Every screenshot above is a comic panel, and the clip exists only to prove the next step is real.&lt;/p&gt;

&lt;h2&gt;
  
  
  The budget, and the one cost rule that matters
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Unit price&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;th&gt;Subtotal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Character sheet&lt;/td&gt;
&lt;td&gt;¥0.18/image&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;¥0.18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Panels (reference edit)&lt;/td&gt;
&lt;td&gt;¥0.20/image&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;¥0.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Voice line&lt;/td&gt;
&lt;td&gt;per character&lt;/td&gt;
&lt;td&gt;12 chars&lt;/td&gt;
&lt;td&gt;≈¥0.01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Panel pipeline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4 images&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;≈¥0.8 (~$0.11)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lip-synced clip (optional)&lt;/td&gt;
&lt;td&gt;¥0.6/second&lt;/td&gt;
&lt;td&gt;5 s&lt;/td&gt;
&lt;td&gt;¥3.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;With one clip&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;≈¥3.8 (~$0.53)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For readers used to token math: there is no prompt-cache lever in image generation. Every panel is a fresh call, so panel count is the budget, full stop. The cache-read economics that dominate LLM bills do not apply here, which makes per-image price transparency the only number worth arguing about. I won't quote your remaining free quota; reading it needs a browser login I can't do for you, so check your own console's quota page.&lt;/p&gt;

&lt;p&gt;On the free tier's promise, stated at its boundary: quotas are 90-day, per-model, and independent of each other, and the exact amounts live on each model's console detail page rather than in any single summary I trust. The honest summary is that free quota is enough to run this whole pipeline once as a demo, and not enough to finish one episode for free. At the community-reported 28.7% usable-take rate, thirty free video seconds do not even yield three usable shots.&lt;/p&gt;

&lt;p&gt;Three cost numbers circulate in this space and they answer different questions, so here they are side by side without a verdict. Alibaba Cloud's own official comic-creation solution page, which I opened and read directly, quotes a 15-minute deploy and roughly ¥70 per episode with a free trial quota, and never mentions the CLI this post uses; that is the price of a managed, batteries-included route. My bare CLI run above is ¥0.8 for four panels, or ¥3.8 including one five-second lip-synced clip; that is the price of doing each step yourself. And first-hand media reports of beginner earnings put the other side of the ledger at ¥2.28 after thirty days online, ¥16.8 for eight episodes and 40k views, and one creator's summary of per-episode income as "not a few mao, but five-plus yuan." Quote cost and revenue in the same breath and you get nonsense; keep them in separate columns and the picture is at least honest.&lt;/p&gt;

&lt;p&gt;A fourth number, labeled as an estimate because it is one: a 90-second episode runs about thirty shots, and at that same 28.7% usable-take rate you render far more than thirty clips to get them, which lands roughly ¥110 to ¥215 per episode at 720P or 480P. Nearly all of it burns on image-to-video retries; images and voice are rounding errors. That estimate, not the ¥0.8 demo figure, is the number to hold against the ¥70 managed-solution quote and the ¥2.28 first-month earnings report.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll actually hit when you publish
&lt;/h2&gt;

&lt;p&gt;Searching beginner questions surfaced two camps: one calling comic drama the last content dividend for ordinary creators, one calling it another course-selling cycle where every tutorial funnels into a paid course. On the second camp there is at least one official datapoint: BOSS Zhipin, a major Chinese job platform, issued a notice on 2026-04-16 naming "recruit-to-train" scams that bait applicants with "high-pay AI comic artist" and "earn 100k on your first drama" promises before collecting training fees. I'm citing the platform's notice, not naming any course or vendor. The counter-move is the same one that applies to the technique: these four commands cost under a dollar and under half an hour to run yourself, so the judgment is worth making firsthand.&lt;/p&gt;

&lt;p&gt;Then there is the regulation nobody's tutorial has caught up with. China's NRTA Order No. 16, the Micro-Short Drama Development Management Measures, took effect 2026-09-01, and Article 34 requires a visible AI-disclosure label on every episode of an AI micro-drama. Filing splits by class: Class-1 applicants must hold a Radio &amp;amp; TV Program Production License, while Class-3 goes through platform self-review plus a program number. In practice the label is the part that reaches solo creators first, because it lands on the episode itself. How strictly the entity-qualification side is enforced varies by platform and I found no plain-language official guidance, so confirm against the platform you actually publish on before you act on any of this; treat my summary as a pointer to the order, not as legal advice.&lt;/p&gt;

&lt;p&gt;If you publish overseas instead, the disclosure picture is narrower than people fear, and the scope is worth stating exactly. YouTube's disclosure requirement applies to AI that photorealistically alters or generates realistic content; animation and clearly unrealistic content are exempt from proactive disclosure, and non-photoreal AI labels surface in the expanded description rather than on the player. Since May 2026 YouTube also auto-detects videos carrying large amounts of photoreal AI material. For a comic or anime pipeline that materially lowers the compliance burden, and it is the reason this post's screenshots are panels rather than photoreal frames. The exemption is not a free pass elsewhere in the same platform: refusing disclosure on photoreal content can bring labeling, takedown, or removal from the YouTube Partner Program.&lt;/p&gt;

&lt;p&gt;On the ownership side, the US position settled negatively and then stopped moving. On 2026-03-02 the Supreme Court declined cert in Thaler v. Perlmutter, leaving purely AI-generated works uncopyrightable for lack of human authorship. What remains protectable runs through three paths: selection and arrangement of the AI output, substantial human modification of it, or expressive human input that is clearly perceptible in the result; mere selection of prompts is not enough. There is no anime-specific rule, and I am not going to claim one. Practically, that pushes value toward the parts of this workflow a human actually authors: the sequence, the edit, the pacing, the panel selection.&lt;/p&gt;

&lt;p&gt;The last publishing constraint is the one you choose yourself. Cloning a living artist's signature style for commercial work, or feeding photographs of real people in as reference, carries infringement risk; designing your own character and using your own generated sheet as the sole reference source is the clean path, and it is also the path this post's pipeline assumes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your turn
&lt;/h2&gt;

&lt;p&gt;If you have pushed reference-image editing past three scenes, or into multi-character frames, I want the panel number where it broke. Same if you found a cheaper lock that survives a scene change. Comments are the point of posting this.&lt;/p&gt;

&lt;p&gt;One expectation to set before you start: everything after the renders is manual. Choosing which takes are usable, shot order, pacing, grading, and the judgment calls in between are yours; FFmpeg will concat, subtitle and mix-export, and that is the extent of its opinion. Budget an evening of editing for every evening of generating.&lt;/p&gt;

&lt;p&gt;The CLI lives on the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;install page&lt;/a&gt;; steps 3 and 4 need an 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;console&lt;/a&gt;. And if you'd rather look around the platform first, start &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>images</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Agent Skills Have No Provenance. A Content Hash Helps.</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Fri, 18 Sep 2026 02:30:01 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/agent-skills-have-no-provenance-a-content-hash-helps-335c</link>
      <guid>https://dev.to/xiaoru_chen/agent-skills-have-no-provenance-a-content-hash-helps-335c</guid>
      <description>&lt;p&gt;TL;DR: everything below was verified on my own laptop on 2026-09-17 against &lt;code&gt;bl&lt;/code&gt; 1.22.0, including one command I assumed was side-effect-free and wasn't. The article, its two figures, and every pasted output came out of running the thing it describes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question nobody's tooling answers
&lt;/h2&gt;

&lt;p&gt;The hard part of agent skills was never writing one.&lt;/p&gt;

&lt;p&gt;The hard part is answering: &lt;em&gt;what exactly did I just install?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I run three coding agents on one laptop. A year of accumulated skills, plugins, and hand-edited instruction files, and until last week I could not tell you which came from a registry, which I copied off a blog, and which I wrote myself. On disk they are indistinguishable. They load identically. One of them had been edited by an update I don't remember approving.&lt;/p&gt;

&lt;p&gt;In a recent two-week Hacker News window, the highest-engagement post in the whole skills category was a one-line question with two comments: has anyone measured how often agents use the skills you ship? The tooling posts (a package manager, an inspector, a context-cost auditor, a risk-scanned catalog) all sat between two and four points. We are building skill infrastructure faster than we are building trust in it.&lt;/p&gt;

&lt;p&gt;So when I found a first-party registry that stores a SHA-256 hash per installed skill, I took it apart. Here's what holds up, and one thing that doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the registry records
&lt;/h2&gt;

&lt;p&gt;Alibaba Cloud Model Studio ships a CLI called &lt;code&gt;bl&lt;/code&gt;. Its skill group is five commands, and the surprise is that &lt;strong&gt;none of them need an API key&lt;/strong&gt;. List, install, update, and remove all run without authentication. Grab the CLI from &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;the install page&lt;/a&gt; if you want to follow along.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bl skill list&lt;/code&gt; diffs the registry index against local installs. Fifteen skills, five states. The states are the interesting part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bailian-gen        installed
bailian-sandbox    installed
spark-video        not-installed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;missing&lt;/code&gt; means the lockfile knows the skill but the directory is gone. &lt;code&gt;untracked&lt;/code&gt; means a directory exists that the lockfile has never heard of, usually something you copied in by hand. That distinction is the first real diagnostic I've seen in skill tooling: it separates "I installed this and it vanished" from "something unmanaged is sitting here."&lt;/p&gt;

&lt;p&gt;The lockfile is where it gets good. Each installed skill records:&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;"contentHash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:d56618ed7fbd14cc4a22830f6df364566c5b725c614faf99c5bbd5a209961044"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"publishedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-17T14:04:49+08:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"installedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-17T07:22:38.894Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"oss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"links"&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="s2"&gt;"/Users/me/.copilot/skills/bailian-docs-llm-wiki"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"/Users/me/.qoder/skills/bailian-docs-llm-wiki"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"/Users/me/.qwen/skills/bailian-docs-llm-wiki"&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;Four fields, four questions answered. &lt;code&gt;contentHash&lt;/code&gt;: are my bytes the published bytes? Checkable now, not assumed. &lt;code&gt;sourceType&lt;/code&gt;: vendor object storage, not someone's repo. &lt;code&gt;publishedAt&lt;/code&gt; vs &lt;code&gt;installedAt&lt;/code&gt;: two timestamps, so "am I behind?" is a comparison. &lt;code&gt;links&lt;/code&gt;: every host pointing at this skill, which is why removal reports a count instead of deleting whatever it finds.&lt;/p&gt;

&lt;p&gt;I'm not citing the circulating skill-security percentages. They come from different scans over different corpora and don't compare. A hash I can recompute myself is a weaker headline and stronger evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  One copy, three hosts
&lt;/h2&gt;

&lt;p&gt;Every entry in all three host skill directories is a symlink to one canonical copy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lrwxr-xr-x  bailian-gen -&amp;gt; /Users/me/.bailian/skills/bailian-gen
&lt;/code&gt;&lt;/pre&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%2Fpmbxiu0v24u45bzbg9z1.png" 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%2Fpmbxiu0v24u45bzbg9z1.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two consequences I'd assumed were unsolvable. Updates are atomic across hosts: &lt;code&gt;bl skill update&lt;/code&gt; patches the single copy and all three see it; I ran it, ten entries, all &lt;code&gt;up-to-date&lt;/code&gt;. Removal is accountable: the lockfile enumerates links, so uninstall reported &lt;code&gt;removedLinks: 3&lt;/code&gt;, and two skills I keep by hand in one host survived untouched.&lt;/p&gt;

&lt;p&gt;This is the missing piece in the "skills are the asset, the harness is replaceable" argument. Portability only becomes cheap when the asset is stored once and referenced many.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug I hit: &lt;code&gt;--dry-run&lt;/code&gt; isn't dry here
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bl --help&lt;/code&gt; lists &lt;code&gt;--dry-run&lt;/code&gt; as a global flag, described in three words. Nothing says which groups honor it. So I did the polite thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl skill add &lt;span class="nt"&gt;--name&lt;/span&gt; spark-video &lt;span class="nt"&gt;--dry-run&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="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"skills"&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;"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;"spark-video"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"installed"&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;installed&lt;/code&gt;. Not "would install."&lt;/p&gt;

&lt;p&gt;I checked disk immediately: the skill existed in all four locations. Before the command, all four were absent and &lt;code&gt;bl skill list&lt;/code&gt; said &lt;code&gt;not-installed&lt;/code&gt;. One command with the dry-run flag in between. &lt;strong&gt;The skill group silently ignores &lt;code&gt;--dry-run&lt;/code&gt; and installs for real.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recovery is clean, which is why this is a footnote and not a warning label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl skill remove &lt;span class="nt"&gt;--name&lt;/span&gt; spark-video
&lt;span class="c"&gt;# { "skills": [ { "name": "spark-video", "status": "removed", "removedLinks": 3 } ] }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All four paths gone, unmanaged skills untouched. If you want a zero-write preview, read the &lt;code&gt;agents&lt;/code&gt; field from a &lt;code&gt;--dry-run&lt;/code&gt; response (that part is real), but use &lt;code&gt;bl skill list&lt;/code&gt; for the view that truly touches nothing.&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%2Fd0bliyih03s1ngv2053m.png" 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%2Fd0bliyih03s1ngv2053m.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bl skill init&lt;/code&gt; installs the ten &lt;code&gt;bailian-*&lt;/code&gt; skills; the five scenario skills need an explicit &lt;code&gt;--name&lt;/code&gt;. The payoff is in one description: &lt;code&gt;bailian-gen&lt;/code&gt; names five command groups verbatim: &lt;code&gt;bl image&lt;/code&gt;, &lt;code&gt;bl video&lt;/code&gt;, &lt;code&gt;bl speech&lt;/code&gt;, &lt;code&gt;bl vision describe&lt;/code&gt;, &lt;code&gt;bl omni&lt;/code&gt;. One skill wires image generation, video, TTS, ASR, and image/video understanding into whichever agents the CLI detected. On my machine: GitHub Copilot, Qoder, Qwen Code.&lt;/p&gt;

&lt;p&gt;Caveat, stated plainly: the registry also ships &lt;code&gt;bailian-sandbox&lt;/code&gt;, whose command group doesn't exist in CLI 1.22.0. Installing it doesn't error; using it would. Wait for an update or run &lt;code&gt;bl update&lt;/code&gt; yourself.&lt;/p&gt;

&lt;p&gt;Everything above is free and keyless. The first paid model call happens when you ask the agent to generate something; you'll need an API key from &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;here&lt;/a&gt; at that point, and quota is something to check in your own console, because the commands that read it need a browser login I can't do for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which skills to delete
&lt;/h2&gt;

&lt;p&gt;There's a real camp deleting skills now: base models improve, so skills that teach the model &lt;em&gt;how to think&lt;/em&gt; get absorbed and just burn context. They're half right. Prompt-class skills (choreography, formatting, reasoning scaffolds) are exactly what model progress obsoletes. Capability-class skills are not. No base model calls an image API on its own. Those abilities live in endpoints, not weights.&lt;/p&gt;

&lt;p&gt;My test is one line: after installing, did the agent gain a paragraph of instructions, or a set of commands? Instructions expire. Commands don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your turn
&lt;/h2&gt;

&lt;p&gt;The compressed version:&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   &lt;span class="c"&gt;# skip if you have bl&lt;/span&gt;
bl skill list
bl skill init
bl skill update
bl skill remove &lt;span class="nt"&gt;--name&lt;/span&gt; all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run this and hit something I got wrong (a state I misread, a host the CLI fails to detect, a lockfile field that lies), I want the report; that's how the next post happens. Issues and PRs welcome. And if you'd rather explore the platform itself first, the entry point is &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>cli</category>
    </item>
    <item>
      <title>CJK Poster Text: 3 Image Tiers, 19 Elements, Counted by Hand</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Thu, 17 Sep 2026 10:15:48 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/cjk-poster-text-3-image-tiers-19-elements-counted-by-hand-3bdp</link>
      <guid>https://dev.to/xiaoru_chen/cjk-poster-text-3-image-tiers-19-elements-counted-by-hand-3bdp</guid>
      <description>&lt;h2&gt;
  
  
  I stopped trusting the zoom-out
&lt;/h2&gt;

&lt;p&gt;I make bilingual promo assets: an event poster, an OG image for the release post, a membership notice for a partner in Taipei. The artwork is never the problem. The small print is, because a generated headline forgives everything and a generated footer line forgives nothing.&lt;/p&gt;

&lt;p&gt;My process used to be re-roll until it looked right at normal zoom. Then a wrong year reached a published asset, and I decided to measure the failure instead of dodging it.&lt;/p&gt;

&lt;p&gt;Two days, two prompts, three model tiers, 19 required text elements, each one read character by character at zoom. This is the ledger, plus the two flags that surprised me.&lt;/p&gt;

&lt;p&gt;Everything ran through the Bailian CLI (&lt;code&gt;bl&lt;/code&gt;). Timings are wall clock from my machine. Prices came out of &lt;code&gt;bl model list&lt;/code&gt; on the day I ran the tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two prompts
&lt;/h2&gt;

&lt;p&gt;Round one is an event poster: headline, sub-headline, three bullet lines that carry numbers, and a footer line with a date, a time and a venue. Ten required text elements.&lt;/p&gt;

&lt;p&gt;Round two is nastier on purpose: a membership benefits notice with a headline, a sub-headline, five bullets, and two footer lines. One footer line is a date range, the other is a legal sentence, and the bullets include a &lt;code&gt;1:1.5&lt;/code&gt; decimal. Nine required text elements.&lt;/p&gt;

&lt;p&gt;Each prompt ran against all three tiers. Only &lt;code&gt;--model&lt;/code&gt; changed between runs, and every other flag stayed at CLI default. Scoring was manual and deliberately dumb: find the element, zoom in, check each character, log it as correct, wrong glyph, missing stroke, transposed or absent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g bailian-cli
bl auth login --api-key &amp;lt;your-key&amp;gt;
bl image generate --prompt "$(cat poster.txt)" --model z-image-turbo --size 3:4 --out-dir ./out/turbo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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 per image&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;z-image-turbo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;¥0.1 (about $0.014)&lt;/td&gt;
&lt;td&gt;The official model card claims first place among open source text-to-image models on Artificial Analysis, with 6B parameters and 8 inference steps. That is a vendor statement; I did not re-verify the leaderboard.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen-image-3.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;¥0.18 (about $0.025)&lt;/td&gt;
&lt;td&gt;Same price at 1K and 2K. Aimed at dense information layouts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen-image-3.0-pro&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;¥0.25 at 1K (about $0.035), ¥0.5 at 2K&lt;/td&gt;
&lt;td&gt;Pro tier of the same family, with layout and text rendering strengthened.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those dollar figures are rough conversions of the yuan list prices, so read them as approximations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round one was the boring result
&lt;/h2&gt;

&lt;p&gt;All three tiers scored 10 out of 10. Headline, sub-headline including its comma, three bullets with their numbers, footer date, time and venue. No errors anywhere.&lt;/p&gt;

&lt;p&gt;The assumption that CJK fine print is a coin flip does not hold on these three tiers at poster-scale type. Round one was still worth the afternoon, because it produced two things I did not expect.&lt;/p&gt;

&lt;p&gt;Speed inverts the price ladder. &lt;code&gt;z-image-turbo&lt;/code&gt; came back in 5.1 seconds, &lt;code&gt;qwen-image-3.0-pro&lt;/code&gt; in 53.8 seconds, and &lt;code&gt;qwen-image-3.0&lt;/code&gt; in 101.1 seconds. The most expensive tier took about half the wall clock of the middle one, and the cheapest was roughly twenty times faster than Standard.&lt;/p&gt;

&lt;p&gt;Watermark behaviour differs between tiers. With identical flags, the Standard image carried an orange corner badge clipped by the canvas edge, with half of the word still visible, and the Pro image had nothing in that corner. &lt;code&gt;--help&lt;/code&gt; says the watermark defaults to on. A follow-up run with &lt;code&gt;--watermark false&lt;/code&gt; removed the badge cleanly, so the flag works. The inconsistency sits between tiers, not in the flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round two, where the cheap tier slipped
&lt;/h2&gt;

&lt;p&gt;Round one's type was still fairly large. The interesting test is footer-scale text carrying digits and long sentences.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;z-image-turbo&lt;/code&gt;, 5.1 seconds: 8 out of 9. The footer asked for a date range opening on 2026-10-01 and rendered that year as &lt;strong&gt;2016&lt;/strong&gt;. One digit, a 2 that became a 1. The other eight elements were correct, including the decimal and the legal sentence.&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%2Fo06z9v9n6na2qpwftr89.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%2Fo06z9v9n6na2qpwftr89.jpg" alt="Hand-drawn sketch: DRAFT lane vs FINAL lane, with timings, per-image prices, and a cross/check on the footer digit" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;qwen-image-3.0&lt;/code&gt;, 112.4 seconds: 9 out of 9.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;qwen-image-3.0-pro&lt;/code&gt;, 81.8 seconds: 9 out of 9.&lt;/p&gt;

&lt;p&gt;Aggregate over both rounds: turbo 18 of 19 (94.7%), Standard 19 of 19, Pro 19 of 19.&lt;/p&gt;

&lt;p&gt;Gibberish would have been obvious and I would have caught it at a glance. What I got was a plausible-looking digit in the smallest line on the page, which is precisely where nobody proofreads. On a published promo asset a wrong year is an ops incident.&lt;/p&gt;

&lt;p&gt;The trade, in numbers you can recompute: 5.1 seconds against 112.4 seconds, ¥0.1 against ¥0.18 or ¥0.25, for a 5.3 point accuracy gap that lived entirely in footer-scale digits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patching instead of re-rolling
&lt;/h2&gt;

&lt;p&gt;When turbo wrote 2016, my first instinct was to patch the image rather than regenerate it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bl image edit --image ./out/turbo/poster.png --prompt "Change the year 2016 in the first footer line to 2026; keep every other character, layout and colour exactly the same" --model qwen-image-3.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Half good. Both footer lines came back correct and 2026 was restored. Half bad: the artwork did not survive. The source was an illustrated poster with figures and colour, and the output was a plain white text-only layout. It corrected the glyphs and re-rendered the whole canvas.&lt;/p&gt;

&lt;p&gt;That gives me a simple rule. &lt;code&gt;bl image edit&lt;/code&gt; suits text-dominant assets such as price lists and benefit notices. If the asset is illustration-heavy and you want to move one line, regenerate from the original prompt on Standard or Pro instead of editing.&lt;/p&gt;

&lt;p&gt;The second trap was seed. I assumed &lt;code&gt;--seed&lt;/code&gt; would make runs comparable for an A/B read. It does not on this path. Same model, same prompt, same seed 42, only the watermark flag different, and the two images came back with completely different compositions. Do not build retry or comparison logic on the assumption that a fixed seed returns the same picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it in an afternoon
&lt;/h2&gt;

&lt;p&gt;Install the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;Bailian CLI (bl)&lt;/a&gt;, create a key on 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;console key management page&lt;/a&gt;, and loop the tiers over one prompt file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;m &lt;span class="k"&gt;in &lt;/span&gt;z-image-turbo qwen-image-3.0 qwen-image-3.0-pro&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;bl image generate &lt;span class="nt"&gt;--prompt&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;poster.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--model&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--size&lt;/span&gt; 3:4 &lt;span class="nt"&gt;--seed&lt;/span&gt; 42 &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; &lt;span class="s2"&gt;"./out/&lt;/span&gt;&lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--out-prefix&lt;/span&gt; poster&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time each iteration with whatever your shell gives you, and pull the price back as data you can log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bl model list &lt;span class="nt"&gt;--model&lt;/span&gt; qwen-image-3.0 &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three settings matter once you batch. &lt;code&gt;--n&lt;/code&gt; goes up to 6 per request, &lt;code&gt;--concurrent&lt;/code&gt; defaults to 1, and &lt;code&gt;--size&lt;/code&gt; accepts a ratio (&lt;code&gt;3:4&lt;/code&gt;, &lt;code&gt;16:9&lt;/code&gt;, &lt;code&gt;1:1&lt;/code&gt;) or explicit pixels (&lt;code&gt;2048*2048&lt;/code&gt;), which is what you want when the artifact has to fill a fixed slot like a social card.&lt;/p&gt;

&lt;p&gt;On money: my entire run caps at ¥0.86 at list price, about $0.12. The official free quota ledger shows 10 free generations each for &lt;code&gt;qwen-image-3.0&lt;/code&gt; and &lt;code&gt;-pro&lt;/code&gt;, expiring 2026-11-03, and no free quota at all for &lt;code&gt;z-image-turbo&lt;/code&gt;. Whether any of that lands on your account depends on your own console bill and quota page, so check there instead of trusting my numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I changed
&lt;/h2&gt;

&lt;p&gt;Anything published, meaning anything carrying a date, a price, a legal line or contact info, now goes to &lt;code&gt;qwen-image-3.0&lt;/code&gt; or &lt;code&gt;-pro&lt;/code&gt;. Both scored 19 of 19 across the two rounds with no footer-digit failures. Between them I lean Pro: ¥0.07 more per image (about $0.01) and faster in both rounds, 53.8 seconds against 101.1 in round one and 81.8 against 112.4 in round two, which adds up when you batch localized variants.&lt;/p&gt;

&lt;p&gt;Drafts and direction-finding go to &lt;code&gt;z-image-turbo&lt;/code&gt;. Five seconds, ¥0.1, and a wrong digit does not matter because the draft is getting rewritten anyway. Its large and mid-size type was as clean as the other two tiers.&lt;/p&gt;

&lt;p&gt;One habit stays regardless of tier: after generating anything with a date, a price or a phone number, zoom in and read the digits. The 2016 was only visible at zoom, and the rest of that poster looked perfect.&lt;/p&gt;

&lt;p&gt;If you generate CJK or bilingual assets from a pipeline, what does your proofreading step look like? Mine is still a human with a magnified crop, and I would like to hear about anything better.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Terminal-Generated B-Roll: Six Runs, Timings and File Sizes</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Thu, 17 Sep 2026 07:27:00 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/terminal-generated-b-roll-six-runs-timings-and-file-sizes-3hmi</link>
      <guid>https://dev.to/xiaoru_chen/terminal-generated-b-roll-six-runs-timings-and-file-sizes-3hmi</guid>
      <description>&lt;h2&gt;
  
  
  I keep needing five seconds of nothing
&lt;/h2&gt;

&lt;p&gt;I make short promotional video on my own: a weekly newsletter, a small video channel, the occasional launch clip for a friend's coffee roastery. I never struggle with the writing; what stalls me is the five second gap at the top of every video.&lt;/p&gt;

&lt;p&gt;A stock footage subscription charges per clip and still costs an evening of searching. Shooting it myself means lights, a tripod and waiting on the weather. The shots I want are deliberately dull: steam lifting off a cup, mist sliding across water, a patch of sun moving over a desk. Nothing happens in them, which is why they are awkward to buy and awkward to film.&lt;/p&gt;

&lt;p&gt;I tried a browser-based video generator last month and came away annoyed. An overseas signup, a card that kept getting refused, a queue, and twenty minutes for five seconds of footage I did not dare publish, because I could not tell what it would end up costing me.&lt;/p&gt;

&lt;p&gt;This week I went back through a command line, chasing one claim: that &lt;code&gt;happyhorse-1.1&lt;/code&gt; had reached number one on the Artificial Analysis video arena with an Elo of 1365. I believe half of that sentence. I pulled the ranking myself on the morning of September 16 from a single source and kept no snapshot, so the order may well have moved since. Following the thread turned up something better than a ranking: my account was sitting on free video quota that lapses in five days. So I ran six generations to see what the command line hands back.&lt;/p&gt;

&lt;p&gt;Everything below ran on &lt;code&gt;bl&lt;/code&gt; 1.22.0 on my own machine, and the wall clock times are mine. Prices came out of &lt;code&gt;bl model list&lt;/code&gt; that same day. The dollar figures are rough conversions of the yuan list prices, so read them as approximations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quota already in the account
&lt;/h2&gt;

&lt;p&gt;Install the &lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;Bailian CLI (bl)&lt;/a&gt; and one read-only command lists the free quota attached to your account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bl auth login --console
bl usage free --expiring 7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The login opens a browser once, which is one step more than creating an API key. The query consumes no quota and costs nothing. My video rows came back like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Free runs&lt;/th&gt;
&lt;th&gt;Expires&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;happyhorse-1.1-t2v&lt;/code&gt; (text to video)&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;2026-09-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;happyhorse-1.1-i2v&lt;/code&gt; (image to video)&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;2026-09-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;happyhorse-1.1-r2v&lt;/code&gt; (reference to video)&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;2026-09-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wan3.0-video&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;2026-11-05&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The top three rows add up to 30 runs expiring on 2026-09-21, and the official documentation is explicit that expired quota is not reissued.&lt;/p&gt;

&lt;p&gt;There is one thing I cannot report, because I never confirmed it. Right after my first generation I ran the query again and &lt;code&gt;happyhorse-1.1-t2v&lt;/code&gt; still read 10 of 10, with nothing deducted. That could be ledger lag, or that call may not have landed on the free tier at all. So this post quotes the counts and expiry dates printed in the ledger and says nothing about how fast they fall.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run one: from a sentence to an mp4
&lt;/h2&gt;

&lt;p&gt;I had no assets prepared, so this was a single line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bl video generate --prompt "清晨的西湖，薄雾贴着水面缓慢流动，一艘小木船从远处划来，桨声惊起两只白鹭，镜头缓慢向前推进" --model happyhorse-1.1-t2v --resolution 720P --ratio 9:16 --duration 5 --download xihu.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That prompt is in Chinese. It asks for mist moving low across West Lake at dawn, a small wooden boat coming in from a distance, oars startling two egrets, and a slow push-in. I wrote it in the language I could describe the scene most precisely in, and the model took it as given.&lt;/p&gt;

&lt;p&gt;Enter at 18:39:18, finished at 18:40:42, so &lt;strong&gt;84.6 seconds&lt;/strong&gt;. A 3.1 MB mp4 appeared on my desktop: 9:16 vertical, five seconds long, status &lt;code&gt;SUCCEEDED&lt;/code&gt;. Nothing queued or errored, and I never left the terminal.&lt;/p&gt;

&lt;p&gt;The willow branches, the mist, the boat and its reflection were all in the frame. The egrets got startled too, which was the specific beat I had written into the prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run two: a still photo that starts moving
&lt;/h2&gt;

&lt;p&gt;B-roll is half the job. The other half is the photographs I already own: product shots, storefronts, event photos sitting in a folder doing nothing.&lt;/p&gt;

&lt;p&gt;I generated the source still first with &lt;code&gt;bl image generate&lt;/code&gt;, which took 51.1 seconds on &lt;code&gt;qwen-image-3.0&lt;/code&gt;. The response carried a temporary URL, and that URL went straight into the video command with no upload step in between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bl video generate --image &amp;lt;temporary URL from the previous step&amp;gt; --prompt "杯口的热气缓缓上升，窗外晨光轻微变化，镜头极缓慢推近杯身" --model happyhorse-1.1-i2v --resolution 720P --duration 5 --download coffee.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;74.5 seconds&lt;/strong&gt;, 1.8 MB. The steam climbs, the light on the table shifts, and the cup and the wood grain match the source image. In English that prompt asks for steam rising slowly off the rim, morning light changing a little outside the window, and a very slow push toward the cup.&lt;/p&gt;

&lt;p&gt;All this path asks for is one decent still. If you make assets rather than films, it is the more useful of the two directions.&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%2Fzl2c6o9hrsn439wzc59m.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%2Fzl2c6o9hrsn439wzc59m.jpg" alt="Left: the static generated still. Right: frame 30 of the same image after animation, 74 seconds" width="684" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Run three: whether the watermark comes off
&lt;/h2&gt;

&lt;p&gt;Out of the box the video carries a "Happy Horse" mark in the lower right corner. &lt;code&gt;--help&lt;/code&gt; documents &lt;code&gt;--watermark false&lt;/code&gt;, and I did not want to take the help text's word for it.&lt;/p&gt;

&lt;p&gt;Same prompt, same &lt;code&gt;--seed 42&lt;/code&gt;, only the watermark flag different:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Filename in the returned URL&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Frame 30, lower right&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;&lt;code&gt;..._refiner_watermark.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1009.2 KB&lt;/td&gt;
&lt;td&gt;"Happy Horse" visible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--watermark false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;..._refiner.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;884.0 KB, 12.4% smaller&lt;/td&gt;
&lt;td&gt;clean&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three independent signals point the same way, so the flag does what it says. The filename is the one I like best: the returned URL encodes whether the watermark was on, which means a script can check the name instead of looking at pixels.&lt;/p&gt;

&lt;p&gt;The same pair of runs also broke a plan I had. Both carried seed 42 and the footage was completely different, a ceramic mug in one and a glass cup on a tray in the other. &lt;code&gt;--seed&lt;/code&gt; does not give you the same picture back on the video path. If you were hoping to hold a seed still while comparing two settings, do what I did here instead: change one variable, keep everything else identical, and accept that the frame itself will differ.&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%2Fj1640pr97u7ik6rixyrv.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%2Fj1640pr97u7ik6rixyrv.jpg" alt="Left: default output with the corner watermark. Right: the same prompt with --watermark false" width="799" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Run four: what 1080P costs over 720P
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bl model list&lt;/code&gt; gave me the per-second list prices for &lt;code&gt;wan3.0-video&lt;/code&gt;: ¥0.6 per second at 720P, about $0.085, and ¥1.2 per second at 1080P, about $0.17. Five seconds works out at ¥3 against ¥6, so roughly 42 cents against 84 cents.&lt;/p&gt;

&lt;p&gt;Same prompt, same seed, same model, only &lt;code&gt;--resolution&lt;/code&gt; different:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resolution&lt;/th&gt;
&lt;th&gt;Wall clock&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Five second cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;720P&lt;/td&gt;
&lt;td&gt;137.2 s&lt;/td&gt;
&lt;td&gt;4.9 MB&lt;/td&gt;
&lt;td&gt;¥3, about $0.42&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1080P&lt;/td&gt;
&lt;td&gt;173.8 s, 27% longer&lt;/td&gt;
&lt;td&gt;11.2 MB, 2.3 times larger&lt;/td&gt;
&lt;td&gt;¥6, about $0.84&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Since that run my habit has been drafts and internal previews at 720P, where it is quicker and the file is less than half the size, and 1080P only for what ships or gets projected in a room.&lt;/p&gt;

&lt;p&gt;I could not price the &lt;code&gt;happyhorse-1.1&lt;/code&gt; tier at all. &lt;code&gt;bl model list --model happyhorse-1.1&lt;/code&gt; returns &lt;code&gt;Model not found&lt;/code&gt;, and its per-second rate lives on a console model page, so there is no figure for it in this post.&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%2Fc0s0cp7vdfwfqmnv97kf.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%2Fc0s0cp7vdfwfqmnv97kf.jpg" alt="Left: 720P, 137 seconds, 4.9 MB. Right: 1080P, 174 seconds, 11.2 MB" width="799" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when the free runs are gone
&lt;/h2&gt;

&lt;p&gt;The default state spends money, which matters more than any number above.&lt;/p&gt;

&lt;p&gt;The official documentation says that on an authenticated account, calls switch to pay-as-you-go automatically once free quota is used up. There is a console switch named "stop when free quota is used up", and on accounts that have completed identity verification that switch is off by default. The quota runs out, your calls keep going, and the bill keeps growing. If you want the hard stop you have to turn it on yourself; with it on, an exhausted quota returns HTTP 403 carrying &lt;code&gt;AllocationQuota.FreeTierOnly&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unverified accounts get the opposite treatment. The stop is forced on and the switch is not even visible, so calls stop working when the quota is gone and nothing bills, but you have to verify and add funds before they start again.&lt;/p&gt;

&lt;p&gt;Keys, the quota ledger and that switch all live 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;console&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two commands I verified without running
&lt;/h2&gt;

&lt;p&gt;I do not want this to read as though I tested everything. Two capabilities are documented in the local &lt;code&gt;--help&lt;/code&gt; output, and I confirmed the flags exist without pressing enter.&lt;/p&gt;

&lt;p&gt;First and last frame control: &lt;code&gt;bl video generate --image &amp;lt;first&amp;gt; --last-frame &amp;lt;last&amp;gt;&lt;/code&gt; pins both ends of the shot and lets the model fill the middle, which is the shape you want for a cup going from full to empty or a street going from afternoon to night. I have both source images sitting in a folder.&lt;/p&gt;

&lt;p&gt;Multiple subjects with their own voices: &lt;code&gt;bl video ref --image A --image B --image-voice &amp;lt;voiceA&amp;gt; --image-voice &amp;lt;voiceB&amp;gt;&lt;/code&gt;. &lt;code&gt;--image&lt;/code&gt; repeats for each subject, voices pair by position, and the prompt refers to them as &lt;code&gt;image1&lt;/code&gt; and &lt;code&gt;Audio 1&lt;/code&gt;. That is the route to a person who stays the same person across shots and speaks, which text to video on its own cannot give you. &lt;code&gt;bl video ref&lt;/code&gt; has no &lt;code&gt;--last-frame&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Those get their own post once I have run them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it fits, and where it does not
&lt;/h2&gt;

&lt;p&gt;Short atmospheric shots are what it is good for: an opening shot, a moving background behind slides, a channel cover that breathes, one still photo that had to become a clip. If your budget is tight, the 30 free runs across the three &lt;code&gt;happyhorse-1.1&lt;/code&gt; directions lapse on September 21.&lt;/p&gt;

&lt;p&gt;Dialogue with a specific face, a logo that has to appear at second three, anything longer than five seconds: those still belong to an editor. The watermark is on unless you say otherwise, so &lt;code&gt;--watermark false&lt;/code&gt; goes in the command before anything ships. And generated B-roll will not rescue weak content; it removes the cost of filming or licensing those five seconds and nothing more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g bailian-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a key on 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;console key management page&lt;/a&gt;, then run &lt;code&gt;bl usage free --expiring 7&lt;/code&gt; and look at what your own account holds and when it lapses. Every ledger is different, so do not copy my counts. Model list, per-second pricing and quota are all checkable from 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;Every timing and file size above came off my machine on September 16, 2026. Flags and their allowed values follow whatever &lt;code&gt;--help&lt;/code&gt; reports in the version you install.&lt;/p&gt;

&lt;p&gt;What does your B-roll workflow look like for shots too small to film and too specific to license? Mine is still "generate it, then check the corner for a watermark", and I would like to hear better.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Priced a Single Code Review Across 7 Model Tiers</title>
      <dc:creator>xiaoru chen</dc:creator>
      <pubDate>Wed, 16 Sep 2026 13:23:56 +0000</pubDate>
      <link>https://dev.to/xiaoru_chen/i-priced-a-single-code-review-across-7-model-tiers-3n05</link>
      <guid>https://dev.to/xiaoru_chen/i-priced-a-single-code-review-across-7-model-tiers-3n05</guid>
      <description>&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%2Fw56fo0t7d2voyrljd1bs.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%2Fw56fo0t7d2voyrljd1bs.jpg" alt="Seven model tiers reviewing the same buggy Python file" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A $1.20 question with no answer
&lt;/h2&gt;

&lt;p&gt;A post hit the Hacker News front page yesterday asking whether a $1.20 model is good enough for code review. 114 comments, no consensus. The thread kept circling one trap: pricing pages quote dollars per million tokens, and nobody pays per million tokens. You pay per review.&lt;/p&gt;

&lt;p&gt;So I measured a review. One 64-line Python file with bugs planted in it, seven Qwen tiers, identical prompts, a client-side stopwatch. The runner was Alibaba's open-source &lt;a href="https://github.com/alibaba/open-code-review" rel="noopener noreferrer"&gt;open-code-review&lt;/a&gt; (&lt;code&gt;ocr&lt;/code&gt;), which shipped v1.12.1 the same week and sat at #2 on GitHub Trending with 26k stars. Token counts are the API's own &lt;code&gt;usage&lt;/code&gt; block. Unit prices come from &lt;code&gt;bl model list --model &amp;lt;name&amp;gt; --output json&lt;/code&gt;, which needs no account and no auth, so every price below is checkable.&lt;/p&gt;

&lt;p&gt;Prices are in CNY because that is what the lookup command returns.&lt;/p&gt;

&lt;h2&gt;
  
  
  The file
&lt;/h2&gt;

&lt;p&gt;A small order service. Four real defects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;fetchone()&lt;/code&gt; result used without a null check. Crashes when the user does not exist.&lt;/li&gt;
&lt;li&gt;An f-string interpolated straight into SQL. Textbook injection.&lt;/li&gt;
&lt;li&gt;A global counter incremented with &lt;code&gt;+=&lt;/code&gt; while the lock defined two lines above is never acquired. Two concurrent orders lose one update.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;open()&lt;/code&gt; without &lt;code&gt;with&lt;/code&gt;. If parsing throws halfway through, the handle leaks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Plus two decoys that look suspicious and are correct: an &lt;code&gt;@lru_cache&lt;/code&gt; on a pure function, and a redundant &lt;code&gt;except Exception: raise&lt;/code&gt; that preserves the traceback exactly as a bare raise should. Flagging either counts as a false positive, and false positives are what make a cheap tier look expensive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Thinking tokens&lt;/th&gt;
&lt;th&gt;Found /4&lt;/th&gt;
&lt;th&gt;Missed&lt;/th&gt;
&lt;th&gt;False +&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Wall time&lt;/th&gt;
&lt;th&gt;In tok&lt;/th&gt;
&lt;th&gt;Out tok&lt;/th&gt;
&lt;th&gt;Cost per review&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;qwen-turbo&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;-2.0&lt;/td&gt;
&lt;td&gt;7.4s&lt;/td&gt;
&lt;td&gt;468&lt;/td&gt;
&lt;td&gt;1,005&lt;/td&gt;
&lt;td&gt;¥0.0007&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3-coder-plus&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;10.1s&lt;/td&gt;
&lt;td&gt;464&lt;/td&gt;
&lt;td&gt;712&lt;/td&gt;
&lt;td&gt;tiered pricing / see console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen-plus&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1.5&lt;/td&gt;
&lt;td&gt;39.3s&lt;/td&gt;
&lt;td&gt;464&lt;/td&gt;
&lt;td&gt;1,766&lt;/td&gt;
&lt;td&gt;¥0.0039&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3.7-plus&lt;/td&gt;
&lt;td&gt;1,182&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;23.4s&lt;/td&gt;
&lt;td&gt;502&lt;/td&gt;
&lt;td&gt;2,052&lt;/td&gt;
&lt;td&gt;not published / see console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3.6-flash&lt;/td&gt;
&lt;td&gt;4,258&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3.0&lt;/td&gt;
&lt;td&gt;42.6s&lt;/td&gt;
&lt;td&gt;502&lt;/td&gt;
&lt;td&gt;5,075&lt;/td&gt;
&lt;td&gt;not published / see console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3.8-flash&lt;/td&gt;
&lt;td&gt;9,517&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;241.0s&lt;/td&gt;
&lt;td&gt;540&lt;/td&gt;
&lt;td&gt;12,909&lt;/td&gt;
&lt;td&gt;¥0.0353&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3.8-max&lt;/td&gt;
&lt;td&gt;9,770&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;293.1s&lt;/td&gt;
&lt;td&gt;540&lt;/td&gt;
&lt;td&gt;11,064&lt;/td&gt;
&lt;td&gt;¥0.4048&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One run per configuration. Same system prompt, same user message, byte-identical payloads, &lt;code&gt;max_tokens&lt;/code&gt; pinned to the CLI default of 4096, wall clock timed on the client including network round trips. Score is found bugs minus false positives, out of 4. The qwen3-coder family is officially tiered-priced, and qwen3.6-flash plus qwen3.7-plus return no unit price from the lookup command at all, so those cells point at the console instead of a guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things I did not expect
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The flagship bought nothing on this file.&lt;/strong&gt; qwen3.7-plus scored a perfect 4.0 in 23.4 seconds with 2,052 output tokens and correct line numbers. qwen3.8-max also scored 4.0, and it caught three real problems I had not planted, including that &lt;code&gt;amount &amp;lt;= 0&lt;/code&gt; does not reject &lt;code&gt;float('nan')&lt;/code&gt; because &lt;code&gt;nan &amp;lt;= 0&lt;/code&gt; is False. It took 293.1 seconds and 9,770 thinking tokens to get there. The extra 8,588 thinking tokens bought time, nothing else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The divider is thinking, not price.&lt;/strong&gt; The three tiers that did not think scored -2.0, 2.0 and 1.5, and every one of them missed at least one planted bug. The five that thought scored 3.0 or better and missed nothing. Past roughly 1,200 thinking tokens the score stops moving. 1,182 tokens of thought is a perfect score here. So is 9,770.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unit price and total bill are different animals.&lt;/strong&gt; qwen-turbo costs ¥0.3 per million input tokens and qwen3.8-max costs ¥12, which is 40x. Per review, turbo spent ¥0.0007 and max spent ¥0.4048, which is 545x, because the expensive run also wrote eleven thousand output tokens and most of them were reasoning. Budget from the pricing page and you will be wrong by an order of magnitude.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line numbers are only trustworthy on the thinking tiers.&lt;/strong&gt; turbo placed the SQL injection on line 9 (it is line 18) and the null deref on line 10 (line 13). Descriptions right, coordinates wrong. qwen3.7-plus and above were accurate to within one line. Treat a cheap tier's output as a lead list and find the code yourself; treat a thinking tier's output as navigation.&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-code-review-cost%2Funit-econ.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-code-review-cost%2Funit-econ.jpg" width="800" alt="Hand-drawn balance scale weighing a price tag against a single coin" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One control run worth recording: qwen3.8-flash with &lt;code&gt;--enable-thinking&lt;/code&gt; forced on. It thought harder (11,611 tokens), wrote more (14,954), cost 16% more, and scored the same 4.0 as its default run. On this task the switch only buys a bigger invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring ocr to DashScope in five commands
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ocr&lt;/code&gt; ships a built-in provider named &lt;code&gt;dashscope&lt;/code&gt;. Its base URL is DashScope's OpenAI-compatible endpoint and the Qwen models are preset, so there is no custom provider file to write.&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; @alibaba-group/open-code-review
ocr config &lt;span class="nb"&gt;set &lt;/span&gt;provider dashscope
ocr config &lt;span class="nb"&gt;set &lt;/span&gt;model qwen3.7-plus
ocr config &lt;span class="nb"&gt;set &lt;/span&gt;providers.dashscope.api_key sk-your-key
ocr llm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;ocr review&lt;/code&gt; inside a repo, or &lt;code&gt;ocr scan --path some/dir&lt;/code&gt; for a full-file pass with no git history. &lt;a href="https://bailian.console.aliyun.com/cn-beijing/?source_channel=key_github&amp;amp;tab=app#/api-key" rel="noopener noreferrer"&gt;Create an API key in the Bailian console&lt;/a&gt; before the fourth command.&lt;/p&gt;

&lt;p&gt;Two notes if you run outside China. DashScope also serves Virginia (&lt;code&gt;https://dashscope-us.aliyuncs.com/compatible-mode/v1&lt;/code&gt;), Singapore and Tokyo, and API keys are region-bound: a key created in one region returns 401 against another region's endpoint. The new-user free quota is per model and independent (typically 1M tokens each), valid for 90 days, Beijing region only, with no reissue after expiry and no automatic failover to another model when it runs out. From Virginia or Singapore you are on pay-as-you-go from the first call, so budget for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would pick
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Per review&lt;/th&gt;
&lt;th&gt;Reviews per ¥1&lt;/th&gt;
&lt;th&gt;10/day × 22 working days&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Coarse sieve on every commit, you read the code anyway&lt;/td&gt;
&lt;td&gt;qwen-turbo&lt;/td&gt;
&lt;td&gt;¥0.0007&lt;/td&gt;
&lt;td&gt;≈1,345&lt;/td&gt;
&lt;td&gt;¥0.16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cheap pass with a published price&lt;/td&gt;
&lt;td&gt;qwen-plus&lt;/td&gt;
&lt;td&gt;¥0.0039&lt;/td&gt;
&lt;td&gt;≈256&lt;/td&gt;
&lt;td&gt;¥0.86&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default PR gate, line numbers you can jump to&lt;/td&gt;
&lt;td&gt;qwen3.7-plus&lt;/td&gt;
&lt;td&gt;≤¥0.0799 (upper bound; real price unpublished)&lt;/td&gt;
&lt;td&gt;price unpublished&lt;/td&gt;
&lt;td&gt;price unpublished&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hard diff, deep reasoning, still cheap&lt;/td&gt;
&lt;td&gt;qwen3.8-flash&lt;/td&gt;
&lt;td&gt;¥0.0353&lt;/td&gt;
&lt;td&gt;≈28&lt;/td&gt;
&lt;td&gt;¥7.76&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same, thinking forced on&lt;/td&gt;
&lt;td&gt;qwen3.8-flash with &lt;code&gt;--enable-thinking&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;¥0.0408&lt;/td&gt;
&lt;td&gt;≈24&lt;/td&gt;
&lt;td&gt;¥8.98&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The gnarliest file in the repo&lt;/td&gt;
&lt;td&gt;qwen3.8-max&lt;/td&gt;
&lt;td&gt;¥0.4048&lt;/td&gt;
&lt;td&gt;≈2&lt;/td&gt;
&lt;td&gt;¥89.05&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The ¥0.0799 is a bound, not an estimate: priced at the flagship's ¥12 and ¥36 rates, that run's 502 input and 2,052 output tokens come to ¥0.0799, one fifth of the flagship's ¥0.4048, and its real price cannot be higher. For the tiers with no published price, &lt;a href="https://bailian.console.aliyun.com/?source_channel=hh_github" rel="noopener noreferrer"&gt;check the model pages in the console&lt;/a&gt; rather than any reposted number.&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-code-review-cost%2Fblindspot.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-code-review-cost%2Fblindspot.jpg" width="800" alt="Two overlapping circles, each holding a different bug and a blank price tag" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One observation, not a conclusion: the two cheap non-thinking tiers missed different bugs. qwen-plus missed the null deref, qwen3-coder-plus missed the race condition, and their misses did not overlap. Running both and cross-reading the reports covers that blind spot at the price of stacked false positives and a human in the loop. With seven configurations measured I would not call this a rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recalculate it yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;decimal&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROUND_HALF_UP&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in_tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price_in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price_out&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;prices in CNY per million tokens&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in_tok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price_in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out_tok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price_out&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;rounding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ROUND_HALF_UP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2052&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;12&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;36&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# 0.0799: what qwen3.7-plus would cost even at flagship prices
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;Decimal&lt;/code&gt;, not Python's &lt;code&gt;round()&lt;/code&gt;, or your totals will disagree with the invoice and neither will explain why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this falls down
&lt;/h2&gt;

&lt;p&gt;A community member reported 1,400 PRs taking over ten hours, and even a few-line PR takes minutes. This is not a pre-commit tool, and the flagship tier definitely is not. It does not replace SAST: a rules engine reports deterministic pattern hits, while &lt;code&gt;ocr&lt;/code&gt; reports what a model understood about your code. They complement each other. And if your team already pays for a hosted review product that works, this is not a pitch to switch.&lt;/p&gt;

&lt;p&gt;Sample size is one file, four bugs, one run per configuration. I ran two of the models twice and their scores moved: qwen-plus went 2.0 then 1.5, and turbo missed the null deref once and caught it the next time. Read every number above as "this run", not as a benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your turn
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://bailian.console.aliyun.com/cli?source_channel=cli_github&amp;amp;" rel="noopener noreferrer"&gt;Install the Bailian CLI&lt;/a&gt; and run the price lookup on your own machine; that command needs no account. The same CLI carries &lt;code&gt;bl advisor recommend --message "..."&lt;/code&gt;, and when I asked it to pick a review model on a budget its first choice was a cheaper third-party model rather than the Qwen flagship. I kept that detail in because it is more useful than any claim of objectivity I could make.&lt;/p&gt;

&lt;p&gt;What would you pay per PR review, and which tier would you trust on a merge gate? Tell me in the comments what your worst file costs.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
