<?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: hc_xshh</title>
    <description>The latest articles on DEV Community by hc_xshh (@hcxshh).</description>
    <link>https://dev.to/hcxshh</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%2F4143807%2F2c438934-1857-446a-a83d-47b92f428b29.jpg</url>
      <title>DEV Community: hc_xshh</title>
      <link>https://dev.to/hcxshh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hcxshh"/>
    <language>en</language>
    <item>
      <title>I read China's FX rulebook for foreign passports. The $50,000 allowance runs one way.</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Thu, 05 Nov 2026 02:24:06 +0000</pubDate>
      <link>https://dev.to/hcxshh/i-read-chinas-fx-rulebook-for-foreign-passports-the-50000-allowance-runs-one-way-9c2</link>
      <guid>https://dev.to/hcxshh/i-read-chinas-fx-rulebook-for-foreign-passports-the-50000-allowance-runs-one-way-9c2</guid>
      <description>&lt;p&gt;A foreign national working in China can convert foreign currency into yuan inside an annual allowance and with nothing but a passport. Going the other way, the same person has no allowance at all unless they hold a permanent-residence ID card. The difference sits in two articles that bank staff work from at the counter.&lt;/p&gt;

&lt;p&gt;The document is the Current-Account Foreign-Exchange Business Guide, 2020 edition, issued as 汇发〔2020〕14号 and amended three times since (&lt;a href="https://www.gov.cn/gongbao/content/2020/content_5560296.htm" rel="noopener noreferrer"&gt;full text in the State Council gazette&lt;/a&gt;, 2020-08-28, read 2026-11-05). The provisions used here run from Article 54 to Article 71, in the guide's personal-conversion section. Everything below comes from that text unless another document is named.&lt;/p&gt;

&lt;h2&gt;
  
  
  A foreign passport gets the $50,000 allowance one way only
&lt;/h2&gt;

&lt;p&gt;A foreign passport carries a $50,000 annual allowance into yuan and no allowance out of yuan without a permanent-residence card (&lt;a href="https://www.gov.cn/gongbao/content/2020/content_5560296.htm" rel="noopener noreferrer"&gt;Articles 54, 55 and 59&lt;/a&gt;, read 2026-11-05).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Annual allowance&lt;/th&gt;
&lt;th&gt;What the counter asks for&lt;/th&gt;
&lt;th&gt;Article&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Foreign currency into yuan&lt;/td&gt;
&lt;td&gt;$50,000 per person&lt;/td&gt;
&lt;td&gt;passport&lt;/td&gt;
&lt;td&gt;54, 55&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Foreign currency into yuan, above the allowance&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;ID plus documents on what the yuan is for, with the amount&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yuan into foreign currency&lt;/td&gt;
&lt;td&gt;none without a permanent-residence ID card&lt;/td&gt;
&lt;td&gt;ID plus documents on where the yuan came from, tax receipts named&lt;/td&gt;
&lt;td&gt;59&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yuan into foreign currency, permanent-residence ID card held&lt;/td&gt;
&lt;td&gt;$50,000 per person&lt;/td&gt;
&lt;td&gt;ID&lt;/td&gt;
&lt;td&gt;54, 59&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yuan converted back to foreign currency&lt;/td&gt;
&lt;td&gt;not an allowance&lt;/td&gt;
&lt;td&gt;ID plus the original exchange receipt&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first line of Article 54 sets both halves in one sentence: individual conversion into yuan, and conversion into foreign currency by a domestic individual, are each under an annual facilitation allowance of $50,000. A foreign national is not a domestic individual, so only the first half reaches them. Article 59 then opens the purchase route for them, and its second paragraph hands the allowance back to anyone holding a 外国人永久居留身份证.&lt;/p&gt;

&lt;h2&gt;
  
  
  The figure sets paperwork, not a ceiling
&lt;/h2&gt;

&lt;p&gt;The $50,000 marks the point where documents start, and Articles 56 and 58 both provide a route above it for anyone with the transaction on paper.&lt;/p&gt;

&lt;p&gt;For a domestic individual, above-allowance conversion into yuan needs identity documents plus source-of-funds documents stating the amount; above-allowance purchase needs identity documents plus purpose documents stating the amount (Article 56). For a foreign national, above-allowance conversion into yuan needs identity documents plus documents on the purpose of the yuan, again with the amount (Article 58). The document follows the money's destination, not its origin.&lt;/p&gt;

&lt;p&gt;One sentence in both articles catches people out. Where a single conversion into yuan exceeds $50,000, the yuan must be paid directly into the counterparty's domestic renminbi account (Articles 56 and 58). A salary conversion that large does not land in your own account first.&lt;/p&gt;

&lt;p&gt;Capital-account money sits outside all of this. The 2006 framework says current-account business is managed by the principle of convertibility and capital-account business by the process of convertibility, which is a polite way of saying the second list is shorter (&lt;a href="https://www.safe.gov.cn/safe/2022/0818/21330.html" rel="noopener noreferrer"&gt;个人外汇管理办法, Order No. 3 of 2006, Article 3&lt;/a&gt;, in force 2007-02-01). Selling a flat and sending the proceeds abroad is a capital-account question, not the one answered here.&lt;/p&gt;

&lt;h2&gt;
  
  
  A passport is the whole definition
&lt;/h2&gt;

&lt;p&gt;A passport holder is a 境外个人 in these rules, whatever the visa inside it says, because the definition turns on the document rather than the permission (&lt;a href="https://www.safe.gov.cn/safe/2022/0818/21330.html" rel="noopener noreferrer"&gt;Order No. 3 of 2006, Article 37&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The same article defines a domestic individual as a Chinese citizen holding a resident ID card, a military ID or an armed-police ID. Hong Kong, Macao and Taiwan residents with their travel permits fall on the foreign-national side. A work permit, a residence permit for work and a permanent-residence card are three different things in this taxonomy: the first two leave you where you were, and only the third moves you into the allowance.&lt;/p&gt;

&lt;p&gt;What the visa list means for opening an account in the first place is a separate question, covered in &lt;a href="https://notebookfield.com/posts/opening-a-bank-account-as-a-visitor/" rel="noopener noreferrer"&gt;Opening a bank account in China as a visitor&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Salary out: the tax receipt is the document named
&lt;/h2&gt;

&lt;p&gt;A bank selling foreign currency against yuan earned in China asks for your ID and for documents on where the yuan came from. Article 59 names tax receipts as part of that evidence.&lt;/p&gt;

&lt;p&gt;The article's words are 税务凭证, tax vouchers. In practice that means the after-tax picture of the income has to exist before the conversion does, which matters if part of your pay arrived informally or the tax was handled by someone else. The rule also says 合法收入, lawful income, and current-account income, both of which are checked rather than assumed.&lt;/p&gt;

&lt;p&gt;Article 68 then hands the branch the pen: a bank may decide for itself which documents to examine, in what form, and to what depth, may refuse a transaction whose authenticity it doubts, and must explain a refusal and the appeal channel (&lt;a href="https://www.gov.cn/gongbao/content/2020/content_5560296.htm" rel="noopener noreferrer"&gt;Article 68&lt;/a&gt;). Two branches of the same bank can therefore ask for different paper. That discretion is the reason this page describes articles rather than promising a checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  The declaration form is written for residents
&lt;/h2&gt;

&lt;p&gt;Article 57 binds the domestic individual to the personal purchase declaration form. Article 59 gives a foreign national a documents-based purchase instead.&lt;/p&gt;

&lt;p&gt;That distinction is worth knowing when a counter hands over the form anyway. The form is also where the six prohibitions are printed, and the fifth one answers a common question (&lt;a href="https://www.gov.cn/zhengce/zhengceku/2021-04/02/content_5597577.htm" rel="noopener noreferrer"&gt;form and filing instructions, 汇发〔2021〕13号&lt;/a&gt;, in use from 2021-04-01, each bank setting its own start date before the end of June 2021):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;What it forbids&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;false declarations about the purchase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;false supporting documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;lending your allowance to someone else&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;borrowing another person's allowance, or splitting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;using purchased foreign currency for overseas property, securities, life insurance, or investment-type insurance that pays returns or dividends&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;money laundering, gambling, tax evasion, underground banking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Line 5 is about the nature of the transaction, not the size of it. A purchase of $10,000 for a deposit on a flat abroad fails on the same line as a purchase of $200,000. It restricts what purchased foreign currency may be spent on; a salary conversion under Article 59 carries its own documentary test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Splitting puts you on a two-year list
&lt;/h2&gt;

&lt;p&gt;Converting in several slices, or through several people's names, to stay under the allowance is 分拆, and the guide treats lending your allowance and borrowing someone else's differently (&lt;a href="https://www.gov.cn/gongbao/content/2020/content_5560296.htm" rel="noopener noreferrer"&gt;Article 62&lt;/a&gt;).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Act&lt;/th&gt;
&lt;th&gt;Consequence&lt;/th&gt;
&lt;th&gt;Provision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;lending your allowance to help someone avoid the allowance or the authenticity check&lt;/td&gt;
&lt;td&gt;a written risk warning through the bank; a second occurrence puts you on the watch list&lt;/td&gt;
&lt;td&gt;62(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;borrowing another person's allowance, or avoiding the rules another way&lt;/td&gt;
&lt;td&gt;watch list directly&lt;/td&gt;
&lt;td&gt;62(2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;while on the list&lt;/td&gt;
&lt;td&gt;listed year plus the next two; every conversion needs identity documents and amount-bearing supporting documents&lt;/td&gt;
&lt;td&gt;62(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A 2009 notice that is still quoted online, with six named patterns of splitting, was repealed by the 2020 guide's own annex of abolished rules. The general prohibition survives in Article 7 of the 2006 framework: no splitting to evade the limit, and no false documents to evade the authenticity check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the exchange receipt
&lt;/h2&gt;

&lt;p&gt;Yuan left over from an earlier conversion turns back into foreign currency with the original exchange receipt, which stays usable for 24 months from the conversion date (&lt;a href="https://www.gov.cn/gongbao/content/2020/content_5560296.htm" rel="noopener noreferrer"&gt;Article 60&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Two daily exceptions need identity documents alone: up to $500 equivalent in a day at a bank, and up to $1,000 equivalent in a day at a shop inside the departure area before leaving the country.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cash and cards have their own numbers
&lt;/h2&gt;

&lt;p&gt;Cash limits are counted per trip and per day, and a card issued in China carries an annual overseas withdrawal cap that follows the person rather than the card.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;bringing foreign cash in&lt;/td&gt;
&lt;td&gt;above $5,000 equivalent, declare in writing to customs&lt;/td&gt;
&lt;td&gt;guide, Article 89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;taking foreign cash out, with nothing on your inbound declaration to cover it&lt;/td&gt;
&lt;td&gt;$5,000 to $10,000: permit from a bank; above that, only for travel to countries in financial disorder, with a permit from the local SAFE office&lt;/td&gt;
&lt;td&gt;guide, Article 89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;taking renminbi cash in or out&lt;/td&gt;
&lt;td&gt;20,000 yuan per person per trip&lt;/td&gt;
&lt;td&gt;PBOC announcement No. 18 of 2004, cited below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;withdrawing foreign cash at a bank counter&lt;/td&gt;
&lt;td&gt;above $10,000 equivalent in a day needs prior SAFE filing, and only for travel to countries in financial disorder&lt;/td&gt;
&lt;td&gt;guide, Article 86&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;overseas cash withdrawal on a card issued in China&lt;/td&gt;
&lt;td&gt;100,000 yuan equivalent per person per calendar year, 10,000 yuan per card per day&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.safe.gov.cn/safe/2017/1230/8116.html" rel="noopener noreferrer"&gt;汇发〔2017〕29号&lt;/a&gt;, in force 2018-01-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;overseas card spending&lt;/td&gt;
&lt;td&gt;ordinary current-account purchases, not counted against the $50,000&lt;/td&gt;
&lt;td&gt;same notice, in the accompanying questions and answers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 20,000 yuan figure predates the baggage rules, which say only that cash above the prescribed amount must be declared. The central bank raised the renminbi limit from 6,000 to 20,000 per person per trip in an announcement effective 2005-01-01 (&lt;a href="http://www.pbc.gov.cn/tiaofasi/144941/3581332/3582861/2018072309595152720.pdf" rel="noopener noreferrer"&gt;PBOC announcement No. 18 of 2004, PDF&lt;/a&gt;, read 2026-11-05; &lt;a href="https://www.gov.cn/zhengce/zhengceku/202502/content_7005697.htm" rel="noopener noreferrer"&gt;baggage rules, Order No. 276&lt;/a&gt;, in force 2025-04-01).&lt;/p&gt;

&lt;h2&gt;
  
  
  How I checked
&lt;/h2&gt;

&lt;p&gt;Three passes, all on 2026-11-05.&lt;/p&gt;

&lt;p&gt;First, the version. The regulator's list of valid foreign-exchange rules, updated to 30 June 2026, carries the 2020 guide and the 2006 framework, and lists the guide under three amending notices (&lt;a href="https://www.safe.gov.cn/safe/file/file/20260805/d2d0d6ce1441436eadc8bc600eb79c43.pdf" rel="noopener noreferrer"&gt;SAFE list, PDF&lt;/a&gt;, read 2026-11-05). It carries 168 documents and no 2026 edition of the guide, although agency pages in search results claim one; those pages attribute the same document number to three different titles, which is enough to leave them out.&lt;/p&gt;

&lt;p&gt;Second, the text. I read the gazette version of Articles 54 to 71 and checked the same articles on the regulator's own site, including Articles 62, 68 and 86 to 89.&lt;/p&gt;

&lt;p&gt;Third, the pipeline. A draft notice published on 2026-11-02 would add current-account business to what a relative may handle on your behalf, allow a non-relative with a written reason, and exempt foreign athletes, referees and delegation members at accredited sports events from submitting documents for each transaction (&lt;a href="http://www.safe.gov.cn/safe/2026/1102/28047.html" rel="noopener noreferrer"&gt;draft and covering note&lt;/a&gt;, comments due 2026-12-01). It is a draft. Nothing in it changes the allowances above today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What a specific branch asks for a salary conversion.&lt;/strong&gt; Article 68 lets each bank set its own document list. I did not survey branches, so the contract, the tax record and the employer letter are described as the shape of the requirement, not as a fixed set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether the 2026-11-02 draft becomes rules, and in what form.&lt;/strong&gt; The comment window closes on 2026-12-01.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capital-account routes.&lt;/strong&gt; Sale proceeds of property, equity and investments follow a different chapter with different approvals, and I did not work through it here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether an unused allowance carries into the next year.&lt;/strong&gt; I found no article in the guide, the 2006 framework or its implementation rules that addresses carry-over, and I am not repeating the version that circulates on bank pages without a citation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An official English translation of the guide.&lt;/strong&gt; The regulator runs an English site, but I read this guide in Chinese, and every article number here refers to that text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The friendlier version of the same material for Chinese citizens moving money abroad, including the nine categories of source documents, is in the Chinese piece &lt;a href="https://notebookfield.com/posts/fx-quota-and-paperwork/" rel="noopener noreferrer"&gt;个人购汇与结汇：5 万美元额度管什么&lt;/a&gt;. Where the yuan is priced, and why a bank's counter rate differs from the published parity, is in &lt;a href="https://notebookfield.com/posts/how-the-yuan-is-quoted/" rel="noopener noreferrer"&gt;How the yuan is quoted&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/opening-a-bank-account-as-a-visitor/" rel="noopener noreferrer"&gt;Opening a bank account in China as a visitor&lt;/a&gt; — The visa list a branch works from, and what changes after the account exists.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/how-the-yuan-is-quoted/" rel="noopener noreferrer"&gt;How the yuan is quoted&lt;/a&gt; — The parity, the bank's counter rates, and why the spread is the bank's decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://notebookfield.com/posts/sending-money-out-of-china-as-a-foreigner/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>china</category>
      <category>finance</category>
      <category>law</category>
      <category>immigration</category>
    </item>
    <item>
      <title>1,510 impressions, 6 clicks: what a 28-day Search Console teardown actually showed</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Wed, 28 Oct 2026 03:52:49 +0000</pubDate>
      <link>https://dev.to/hcxshh/1510-impressions-6-clicks-what-a-28-day-search-console-teardown-actually-showed-6nj</link>
      <guid>https://dev.to/hcxshh/1510-impressions-6-clicks-what-a-28-day-search-console-teardown-actually-showed-6nj</guid>
      <description>&lt;h1&gt;
  
  
  1,510 impressions, 6 clicks: what a 28-day Search Console teardown actually showed
&lt;/h1&gt;

&lt;p&gt;A small English site, roughly 70 posts, written for foreigners dealing with practical things in China. It is about three weeks old. Two weeks ago I pulled the query-level data for the first time and got 157 impressions and 4 clicks over 28 days.&lt;/p&gt;

&lt;p&gt;Today the same window reads 1,510 impressions and 6 clicks. Impressions up almost tenfold in twelve days.&lt;/p&gt;

&lt;p&gt;The easy read is "growth". What I actually found after sorting all 71 pages with any impression is a single, concrete leak: 1,131 of those impressions land on pages already ranking in positions 1 to 10, and that whole group converted into 5 clicks. A 0.44% click-through rate on pages that are already on page one.&lt;/p&gt;

&lt;p&gt;The site is no longer in the "not indexed" phase. It gets impressions, it has rankings. What it does not have is anyone clicking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three numbers on one screen, and they disagree
&lt;/h2&gt;

&lt;p&gt;Before any conclusion, the accounting. Google Search Console reports this period three ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Header: 6 clicks, 1,510 impressions, 0.4% CTR, average position 11.1.&lt;/li&gt;
&lt;li&gt;Pages tab: 71 pages with impressions, 8 clicks, 1,559 impressions.&lt;/li&gt;
&lt;li&gt;Queries tab: 47 queries, 104 impressions total, zero clicks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The queries tab accounts for 6.9% of the impressions shown in the header.&lt;/p&gt;

&lt;p&gt;This is not a bug. At low volume, Search Console applies privacy thresholding to queries: it only lists the ones it is willing to list, and it rounds the numbers. The consequence matters more than the mechanism: if you treat the query table as a complete picture, you will draw the opposite conclusion from the one the data supports.&lt;/p&gt;

&lt;p&gt;Everything below uses the pages tab, because that layer is complete. The three totals are printed here so the discrepancy is visible rather than silently resolved.&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%2Fb772n90onlmgt7mxcl4r.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%2Fb772n90onlmgt7mxcl4r.png" alt="Impressions by page, top six" width="799" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Impressions are already on page one
&lt;/h2&gt;

&lt;p&gt;Sorting the 71 pages by average position into four buckets:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Position&lt;/th&gt;
&lt;th&gt;Pages&lt;/th&gt;
&lt;th&gt;Impressions&lt;/th&gt;
&lt;th&gt;Clicks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1-10&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;td&gt;1,131&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11-30&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;296&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;31-60&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;131&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;61-100&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The four buckets add up to 71 pages, 1,559 impressions and 8 clicks, matching the pages tab.&lt;/p&gt;

&lt;p&gt;The first row is the interesting one. More than half the pages already sit in the top 10. They take about 72% of all impressions. They convert at 5 clicks per 1,131 impressions, which is under half a percent.&lt;/p&gt;

&lt;p&gt;A new site's usual problem is not being indexed. This one is past that. The bottleneck is the two lines of text Google shows: the title and the description are not giving anyone a reason to click.&lt;/p&gt;

&lt;p&gt;So the next move is not writing fifty more posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The six biggest pages, and two counter-examples
&lt;/h2&gt;

&lt;p&gt;Top six by impressions (clicks and average position in brackets):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LPR and mortgage rates: 367 impressions, 0 clicks, position 6.1&lt;/li&gt;
&lt;li&gt;FX quota and paperwork: 128, 0, position 7.1&lt;/li&gt;
&lt;li&gt;Paying in China as a foreigner: 94, 0, position 15.6&lt;/li&gt;
&lt;li&gt;Air quality index explained: 84, 0, position 5.8&lt;/li&gt;
&lt;li&gt;Sleep data by group: 71, 2, position 31.8&lt;/li&gt;
&lt;li&gt;Setting up Alipay and WeChat Pay: 70, 0, position 8.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Five of these share a shape: they answer a factual question, but the title is written as a topic label while the reader typed a question. The LPR page is the extreme case. 367 impressions, zero clicks, position 6.1. It was seen 367 times and never once looked worth opening.&lt;/p&gt;

&lt;p&gt;Two counter-examples are worth copying.&lt;/p&gt;

&lt;p&gt;One page, explaining why speed is the default in China, got 4 impressions, 2 clicks, a 50% CTR at position 5.0. Almost no impressions, but when it was seen, people clicked. Its title is a standalone sentence; you do not have to guess what the page argues.&lt;/p&gt;

&lt;p&gt;The other is the sleep data page: position 31.8, still 2 clicks, 2.8% CTR. It is picking up non-English long-tail queries, which says the audience is not confined to English-language markets. I have not decided whether to follow that thread yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query-level optimization at this volume is blind work
&lt;/h2&gt;

&lt;p&gt;My first instinct was to go query by query. The numbers killed that plan.&lt;/p&gt;

&lt;p&gt;The 47 listed queries total 104 impressions and zero clicks. All 6 header clicks land on words the query table does not show. The terms that actually convert are the ones Search Console is not telling me. Optimizing against the visible list means optimizing traffic that was never going to click.&lt;/p&gt;

&lt;p&gt;Two things in the query table are still usable.&lt;/p&gt;

&lt;p&gt;The first is a clear "do not bother". Five name queries total 22 impressions at positions 1.0 to 5.7, the best positions on the site, with zero clicks. They come from note pages that quote specific authors; the searcher wants the person, not the page. No amount of title work will change that.&lt;/p&gt;

&lt;p&gt;The second is "real demand, far away". Three variants of the word for a Chinese tax invoice total 23 impressions at positions 55 to 91. Four phrasings of the eSIM question sit at 75 to 82. The demand exists, but it is dozens of positions from a click, and working on it now changes nothing.&lt;/p&gt;

&lt;p&gt;The usable substitute is the pages tab, which is complete. The rule I settled on: decide per page, grouped by position band, never per query.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I changed, and the test I will judge it by
&lt;/h2&gt;

&lt;p&gt;Five pages: FX quota, LPR, paying in China, Alipay setup, the leaving-and-returning checklist. All five had heavy impressions and zero clicks.&lt;/p&gt;

&lt;p&gt;Two edits only. Move the answer to the front of the title, and put the real question phrasing into the description. Body copy untouched. The reasoning is simple: at position six, the title line is the only thing anyone reads.&lt;/p&gt;

&lt;p&gt;Baseline is the table above, window 10 to 25 October, pulled on 28 October.&lt;/p&gt;

&lt;p&gt;The pass criteria are fixed in advance. In two weeks I look at exactly two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Whether CTR on those five pages moves off 0%.&lt;/li&gt;
&lt;li&gt;Whether average position falls out of the top 10.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Not total impressions. Impressions moving is an indexing story; clicks moving is a title story. Mixing them means never knowing which lever did anything.&lt;/p&gt;

&lt;p&gt;One risk, recorded up front: three of the five are utility pages. A punchier title that overpromises sends people into a page that does not answer their question, which is worse than no click. The descriptions have to say what the page actually contains, not just bait.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open questions
&lt;/h2&gt;

&lt;p&gt;Why the query table covers only 6.9% of impressions: no official answer found. I can only confirm the gap at low volume, and I do not know when it closes.&lt;/p&gt;

&lt;p&gt;Why the invoice cluster gets 23 impressions but sits at 55 to 91: my guess is the page explains the process but never defines the concept, and English searchers are looking for the concept. That is a guess, untested.&lt;/p&gt;

&lt;p&gt;Why the sleep page converts at 2.8% from position 31.8: the non-English long-tail lead. Whether to build a line around it is undecided.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduction
&lt;/h2&gt;

&lt;p&gt;Data: Google Search Console, Performance report, Pages tab, 28-day window (the page itself displayed 10 to 25 October), pulled 28 October 2026, with the dashboard reporting "last updated 3 hours ago".&lt;/p&gt;

&lt;p&gt;The bucket table was produced by exporting the 71 page rows and summing by average position. Bucket totals match the tab total.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Drafted with an AI assistant from my own measurements; every number and link here was checked against the source.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>analytics</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>My content-gap detector found 38 gaps. Two-thirds were bugs in the detector.</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Mon, 26 Oct 2026 01:33:53 +0000</pubDate>
      <link>https://dev.to/hcxshh/my-content-gap-detector-found-38-gaps-two-thirds-were-bugs-in-the-detector-75l</link>
      <guid>https://dev.to/hcxshh/my-content-gap-detector-found-38-gaps-two-thirds-were-bugs-in-the-detector-75l</guid>
      <description>&lt;p&gt;I write a small English blog about China — 70 posts, a domain registered on 11 October 2026. Like anyone with a new site, I wanted to know what to write next, and the cheapest signal available is autocomplete: send a seed phrase to Google's and Bing's suggestion endpoints, collect what people are actually typing, then compare that against what you have already published.&lt;/p&gt;

&lt;p&gt;So I wired it into a status dashboard I check each morning. This morning it ran in 5.2 seconds and collected 466 unique suggestions from 56 requests (51 returned; the other five were dropped by a proxy or a rate limit).&lt;/p&gt;

&lt;p&gt;Then it told me I had 38 content gaps.&lt;/p&gt;

&lt;p&gt;That number was wrong, and the way it was wrong turned out to be more useful than the number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scoring, in one paragraph
&lt;/h2&gt;

&lt;p&gt;Each query is lowercased, tokenised, stemmed, and stripped of question words and place-or-language words — "how", "what", "china", "english", "foreigner" and so on. What is left is a bag of content words. Each post gets the same treatment, but only from its slug and title. The score is matched content words divided by total content words, plus 0.15 if an adjacent pair also matches. Above 0.60 counts as covered, 0.30–0.59 as partial, below that as a gap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;stem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[a-z0-9]+&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;())]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;GEO&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ASK&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three thresholds, one table, a number I can put in a commit message. What could go wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 38 gaps, by cluster
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cluster&lt;/th&gt;
&lt;th&gt;Queries&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Meituan app in English&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;"meituan app english version", "how to change language in meituan app"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Didi&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;"how to call didi in china", "didi alternatives in china"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;China bank foreign exchange&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;"china bank foreign exchange policy"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1688&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;"translate 1688 as a year in history"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Company registration&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;"business registration in china"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote work&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;"working remotely from another country"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;China Mobile&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;"does china mobile support esim"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hukou, in Dutch or German&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;"wat is het hukou systeem", "was ist das hukou system"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hukou, in hip hop&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;"hukou has hip hop music"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fapiao check&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;"china tax bureau fapiao check"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customs law&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;"customs law of the people's republic of china"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One apartment&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;"apartment in china that holds 30000 people"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Failure one: my stopword list ate the query
&lt;/h2&gt;

&lt;p&gt;Take "how to change language in meituan app". After filtering, the content words are &lt;code&gt;change, language&lt;/code&gt;. "Meituan" sat in my geography set, because I had added it while tuning noise from "meituan vs eleme" comparisons. "English" sat there too, for the same reason. "App" was in the ask set, because it appears in half of all autocomplete suggestions and carries no topic.&lt;/p&gt;

&lt;p&gt;So "meituan app english download" reduced to &lt;code&gt;download&lt;/code&gt;. "login to meituan english" reduced to &lt;code&gt;login&lt;/code&gt;. "meituan waimai app english" reduced to &lt;code&gt;waimai&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Those queries then scored 0.00 against every post I have, including one titled &lt;em&gt;Chinese apps in English: what Meituan and Dianping ship&lt;/em&gt;, which answers them. The word that would have matched was deleted before the matching started.&lt;/p&gt;

&lt;p&gt;It gets better. The seed I typed for that topic was "meituan english app". It produced zero content words, so the loop skipped it entirely — 465 of the 466 suggestions were scored, and the one that was dropped was my own seed phrase.&lt;/p&gt;

&lt;p&gt;All twelve Meituan queries were that bug. Same word list, same week, same collector.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure two: matching titles instead of pages
&lt;/h2&gt;

&lt;p&gt;The Didi cluster is the opposite mistake. &lt;code&gt;didi&lt;/code&gt; survived the filter, but posts are indexed from slug and title only, and my ride-hailing post is called "Ride-hailing in China: licences, fares and invoices". The body mentions Didi repeatedly. The title does not. One content word, zero matches, five queries into the gap bucket.&lt;/p&gt;

&lt;p&gt;Both bugs point the same way: the score measures how well my titles match a query, not whether a page answers it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What survived a human pass
&lt;/h2&gt;

&lt;p&gt;I read all 38 by hand. Twelve were the stopword bug. Five were title-only matching. Two were the same question asked in Dutch and German. Six were entity collisions — Google reading 1688 as a year, "hukou" as hip hop, and one viral-sounding apartment query with no matching page. That is 25 of 38, about two-thirds, that were noise from four causes, none of them editorial.&lt;/p&gt;

&lt;p&gt;Three clusters were left that are worth acting on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;China bank foreign exchange&lt;/strong&gt; — four queries, and no page whose title names the policy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registering a company&lt;/strong&gt; — "business registration in china" and "how long does it take to register a company in china". I have a page titled as a process, not as a duration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;China Mobile&lt;/strong&gt; — "china mobile international customer service number" and "does china mobile support esim", both answered in sections on a page whose title does not contain either phrase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the useful output: three clusters, not 38 gaps, and the reason each one was invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I changed in the tool, not the site
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Index the body, not only slug and title.&lt;/strong&gt; The posts are markdown on disk; a bag of body words per post costs nothing and would have caught the Didi cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never strip a token that appears in fewer than N posts.&lt;/strong&gt; Brand and language words are exactly the ones that carry intent. If I need to suppress noise, suppress it after matching, not before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report clusters, not counts.&lt;/strong&gt; "38 gaps" is a number I can quote. "Twelve of them are one app's language settings" is the information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the human pass.&lt;/strong&gt; The tool's job is to turn 466 suggestions into a list I can read in ten minutes. It is a triage queue, not an editorial calendar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The collector still runs every morning at 07:00, because as a triage queue it works. It just should not be allowed to name a number by itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method notes, so the arithmetic can be checked
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;28 seed phrases × 2 engines = 56 requests. 51 returned a body. The 466 suggestions are unique after lowercasing.&lt;/li&gt;
&lt;li&gt;Scoring is &lt;code&gt;hits / content_words + 0.15&lt;/code&gt; if any adjacent pair matches; it is a lexical overlap score, not a semantic one. A page that answers a question in different words scores zero.&lt;/li&gt;
&lt;li&gt;Read 2026-10-26, from the same dashboard run that produced the site's traffic numbers. No API key is involved: both suggestion endpoints are public.&lt;/li&gt;
&lt;li&gt;The blog is at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;notebookfield.com&lt;/a&gt;, and the Meituan page the scorer missed is &lt;a href="https://notebookfield.com/posts/chinese-apps-in-english/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>seo</category>
      <category>python</category>
      <category>webdev</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Five numbers on my dashboard that were true and still wrong</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 23 Oct 2026 05:21:20 +0000</pubDate>
      <link>https://dev.to/hcxshh/five-numbers-on-my-dashboard-that-were-true-and-still-wrong-561h</link>
      <guid>https://dev.to/hcxshh/five-numbers-on-my-dashboard-that-were-true-and-still-wrong-561h</guid>
      <description>&lt;p&gt;Every morning at 07:10 a script collects numbers for a dashboard I keep for my own blog: is the site up, what did I publish, how many people came, what Google showed, which posts did anything. None of the numbers is wrong. Several of them were, in practice, useless — because they were true in a way that made me read the wrong story.&lt;/p&gt;

&lt;p&gt;These are the five that lied to me this month, with the arithmetic and the fix for each.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. "Views are down 83% this week"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it said.&lt;/strong&gt; One morning: 290 page views, 290 visitors. The next morning: 50 views, 50 visitors. A 83% collapse, the kind of chart that makes you check whether the site is down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it was wrong.&lt;/strong&gt; The 7-day window had slid by one day. Cloudflare Web Analytics aggregates by UTC day, and the first window (2026-10-14 to 2026-10-20) contained a single crawler sweep on 10-14: about 250 page views across 20+ paths, where every path showed exactly one view and one visitor. The next window (10-15 to 10-21) no longer contained it. Same site, same traffic, different window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tell:&lt;/strong&gt; 250 views and 250 visitors. Real readers occasionally read two pages. A ratio of exactly 1.00 across hundreds of views is a process walking your sitemap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; print the window next to every number, not in a tooltip. A metric without its window is not a metric, it is a mood. This one is now a labelled field in the data file, so the chart cannot be read without it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. "One page per visit means nobody reads past the landing page"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it said.&lt;/strong&gt; Pages per visit: 1.0. A classic engagement death signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it was wrong.&lt;/strong&gt; The denominator included the crawler. When 250 of 290 "visitors" are one automated client fetching one page each, the average is arithmetic on a population that mostly does not read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; split automation out before computing anything per-user. Cloudflare's RUM GraphQL exposes a &lt;code&gt;userAgentBrowser&lt;/code&gt; dimension, so the collector now queries two buckets and reports "non-bot views" as its own metric, with the bot share shown next to it. This week: 70 views in 7 days, 60 of them non-bot, 14% automated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The uncomfortable part:&lt;/strong&gt; after the split, pages per visit is still 1.0. Now the number means what I thought it meant the first time — people arrive on one page and leave. A metric can be broken and then confirm bad news; those are different events and I only learned which one I had by fixing the measurement first.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. "Upvote ratio 0.61 — so 39% of voters disliked it"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it said.&lt;/strong&gt; A post read once gave 17 points and a ratio of 0.61. Read again two minutes later: 18 points, ratio 0.62. On a smaller post a week earlier, the same check gave 5 points at ratio 1.0, then 4 points at 0.84–0.86.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it was wrong.&lt;/strong&gt; At single-digit and low-double-digit scores, platforms fuzz the ratio so outsiders cannot reverse-engineer individual votes. The fuzzing means "ratio 1.0" is not "no downvotes"; it is "not enough votes for us to tell you". Re-reading gives different numbers because the fuzz is recomputed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; at low counts, report counts and stop. Score and comment count are integers and reproducible; a ratio at n=5 is noise with a decimal point. If a trend matters, require two reads pointing the same way before writing it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. "Zero traffic on two days"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it said.&lt;/strong&gt; The daily series had no rows for 2026-10-17 and 2026-10-19. Two flat-zero days between normal days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it was wrong.&lt;/strong&gt; Nothing was collected those days because the analytics beacon was not live on the site yet — it was added later. A missing row and a zero row rendered identically, and zero rendered as "traffic died".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; missing data gets its own state, not a zero. The collector now writes the expected dates, marks the ones with no data as missing, and the chart draws a gap instead of a line at zero. The alert text lists the missing dates by name, because "no data" is information and "0" is a claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. "4 clicks. No — 6 clicks."
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it said.&lt;/strong&gt; The same Search Console panel, same 28-day window, two numbers: the headline said 4 clicks and 612 impressions; adding up the per-page table gave 6 clicks and 563 impressions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it was wrong.&lt;/strong&gt; Neither is wrong. At low volume Search Console applies thresholds and rounding in different places, so the aggregate and the detail do not reconcile. This is not a bug I can fix, and it is not a reason to distrust everything — it is a reason to stop treating one of them as "the" number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; print both, labelled, and never do arithmetic across them. My dashboard now stores the headline figures and the summed figures as separate fields. If a decision depends on the difference between 4 and 6 clicks, the decision is not ready to be made.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule I ended up with
&lt;/h2&gt;

&lt;p&gt;Every number on that dashboard now has to answer four questions before I am allowed to act on it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What window is it over,&lt;/strong&gt; written next to the number?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who is in the denominator&lt;/strong&gt; — people, sessions, or automated clients?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is a missing value being rendered as a zero?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;At this sample size, is the difference I am looking at even measurable?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The dashboard still has plenty of numbers that are small. That is fine. Small is a fact; a small number presented without its window, its population and its missing days is a story, and usually the wrong one.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Drafted with an AI assistant from my own measurements; every number and link here was checked against the source.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The dashboard itself is a static page rebuilt every morning from the raw sources. The longer write-ups and the measurements live at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/hcxshh/collecting-trending-feeds-from-20-platforms-only-8-had-a-usable-api-and-one-silently-failed-for-1k3i"&gt;Collecting trending feeds from 20 platforms&lt;/a&gt; — The same failure mode one layer down: a job that reports "success" while the data is twelve days old.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/what-this-site-ships/" rel="noopener noreferrer"&gt;What this site ships&lt;/a&gt; — The measurement rules this dashboard is built on, written down before the numbers arrived.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>analytics</category>
      <category>data</category>
      <category>monitoring</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I rewrote five article titles to match real search queries. Then Google showed them 122 times and nobody clicked.</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 23 Oct 2026 05:21:12 +0000</pubDate>
      <link>https://dev.to/hcxshh/i-rewrote-five-article-titles-to-match-real-search-queries-then-google-showed-them-122-times-and-4gnc</link>
      <guid>https://dev.to/hcxshh/i-rewrote-five-article-titles-to-match-real-search-queries-then-google-showed-them-122-times-and-4gnc</guid>
      <description>&lt;p&gt;I run a small English blog — 70 posts, a domain registered on 2026-10-11, so twelve days old when I wrote this. Search Console was showing a strange pattern: pages getting impressions and almost no clicks. The cheapest thing I could change was the headline, so I changed five of them.&lt;/p&gt;

&lt;p&gt;This is what happened, including the part where the data could not answer the question.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;On 2026-10-16 I rewrote the titles and descriptions of five posts. Frontmatter only; not a word of the bodies changed.&lt;/li&gt;
&lt;li&gt;In the first five days of usable data after the change, those five pages collected &lt;strong&gt;122 impressions and zero clicks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;That is not proof the titles failed. It is proof that at this volume I cannot measure a title at all. So I stopped the experiment instead of starting a second batch, which is the more useful outcome.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I changed, and why those five
&lt;/h2&gt;

&lt;p&gt;The five pages had impressions but no clicks, and the queries they appeared for had a different shape from the headline. People searched questions; my titles were noun phrases.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Page&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;using-esim-in-china&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Using an eSIM in China: what the official pages say&lt;/td&gt;
&lt;td&gt;Can you use an eSIM in China? It depends on the phone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;china-transit-visa-free-rules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;China's transit visa-free rules: 24 hours and 240 hours&lt;/td&gt;
&lt;td&gt;Transiting China without a visa: 24 hours and 240 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chinas-fapiao&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;China's fapiao: what it carries, and how to check one&lt;/td&gt;
&lt;td&gt;China's fapiao: the invoice system, explained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sleep-duration-what-guidance-says&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where the sleep-duration numbers come from&lt;/td&gt;
&lt;td&gt;How much sleep do adults need? The published ranges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;air-quality-index-explained&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The AQI is a definition: China vs US breakpoints&lt;/td&gt;
&lt;td&gt;The AQI is a definition: China's HJ 633 vs US breakpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The reasoning was simple: put the words people actually type into the title, keep the promise the page can keep. I verified afterwards that all five new titles were live in the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; tag, not just in the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the numbers actually say
&lt;/h2&gt;

&lt;p&gt;Search Console, read on 2026-10-23. The window is 2026-10-10 to 2026-10-20 — console data lags two to three days, so the "week" is really five days after the change.&lt;/p&gt;

&lt;p&gt;Whole site, 28 days:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clicks        4
impressions 612
CTR         0.7%
avg position 11.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whole site, last 7 days: &lt;strong&gt;0 clicks, 455 impressions, average position 13.4&lt;/strong&gt;. Every click in the 28-day window happened before 2026-10-14.&lt;/p&gt;

&lt;p&gt;The five rewritten pages, same 28-day window:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Page&lt;/th&gt;
&lt;th&gt;Impressions&lt;/th&gt;
&lt;th&gt;Clicks&lt;/th&gt;
&lt;th&gt;Avg position&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;air-quality-index-explained&lt;/td&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;6.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;chinas-fapiao&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;38.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;china-transit-visa-free-rules&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;27.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;using-esim-in-china&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;20.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sleep-duration-what-guidance-says&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;7.2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At the query level, &lt;code&gt;can you use esim in china&lt;/code&gt; was still at position 82 with one impression, and &lt;code&gt;can i transit in china without visa&lt;/code&gt; at 75 with one impression — the same readings I had before the change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "122 impressions, 0 clicks" is weaker evidence than it looks
&lt;/h2&gt;

&lt;p&gt;Three reasons, in order of how much they matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Most of those pages sit where nobody clicks.&lt;/strong&gt; Three of the five average position 20 to 38. At that depth an impression is a rounding error, not an opportunity. The honest expectation for 122 impressions spread across positions 6 to 38 is less than one click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The volume cannot separate a good title from a bad one.&lt;/strong&gt; Published sample-size arithmetic: to tell a 3% click-through rate from a 6% one with any confidence, each arm needs roughly 750 impressions. The entire site had 612 impressions in 28 days. At a true CTR of 3%, seeing zero clicks on 46 impressions happens about a quarter of the time anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The console hides most of the queries.&lt;/strong&gt; The query table returned 36 rows covering 67 of 612 impressions — about 11%. The rest sits below Google's reporting threshold. So for most pages I do not know which query produced the impression, which means I cannot tell whether the new title matches it or misses it.&lt;/p&gt;

&lt;p&gt;There is also a timing problem I can name but not quantify: a twelve-day-old domain is still being crawled and re-evaluated, so any effect of a title change is mixed with the effect of Google simply noticing the site for the first time. I cannot separate the two with this data.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I changed in the process, not the site
&lt;/h2&gt;

&lt;p&gt;The experiment did produce a decision, just not the one I was hoping for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No second batch.&lt;/strong&gt; I had three more titles ready. Changing them now would mean two experiments running at once, and then no result would be attributable to either.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A title change needs a query phrase I can show, not a query theme I can guess.&lt;/strong&gt; If the console will not tell me the sentence, I do not write the sentence into the title. Guessing produces a plausible-looking string and no information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The bottleneck is impressions, not click-through rate.&lt;/strong&gt; 612 impressions in 28 days on 70 posts means almost nobody sees the pages. Editing headlines optimises a conversion step that is not the constraint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I stopped editing titles and started spending the same hours on distribution: getting the pages in front of people somewhere other than a search results page they never reach. This post is part of that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method notes, so the numbers can be checked
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;All figures are from Google Search Console for the &lt;code&gt;https://notebookfield.com/&lt;/code&gt; property, read 2026-10-23, window 2026-10-10 to 2026-10-20.&lt;/li&gt;
&lt;li&gt;Search Console's own totals disagree with the sum of its per-page table (4 clicks / 612 impressions at the top, 6 clicks / 563 impressions when summed). Both are printed here rather than picking the friendlier one. This is a known behaviour at low volume.&lt;/li&gt;
&lt;li&gt;Titles and descriptions live in frontmatter and are not part of the article body, which is why this counted as a metadata change and not a content change. Five social-preview images were regenerated because they render the title.&lt;/li&gt;
&lt;li&gt;What I did not measure: click-through rate by position for these specific pages. Google does not publish it per page, and with 122 impressions the confidence interval would be useless anyway.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Drafted with an AI assistant from my own measurements; every number and link here was checked against the source.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/using-esim-in-china/" rel="noopener noreferrer"&gt;Can you use an eSIM in China? It depends on the phone&lt;/a&gt; — One of the five rewritten pages, and what the carriers actually publish.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/chinas-fapiao/" rel="noopener noreferrer"&gt;China's fapiao: the invoice system, explained&lt;/a&gt; — Another of the five: what the tax rules put on the document.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>seo</category>
      <category>analytics</category>
      <category>writing</category>
      <category>blogging</category>
    </item>
    <item>
      <title>One of China's four big e-commerce platforms publishes its fee card. The rest don't.</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Sun, 18 Oct 2026 02:28:39 +0000</pubDate>
      <link>https://dev.to/hcxshh/one-of-chinas-four-big-e-commerce-platforms-publishes-its-fee-card-the-rest-dont-b6</link>
      <guid>https://dev.to/hcxshh/one-of-chinas-four-big-e-commerce-platforms-publishes-its-fee-card-the-rest-dont-b6</guid>
      <description>&lt;p&gt;I spent an afternoon opening the merchant fee pages of Douyin, Taobao, Tmall, JD and Pinduoduo as an anonymous visitor. One of them publishes a full rate card. One publishes a notice with the rates inside a PNG. One answers a four-field form. The other two stop at a login.&lt;/p&gt;

&lt;p&gt;That distribution is the story: in Chinese e-commerce, the fee is public only when the platform decides to make it public, and the form of publication tells you how comparable the numbers are.&lt;/p&gt;

&lt;p&gt;Full tables and the per-platform notes are on my site: &lt;a href="https://notebookfield.com/posts/platform-commission-rates/" rel="noopener noreferrer"&gt;What Chinese e-commerce platforms charge a merchant&lt;/a&gt;. This post is the short version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Douyin is the only full card
&lt;/h2&gt;

&lt;p&gt;Douyin publishes a category technical service fee schedule dated 14 July 2026: 269 category rows, base rates from 0.30% (phone top-ups) to 10.00% (a catch-all services row), median total 6.90%.&lt;/p&gt;

&lt;p&gt;Those aggregates are mine, not the platform's. I pulled each row out of the rendered DOM, forward-filled the merged category cells, dropped the one placeholder row with no category and a 0.00% rate, then computed the range, median and distribution in Python. The row-level percentages are Douyin's; the arithmetic is mine.&lt;/p&gt;

&lt;p&gt;Two columns matter. The base rate comes from the category you list in. A second column adds up to 5.20 percentage points when the order arrives through the Douyin Mall app or Doubao. On 98 of the 269 rows the supplement equals the base exactly, so the same apparel category is 5.00% in a livestream and 10.00% in mall search. On 151 rows the supplement is 1.3x the base, which is where the 4.60%, 6.90% and 9.20% totals come from.&lt;/p&gt;

&lt;p&gt;Deposits follow last month's turnover, not the category: ¥500 up to ¥50,000 GMV, ¥2,000 to ¥100,000, ¥5,000 above that. Categories the platform considers risky — top-ups, game services, fuel cards, cinema — carry ¥200,000; second-hand phones ¥20,000.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tmall: a notice that points at an image
&lt;/h2&gt;

&lt;p&gt;Tmall's only public statement on category fees this year is a cut notice dated 27 March 2026. It says the rates change from 1 April and points to a list of new categories and rates. That list is one PNG, 1552 by 928 pixels.&lt;/p&gt;

&lt;p&gt;I did not transcribe it. Percentages read off a screenshot are not a source I would publish, and the rows being cut are online-game cards, digital services, gift cards and video-game software — not apparel or appliances.&lt;/p&gt;

&lt;p&gt;Taobao's 0.6% base fee, capped at ¥60 per sub-order, circulates in Chinese trade press from the 2024 rule. I could not open the help-centre article carrying it, so on my site it stays labelled second-hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  JD answers with a form
&lt;/h2&gt;

&lt;p&gt;JD's recruitment site asks for entity type, shop type, import status and category, then returns three numbers. One query — 企业, 旗舰店, 非进口, 家用电器 / 大家电 / 平板电视 — returned a ¥5,000.00 deposit, an operating support service fee of 3.70% and a transaction service fee of 0.60%.&lt;/p&gt;

&lt;p&gt;Two failed attempts taught me the interaction: clicking a category name drills into its children but leaves the selection count at zero. The checkbox beside the name is what ticks it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pinduoduo: the rulebook is behind the login
&lt;/h2&gt;

&lt;p&gt;The rules centre answers with a login form. The public onboarding manual (version 6.3, fifteen pages) lists documents, not money: business licence, legal representative's ID, shop manager's ID, brand authorisation. No percentage, deposit or monthly fee.&lt;/p&gt;

&lt;p&gt;One clause is useful for a foreign brand: authorisation can come from a trademark owner's wholly owned subsidiary, or from its 境内唯一总代理 — sole agent inside China — which the manual marks as applying only to overseas trademark owners. In practice a foreign brand reaches a Pinduoduo storefront through a mainland company.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number that is not comparable
&lt;/h2&gt;

&lt;p&gt;A headline rate is only comparable if both sides compute it on the same base. Douyin's settlement base is what the buyer paid including shipping, plus host coupons, platform coupons and subsidies, minus the merchant-funded parts. JD reports a category rate and a separate 0.60% transaction fee. Tmall states one category rate in a picture and nothing else.&lt;/p&gt;

&lt;p&gt;So "Douyin charges 5%" and "JD charges 3.7%" are not two points on one scale. They are two different objects, published in two different forms, on two different bases.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tmall's rates as numbers: the notice points to an image.&lt;/li&gt;
&lt;li&gt;Taobao's 0.6% base fee and ¥60 cap: trade press only, no live rule page I could open.&lt;/li&gt;
&lt;li&gt;Whether Tmall still charges an annual fee: the old tables paired it with the category rate; status unverified in 2026.&lt;/li&gt;
&lt;li&gt;Every Pinduoduo rate and deposit: merchant login required.&lt;/li&gt;
&lt;li&gt;Alipay's and WeChat Pay's standard merchant rates: they circulate widely, but I could not pin either to a dated official page, so neither appears.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full page has the 269-row breakdown, the cross-border card (145 rows, 1.60%–6.00% base), the deposit rule and the source links, each with the date the platform printed on it: &lt;a href="https://notebookfield.com/posts/platform-commission-rates/" rel="noopener noreferrer"&gt;notebookfield.com/posts/platform-commission-rates&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/minimum-wage-by-province/" rel="noopener noreferrer"&gt;China's minimum wage by province, 2026&lt;/a&gt; — The provincial wage floor the same merchants hire against.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/how-live-commerce-money-moves/" rel="noopener noreferrer"&gt;How the money actually moves in live commerce&lt;/a&gt; — Where the commission sits in the settlement chain.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>china</category>
      <category>ecommerce</category>
      <category>data</category>
      <category>business</category>
    </item>
    <item>
      <title>Measuring error in support AI, and what accuracy hides</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:01:48 +0000</pubDate>
      <link>https://dev.to/hcxshh/measuring-error-in-support-ai-and-what-accuracy-hides-9nc</link>
      <guid>https://dev.to/hcxshh/measuring-error-in-support-ai-and-what-accuracy-hides-9nc</guid>
      <description>&lt;p&gt;The way I test a support or ticketing system is against a task book, not a demo. The number people&lt;br&gt;
ask for afterwards is accuracy, and I have stopped handing that over on its own: on every realistic&lt;br&gt;
test set I have built, the average was the least informative thing in the results file.&lt;/p&gt;

&lt;p&gt;What follows is the method I use now, plus a run on public data so the numbers can be checked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five failure modes, five different bills
&lt;/h2&gt;

&lt;p&gt;A support model fails in at least five ways, and they do not cost the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fabrication.&lt;/strong&gt; The reply asserts something absent from the policy or the record: a fee that does&lt;br&gt;
not exist, a wrong refund window, an invented delivery date. This is what people call&lt;br&gt;
hallucination. The taxonomy I lean on, OlaBench, splits it into four sub-types — factual&lt;br&gt;
hallucination, misuse of retrieved results, relevance hallucination, and logical inconsistency&lt;br&gt;
(&lt;a href="https://arxiv.org/abs/2510.22143v3" rel="noopener noreferrer"&gt;OlaBench, arXiv:2510.22143v3&lt;/a&gt;, §3.1; v1 25 Oct 2025, v3 25&lt;br&gt;
May 2026, retrieved 9 Oct 2026). A sentence that reads well and is false costs money, and the cost&lt;br&gt;
usually arrives months later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missed detection.&lt;/strong&gt; The ticket contains a legal threat, a regulator's name, a safety issue or a&lt;br&gt;
fraud signal, and the system files it as routine. Nothing false was said. The failure is that no&lt;br&gt;
human ever reads it. On a scoring sheet this often counts as a correct classification, because the&lt;br&gt;
model did pick a queue that exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Misrouting.&lt;/strong&gt; The reply is reasonable and the queue is wrong, so a differently-skilled team&lt;br&gt;
applies its own policy. The measurable cost is time to resolution and repeat contact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out-of-scope answers.&lt;/strong&gt; The system answers a question it was never built for — tax, legal&lt;br&gt;
advice, another provider's product. The content may be accurate. The exposure is regulatory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refusal.&lt;/strong&gt; The system declines, loops, or hands off. There is no answer, so there is no error to&lt;br&gt;
score. This one gets tracked as a transfer or containment rate and drops out of the error count&lt;br&gt;
entirely.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure&lt;/th&gt;
&lt;th&gt;What is wrong&lt;/th&gt;
&lt;th&gt;Where the cost lands&lt;/th&gt;
&lt;th&gt;Commonly reported as&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fabrication&lt;/td&gt;
&lt;td&gt;the content&lt;/td&gt;
&lt;td&gt;refunds, goodwill, disputes&lt;/td&gt;
&lt;td&gt;hallucination rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missed detection&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;td&gt;escalation, legal, safety&lt;/td&gt;
&lt;td&gt;almost nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Misrouting&lt;/td&gt;
&lt;td&gt;the destination&lt;/td&gt;
&lt;td&gt;time to resolution, repeats&lt;/td&gt;
&lt;td&gt;accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Out of scope&lt;/td&gt;
&lt;td&gt;the remit&lt;/td&gt;
&lt;td&gt;compliance&lt;/td&gt;
&lt;td&gt;rarely measured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refusal&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;td&gt;churn, containment&lt;/td&gt;
&lt;td&gt;transfer rate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One accuracy figure blends these five at whatever ratio the sample happens to contain, and that&lt;br&gt;
ratio is set by whoever picked the sample.&lt;/p&gt;

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

&lt;p&gt;I needed a routing task with a ground-truth label and text a model would really see. Public&lt;br&gt;
complaint data has both.&lt;/p&gt;

&lt;p&gt;The US CFPB publishes its Consumer Complaint Database under CC0 (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/" rel="noopener noreferrer"&gt;CFPB Consumer Complaint&lt;br&gt;
Database&lt;/a&gt;, retrieved 9 Oct&lt;br&gt;
2026); the search API's own metadata reports 18,274,023 records, license CC0, last updated&lt;br&gt;
2026-10-08 (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/?size=0&amp;amp;no_aggs=true" rel="noopener noreferrer"&gt;API metadata&lt;/a&gt;,&lt;br&gt;
retrieved 9 Oct 2026).&lt;/p&gt;

&lt;p&gt;The JSON search endpoint returns 15 fields and no narrative (&lt;a href="https://cfpb.github.io/api/ccdb/fields.html" rel="noopener noreferrer"&gt;field&lt;br&gt;
reference&lt;/a&gt;, retrieved 9 Oct 2026). That is new: the&lt;br&gt;
September 2026 release removed complaint narratives from the database (&lt;a href="https://cfpb.github.io/api/ccdb/release-notes.html" rel="noopener noreferrer"&gt;CFPB release&lt;br&gt;
notes&lt;/a&gt;, Release 24, September 2026). So I took&lt;br&gt;
the text from a Hugging Face mirror instead, &lt;code&gt;BEE-spoke-data/consumer-finance-complaints&lt;/code&gt; (&lt;a href="https://huggingface.co/datasets/BEE-spoke-data/consumer-finance-complaints" rel="noopener noreferrer"&gt;dataset&lt;br&gt;
card&lt;/a&gt;, CC0, mirror last&lt;br&gt;
modified 29 Dec 2025); its &lt;code&gt;has-text&lt;/code&gt; config holds 1,689,573 rows (&lt;a href="https://datasets-server.huggingface.co/size?dataset=BEE-spoke-data%2Fconsumer-finance-complaints" rel="noopener noreferrer"&gt;split&lt;br&gt;
sizes&lt;/a&gt;,&lt;br&gt;
retrieved 9 Oct 2026).&lt;/p&gt;

&lt;p&gt;Task: read the narrative only, predict the product queue. Ground truth: the product label the CFPB&lt;br&gt;
publishes. I drew 3,894 complaints from it. The oldest is dated 2015-03-20, the newest&lt;br&gt;
2024-01-04.&lt;/p&gt;

&lt;h2&gt;
  
  
  The label set changed under the data
&lt;/h2&gt;

&lt;p&gt;The raw product field holds 17 distinct strings in this sample, and they are not 17 products. The&lt;br&gt;
date ranges are what give it away.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product string as published&lt;/th&gt;
&lt;th&gt;Rows&lt;/th&gt;
&lt;th&gt;Received, first → last in this sample&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting, credit repair services, or other personal consumer reports&lt;/td&gt;
&lt;td&gt;1,892&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-08-24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debt collection&lt;/td&gt;
&lt;td&gt;547&lt;/td&gt;
&lt;td&gt;2015-03-20 → 2023-12-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit card or prepaid card&lt;/td&gt;
&lt;td&gt;267&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-08-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mortgage&lt;/td&gt;
&lt;td&gt;248&lt;/td&gt;
&lt;td&gt;2015-04-21 → 2023-11-02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting or other personal consumer reports&lt;/td&gt;
&lt;td&gt;245&lt;/td&gt;
&lt;td&gt;2023-08-26 → 2024-01-04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checking or savings account&lt;/td&gt;
&lt;td&gt;195&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-11-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;td&gt;2015-04-17 → 2017-04-09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Student loan&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;td&gt;2015-10-06 → 2023-11-13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money transfer, virtual currency, or money service&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;td&gt;2017-08-08 → 2023-11-13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vehicle loan or lease&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-11-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit card&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;td&gt;2015-04-06 → 2023-12-22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payday loan, title loan, or personal loan&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-06-20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bank account or service&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;2015-04-04 → 2017-01-31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer Loan&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;2015-03-25 → 2017-02-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payday loan&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2015-10-08 → 2017-02-24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money transfers&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2015-07-17 → 2015-12-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payday loan, title loan, personal loan, or advance loan&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2023-09-20 → 2023-10-06&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the dates down the table. &lt;code&gt;Bank account or service&lt;/code&gt; last appears here on 2017-01-31, and&lt;br&gt;
&lt;code&gt;Checking or savings account&lt;/code&gt; first appears on 2017-04-24. Three more strings end in 2017 and their&lt;br&gt;
successors start the same year. The three credit-reporting strings sit end to end: 2015 to 2017,&lt;br&gt;
2017 to 2023, 2023 into 2024. Same category, renamed.&lt;/p&gt;

&lt;p&gt;I collapsed the 17 strings into 9 categories before scoring anything. The mapping is in the script,&lt;br&gt;
applied in one place rather than inferred per row. Without that step an accuracy compares a 2016&lt;br&gt;
row and a 2023 row whose label sets do not line up, and the model gets credit or blame for the&lt;br&gt;
taxonomy.&lt;/p&gt;

&lt;p&gt;The live database disagrees with the mirror about which names are current. Aggregated over&lt;br&gt;
complaints received in 2026, the CFPB API returns 11 product strings, with &lt;code&gt;Credit card&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;Prepaid card&lt;/code&gt; listed separately and no &lt;code&gt;Credit card or prepaid card&lt;/code&gt; at all (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/?size=0&amp;amp;aggs=product&amp;amp;date_received_min=2026-01-01" rel="noopener noreferrer"&gt;product aggregation,&lt;br&gt;
complaints received 2026-01-01&lt;br&gt;
onward&lt;/a&gt;,&lt;br&gt;
retrieved 9 Oct 2026). I did not reconcile the two taxonomies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways one accuracy figure misleads
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Imbalance.&lt;/strong&gt; After collapsing, the sample looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Collapsed category&lt;/th&gt;
&lt;th&gt;Rows&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting&lt;/td&gt;
&lt;td&gt;2,229&lt;/td&gt;
&lt;td&gt;57.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debt collection&lt;/td&gt;
&lt;td&gt;547&lt;/td&gt;
&lt;td&gt;14.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit card or prepaid card&lt;/td&gt;
&lt;td&gt;335&lt;/td&gt;
&lt;td&gt;8.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mortgage&lt;/td&gt;
&lt;td&gt;248&lt;/td&gt;
&lt;td&gt;6.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checking or savings account&lt;/td&gt;
&lt;td&gt;229&lt;/td&gt;
&lt;td&gt;5.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Student loan&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;td&gt;2.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money transfer or virtual currency&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;2.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vehicle loan or lease&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;1.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal / payday / title loan&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;td&gt;1.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A predictor that answers "credit reporting" for every ticket scores 0.572 here. That is a&lt;br&gt;
constant, and it sits close enough to a real model's score to be mistaken for one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample selection.&lt;/strong&gt; Same data, same model, two splits:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Split&lt;/th&gt;
&lt;th&gt;Test n&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Macro F1&lt;/th&gt;
&lt;th&gt;Credit-reporting share of test set&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Random 60/40&lt;/td&gt;
&lt;td&gt;1,558&lt;/td&gt;
&lt;td&gt;0.756&lt;/td&gt;
&lt;td&gt;0.428&lt;/td&gt;
&lt;td&gt;57.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chronological, split at 2022-04-21&lt;/td&gt;
&lt;td&gt;1,560&lt;/td&gt;
&lt;td&gt;0.832&lt;/td&gt;
&lt;td&gt;0.377&lt;/td&gt;
&lt;td&gt;74.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The chronological run scores 7.6 points higher while getting worse on most categories. The test&lt;br&gt;
set became more concentrated in the biggest class, and the dominant-class predictor rides that&lt;br&gt;
concentration up. Anyone reporting the 0.832 as an improvement after retraining would be reading&lt;br&gt;
their own sampling as progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coverage.&lt;/strong&gt; Sort the test set by the model's confidence and score only the top slice:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Coverage&lt;/th&gt;
&lt;th&gt;Tickets scored&lt;/th&gt;
&lt;th&gt;Accuracy on that slice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1,558&lt;/td&gt;
&lt;td&gt;0.756&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;75%&lt;/td&gt;
&lt;td&gt;1,168&lt;/td&gt;
&lt;td&gt;0.854&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;779&lt;/td&gt;
&lt;td&gt;0.904&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;389&lt;/td&gt;
&lt;td&gt;0.900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;155&lt;/td&gt;
&lt;td&gt;0.897&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 0.904 at half coverage is a real number computed on held-out data, and published alone it&lt;br&gt;
misdescribes the system by 15 points. Note where it stops moving: from 50% coverage down to 10%,&lt;br&gt;
accuracy drifts from 0.904 to 0.897, so the model's confidence and its correctness come apart on&lt;br&gt;
the tail. That tail is where the misroutes are.&lt;/p&gt;

&lt;p&gt;Underneath the average, the per-category picture on the random split:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Test n&lt;/th&gt;
&lt;th&gt;Recall&lt;/th&gt;
&lt;th&gt;Precision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting&lt;/td&gt;
&lt;td&gt;889&lt;/td&gt;
&lt;td&gt;0.879&lt;/td&gt;
&lt;td&gt;0.886&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debt collection&lt;/td&gt;
&lt;td&gt;225&lt;/td&gt;
&lt;td&gt;0.631&lt;/td&gt;
&lt;td&gt;0.637&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit card or prepaid card&lt;/td&gt;
&lt;td&gt;129&lt;/td&gt;
&lt;td&gt;0.760&lt;/td&gt;
&lt;td&gt;0.430&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mortgage&lt;/td&gt;
&lt;td&gt;114&lt;/td&gt;
&lt;td&gt;0.860&lt;/td&gt;
&lt;td&gt;0.737&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checking or savings account&lt;/td&gt;
&lt;td&gt;93&lt;/td&gt;
&lt;td&gt;0.548&lt;/td&gt;
&lt;td&gt;0.607&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money transfer or virtual currency&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Student loan&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;0.276&lt;/td&gt;
&lt;td&gt;0.889&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vehicle loan or lease&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal / payday / title loan&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three categories are never once predicted correctly. Accuracy is 0.756. Macro F1 is 0.428. The&lt;br&gt;
distance between those two is the honest headline for this model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pulling the high-risk errors out of the average
&lt;/h2&gt;

&lt;p&gt;The useful published idea here is OlaBench's risk axis, Critical Business Risk Rate. It counts only&lt;br&gt;
responses that inappropriately assert one of four things, each a high-stakes failure that may&lt;br&gt;
trigger compliance exposure, user disputes or reputational damage: admitting platform liability&lt;br&gt;
(618 test cases), misidentifying the ICS role (228), overcommitting (138), and disparaging&lt;br&gt;
individuals or merchants (16) (&lt;a href="https://arxiv.org/abs/2510.22143v3" rel="noopener noreferrer"&gt;OlaBench,&lt;br&gt;
arXiv:2510.22143v3&lt;/a&gt;, §3.1 and Table 1; 25 May 2026, retrieved 9&lt;br&gt;
Oct 2026). It is reported as its own number, beside the hallucination rate.&lt;/p&gt;

&lt;p&gt;The shape of that is what to copy: choose failures whose consequences differ in kind, count them&lt;br&gt;
separately, publish both, and never average them.&lt;/p&gt;

&lt;p&gt;For the routing test I defined three tiers, and I chose them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;critical&lt;/strong&gt; — a complaint about funds or account access (checking or savings, money transfer)
lands in the reporting queue&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;material&lt;/strong&gt; — a credit or loan dispute lands in a different loan or reporting family&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;routine&lt;/strong&gt; — a misroute inside the same family&lt;/li&gt;
&lt;/ul&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;Random split&lt;/th&gt;
&lt;th&gt;Chronological split&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;7 / 1,558 = 0.4%&lt;/td&gt;
&lt;td&gt;11 / 1,560 = 0.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Material&lt;/td&gt;
&lt;td&gt;43 / 1,558 = 2.8%&lt;/td&gt;
&lt;td&gt;28 / 1,560 = 1.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Routine&lt;/td&gt;
&lt;td&gt;330 / 1,558 = 21.2%&lt;/td&gt;
&lt;td&gt;223 / 1,560 = 14.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two splits swap places depending on which tier you read: the one with the prettier accuracy&lt;br&gt;
hides a critical error rate almost twice as high.&lt;/p&gt;

&lt;p&gt;Published work also shows how far the same named metric can move when the population changes. The&lt;br&gt;
OlaBench paper reports a critical business risk rate of 8.7% for its OlaMind-Stage-2 model on the&lt;br&gt;
benchmark, and a critical business risk rate below 0.05% in daily manual annotation of live&lt;br&gt;
dialogues after launch (&lt;a href="https://arxiv.org/abs/2510.22143v3" rel="noopener noreferrer"&gt;OlaBench, arXiv:2510.22143v3&lt;/a&gt;, §5.2 and&lt;br&gt;
Table 3; 25 May 2026, retrieved 9 Oct 2026). Both are the same metric, measured on different&lt;br&gt;
populations with different annotators. Quoting one without the other is a choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  How these numbers were produced
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data.&lt;/strong&gt; 3,894 complaints from a CC0 mirror of the CFPB Consumer Complaint Database (config
&lt;code&gt;has-text&lt;/code&gt;, 1,689,573 rows). The draw is 40 pages of 100 consecutive rows at random offsets in
the split, deduplicated by complaint ID, seed 20261009. Rows with a narrative under 80 characters
were dropped. It is a clustered draw, not an independent one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Label hygiene.&lt;/strong&gt; 17 published product strings collapsed to 9 categories using the date ranges
in the table above. The mapping is written out in the script, not inferred per row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model.&lt;/strong&gt; Multinomial naive Bayes over word unigrams and bigrams, Laplace smoothing α = 1,
features seen at least twice in training. Python standard library only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Splits.&lt;/strong&gt; Random 60/40 with seed 7; chronological split at the 60th percentile of the
received-date order, which lands on 2022-04-21.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selective accuracy.&lt;/strong&gt; Test rows sorted by the model's maximum posterior probability, top k
scored.&lt;/li&gt;
&lt;li&gt;Every number in the tables above is printed by that one script over that sample.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deliberate choice.&lt;/strong&gt; No language model appears in this pipeline, including the scoring. A
measurement instrument that is itself a model carries its own error, and OlaBench publishes the
size of its own: its judge agrees with human annotators at 91.7% on risk identification and 82.6%
on hallucination detection, over 5,000 human-annotated instances (&lt;a href="https://arxiv.org/abs/2510.22143v3" rel="noopener noreferrer"&gt;OlaBench,
arXiv:2510.22143v3&lt;/a&gt;, §3.3 and Table 2; 25 May 2026, retrieved
9 Oct 2026). A judge that disagrees with people 8% of the time on the risk axis puts a floor under
everything else you can measure with it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Whether the mirror's product strings match the live database's current taxonomy. The mirror's
rows use names the live API no longer returns for complaints received in 2026. I collapsed by date
range and did not reconcile the two.&lt;/li&gt;
&lt;li&gt;Whether the mirror is complete. Its files are timestamped December 2025 and the newest complaint
in my 3,894-row sample is dated 2024-01-04. I did not verify whether later records exist in it.&lt;/li&gt;
&lt;li&gt;Whether complaints published with a narrative are representative of all complaints. Narratives
appeared only with consent, so every number above sits on that consenting subset.&lt;/li&gt;
&lt;li&gt;Whether this sample's class mix reflects the population. Credit reporting is 57.2% of the sample
and about 82% of the live database: 14,978,722 of 18,274,023 records carry one of the three
credit-reporting strings (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/?size=0&amp;amp;aggs=product" rel="noopener noreferrer"&gt;product
aggregation&lt;/a&gt;,
retrieved 9 Oct 2026). Most of the gap is the mirror's date cap.&lt;/li&gt;
&lt;li&gt;How noisy the product label is. It is chosen by the consumer at submission, not assigned by an
expert, and I treated it as ground truth because companies route on it. If it is noisy, my error
rates are a floor, not a ceiling.&lt;/li&gt;
&lt;li&gt;Whether a published tiering exists for complaint-routing consequences. I could not find one, so
the three tiers are mine. The OlaBench categories are a different kind of thing: they score what
a reply asserts about liability and commitment, and they do not classify misroutes.&lt;/li&gt;
&lt;li&gt;Whether error rates vary with company size, state, or complaint age. A stratified rate is the
next thing to compute.&lt;/li&gt;
&lt;li&gt;The naive Bayes model is a floor. A trained transformer would score higher on the same task. The
run was to see the shape of the error distribution, and a better model changes the level without
changing the shape.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/how-an-eval-set-is-built/" rel="noopener noreferrer"&gt;How an eval set is built, and why it rots&lt;/a&gt; — How the test set behind an accuracy number is built, and how it rots.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/llm-api-pricing-per-task/" rel="noopener noreferrer"&gt;From token prices to task prices: three jobs, seven APIs&lt;/a&gt; — What the same support task costs per ticket across seven APIs.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
    <item>
      <title>The Chinese open-weights release timeline, dated</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:01:16 +0000</pubDate>
      <link>https://dev.to/hcxshh/the-chinese-open-weights-release-timeline-dated-44ie</link>
      <guid>https://dev.to/hcxshh/the-chinese-open-weights-release-timeline-dated-44ie</guid>
      <description>&lt;p&gt;This is a list of dates, not an argument. Every row is a model whose weights were&lt;br&gt;
published for anyone to download, with a date I could trace back to the organization that&lt;br&gt;
published it, a parameter count as stated by that organization, and a link.&lt;/p&gt;

&lt;p&gt;Most timelines of this kind are written from memory or from coverage, which is why they drift.&lt;br&gt;
I built this one from vendor pages, papers and Hugging Face's API. This revision leaves the table&lt;br&gt;
at 67 rows: seven are new, one of them because an entry I dropped in the first pass turns out to&lt;br&gt;
be open-weights after all. The dropped list at the end has eleven entries, each with a reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to read this timeline
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Open weights only.&lt;/strong&gt; A release is in the table only if the organization published&lt;br&gt;
downloadable weights. API-only and hosted-only models are out, including flagship ones:&lt;br&gt;
Qwen3-Max, the served ERNIE models, Kimi's hosted variants, DeepSeek's API-only models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A date I could trace.&lt;/strong&gt; The Date column has two kinds of value. A plain date is the date on&lt;br&gt;
the organization's own dated page — a research blog post, a news post, a release page, an&lt;br&gt;
accepted paper. A date marked &lt;code&gt;†&lt;/code&gt; is the Hugging Face repository creation timestamp in UTC,&lt;br&gt;
read from the Hugging Face API on 2026-10-09, used when the vendor had no dated page I could&lt;br&gt;
read. The two can differ by a few days, and that is usually preparation rather than a&lt;br&gt;
different release: DeepSeek-V3's weights repository was created 2024-12-25 12:52 UTC and the&lt;br&gt;
announcement is dated 2024-12-26.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameter counts come from the vendor, or they are blank.&lt;/strong&gt; Total parameters with activated&lt;br&gt;
parameters for mixture-of-experts models, as stated on the announcement, the model card, or&lt;br&gt;
the repository holding the same weights. A family that ships many sizes gets a range. Where&lt;br&gt;
none of the vendor's pages for that release states a count, the cell is &lt;code&gt;—&lt;/code&gt; rather than a&lt;br&gt;
number carried over from a sibling model, even when the lineage makes the number obvious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One link per row, to the vendor or the weight repository.&lt;/strong&gt; No aggregator links, no&lt;br&gt;
news coverage. That makes most rows single-source by construction: each cites the vendor's own&lt;br&gt;
statement about its own release, and every &lt;code&gt;†&lt;/code&gt; row is single-source by definition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope.&lt;/strong&gt; Language models and the flagship multimodal lines that carry the same brand and&lt;br&gt;
weights: Qwen3-VL, Qwen3-Omni, MiniMax-M3. Pure image, video and speech models are out of scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the vendor's page is client-rendered
&lt;/h2&gt;

&lt;p&gt;Two of the labs whose releases I date most often publish announcements as a page that ships no&lt;br&gt;
text to a plain HTTP request. z.ai's blog returns a 598-byte HTML shell that loads a JavaScript&lt;br&gt;
bundle; qwen.ai's blog returns a shell that fetches its content after load. Both render fine in&lt;br&gt;
a browser, which is what a reader clicking the link sees.&lt;/p&gt;

&lt;p&gt;For z.ai I did read something: the date string sits inside the page's own bundle, so the bundle&lt;br&gt;
is where I checked &lt;a href="https://z.ai/blog/glm-4.5" rel="noopener noreferrer"&gt;GLM-4.5&lt;/a&gt; (2025-07-28),&lt;br&gt;
&lt;a href="https://z.ai/blog/glm-4.6" rel="noopener noreferrer"&gt;GLM-4.6&lt;/a&gt; (2025-09-30), &lt;a href="https://z.ai/blog/glm-4.7" rel="noopener noreferrer"&gt;GLM-4.7&lt;/a&gt;&lt;br&gt;
(2025-12-22) and &lt;a href="https://z.ai/blog/glm-5" rel="noopener noreferrer"&gt;GLM-5&lt;/a&gt; (2026-02-12). That is weaker evidence than a&lt;br&gt;
rendered page, and it is why &lt;a href="https://z.ai/blog/glm-5.2" rel="noopener noreferrer"&gt;GLM-5.2&lt;/a&gt; stays out: its bundle contains&lt;br&gt;
2026-06-16 and no other date, and a date visible only inside a build artifact is not one I will&lt;br&gt;
print as sourced.&lt;/p&gt;

&lt;p&gt;Qwen's older blog carries the full text and a date; the newer qwen.ai copies do not. So the&lt;br&gt;
three Qwen3-* rows dated from qwen.ai in the first pass now cite the weight repository and&lt;br&gt;
carry a &lt;code&gt;†&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2023
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q1–Q2
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGLM-6B&lt;/td&gt;
&lt;td&gt;2023-03-13 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;6.2B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/THUDM/chatglm-6b" rel="noopener noreferrer"&gt;THUDM/chatglm-6b&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Baichuan-7B&lt;/td&gt;
&lt;td&gt;2023-06-13 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/baichuan-inc/Baichuan-7B" rel="noopener noreferrer"&gt;baichuan-inc/Baichuan-7B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChatGLM2-6B&lt;/td&gt;
&lt;td&gt;2023-06-24 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/THUDM/chatglm2-6b" rel="noopener noreferrer"&gt;THUDM/chatglm2-6b&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q3–Q4
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen-7B&lt;/td&gt;
&lt;td&gt;2023-08-03 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen-7B-Chat" rel="noopener noreferrer"&gt;Qwen/Qwen-7B-Chat&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Baichuan 2&lt;/td&gt;
&lt;td&gt;2023-09-19&lt;/td&gt;
&lt;td&gt;7B, 13B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2309.10305" rel="noopener noreferrer"&gt;arXiv:2309.10305&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChatGLM3-6B&lt;/td&gt;
&lt;td&gt;2023-10-25 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/THUDM/chatglm3-6b" rel="noopener noreferrer"&gt;THUDM/chatglm3-6b&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yi-34B&lt;/td&gt;
&lt;td&gt;2023-11-01 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;34B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/01-ai/Yi-34B" rel="noopener noreferrer"&gt;01-ai/Yi-34B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek Coder&lt;/td&gt;
&lt;td&gt;2023-11-01 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1.3B–33B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/deepseek-coder-33b-instruct" rel="noopener noreferrer"&gt;deepseek-ai/deepseek-coder-33b-instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek LLM&lt;/td&gt;
&lt;td&gt;2023-11-29 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;7B, 67B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-LLM-67B-Chat" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-LLM-67B-Chat&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Baichuan 2 is the only 2023 row dated by a paper: &lt;code&gt;arXiv:2309.10305&lt;/code&gt; is dated 2023-09-19,&lt;br&gt;
while its weight repositories appear earlier, on 2023-08-30 &lt;code&gt;†&lt;/code&gt;, so the paper is the single&lt;br&gt;
source for that date. The two ChatGLM cells are blank because their cards do not state a count:&lt;br&gt;
6.2 billion is on the ChatGLM-6B card and not on the cards of its two successors, and copying&lt;br&gt;
it across is the habit that makes timelines of this kind drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  2024
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q1
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen1.5&lt;/td&gt;
&lt;td&gt;2024-02-04&lt;/td&gt;
&lt;td&gt;0.5B–110B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen1.5/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Qwen1.5 was in the dropped list in the first pass, because I looked for it on the new blog and&lt;br&gt;
found nothing readable. The old blog still carries the post, dated 2024-02-04, listing six&lt;br&gt;
sizes plus 110B.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q2–Q3
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V2&lt;/td&gt;
&lt;td&gt;2024-05-07&lt;/td&gt;
&lt;td&gt;236B total, 21B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2405.04434" rel="noopener noreferrer"&gt;arXiv:2405.04434&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4-9B&lt;/td&gt;
&lt;td&gt;2024-06-04 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;9B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/zai-org/GLM-4-9B-Chat" rel="noopener noreferrer"&gt;zai-org/GLM-4-9B-Chat&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2&lt;/td&gt;
&lt;td&gt;2024-06-07&lt;/td&gt;
&lt;td&gt;0.5B–72B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen2/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2-VL&lt;/td&gt;
&lt;td&gt;2024-08-29&lt;/td&gt;
&lt;td&gt;2B, 7B, 72B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen2-vl/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V2.5&lt;/td&gt;
&lt;td&gt;2024-09-05 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-V2.5" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-V2.5&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5&lt;/td&gt;
&lt;td&gt;2024-09-19&lt;/td&gt;
&lt;td&gt;0.5B–72B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen2.5/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q4
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hunyuan-Large&lt;/td&gt;
&lt;td&gt;2024-10-22 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;389B total, 52B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/tencent/Hunyuan-Large" rel="noopener noreferrer"&gt;tencent/Hunyuan-Large&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5-Coder&lt;/td&gt;
&lt;td&gt;2024-11-06 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0.5B–32B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen2.5-Coder-32B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen2.5-Coder-32B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V2.5-1210&lt;/td&gt;
&lt;td&gt;2024-12-10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news1210" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3&lt;/td&gt;
&lt;td&gt;2024-12-26&lt;/td&gt;
&lt;td&gt;671B total, 37B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/deepseek-v3/" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The DeepSeek point releases inside a model line are dated on DeepSeek's own changelog, one page&lt;br&gt;
per release. Hunyuan-Large's 389B/52B comes from&lt;br&gt;
&lt;a href="https://arxiv.org/abs/2411.02265" rel="noopener noreferrer"&gt;arXiv:2411.02265&lt;/a&gt;, submitted 2024-11-04, three weeks after&lt;br&gt;
the weights appeared.&lt;/p&gt;

&lt;h2&gt;
  
  
  2025
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q1
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-Text-01&lt;/td&gt;
&lt;td&gt;2025-01-14&lt;/td&gt;
&lt;td&gt;456B total, 45.9B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2501.08313" rel="noopener noreferrer"&gt;arXiv:2501.08313&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-R1&lt;/td&gt;
&lt;td&gt;2025-01-20&lt;/td&gt;
&lt;td&gt;671B total, 37B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/deepseek-r1/" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5-VL&lt;/td&gt;
&lt;td&gt;2025-01-27 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;3B, 7B, 72B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen2.5-VL-72B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen2.5-VL-72B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QwQ-32B&lt;/td&gt;
&lt;td&gt;2025-03-06&lt;/td&gt;
&lt;td&gt;32B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwq-32b/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3-0324&lt;/td&gt;
&lt;td&gt;2025-03-25&lt;/td&gt;
&lt;td&gt;671B total, 37B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news250325" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two DeepSeek updates here do not restate a parameter count on their own changelog pages;&lt;br&gt;
the cells carry the count from the repositories that hold those weights, which are the V3 and&lt;br&gt;
R1 repositories listed above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q2
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3&lt;/td&gt;
&lt;td&gt;2025-04-29&lt;/td&gt;
&lt;td&gt;0.6B–235B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen3/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-R1-0528&lt;/td&gt;
&lt;td&gt;2025-05-28&lt;/td&gt;
&lt;td&gt;671B total, 37B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news250528" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniCPM4-8B&lt;/td&gt;
&lt;td&gt;2025-06-05 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;8B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/openbmb/MiniCPM4-8B" rel="noopener noreferrer"&gt;openbmb/MiniCPM4-8B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M1&lt;/td&gt;
&lt;td&gt;2025-06-16&lt;/td&gt;
&lt;td&gt;456B total, 45.9B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2506.13585" rel="noopener noreferrer"&gt;arXiv:2506.13585&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hunyuan-A13B&lt;/td&gt;
&lt;td&gt;2025-06-27&lt;/td&gt;
&lt;td&gt;80B total, 13B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/Tencent-Hunyuan/Hunyuan-A13B" rel="noopener noreferrer"&gt;Tencent-Hunyuan/Hunyuan-A13B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ERNIE 4.5&lt;/td&gt;
&lt;td&gt;2025-06-30&lt;/td&gt;
&lt;td&gt;0.3B–300B; flagship 300B/47B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ernie.baidu.com/blog/posts/ernie4.5/" rel="noopener noreferrer"&gt;ERNIE blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q3
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2&lt;/td&gt;
&lt;td&gt;2025-07-11 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1T total, 32B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/moonshotai/Kimi-K2-Instruct" rel="noopener noreferrer"&gt;moonshotai/Kimi-K2-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Coder&lt;/td&gt;
&lt;td&gt;2025-07-22&lt;/td&gt;
&lt;td&gt;480B total, 35B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen3-coder/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4.5&lt;/td&gt;
&lt;td&gt;2025-07-28&lt;/td&gt;
&lt;td&gt;355B total, 32B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://z.ai/blog/glm-4.5" rel="noopener noreferrer"&gt;z.ai blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step-3&lt;/td&gt;
&lt;td&gt;2025-07-31&lt;/td&gt;
&lt;td&gt;321B total, 38B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://chat.stepfun.com/research/zh/step3" rel="noopener noreferrer"&gt;StepFun research&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seed-OSS-36B&lt;/td&gt;
&lt;td&gt;2025-08-21&lt;/td&gt;
&lt;td&gt;36B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://seed.bytedance.com/en/blog/seed-oss-open-source-models-release" rel="noopener noreferrer"&gt;ByteDance Seed blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3.1&lt;/td&gt;
&lt;td&gt;2025-08-21 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-V3.1" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-V3.1&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LongCat-Flash-Chat&lt;/td&gt;
&lt;td&gt;2025-08-29 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;560B total, 18.6B–31.3B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/meituan-longcat/LongCat-Flash-Chat" rel="noopener noreferrer"&gt;meituan-longcat/LongCat-Flash-Chat&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Next-80B-A3B&lt;/td&gt;
&lt;td&gt;2025-09-09 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;80B total, 3B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen3-Next-80B-A3B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen3-Next-80B-A3B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ling-flash-2.0&lt;/td&gt;
&lt;td&gt;2025-09-17 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;100B total, 6.1B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/inclusionAI/Ling-flash-2.0" rel="noopener noreferrer"&gt;inclusionAI/Ling-flash-2.0&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Omni-30B-A3B&lt;/td&gt;
&lt;td&gt;2025-09-20 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;30B total, 3B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen3-Omni-30B-A3B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen3-Omni-30B-A3B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-VL&lt;/td&gt;
&lt;td&gt;2025-09-22 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;235B total, 22B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen3-VL-235B-A22B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen3-VL-235B-A22B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3.2-Exp&lt;/td&gt;
&lt;td&gt;2025-09-29&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/v3-2-exp/" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4.6&lt;/td&gt;
&lt;td&gt;2025-09-30&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://z.ai/blog/glm-4.6" rel="noopener noreferrer"&gt;z.ai blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q4
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ling-1T&lt;/td&gt;
&lt;td&gt;2025-10-02 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1T total, 50B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/inclusionAI/Ling-1T" rel="noopener noreferrer"&gt;inclusionAI/Ling-1T&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-OCR&lt;/td&gt;
&lt;td&gt;2025-10-17 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-OCR" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-OCR&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LongCat-Flash-Omni&lt;/td&gt;
&lt;td&gt;2025-10-23 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;560B total, 27B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/meituan-longcat/LongCat-Flash-Omni" rel="noopener noreferrer"&gt;meituan-longcat/LongCat-Flash-Omni&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M2&lt;/td&gt;
&lt;td&gt;2025-10-27&lt;/td&gt;
&lt;td&gt;230B total, 10B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.minimax.io/blog/minimax-m2-en-1748600000" rel="noopener noreferrer"&gt;MiniMax blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2 Thinking&lt;/td&gt;
&lt;td&gt;2025-11-04 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/moonshotai/Kimi-K2-Thinking" rel="noopener noreferrer"&gt;moonshotai/Kimi-K2-Thinking&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3.2&lt;/td&gt;
&lt;td&gt;2025-12-01&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/deepseek-v3-2/" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4.7&lt;/td&gt;
&lt;td&gt;2025-12-22&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://z.ai/blog/glm-4.7" rel="noopener noreferrer"&gt;z.ai blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  2026
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q1
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4.7-Flash&lt;/td&gt;
&lt;td&gt;2026-01-19 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;30B total, 3B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/zai-org/GLM-4.7-Flash" rel="noopener noreferrer"&gt;zai-org/GLM-4.7-Flash&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step-3.5-Flash&lt;/td&gt;
&lt;td&gt;2026-02-01 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;196B total, 11B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/stepfun-ai/Step-3.5-Flash" rel="noopener noreferrer"&gt;stepfun-ai/Step-3.5-Flash&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M2.5&lt;/td&gt;
&lt;td&gt;2026-02-12&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.minimax.io/news/minimax-m25" rel="noopener noreferrer"&gt;MiniMax news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-5&lt;/td&gt;
&lt;td&gt;2026-02-12&lt;/td&gt;
&lt;td&gt;744B total, 40B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://z.ai/blog/glm-5" rel="noopener noreferrer"&gt;z.ai blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.5-397B-A17B&lt;/td&gt;
&lt;td&gt;2026-02-16&lt;/td&gt;
&lt;td&gt;397B total, 17B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://home.alibabagroup.com/en-US/document-1960233590314762240" rel="noopener noreferrer"&gt;Alibaba press release&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q2
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M2.7&lt;/td&gt;
&lt;td&gt;2026-04-09 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/MiniMaxAI/MiniMax-M2.7" rel="noopener noreferrer"&gt;MiniMaxAI/MiniMax-M2.7&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2.6&lt;/td&gt;
&lt;td&gt;2026-04-14 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1T total, 32B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/moonshotai/Kimi-K2.6" rel="noopener noreferrer"&gt;moonshotai/Kimi-K2.6&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V4 (Pro and Flash)&lt;/td&gt;
&lt;td&gt;2026-04-24&lt;/td&gt;
&lt;td&gt;Pro 1.6T total / 49B active; Flash 284B total / 13B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/v4-preview" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step 3.7 Flash&lt;/td&gt;
&lt;td&gt;2026-05-23 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;198B total, 11B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/stepfun-ai/Step-3.7-Flash" rel="noopener noreferrer"&gt;stepfun-ai/Step-3.7-Flash&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M3&lt;/td&gt;
&lt;td&gt;2026-06-01&lt;/td&gt;
&lt;td&gt;~428B total, ~23B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.minimax.io/blog/minimax-m3" rel="noopener noreferrer"&gt;MiniMax blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The DeepSeek-V4 row now carries both counts; the Flash model's 284B total is stated on the same&lt;br&gt;
announcement page as the Pro figures. Step 3.7 Flash moved to a &lt;code&gt;†&lt;/code&gt; date: its GitHub repository&lt;br&gt;
has no releases, and the page I first cited carries a creation timestamp rather than a release&lt;br&gt;
date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q3
&lt;/h3&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;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LongCat-2.0&lt;/td&gt;
&lt;td&gt;2026-07-05 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1.6T total, ~48B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/meituan-longcat/LongCat-2.0" rel="noopener noreferrer"&gt;meituan-longcat/LongCat-2.0&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hunyuan Hy3&lt;/td&gt;
&lt;td&gt;2026-07-06&lt;/td&gt;
&lt;td&gt;295B total, 21B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.tencent.com/tencent-hunyuan-officially-releases-hy3-advancing-agent-capabilities-and-deeper-product-integration/" rel="noopener noreferrer"&gt;Tencent newsroom&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.8&lt;/td&gt;
&lt;td&gt;2026-08-05 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;27B; 2.4T total / 95B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen3.8-2.4T-A95B" rel="noopener noreferrer"&gt;Qwen/Qwen3.8-2.4T-A95B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V4-Pro GA&lt;/td&gt;
&lt;td&gt;2026-08-13&lt;/td&gt;
&lt;td&gt;1.6T total, 49B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news260813" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V4-Flash-Vision-Exp&lt;/td&gt;
&lt;td&gt;2026-08-21&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news260821" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V4.1-Flash&lt;/td&gt;
&lt;td&gt;2026-09-10&lt;/td&gt;
&lt;td&gt;552B total; 8B active for input, 16B for output&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news260910" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Hy3 is the entry whose status changed in this revision. In the first pass I dropped it because&lt;br&gt;
the page I found did not say whether weights were published. It does: Hy3 is open-sourced under&lt;br&gt;
Apache 2.0 with weights on Hugging Face, and Tencent's newsroom names the parameter counts. The&lt;br&gt;
V4-Pro GA row repeats the count from the April preview announcement, because the GA page does&lt;br&gt;
not restate it. The Qwen3.8 row is dated by the repository of the 27B model, the first of the&lt;br&gt;
line to appear; the flagship 2.4T-A95B repository was created three days later, and Qwen's own&lt;br&gt;
announcement page is one of the client-rendered ones described above.&lt;/p&gt;

&lt;p&gt;Two rows elsewhere disagree with third-party timelines for the same reason: LongCat-2.0's&lt;br&gt;
repository was created 2026-07-05 while secondary coverage puts its launch at the end of June,&lt;br&gt;
and DeepSeek-V4-Flash's repository was created 2026-04-22, two days before the announcement&lt;br&gt;
page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in this revision
&lt;/h2&gt;

&lt;p&gt;Seven rows were added and three cells changed, listed because the edit history is the point of&lt;br&gt;
a table like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Qwen1.5 promoted into 2024 Q1&lt;/strong&gt; (2024-02-04), after finding the dated post on the old
Qwen blog.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hunyuan Hy3 promoted into 2026 Q3&lt;/strong&gt; (2026-07-06). It was dropped in error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four DeepSeek point releases added&lt;/strong&gt;: V2.5-1210 (2024-12-10), V3-0324 (2025-03-25),
R1-0528 (2025-05-28) and V4-Pro GA (2026-08-13). All four are dated on DeepSeek's own
changelog, the same source as V3.2 and V3.2-Exp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qwen3.8 added&lt;/strong&gt; (2026-08-05 &lt;code&gt;†&lt;/code&gt;), which the first pass omitted entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two ChatGLM parameter cells blanked.&lt;/strong&gt; The ChatGLM2-6B and ChatGLM3-6B cards do not state
a count, so the cells are now &lt;code&gt;—&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek-V4-Flash gained its total.&lt;/strong&gt; The row said "Flash 13B active" and dropped the
284B total, which the same page states.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Three rows changed source or date type for the same structural reason: the Qwen3-Next,&lt;br&gt;
Qwen3-Omni and Step 3.7 Flash rows now cite a weight repository with a &lt;code&gt;†&lt;/code&gt;, because the pages&lt;br&gt;
cited in the first pass were either unreadable to a plain request or carried no release date.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this table gets updated
&lt;/h2&gt;

&lt;p&gt;The table is edited in place. Every quarter I re-read the vendor pages for the labs already&lt;br&gt;
listed, then check Hugging Face for new repositories under those organizations. A release&lt;br&gt;
enters the table when it has a date and a link; until then it sits in the dropped list, with&lt;br&gt;
the reason.&lt;/p&gt;

&lt;p&gt;Two rules keep it stable: I do not replace a &lt;code&gt;†&lt;/code&gt; date with a later one unless the vendor&lt;br&gt;
publishes its own dated page, and I do not remove a superseded row.&lt;/p&gt;

&lt;p&gt;The current table has 67 entries. Twenty-three of them fall in the twelve months from October&lt;br&gt;
2025 to October 2026. That is a count of this table and nothing more; it is not a claim about&lt;br&gt;
the field, because the table only holds releases I could date.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Eleven candidates were dropped&lt;/strong&gt;, for these reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Yi-1.5&lt;/strong&gt; (May 2024) — the weight repository
(&lt;a href="https://huggingface.co/01-ai/Yi-1.5-34B" rel="noopener noreferrer"&gt;01-ai/Yi-1.5-34B&lt;/a&gt;) was created 2024-05-11 &lt;code&gt;†&lt;/code&gt;, and
I could not date the announcement itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;InternLM2 and InternLM2.5&lt;/strong&gt; — the weight repositories
(&lt;a href="https://huggingface.co/internlm/internlm2-7b" rel="noopener noreferrer"&gt;internlm/internlm2-7b&lt;/a&gt;,
&lt;a href="https://huggingface.co/internlm/internlm2_5-7b-chat" rel="noopener noreferrer"&gt;internlm/internlm2_5-7b-chat&lt;/a&gt;) look
like renamed predecessors, so the creation timestamp does not mark the release.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MiniCPM 1 and 2&lt;/strong&gt; — same problem; the repository
(&lt;a href="https://huggingface.co/openbmb/MiniCPM-2B-sft-bf16" rel="noopener noreferrer"&gt;openbmb/MiniCPM-2B-sft-bf16&lt;/a&gt;) is now a
monorepo for a whole series.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baichuan 3 and 4&lt;/strong&gt; — I could not find weight artifacts at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ERNIE 5.0&lt;/strong&gt; and &lt;strong&gt;ERNIE 5.1&lt;/strong&gt; — Baidu's blog dates them 2026-02-06 and 2026-05-09
(&lt;a href="https://ernie.baidu.com/blog/posts/ernie5.0/" rel="noopener noreferrer"&gt;ERNIE 5.0&lt;/a&gt;,
&lt;a href="https://ernie.baidu.com/blog/posts/ernie-5.1-0508-release/" rel="noopener noreferrer"&gt;ERNIE 5.1&lt;/a&gt;) and describes
ERNIE 5.0 as a 2.4-trillion-parameter model, but I found no open-weight repository for
either.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kimi K3&lt;/strong&gt; — the model card (&lt;a href="https://huggingface.co/moonshotai/Kimi-K3" rel="noopener noreferrer"&gt;moonshotai/Kimi-K3&lt;/a&gt;)
carries the full architecture table (2.8T total, 104B activated) and a license, but no
release date. The repository timestamp is 2026-06-13 and secondary coverage says mid-July. I
could not reconcile that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kimi K2.5&lt;/strong&gt; — same shape of problem: the weight repository timestamp
(&lt;a href="https://huggingface.co/moonshotai/Kimi-K2.5" rel="noopener noreferrer"&gt;moonshotai/Kimi-K2.5&lt;/a&gt;) is 2026-01-01 while the
launch is reported at the end of January.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GLM-5.2&lt;/strong&gt; — the post is client-rendered and the only date string in its bundle is
2026-06-16. I am not willing to print that as the release date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek-V3.1-Terminus&lt;/strong&gt; — a mid-cycle rename with a 2025-09-22 &lt;code&gt;†&lt;/code&gt; repository
(&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-V3.1-Terminus" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-V3.1-Terminus&lt;/a&gt;);
including it would have double-counted the V3.1 line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qwen-Image-2.1&lt;/strong&gt; — a dated weight repository
(&lt;a href="https://huggingface.co/Qwen/Qwen-Image-2.1" rel="noopener noreferrer"&gt;Qwen/Qwen-Image-2.1&lt;/a&gt;, 2026-09-14 &lt;code&gt;†&lt;/code&gt;) but an
image model, outside this table's scope by the rule stated at the top.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Step-Audio line&lt;/strong&gt; — the same, for speech. The repositories exist
(&lt;a href="https://huggingface.co/stepfun-ai/Step-Audio-R1.1" rel="noopener noreferrer"&gt;stepfun-ai/Step-Audio-R1.1&lt;/a&gt;) and I did
not date them, because they would need their own table with its own scope.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Three other limits on this table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;†&lt;/code&gt; timestamps are preparation times, not launch times.&lt;/strong&gt; A repository can be created
days before the announcement, and from outside I cannot see whether weights were uploaded
at once or in pieces. Where the gap is large I noted it above the table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The dates are the vendor's own.&lt;/strong&gt; No release date here has been confirmed by a second
source, and one link per release is deliberate: a vendor correcting itself is the signal
worth keeping, and coverage repeating the vendor is not a second source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License differences are not reflected.&lt;/strong&gt; Apache-2.0, MIT and the vendor-specific
licenses Kimi, MiniMax and LongCat ship under are not the same permission. Each row links to
the page that states its license, and for Hy3 that page states Apache 2.0.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/llm-api-pricing-per-task/" rel="noopener noreferrer"&gt;From token prices to task prices: three jobs, seven APIs&lt;/a&gt; — What these models cost per task once they are routed through an API.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/how-an-eval-set-is-built/" rel="noopener noreferrer"&gt;How an eval set is built, and why it rots&lt;/a&gt; — How to build an eval set for them, and why it ages.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>machinelearning</category>
      <category>llm</category>
    </item>
    <item>
      <title>Why there is no Western mini program</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:00:37 +0000</pubDate>
      <link>https://dev.to/hcxshh/why-there-is-no-western-mini-program-pem</link>
      <guid>https://dev.to/hcxshh/why-there-is-no-western-mini-program-pem</guid>
      <description>&lt;p&gt;The usual version of this question is about super apps, and it has been answered at length by people with better data than mine; mine is narrower. A mini program is a small bundle of code that runs inside somebody else's application, and the question is which parts of that arrangement exist in what Apple and Google ship.&lt;/p&gt;

&lt;p&gt;I read the vendors' own documentation for WeChat Mini Programs, Apple App Clips, Google Play Instant and Progressive Web Apps, and compared them on five dimensions: install and size, identity, payment, entry point, distribution and review.&lt;/p&gt;

&lt;p&gt;One mechanism carries most of the answer. A mini program brings neither its own user account nor its own checkout. It borrows both from the application hosting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five dimensions, side by side
&lt;/h2&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;WeChat Mini Program&lt;/th&gt;
&lt;th&gt;Progressive web app&lt;/th&gt;
&lt;th&gt;Native app (iOS/Android)&lt;/th&gt;
&lt;th&gt;App Clip (iOS)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install and size&lt;/td&gt;
&lt;td&gt;Runs inside WeChat. Main package 2 MB maximum, all packages together 30 MB (20 MB for programs developed by a third-party provider) (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages.html" rel="noopener noreferrer"&gt;WeChat subpackages&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;No package at all. Chrome offers install after HTTPS plus a manifest carrying a name, 192 px and 512 px icons, &lt;code&gt;start_url&lt;/code&gt; and a display mode (&lt;a href="https://web.dev/articles/install-criteria" rel="noopener noreferrer"&gt;web.dev install criteria&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Full binary from the store. Apple states the App Store limits the size of apps installable over a mobile connection and gives no number on that page (&lt;a href="https://developer.apple.com/documentation/xcode/reducing-your-app-s-size" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09); Google states a compressed download limit of 200 MB for apps published as app bundles (&lt;a href="https://developer.android.com/topic/performance/reduce-apk-size" rel="noopener noreferrer"&gt;Google&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;10 MB uncompressed on iOS 15 and earlier, 15 MB on iOS 16 and earlier, 100 MB on iOS 17 or later under conditions (&lt;a href="https://developer.apple.com/documentation/appclip/choosing-the-right-functionality-for-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;openid&lt;/code&gt; per mini program, &lt;code&gt;unionid&lt;/code&gt; shared across every app under one Open Platform account. Both come from &lt;code&gt;wx.login&lt;/code&gt; plus &lt;code&gt;code2Session&lt;/code&gt;, which WeChat documents as returning the UnionID "without user authorization" (&lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/login.html" rel="noopener noreferrer"&gt;login&lt;/a&gt;, &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/union-id.html" rel="noopener noreferrer"&gt;UnionID&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Each origin keeps its own account. The browser hands nothing to a site silently&lt;/td&gt;
&lt;td&gt;Account per app. Sign in with Apple where the app offers it, as an explicit step by the user (&lt;a href="https://developer.apple.com/sign-in-with-apple" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Sign in with Apple, the keychain and CloudKit, shared with the parent app (&lt;a href="https://developer.apple.com/documentation/appclip/sharing-data-between-your-app-clip-and-your-full-app" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment&lt;/td&gt;
&lt;td&gt;WeChat Pay. A merchant number is bound to the mini program's AppID, at most 50 AppIDs per merchant number (&lt;a href="https://pay.weixin.qq.com/doc/v3/merchant/4013287504" rel="noopener noreferrer"&gt;WeChat Pay&lt;/a&gt;, page updated 2025-07-02). Virtual goods must go through the mini program virtual-payment channel, where "the platform charges a technical service fee" (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/en/business-capabilities/virtual-payment.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Card and wallet APIs called by the merchant, on the merchant's own acquiring contract&lt;/td&gt;
&lt;td&gt;In-app purchase is required to unlock features. Apple's guidelines name QR codes among the mechanisms that may not be used instead (&lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Apple's in-app purchase. Hosts in the Mini Apps Partner Program "earn 85% of qualifying In-App Purchase sales within qualifying mini apps" (&lt;a href="https://developer.apple.com/programs/mini-apps-partner/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entry&lt;/td&gt;
&lt;td&gt;Documented entry paths carry IDs: 1011 scan a QR code, 1012 long-press an image, 1013 pick a code from the album, 1023 the Android home-screen icon, 1005 and 1006 search boxes, 1026 the nearby list, 1183 search inside the PC WeChat mini program panel (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html" rel="noopener noreferrer"&gt;WeChat scene values&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;A URL, a search result, or a home-screen shortcut the user adds by hand. On iOS there is no install prompt at all (&lt;a href="https://web.dev/learn/pwa/installation/" rel="noopener noreferrer"&gt;web.dev&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Store search and the home-screen icon&lt;/td&gt;
&lt;td&gt;An App Clip Code, NFC tag or QR code at a physical location; Siri suggestions; Maps; Messages; a Smart App Banner on a website in Safari (&lt;a href="https://developer.apple.com/documentation/appclip/configuring-the-launch-experience-of-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distribution and review&lt;/td&gt;
&lt;td&gt;Published from the mini program console. Virtual payment requires a certified entity: an enterprise, institution or individual merchant (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/en/business-capabilities/virtual-payment.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Nothing is submitted. It is HTTPS plus a manifest&lt;/td&gt;
&lt;td&gt;Store review against the App Review Guidelines&lt;/td&gt;
&lt;td&gt;No listing of its own. It ships inside a full app, which the App Clip documentation says "must include the same functionality as the App Clip" (&lt;a href="https://developer.apple.com/documentation/appclip/choosing-the-right-functionality-for-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09); Guideline 2.5.16(a) adds that all App Clip features must be in the main app binary and that "App Clips cannot contain advertising" (&lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Android equivalent is gone. "Starting December 2025, Instant Apps cannot be published through Google Play, and all Google Play services Instant APIs will no longer work" (&lt;a href="https://developer.android.com/topic/google-play-instant" rel="noopener noreferrer"&gt;Google Play Instant&lt;/a&gt;, last updated 2026-06-24).&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity arrives before the first screen
&lt;/h2&gt;

&lt;p&gt;The WeChat login flow is one call on the client and one on the server. &lt;code&gt;wx.login&lt;/code&gt; returns a code, the server exchanges it through &lt;code&gt;code2Session&lt;/code&gt;, and the reply carries an &lt;code&gt;openid&lt;/code&gt; for that user in that mini program, a &lt;code&gt;unionid&lt;/code&gt; and a session key (&lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/login.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;The UnionID page says what this costs the user: the developer gets it "without user authorization" (&lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/union-id.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09). No phone number, no password, no consent sheet. The &lt;code&gt;openid&lt;/code&gt; is scoped to one mini program; the &lt;code&gt;unionid&lt;/code&gt; is identical across every mobile app, website app, official account and mini program bound to the same Open Platform account, so a merchant with several surfaces sees one person.&lt;/p&gt;

&lt;p&gt;Compare the alternatives. On the web, an origin cannot read an identifier out of the browser; it asks the user to sign in and then owns a credential to protect. On iOS, an App Clip can use Sign in with Apple and the keychain, which Apple's documentation points developers at sharing with the full app, but those are technologies an app opts into rather than an identifier handed over at launch.&lt;/p&gt;

&lt;p&gt;That is why the first screen of a mini program is the product. Onboarding was paid for at install time, by the host.&lt;/p&gt;

&lt;h2&gt;
  
  
  Payment is a binding between two accounts
&lt;/h2&gt;

&lt;p&gt;Paying inside a mini program does not mean the mini program holds money. WeChat Pay binds a merchant number to the mini program's AppID. One merchant number can bind at most 50 AppIDs, the binding starts on the merchant side and is confirmed from the mini program side, and once established cannot be unbound (&lt;a href="https://pay.weixin.qq.com/doc/v3/merchant/4013287504" rel="noopener noreferrer"&gt;WeChat Pay&lt;/a&gt;, page updated 2025-07-02). The rail and the code package are joined by an account relationship neither side can create alone.&lt;/p&gt;

&lt;p&gt;Virtual goods get a second layer. The documentation is blunt: virtual currency, unlocked features, subscription content, paid services, tips and virtual gifts "require integration with Mini Program's virtual payment system for both purchase and payment", and the platform charges a technical service fee on the amount (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/en/business-capabilities/virtual-payment.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09). Physical goods go through ordinary WeChat Pay; digital ones go through the host's channel, with the host's cut.&lt;/p&gt;

&lt;p&gt;Apple's version of that cut is recent and documented. The Mini Apps Partner Program was announced on 13 November 2025 (&lt;a href="https://developer.apple.com/news/?id=xcz1s7cz" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, 2025-11-13): host apps carrying mini apps can join, earn 85 percent of qualifying in-app purchase sales inside those mini apps, and must use Apple's in-app purchase system for them (&lt;a href="https://developer.apple.com/programs/mini-apps-partner/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). Admission is not automatic: a host needs the Advanced Commerce API and a manifest describing itself and its mini apps.&lt;/p&gt;

&lt;p&gt;Apple's App Review Guidelines still require in-app purchase to unlock features or functionality, and the same sentence lists QR codes among the mechanisms that cannot substitute for it (&lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). What changed, on 1 May 2025, is that Guidelines 3.1.1, 3.1.1(a), 3.1.3 and 3.1.3(a) were updated for the United States storefront after a court decision about buttons and external links (&lt;a href="https://developer.apple.com/news/?id=9txfddzf" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, 2025-05-01).&lt;/p&gt;

&lt;h2&gt;
  
  
  The QR code is the offline switch
&lt;/h2&gt;

&lt;p&gt;A WeChat developer does not have to choose between a printed code and a link shared in a chat. The official scene-value table lists both, with IDs: 1011 scanning a QR code, 1012 long-pressing an image, 1013 picking one from the album, 1023 the Android home-screen icon, 1005 and 1006 the two search boxes, 1026 the nearby list, 1183 search inside the PC WeChat mini program panel (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;Two details make the printed code cheap. Codes come from an API, and WeChat states every generated mini program code is permanently valid. The "one item, one code" interface has no quantity limit and needs a smaller printed area than an ordinary URL code (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/qr-code.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09). A code already printed and pointing at a URL can be configured to open a chosen page inside a mini program (&lt;a href="https://developers.weixin.qq.com/miniprogram/introduction/qrcode.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09). The person scans what is already on the table.&lt;/p&gt;

&lt;p&gt;App Clips have the same physical invocations: an App Clip Code, an NFC tag, a QR code at a physical location (&lt;a href="https://developer.apple.com/documentation/appclip/configuring-the-launch-experience-of-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). Two constraints pull the other way.&lt;/p&gt;

&lt;p&gt;The first constraint trades size against physical entry. The 100 MB ceiling for iOS 17 or later carries four conditions: the App Clip "only supports digital invocations" and not "physical invocations such as App Clip Codes, QR codes, or NFC tags" (&lt;a href="https://developer.apple.com/documentation/appclip/choosing-the-right-functionality-for-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09), it is used where a reliable connection is likely, and it "doesn't support iOS 16 and earlier". The exception Apple names is a demo link from App Store Connect, which may use the 100 MB limit and still support App Clip Codes, NFC tags and QR codes. An App Clip with a counter code stays inside the smaller limit.&lt;/p&gt;

&lt;p&gt;The second concerns persistence. Apple's Human Interface Guidelines say App Clips "remain on the device for a limited amount of time", and that only App Clip Codes produced in App Store Connect or with Apple's own code generator are approved for use (&lt;a href="https://developer.apple.com/design/human-interface-guidelines/app-clips" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). A merchant cannot print its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who reviews it, and who is answerable
&lt;/h2&gt;

&lt;p&gt;Reviews differ in kind as well as in strictness. A PWA is submitted to nobody; Chrome installs it once its manifest and HTTPS satisfy the criteria (&lt;a href="https://web.dev/articles/install-criteria" rel="noopener noreferrer"&gt;web.dev&lt;/a&gt;, read 2026-10-09). An App Clip cannot be submitted alone. It ships inside a full app that must already contain the same functionality, cannot carry advertising, and is reviewed as part of that app (&lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). A mini program is submitted inside WeChat, and the entity requirement arrives with payment rather than with the code: to sell virtual items, the program must be a certified enterprise, institution or individual merchant (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/en/business-capabilities/virtual-payment.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;The commercial consequence: in the App Store, a mini-app-like product reaches users through a host that already has an account relationship and a reviewed app behind it. In WeChat, the party needing the corporate identity, the certification and the merchant binding is the mini program author.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the four pieces sit
&lt;/h2&gt;

&lt;p&gt;In WeChat, four things a mini program depends on sit with one owner: the identity system, the payment rail, the entry surface (camera, search, chat, home screen, desktop) and the distribution channel with its review. An author plugs into a host that has all four and pays for the parts it uses.&lt;/p&gt;

&lt;p&gt;In the Apple and Google stacks those four are held by parties with different interests. The operating system vendor issues the platform account and runs the payment system; the browser owns the entry surface for web content; the stores own distribution and review; the rail for physical goods sits with the merchant's bank relationship.&lt;/p&gt;

&lt;p&gt;No single party there can hand all four to a third party. Apple's answer in November 2025 was to supply the missing pieces itself and keep the commission: hosts get Apple's in-app purchase for mini apps and keep 85 percent of qualifying sales, in exchange for approval, a manifest and Apple's review.&lt;/p&gt;

&lt;p&gt;The mechanism that makes mini programs work is therefore an ownership arrangement rather than a technology gap. Reproducing it outside WeChat means finding one party that holds the users, the identity, the payment rail and the entry surface, and getting it to open all four to third-party code at a price the third party will pay. In the West, the two parties that hold those pieces chose to be the host themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I checked this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every figure and quote comes from documentation published by the vendor it describes: WeChat's mini program and WeChat Pay docs, Apple's developer documentation, guidelines and news items, Google's Android documentation, and web.dev.&lt;/li&gt;
&lt;li&gt;All pages were fetched on 2026-10-09. Most carry no publication date, so a date beside a source is my reading date unless the page states its own: the WeChat Pay AppID binding page (updated 2025-07-02), the Google Play Instant page (last updated 2026-06-24), Apple's updated-guidelines news item (2025-05-01), Apple's Mini Apps Partner Program news item (2025-11-13) and Tencent's write-up of the 2022 WeChat Open Class (2022-01-07).&lt;/li&gt;
&lt;li&gt;Size limits and scene-value IDs come from the pages that state them: WeChat's subpackage page for 2 MB and 30 MB, Apple's App Clip page for the 10/15/100 MB table, WeChat's scene-value table for the ID numbers.&lt;/li&gt;
&lt;li&gt;Google Play Instant's page would not load in a plain HTTP client, so I read it through the Internet Archive and then through a rendering fetch that returned the live page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The fee WeChat charges on virtual payments.&lt;/strong&gt; The page says a fee is charged on the payment amount and gives no percentage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The standard WeChat Pay rate for physical goods.&lt;/strong&gt; I found no official rate page; what I reached describes partner fee schemes and refers to a merchant rate without the base number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The extra steps for virtual payment on iOS.&lt;/strong&gt; WeChat's virtual payment page says iOS "requires additional activation and adaptation procedures" and points at separate documentation; I could not load that page, so I do not know whether an Apple-side charge is involved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether an App Clip can take payment for physical goods through Apple Pay.&lt;/strong&gt; Not stated on the App Clip pages I read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any current count of mini programs or mini-program daily users.&lt;/strong&gt; Tencent's release discloses Weixin monthly accounts and names Mini Games and Mini Shops, but carries no mini-program count. The most recent first-party figure I found: more than 450 million daily users at the end of 2021, from Tencent's write-up of the 2022 WeChat Open Class (&lt;a href="https://www.tencent.com/zh-cn/articles/2201267.html" rel="noopener noreferrer"&gt;Tencent&lt;/a&gt;, 2022-01-07). That figure is four years old.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What "certified Mini Program" requires in practice&lt;/strong&gt;, beyond the entity types named on the virtual payment page, and how long WeChat's review takes.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/chinese-apps-in-english/" rel="noopener noreferrer"&gt;Chinese apps in English: what Meituan and Dianping ship&lt;/a&gt; — What Meituan and Dianping actually ship in English.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/why-fast-is-the-default-in-china/" rel="noopener noreferrer"&gt;Why fast is the default in China&lt;/a&gt; — The default-speed argument, with the records behind it.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>mobile</category>
      <category>apple</category>
      <category>javascript</category>
    </item>
    <item>
      <title>From token prices to task prices: three jobs, seven APIs</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:00:13 +0000</pubDate>
      <link>https://dev.to/hcxshh/from-token-prices-to-task-prices-three-jobs-seven-apis-3jah</link>
      <guid>https://dev.to/hcxshh/from-token-prices-to-task-prices-three-jobs-seven-apis-3jah</guid>
      <description>&lt;p&gt;Every vendor publishes a price per million tokens. Almost none publishes what a job costs; the number that decides whether a feature ships is left as arithmetic homework.&lt;/p&gt;

&lt;p&gt;I did that arithmetic for three jobs, from first-party price lists read on 2026-10-09. The token counts are assumptions, not measurements, and they are written out below. The money is the vendor's standard-tier list price, in USD.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three jobs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Job 1, label 1,000 support messages.&lt;/strong&gt; Each call sends a 400-token instruction block and a 120-token message, and asks for a 15-token answer. Billed per run: 520,000 input tokens, of which 400,000 are the instruction block sent 1,000 times, plus 15,000 output tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job 2, turn a 100,000-word draft into a structured summary.&lt;/strong&gt; Google's token guide says a token is about 4 characters and 100 tokens about 60-80 English words, putting 100,000 words at 125,000-167,000 tokens (&lt;a href="https://ai.google.dev/gemini-api/docs/tokens" rel="noopener noreferrer"&gt;Google token counting guide&lt;/a&gt;, read 2026-10-09). I used 140,000:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One shot:&lt;/strong&gt; 140,000 tokens in, a 3,000-token structured summary out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map-reduce:&lt;/strong&gt; 14 chunk calls of about 10,000 tokens each, 700 tokens of notes per chunk, then a 9,800-token reduce call. Totals: 149,800 in, 12,800 out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Job 3, one round of tool-assisted bug fixing.&lt;/strong&gt; Eight turns against a 20,000-token repository: system prompt, tool schemas, three files. The prompt grows about 2,500 tokens per turn as tool results come back, from 20,000 to 37,500. Cumulative billed input is 230,000 tokens, of which 192,500 are a re-sent prefix a cache can serve; output is 6,000 tokens including reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I read the price lists
&lt;/h2&gt;

&lt;p&gt;Five first-party pages, all read on 2026-10-09; the Gemini API page builds its tables client-side, so I read it in a browser.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI API pricing&lt;/a&gt; — standard tier&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini API pricing&lt;/a&gt; — paid tier, introductory rates through 2026-12-31&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek Models &amp;amp; Pricing&lt;/a&gt; — off-peak column&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rules I applied:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard tier only&lt;/strong&gt;, unless a row says batch: the real-time tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache reads are billed at the published cached-input rate. Cache writes are billed where a vendor publishes a write price.&lt;/strong&gt; OpenAI and Anthropic do: $2.50 per million tokens for the two frontier models here, $0.125 for the two small ones, on Anthropic's 5-minute cache. Google, DeepSeek and xAI publish no write price, so I charged zero, which flatters those three in every cached row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In the cached rows I charge a write for every input token that is not a cache read:&lt;/strong&gt; 400 tokens in Job 1, 37,500 in Job 3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning tokens are output tokens.&lt;/strong&gt; OpenAI states they are billed as output tokens (&lt;a href="https://developers.openai.com/api/docs/guides/reasoning" rel="noopener noreferrer"&gt;OpenAI reasoning guide&lt;/a&gt;, read 2026-10-09). Gemini's output price is quoted "including thinking tokens" and DeepSeek's V4.1-Flash runs in thinking mode by default.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tool and search fees sit outside the main table.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No prompt hits a long-context threshold&lt;/strong&gt;; the prompt-length step that does apply, Anthropic's 100,000-token rule, is below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No aggregator site was used&lt;/strong&gt; for any price.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What each job costs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Estimated cost&lt;/th&gt;
&lt;th&gt;Assumptions&lt;/th&gt;
&lt;th&gt;Price source (read 2026-10-09)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$1.19&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.060&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$1.19&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.060&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.45&lt;/td&gt;
&lt;td&gt;standard tier, introductory rate; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.087&lt;/td&gt;
&lt;td&gt;off-peak window; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$1.13&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.43&lt;/td&gt;
&lt;td&gt;400k of input served from cache; one 400-token write&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.024&lt;/td&gt;
&lt;td&gt;400k of input served from cache; one 400-token write&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.43&lt;/td&gt;
&lt;td&gt;400k cached, 5-minute TTL and write&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.024&lt;/td&gt;
&lt;td&gt;400k cached, prompts under 100k, 5-minute TTL&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.18&lt;/td&gt;
&lt;td&gt;400k of input served from cache, storage excluded&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.028&lt;/td&gt;
&lt;td&gt;off-peak; cache hits free of a write fee&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.53&lt;/td&gt;
&lt;td&gt;400k of input served from cache&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.31&lt;/td&gt;
&lt;td&gt;140k in / 3k out, one call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.016&lt;/td&gt;
&lt;td&gt;140k in / 3k out, one call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.31&lt;/td&gt;
&lt;td&gt;140k in / 3k out, one call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.078&lt;/td&gt;
&lt;td&gt;140k in / 3k out, above the 100k step&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.12&lt;/td&gt;
&lt;td&gt;140k in / 3k out, one call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.023&lt;/td&gt;
&lt;td&gt;off-peak; 140k in / 3k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;td&gt;140k in / 3k out, under the 200k threshold&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.43&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call; 149.8k in / 12.8k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.021&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.43&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.11&lt;/td&gt;
&lt;td&gt;15 calls, each prompt under 100k&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.16&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.030&lt;/td&gt;
&lt;td&gt;off-peak window, 15 calls&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.38&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;td&gt;8 turns; 230k in (192.5k read, 37.5k written) / 6k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.013&lt;/td&gt;
&lt;td&gt;8 turns; 230k in (192.5k read, 37.5k written) / 6k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;td&gt;8 turns; 192.5k reads and 37.5k 5-minute writes / 6k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.013&lt;/td&gt;
&lt;td&gt;8 turns, every prompt under 100k&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.065&lt;/td&gt;
&lt;td&gt;8 turns; 230k in (192.5k cached) / 6k out, storage excluded&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.0098&lt;/td&gt;
&lt;td&gt;off-peak window; 8 turns&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.21&lt;/td&gt;
&lt;td&gt;8 turns; 230k in (192.5k cached) / 6k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two pairs of rows are identical on purpose. gpt-6.1-sol and Claude Sonnet 5.5 both list $2.00 per million input tokens, $10.00 output, $0.10 cached input and a $2.50 cache write (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt; and &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09); gpt-6-luna and Claude Haiku 5.5 both list $0.10, $0.50, $0.01 and $0.125.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same jobs with each vendor's biggest discount
&lt;/h2&gt;

&lt;p&gt;Batch is the biggest published discount: 50% on OpenAI (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;, read 2026-10-09), on Anthropic ("a 50% discount on both input and output tokens", &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;, read 2026-10-09) and on Google's paid tier ("Batch API (50% cost reduction)", &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;, read 2026-10-09), where the Batch column halves the cached-input rate too. Not everywhere: xAI's batch section gives 20% to grok-4.3 and the grok-4.20 variants and says unlisted models have no batch discount, which leaves Grok 4.7 at full price (&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt;, read 2026-10-09). DeepSeek publishes no batch tier.&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;Job 1, cached&lt;/th&gt;
&lt;th&gt;Job 2, one shot&lt;/th&gt;
&lt;th&gt;Job 3, loop&lt;/th&gt;
&lt;th&gt;Batch discount&lt;/th&gt;
&lt;th&gt;Price source (read 2026-10-09)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.22&lt;/td&gt;
&lt;td&gt;$0.16&lt;/td&gt;
&lt;td&gt;$0.12&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.012&lt;/td&gt;
&lt;td&gt;$0.0078&lt;/td&gt;
&lt;td&gt;$0.0067&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.22&lt;/td&gt;
&lt;td&gt;$0.16&lt;/td&gt;
&lt;td&gt;$0.12&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.012&lt;/td&gt;
&lt;td&gt;$0.039&lt;/td&gt;
&lt;td&gt;$0.0067&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.088&lt;/td&gt;
&lt;td&gt;$0.058&lt;/td&gt;
&lt;td&gt;$0.033&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.028&lt;/td&gt;
&lt;td&gt;$0.023&lt;/td&gt;
&lt;td&gt;$0.0098&lt;/td&gt;
&lt;td&gt;none published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.53&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;td&gt;$0.21&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Claude Haiku 5.5's one-shot cell is the outlier: batch brings it to $0.039, still three times its cached Job 1 of $0.012, because a 140,000-token prompt sits above the 100,000-token step (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;h2&gt;
  
  
  What the numbers say
&lt;/h2&gt;

&lt;p&gt;Every figure here is arithmetic on the rows above, whose rates are linked (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;, &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;, &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;, &lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;, &lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output tokens decide the bill at the frontier tier.&lt;/strong&gt; In cached Job 1 on gpt-6.1-sol, $0.15 of the $0.43 is output (35%) and $0.04 is cached instruction tokens (9%).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The spread inside one vendor is bigger than the spread between vendors.&lt;/strong&gt; Cached Job 1 is $0.024 on gpt-6-luna and Haiku 5.5, $0.43 on gpt-6.1-sol and Sonnet 5.5, $0.53 on Grok 4.7: 22x from dearest to cheapest, 18x between the cheap pair and the frontier pair. At the same tier, the vendor changes nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The agent loop is the cheapest job here.&lt;/strong&gt; Job 3 is $0.25 on gpt-6.1-sol against $0.31 for the one-shot summary and $1.19 for Job 1. On the small models the order flips: Haiku 5.5 pays $0.078 for the one-shot summary against $0.013 for the loop, because 140,000 tokens crosses its threshold and 230,000 over eight turns does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Map-reduce costs more than one shot.&lt;/strong&gt; On the two $2/$10 models it is $0.43 against $0.31, and the gap is all output tokens, 12,800 against 3,000. Chunking buys reliability and pays for it in output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The absolute numbers are small.&lt;/strong&gt; The main table spans $0.0098 to $1.19; batch pulls the floor to $0.0067. At this scale, a cost problem is usually a volume problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The variables that break the conversion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Thresholds re-price the whole request.&lt;/strong&gt; Claude Haiku 5.5 charges $0.10 and $0.50 per million input and output tokens up to 100,000 prompt tokens, and $0.50 and $2.50 above it; the length counts cache reads and writes, and each request is priced on its own (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). Job 2's 140,000-token prompt pays the higher rate on everything: $0.078, against $0.016 under the cap. xAI applies long-context rates to all tokens in a request once the prompt passes 200,000, where Grok 4.7 costs $4.00 input, $1.00 cached input and $12.00 output instead of $2.00, $0.50 and $6.00 (&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt;, read 2026-10-09). Gemini 3.1 Pro Preview steps from $2.00 to $4.00 input per million above 200,000 tokens (&lt;a href="https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing" rel="noopener noreferrer"&gt;Gemini Enterprise Agent Platform pricing&lt;/a&gt;, read 2026-10-09). OpenAI's line is 272,000 input tokens, published in a column tooltip; above it gpt-6.1-sol's input doubles to $4.00 and its output to $15.00 (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt;, read 2026-10-09). All three jobs stay under every line; Job 2 is halfway to OpenAI's.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tokenizer differences.&lt;/strong&gt; Anthropic notes that Claude 4.7 and later use a tokenizer that "produces approximately 30% more tokens for the same text" (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). A comparison built on one words-per-token ratio, as mine is, is off by that much for Anthropic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time of day.&lt;/strong&gt; DeepSeek's peak hours are 01:00-04:00 and 06:00-10:00 UTC, Monday to Friday, excluding Chinese public holidays; every other hour is off-peak at half price (&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek Models &amp;amp; Pricing&lt;/a&gt;, read 2026-10-09). Job 1 uncached is $0.087 off-peak and $0.174 at peak. Off-peak is 133 of the week's 168 hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache economics differ in shape as well as in rate.&lt;/strong&gt; Anthropic's cache hit is 10% of the input price on most models, 5% on Opus 5.5 and Sonnet 5.5, 2.5% on Fable 5.1 and Mythos 5.1; writes cost 1.25x for five minutes and 2x for an hour, so a 5-minute cache pays for itself after one read (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). DeepSeek's hit price is $0.003 per million against $0.15 for a miss (&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek pricing&lt;/a&gt;, read 2026-10-09). Google charges no write fee but charges storage, $0.50 per million tokens per hour on Gemini 3.8 Flash through 2026-12-31 and $4.50 on Gemini 3.1 Pro (&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini pricing&lt;/a&gt; and &lt;a href="https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing" rel="noopener noreferrer"&gt;Gemini Enterprise Agent Platform pricing&lt;/a&gt;, read 2026-10-09); a cached 140,000-token document runs $0.07 an hour on Flash and $0.63 on Pro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform fees sit outside the token meter.&lt;/strong&gt; OpenAI bills $2.50 per 1,000 tool calls and $10 per 1,000 web searches, so Job 3's eight calls cost $0.02, and Anthropic's tool-use prompt adds 286 to 675 tokens (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt; and &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). xAI bills X search per item fetched: $5 per 1,000 posts, $10 per 1,000 profiles (&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency and geography carry multipliers.&lt;/strong&gt; OpenAI's Fast mode, renamed from Priority on 2026-07-30, is 2x, and data-residency endpoints add 10%; Gemini 3.8 Flash's Priority tier is $1.35 input against $0.75 standard (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt; and &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini pricing&lt;/a&gt;, read 2026-10-09). xAI's priority tier is 2x and its US regional endpoint 1.1x; Anthropic charges 1.1x for US-only inference; regional Bedrock and Google Cloud endpoints carry a 10% premium (&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt; and &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The list price itself moves.&lt;/strong&gt; Google quotes its Flash prices "through December 31, 2026" and doubles them on 2027-01-01: $0.75 to $1.50 input, $3.75 to $7.50 output, $0.075 to $0.15 cached input, $0.50 to $1.00 per million tokens per hour of cache storage (&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini pricing&lt;/a&gt;, read 2026-10-09). Anthropic's $2/$10 for Claude Sonnet 5 was launched as introductory pricing through 2026-08-31 and is now standard, with the rise to $3/$15 cancelled (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). OpenAI's promotional pricing for GPT-5.6 Sol runs at least through 2026-11-21, on a model not in this table (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt;, read 2026-10-09). Any per-task table is a snapshot with an expiry date.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;I ran none of these three jobs.&lt;/strong&gt; The token counts are stated assumptions, and a measured run would move every number here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache-write pricing for Google, DeepSeek and xAI.&lt;/strong&gt; I found no published write price for any of the three, so I charged zero. xAI's prompt-caching page lists a billing rate per token type with no write line; Google's context-caching price is a read rate plus hourly storage; DeepSeek's table has only a hit price and a miss price. If any of them does charge for writes, its cached rows are too low.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether the Haiku 5.5 threshold behaves as written at the boundary.&lt;/strong&gt; The page counts cache reads and writes in a prompt's length, which would mean a cached 140,000-token document pays the higher rate. I did not test it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real token counts.&lt;/strong&gt; I used Google's words-per-token ratio across every vendor; Anthropic's own note about a 30% tokenizer difference is a warning that this is approximate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume, committed-use and negotiated enterprise pricing, free tiers and trial credits&lt;/strong&gt; are outside this table. The price on the page is the list price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resale through cloud platforms.&lt;/strong&gt; Anthropic bills Bedrock and Google Cloud in consumption units, and Gemini Enterprise Agent Platform prices are not the Gemini API prices: on 2026-10-09 the two pages disagreed on cache storage, $0.50 per million tokens per hour (&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini pricing&lt;/a&gt;) against $1.00 for one model (&lt;a href="https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing" rel="noopener noreferrer"&gt;Gemini Enterprise Agent Platform pricing&lt;/a&gt;). The route you buy through can change the number.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/hallucination-rates-in-support-ai/" rel="noopener noreferrer"&gt;Measuring error in support AI, and what accuracy hides&lt;/a&gt; — The error side of the same task: what accuracy hides.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/chinese-open-weights-timeline/" rel="noopener noreferrer"&gt;The Chinese open-weights release timeline, dated&lt;/a&gt; — A dated timeline of the Chinese open-weight releases that set these prices.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>How an eval set is built, and why it rots</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:00:02 +0000</pubDate>
      <link>https://dev.to/hcxshh/how-an-eval-set-is-built-and-why-it-rots-1a6</link>
      <guid>https://dev.to/hcxshh/how-an-eval-set-is-built-and-why-it-rots-1a6</guid>
      <description>&lt;p&gt;An eval set is an instrument. It has a purpose, a scoring rule, a calibration record and an expiry date. Treating it as a file you download is where the trouble starts: the file already made those decisions without writing them down.&lt;/p&gt;

&lt;p&gt;What follows is the build document I would want for a set I have to defend six months from now. Each rule is traceable to a public source or marked as my practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the samples come from
&lt;/h2&gt;

&lt;p&gt;Four sources, each importing a different bias.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;th&gt;The bias it imports&lt;/th&gt;
&lt;th&gt;Documented where&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A public benchmark, reused&lt;/td&gt;
&lt;td&gt;Comparability with published numbers&lt;/td&gt;
&lt;td&gt;Contamination; a task choice someone else made&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2310.18018" rel="noopener noreferrer"&gt;Sainz et al., 2023-10-27&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public administrative data&lt;/td&gt;
&lt;td&gt;A real distribution, a public licence, dated updates&lt;/td&gt;
&lt;td&gt;A sample of who reports, not of who is affected&lt;/td&gt;
&lt;td&gt;&lt;a href="https://cfpb.github.io/api/ccdb/index.html" rel="noopener noreferrer"&gt;CFPB, read 2026-10-09&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Synthetic generation&lt;/td&gt;
&lt;td&gt;Volume you can afford, labels you control&lt;/td&gt;
&lt;td&gt;Generator artifacts in the surface form&lt;/td&gt;
&lt;td&gt;&lt;a href="https://aclanthology.org/N18-2017.pdf" rel="noopener noreferrer"&gt;Gururangan et al., ACL 2018&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your own production traffic&lt;/td&gt;
&lt;td&gt;Items that match what you ship&lt;/td&gt;
&lt;td&gt;A convenience sample that drifts with the product&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/1802.03916" rel="noopener noreferrer"&gt;Lipton et al., ICML 2018&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Reuse.&lt;/strong&gt; LiveBench's authors state the problem in one sentence: test set contamination "can quickly render benchmarks obsolete" (&lt;a href="https://arxiv.org/abs/2406.19314" rel="noopener noreferrer"&gt;LiveBench, v2 2025-04-18&lt;/a&gt;). Their answer was questions from recent competitions, arXiv papers and news, added monthly. LiveCodeBench did the same for code: 400 problems published between May 2023 and February 2024 (&lt;a href="https://arxiv.org/abs/2403.07974" rel="noopener noreferrer"&gt;LiveCodeBench, arXiv v1 2024-03-12&lt;/a&gt;). A static benchmark decays without anyone editing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Administrative data.&lt;/strong&gt; The CFPB complaint database is the best example I know. Its search API reported 18,274,023 records under a CC0 licence on 2026-10-09, with a &lt;code&gt;last_updated&lt;/code&gt; of 2026-10-08 (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/?size=0&amp;amp;no_aggs=true" rel="noopener noreferrer"&gt;CFPB complaint search API, read 2026-10-09&lt;/a&gt;). Complaints are published only after the company responds or after 15 days, whichever comes first, and complaints referred to other regulators, including depository institutions with less than $10 billion in assets, are not published (&lt;a href="https://cfpb.github.io/api/ccdb/index.html" rel="noopener noreferrer"&gt;CFPB API docs, read 2026-10-09&lt;/a&gt;). The agency states that the database "is not a statistical sample of consumers' experiences in the marketplace" (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/" rel="noopener noreferrer"&gt;CFPB database page, read 2026-10-09&lt;/a&gt;). A random draw samples the people who complained and whose complaints survived publication, not the people who were harmed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Synthetic.&lt;/strong&gt; Gururangan et al. showed that a text classifier reading only the hypothesis, with the premise deleted, reached about 67% on SNLI and 53% on MultiNLI (&lt;a href="https://aclanthology.org/N18-2017.pdf" rel="noopener noreferrer"&gt;Annotation Artifacts in NLI Data, ACL 2018&lt;/a&gt;). The label was recoverable from phrasing because the crowd workers followed a template. Every generator has one. My test: delete the part of the input meant to carry the answer, score again, and compare with the majority-class rate. If accuracy stays well above it, the set is partly measuring its own generator. &lt;em&gt;(my practice)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production sampling.&lt;/strong&gt; This is the source I trust least and use most. It produces items that match what the system sees, and its composition changes when the product does. Label shift is the named version of that failure: the marginal &lt;code&gt;p(y)&lt;/code&gt; moves while &lt;code&gt;p(x|y)&lt;/code&gt; does not, measurable without test labels using Black Box Shift Estimation (&lt;a href="https://arxiv.org/abs/1802.03916" rel="noopener noreferrer"&gt;Lipton et al., ICML 2018&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  What counts as correct
&lt;/h2&gt;

&lt;p&gt;The match rule is a parameter, and the same predictions score differently under each choice. Four conventions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Convention&lt;/th&gt;
&lt;th&gt;The rule, as the source writes it&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exact match against any reference&lt;/td&gt;
&lt;td&gt;Counts predictions that match any one ground truth answer exactly; punctuation and articles ignored&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/1606.05250" rel="noopener noreferrer"&gt;SQuAD, arXiv v3 2016-10-11&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial credit against the best reference&lt;/td&gt;
&lt;td&gt;F1 over bags of tokens, "the maximum F1 over all of the ground truth answers", then averaged&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/1606.05250" rel="noopener noreferrer"&gt;SQuAD, arXiv v3 2016-10-11&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A threshold on localisation&lt;/td&gt;
&lt;td&gt;IoU thresholds &lt;code&gt;[.5:.05:.95]&lt;/code&gt;, 10 of them; 101 recall thresholds; &lt;code&gt;maxDets&lt;/code&gt; of 1, 10 and 100&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/cocoeval.py" rel="noopener noreferrer"&gt;cocoapi &lt;code&gt;cocoeval.py&lt;/code&gt;, read 2026-10-09&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A graded rubric with an exclusion flag&lt;/td&gt;
&lt;td&gt;0–3 for how well specified the issue is; 0–3 for whether the tests are scoped to it; a separate 0/1 question for any other reason the sample should not be used; a 1–5 confidence rating&lt;/td&gt;
&lt;td&gt;&lt;a href="https://cdn.openai.com/introducing-swe-bench-verified/swe-b-annotation-instructions.pdf" rel="noopener noreferrer"&gt;SWE-bench annotation instructions, undated&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three things this table taught me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Say who the ceiling belongs to.&lt;/strong&gt; In SQuAD the human number comes from the data: the second annotator's answer is treated as the prediction, the rest as ground truth, and humans score 77.0 exact match and 86.8 F1 on the test set (&lt;a href="https://arxiv.org/abs/1606.05250" rel="noopener noreferrer"&gt;SQuAD, arXiv v3 2016-10-11&lt;/a&gt;). A ceiling quoted without its rule says as much about the rule as about the task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One published number can hide ten thresholds.&lt;/strong&gt; COCO's headline detection score averages over ten IoU thresholds, three detection budgets and a recall grid. A single figure without a named threshold is a claim nobody can re-check. Pick the threshold, publish it, keep it fixed across versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The exclusion question is the part people skip.&lt;/strong&gt; SWE-bench Verified is 500 samples "verified to be non-problematic by our human annotators", drawn from 1,699 randomly sampled items that 93 Python developers annotated, with the full annotation set and the rubric released alongside (&lt;a href="https://openai.com/index/introducing-swe-bench-verified/" rel="noopener noreferrer"&gt;OpenAI, updated 2025-02-24&lt;/a&gt;). The rubric's last section asks, 0/1, whether there is any other reason not to use the sample. My handling of it, &lt;em&gt;(my practice)&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A closed list of exclusion reason codes, decided before annotation starts.&lt;/li&gt;
&lt;li&gt;Two raters must agree before an item is dropped; one rater's "unclear" is not an exemption.&lt;/li&gt;
&lt;li&gt;The dropped item stays in the file with its reason code, so the exemption stays auditable.&lt;/li&gt;
&lt;li&gt;The exemption rate is reported next to every result; if it moves between releases, the set changed even when the questions did not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ground truth built by pooling is biased by construction.&lt;/strong&gt; Test collections are built by judging only a sample of documents. When the pool is small relative to the collection, the judgment set "can be biased in that they favor relevant documents that contain topic title words", a bias that depends on collection size and not on the number of relevant documents (&lt;a href="https://www.nist.gov/publications/bias-and-limits-pooling-large-collections" rel="noopener noreferrer"&gt;Buckley et al., NIST, 2007-07-17&lt;/a&gt;). In a modern eval, if your labels come from checking only what your current systems returned, the label set favours items phrased the way those systems phrase things. My rule: draw a random slice from outside the candidate pool. &lt;em&gt;(my practice)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Whether the raters agree
&lt;/h2&gt;

&lt;p&gt;Agreement is a property of the pair and the setup.&lt;/p&gt;

&lt;p&gt;Krippendorff's alpha is &lt;code&gt;1 − Do/De&lt;/code&gt;, and it accepts any number of coders and missing entries (&lt;a href="https://www.asc.upenn.edu/sites/default/files/2021-03/Computing%20Krippendorff%27s%20Alpha-Reliability.pdf" rel="noopener noreferrer"&gt;Krippendorff, 2011-01-25&lt;/a&gt;). For two raters, Cohen's kappa, with Landis and Koch's 1977 bands (&lt;a href="https://www.ncbi.nlm.nih.gov/books/NBK52665/table/ch3.t5" rel="noopener noreferrer"&gt;reproduced in an AHRQ report, 2010&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Here is why the setup has to travel with the number. First-turn results on MT-bench, in the paper's two setups: S1 counts ties and inconsistent votes, S2 excludes ties (&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pair (first turn)&lt;/th&gt;
&lt;th&gt;S1, R = 33%&lt;/th&gt;
&lt;th&gt;S2, R = 50%&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPT-4 pairwise vs human expert&lt;/td&gt;
&lt;td&gt;66% (n = 1,343)&lt;/td&gt;
&lt;td&gt;85% (n = 859)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-4 single-answer vs human expert&lt;/td&gt;
&lt;td&gt;60% (n = 1,280)&lt;/td&gt;
&lt;td&gt;85% (n = 739)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human vs human&lt;/td&gt;
&lt;td&gt;63% (n = 721)&lt;/td&gt;
&lt;td&gt;81% (n = 479)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The headline everyone quotes is 85% against 81% for human agreement (&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;). The same table holds 66% for the same judge under the other tie rule. An agreement figure quoted without its setup is not comparable to anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measure agreement per stratum, not pooled.&lt;/strong&gt; ChaosNLI collected 464,500 annotations, 100 per example, over 3,113 SNLI and MNLI examples and 1,532 ANLI examples (&lt;a href="https://arxiv.org/abs/2010.03532" rel="noopener noreferrer"&gt;Nie et al., 2020-10-08&lt;/a&gt;). Models are near-perfect where humans agree and "can barely beat a random guess" where they do not, and those low-agreement items are most of the errors. A pooled score mixes two measurement regimes whose mixing ratio changes as models improve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sizing.&lt;/strong&gt; For an agreement study, start from the kappa minimum-sample-size tables (&lt;a href="https://riviste.unimi.it/index.php/ebph/article/view/17614" rel="noopener noreferrer"&gt;Bujang and Baharum, EBPH 14(2), 2017&lt;/a&gt;). For comparing two systems, use a power analysis: Miller gives the MDE formula and shows that raising answers per question from 1 to 10 moved the MDE from 13.2% to 7.5%; in his illustrative table, HumanEval's 164 questions at a fictional 83.6% carry a standard error of 3.2% (&lt;a href="https://arxiv.org/abs/2411.00640" rel="noopener noreferrer"&gt;Miller, 2024-11-01&lt;/a&gt;). If items arrive in groups, such as the same document or template, compute clustered standard errors (&lt;a href="https://arxiv.org/abs/2411.00640" rel="noopener noreferrer"&gt;Miller, 2024-11-01&lt;/a&gt;, Table 4); on DROP they were 1.34 against 0.44 naive.&lt;/p&gt;

&lt;p&gt;My rule: size the calibration slice for the agreement interval I need, the scoring slice for the effect I want to detect, and refuse to report a gap below the MDE. &lt;em&gt;(my practice)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the set rots
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decay&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;What I do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contamination&lt;/td&gt;
&lt;td&gt;One set improves while unrelated sets do not&lt;/td&gt;
&lt;td&gt;Keep item dates and prompt hashes; treat a stale set as historical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Label shift&lt;/td&gt;
&lt;td&gt;Scores move while the system did not&lt;/td&gt;
&lt;td&gt;Estimate the label mix and re-weight or re-cut the slice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Taxonomy and rubric drift&lt;/td&gt;
&lt;td&gt;The same question means something different&lt;/td&gt;
&lt;td&gt;Version the labels; freeze the rubric text per set version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saturation&lt;/td&gt;
&lt;td&gt;Every system sits near the ceiling&lt;/td&gt;
&lt;td&gt;Retire the set and say when&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Contamination's extent "is unknown, as it is not straightforward to measure", and where it exists it overestimates performance (&lt;a href="https://arxiv.org/abs/2310.18018" rel="noopener noreferrer"&gt;Sainz et al., 2023-10-27&lt;/a&gt;). I cannot audit training corpora, so I keep the dates and admit the exposure window.&lt;/p&gt;

&lt;p&gt;Saturation is not a failure of the set: GLUE's performance passed the level of non-expert humans, which is what prompted SuperGLUE (&lt;a href="https://arxiv.org/abs/1905.00537" rel="noopener noreferrer"&gt;Wang et al., 2019-05-02&lt;/a&gt;). Benchmark choice is fragile on its own: swapping tasks can reorder methods even when the methods do not change (&lt;a href="https://arxiv.org/abs/2107.07002" rel="noopener noreferrer"&gt;Dehghani et al., 2021-07-14&lt;/a&gt;). Both are reasons to keep more than one set.&lt;/p&gt;

&lt;p&gt;Taxonomy drift has a public paper trail. The CFPB's September 2026 release notes record that "consumers' complaint narratives ... have been removed from the database"; the June 2026 release removed "Consumer disputed" and "Consumer consent provided" from exports, long after those filters went away; the May 2023 release re-based the ZIP code field on 2019 census estimates (&lt;a href="https://cfpb.github.io/api/ccdb/release-notes.html" rel="noopener noreferrer"&gt;CFPB release notes, read 2026-10-09&lt;/a&gt;). A set built on those narratives became unmaintainable one month ago. The same page explains the general rule: the database shows "the consumer's original products, sub-products, issues, and sub-issues selections consistent with the options available on the form at the time the consumer submitted the complaint" (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/" rel="noopener noreferrer"&gt;CFPB database page, read 2026-10-09&lt;/a&gt;). Labels frozen at submission, against a taxonomy that keeps moving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping it maintainable
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(my practice, with borrowed pieces named)&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Version the set.&lt;/strong&gt; Additions and exclusions get a version bump and a changelog line; never edit an item silently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two slices.&lt;/strong&gt; A frozen slice for comparability, a rolling window for freshness. LiveCodeBench and LiveBench are public versions of the same idea.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the metadata down.&lt;/strong&gt; A datasheet (&lt;a href="https://arxiv.org/abs/1803.09010" rel="noopener noreferrer"&gt;Gebru et al., 2018, CACM 2021&lt;/a&gt;) or a Croissant record (&lt;a href="https://arxiv.org/abs/2403.19546" rel="noopener noreferrer"&gt;2024-03-28&lt;/a&gt;), so the next person need not infer the collection process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A desensitisation rule that is checkable.&lt;/strong&gt; The CFPB's ZIP rule is a good template: publish the 5-digit ZIP unless the census area has under 20,000 people, then 3 digits only if the wider area has more than 20,000, otherwise nothing (&lt;a href="https://cfpb.github.io/api/ccdb/fields.html" rel="noopener noreferrer"&gt;CFPB field reference, read 2026-10-09&lt;/a&gt;). A rule stated as a threshold can be re-run; "we removed personal information" cannot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch the exemption rate.&lt;/strong&gt; It is the leading indicator that the rubric stopped fitting the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State the blind spot in one line.&lt;/strong&gt; SQuAD's metrics ignore punctuation and articles; COCO's headline averages ten thresholds. Both are fine as long as the limitation is on the label.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan the retirement.&lt;/strong&gt; When a set saturates or its taxonomy moves, freeze it, date it, and build the next one from the same seed items.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How many SWE-bench samples were dropped to leave 500.&lt;/strong&gt; The announcement gives the annotated total (1,699) and the rubric, not the drop count (&lt;a href="https://openai.com/index/introducing-swe-bench-verified/" rel="noopener noreferrer"&gt;OpenAI, updated 2025-02-24&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether any specific model saw any specific benchmark item.&lt;/strong&gt; Sainz et al. state the extent is unknown; I have no method that resolves this, so I do not claim cleanliness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CFPB's narrative scrubbing standard.&lt;/strong&gt; The field is gone and the current field reference no longer documents it; I could not reach a dated scrubbing specification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CFPB's historical &lt;code&gt;consumer_consent_provided&lt;/code&gt; semantics.&lt;/strong&gt; The export field and its filter are gone; I found the removal dates, not the values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Krippendorff's own minimum sample size for alpha.&lt;/strong&gt; I have the kappa tables from Bujang and Baharum; I found no equivalent table for alpha.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Landis and Koch bands at first hand.&lt;/strong&gt; I read a reproduction of the 1977 table in a 2010 AHRQ report, not the original.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LiveCodeBench's current item count.&lt;/strong&gt; The 400 figure comes from the March 2024 paper; the platform's live count may differ.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The OpenAI SWE-bench Verified page at first hand.&lt;/strong&gt; The live page served an automated-client challenge when I read it; the same version is readable in an &lt;a href="https://web.archive.org/web/20260101233457/https://openai.com/index/introducing-swe-bench-verified/" rel="noopener noreferrer"&gt;Internet Archive copy&lt;/a&gt;, and the annotation instruction PDF is undated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CFPB record count above is a live API value&lt;/strong&gt; and changes daily. It was 18,274,023 when I read it on 2026-10-09.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/hallucination-rates-in-support-ai/" rel="noopener noreferrer"&gt;Measuring error in support AI, and what accuracy hides&lt;/a&gt; — One eval run, with the error distribution instead of a single accuracy number.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/births-counted-three-ways/" rel="noopener noreferrer"&gt;Births counted three ways, and none agree&lt;/a&gt; — Three official counts of the same thing, and why none agree.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>machinelearning</category>
      <category>datascience</category>
    </item>
    <item>
      <title>DeepSeek filled in the Ascend column: six repos, one technical report, and my machine with no NPU</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Wed, 30 Sep 2026 08:35:30 +0000</pubDate>
      <link>https://dev.to/hcxshh/deepseek-filled-in-the-ascend-column-six-repos-one-technical-report-and-my-machine-with-no-npu-4d15</link>
      <guid>https://dev.to/hcxshh/deepseek-filled-in-the-ascend-column-six-repos-one-technical-report-and-my-machine-with-no-npu-4d15</guid>
      <description>&lt;p&gt;DeepSeek released a batch of things over the last two days: six repositories plus one technical report in Chinese, all around Huawei's Ascend. I cloned all six, read them, and tried them on my own machine.&lt;/p&gt;

&lt;p&gt;This post covers three things: what was actually released, whether those impressive-looking numbers hold up, and exactly where my machine gave up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Six components were open sourced this time, and every one of them has a CUDA counterpart: a language for writing kernels, matrix multiply, multi-card communication, a general kernel library, an attention kernel, and a TopK kernel. Together that's filling in a column, not shipping a new model.&lt;/li&gt;
&lt;li&gt;The numbers are strong: dense matrix multiply at 99.8% of the hardware limit, sparse attention prefill at 95%, multi-card communication at 90–95% of physical bandwidth. But every one of these was measured on PoC hardware Huawei provided to DeepSeek. The commercial version is planned for mid-October. No third party can reproduce them today.&lt;/li&gt;
&lt;li&gt;The most valuable thing in this release isn't the announcement — it's the Chinese technical report published alongside it. It lays out the trade-offs of writing a sparse attention kernel on Ascend step by step: why one AI Core handles 64 heads, why you must pair each CUBE with two vector cores, and why one layout conversion has to detour through UB. I've copied the parts I found most revealing below.&lt;/li&gt;
&lt;li&gt;What I could verify locally: all six repos cloned, I read and counted the code, and I could not run a single kernel. The build dies at line 7 of &lt;code&gt;setup.py&lt;/code&gt;. That section is below too — it's measured, not paraphrased.&lt;/li&gt;
&lt;/ol&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%2Farticle-images.zsxq.com%2FFvaTD1Jo8_uGlYPAuaMj93O1HXCL" 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%2Farticle-images.zsxq.com%2FFvaTD1Jo8_uGlYPAuaMj93O1HXCL" width="1827" height="871"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Component-by-component comparison. The rightmost column is the size of the Ascend-specific part, which I counted from the source.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Getting this layer straight first
&lt;/h2&gt;

&lt;p&gt;Between a large model and the silicon sits a software layer: it tells the chip how to compute (kernels) and how to move data between cards (communication libraries). NVIDIA's version of that layer is CUDA; Huawei's is CANN. Anyone can write the model on top, but this layer has to be rewritten in the chip vendor's own language.&lt;/p&gt;

&lt;p&gt;When DeepSeek V4 launched in April, Huawei got there first with Ascend support — but that delivery was about making it &lt;em&gt;run&lt;/em&gt;. The pieces that matter most for performance were still open sourced only in their CUDA form. One media analysis at the time put it this way: moving from CUDA to CANN involves rewriting kernels at scale, restructuring the training pipeline, and re-tuning accuracy and performance — not less work than training the model itself. (Source: 21st Century Business Herald, 2026-04-24, in Chinese.)&lt;/p&gt;

&lt;p&gt;That is exactly what this release is. Which is why the sentence in the announcement — "all components correspond one-to-one with the previously open-sourced NVIDIA-platform components" — is the most substantive line in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The six components, one by one
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TileLang's Ascend 950 backend.&lt;/strong&gt; TileLang is a language for writing high-performance kernels in Python, led by a team at Peking University and used for most of the kernels in DeepSeek V4's training. This time it landed in the main repository, not as a bolt-on adapter: PR #3308, "Introduce Ascend 950 backend", merged on 30 September and shipped with v0.1.15. The official support matrix lists Ascend 950 as Supported, built from source with &lt;code&gt;USE_ASCEND=ON USE_CUDA=OFF&lt;/code&gt;, requiring CANN, the bisheng compiler and torch_npu on the machine, with &lt;code&gt;torch.npu.is_available()&lt;/code&gt; returning True.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeepGEMM-Ascend (matrix multiply).&lt;/strong&gt; Same package name and same API as the NVIDIA version, so existing code keeps working. The repo was created on 29 September.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeepEP-Ascend (multi-card communication).&lt;/strong&gt; MoE dispatch and combine — sending tokens to the right expert and collecting them back. The kernels are all header-only &lt;code&gt;.hpp&lt;/code&gt; files sitting on Huawei's HCCL/HCOMM, UBMEM and URMA. It's also the only Ascend repo in this batch that was built from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TileKernels v2.0.0 (general kernel library).&lt;/strong&gt; The small but frequently used kernels: MoE routing, quantization, Engram, hyper-connections. The README's own words: "added an Ascend backend, selected automatically at runtime; the same Python API runs on both NVIDIA GPUs and Huawei NPUs."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FlashMLA (attention).&lt;/strong&gt; Sparse attention prefill and decode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeepSelect (TopK).&lt;/strong&gt; The step that picks compressed blocks in sparse attention, plus the TopK the sampler needs. The authors claim 2–20× faster than native &lt;code&gt;torch.topk&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The numbers are strong — read them together with their sources
&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%2Farticle-images.zsxq.com%2FFpc4NGsrTRzsdOv7xayIkvUZw_5s" 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%2Farticle-images.zsxq.com%2FFpc4NGsrTRzsdOv7xayIkvUZw_5s" width="1744" height="900"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Performance figures and where they come from. Bar length = the fraction of that hardware's theoretical limit; further right means closer to the ceiling.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The key readings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dense matrix multiply at 99.8% of the hardware limit (BF16, FP8 and FP4 all land around there). FP4 measured 1701 TFLOPS on a 4096×7168×16384 shape against a 1730 TFLOPS hardware limit, on Ascend 950DT with CANN 9.2.0.&lt;/li&gt;
&lt;li&gt;Sparse attention prefill at 410 TFLOPS, 95% of the theoretical limit; decode at 360 TFLOPS, 83%.&lt;/li&gt;
&lt;li&gt;MoE communication: at EP8, dispatch 373–375 GB/s and combine 345–347 GB/s; within 32 cards it reaches 90–95% of physical bandwidth. At EP128 dispatch drops to 313–320 and combine to 272–278, with the vendor saying combine is still being optimized.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sourcing point has to be stated plainly: DeepEP-Ascend's own README says these numbers were measured on a PoC HDK that Huawei provided to DeepSeek, with additional manual configuration that is not part of any public release. Huawei's commercial HDK for Atlas 850E is planned for mid-October. A third party cannot get the same hardware and configuration right now, so this set of numbers should be read as a vendor claim, not as a reproducible public benchmark.&lt;/p&gt;

&lt;p&gt;Two pieces of hardware background, both from official statements: the Atlas 950 supernode supports 8192 950DT chips across 160 cabinets (128 compute, 32 interconnect), with an FP8 rating of 8 EFLOPS and FP4 up to 16 EFLOPS, and interconnect bandwidth of 16 PB/s — Huawei's own line being that this single product's total interconnect bandwidth is more than ten times the peak bandwidth of today's entire global internet. Availability is Q4 2026. A single Ascend 950DT is planned at 144 GB of memory, 4 TB/s of memory bandwidth and 2 TB/s of interconnect bandwidth.&lt;/p&gt;

&lt;p&gt;That is the basis for the fine print on DeepSeek's side: the pricing cadence of V4-Pro is tied to the shipping schedule of the Ascend 950 supernode.&lt;/p&gt;

&lt;p&gt;Further out, there are moves in progress: Bloomberg reported on 4 September (relayed by Guancha) that DeepSeek plans to deploy at least 160,000 Ascend 950DT chips for inference in an Inner Mongolia data center, with a gigawatt-scale power plan and full delivery possibly taking more than a year. Huawei announced the Ascend 960 supernode at its 17 September Connect conference — 4096 cards, replacing 48,000 800G optical modules with 5,500 in-house optical engines. Both of these are third-party reporting or vendor announcements; I have no independent verification to offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. That Chinese technical report is the part worth reading
&lt;/h2&gt;

&lt;p&gt;FlashMLA shipped with a paper called 《Ascend Sparse Attention Forward 算法与优化技术简析》("A brief analysis of the Ascend sparse attention forward algorithm and optimization"), in Chinese, released today and sitting in the repo. It lays out line by line &lt;em&gt;why&lt;/em&gt; the kernel is written the way it is. A few of the parts that stuck with me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One AI Core handling 64 query heads is not arbitrary.&lt;/strong&gt; The report puts the compute-to-memory ratio on the table: with CUBE saturated (4096 multiply-adds per cycle), L2 total bandwidth of 5 TB/s, 32 cores and 1.65 GHz, the head count has to be 64 or more before compute becomes the bottleneck. At the same time, 128 heads would overflow L0C (a 128×512 float32 output is 256 KB), forcing the accumulator onto the vector core, which is extremely hard to write; empirically M=64 is already enough to saturate CUBE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every CUBE needs two vector cores; one is not enough.&lt;/strong&gt; All three reasons are hard constraints: a single vector core can have at most 16 MTE2 copy requests in flight; decode has to read FP8 or FP4 KV and dequantize to bf16, and one vector core can't dequantize fast enough to keep up with CUBE; and FixPipe writes out at only 128 bytes per cycle per core, which by the formula forces B_TOPK above 73 — in practice they use 96 or 128. Taken together, the second vector core is not redundancy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A purely arithmetic optimization saves a large round of data movement.&lt;/strong&gt; The conventional online softmax approach may need to rescale the accumulated output against a new maximum every iteration. Their approach sets a threshold of 6 for "does this need rescaling" and skips the rescale when it isn't triggered — a trick they call skip-scale. The payoff is bigger on an NPU than a GPU, because on an NPU scale-O has to touch three units: FixPipe, UB and SIMD. The hard part is that the signal is produced by the vector core while the consumers are the CUBE and the vector core, which they solved with an SS buffer plus a cross-core flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The KV layout conversion goes the long way around because of a memory access pitfall.&lt;/strong&gt; CUBE wants NZ layout while KV sits in memory as ND. The normal approach is to convert in flight during the copy, but they fetch KV sparsely token by token, which rules that out. They tried copying tokens as 32-byte blocks directly, and found that the BIU inside the AI Core only coalesces accesses when &lt;em&gt;both&lt;/em&gt; source and destination are contiguous — "source contiguous only" doesn't coalesce, L2 bandwidth utilization drops, and the bottleneck becomes L2 bandwidth itself. In the end the path is: memory to UB, SIMD vector instructions to convert into NZ, then into L1.&lt;/p&gt;

&lt;p&gt;The report also covers gather2 (fusing two token copies into one to work around the MTE2 queue depth of 16), L1 dual-bank balancing (splitting each tensor into two, one per bank), and the fact that icache is only 32K (CUBE), 16K (VECTOR) and 8K (SIMD VF) — which is why they use hardware loop instructions instead of unrolled loops. None of these details appears in the announcement.&lt;/p&gt;

&lt;p&gt;One companion piece worth reading: Huawei's Ascend community has a long tutorial on TileLang from beginner to expert (published 2026-05-08) that lists the expert-mode conversion as six steps — split the requested cache by architecture into UB, L1 and L0C; insert workspace (because UB and L1/L0C cannot be copied between directly, so data used in both places has to exist twice); separate CV cores; synchronize across cores; have the two vector cores each handle half the data; and finally deal with the hardware special case that L0C cannot be written to directly. One of their measurements: hand-written FlashAttention at 44.9 microseconds, 22.28 microseconds after manually pipelining it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. How third parties see it
&lt;/h2&gt;

&lt;p&gt;The most systematic external tracking comes from SemiAnalysis's InferenceX. They've been recording DeepSeek V4 1.6T inference performance across hardware from Day 0 through Day 43, with a dedicated section analyzing inference on Ascend 950DT — how compute and communication overlap, and which compute streams Huawei used. One of their own observations is worth keeping: on the CUDA side, vLLM and SGLang were usable the day the model shipped, while ROCm wasn't usable on day one and only caught up on day 26, with an improvement of more than 100×. The ecosystem maturity gap is the genuinely hard part of this road. The same piece also notes that V4's fused MegaMoE kernel carries a theoretical 1.92× speedup over a naive implementation — working backwards, a naive implementation spends close to half its time in dispatch and combine.&lt;/p&gt;

&lt;p&gt;Two industry figures as reference points: IDC's data has NVIDIA's share of China's AI accelerator market falling from 95% two years ago to 55% in 2025, with domestic chips passing 41% for the first time — Ascend alone shipping 812,000 cards. China Mobile's April AI supernode tender this year was for 6,208 cards at roughly 2.06 billion RMB, requiring CANN-ecosystem solutions across the board. Both are third-party figures; I'd re-check them before using them.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. How far I got on my own machine
&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%2Farticle-images.zsxq.com%2FFndRVjzgRU4tRQ0t8OgQ3rOZyrTS" 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%2Farticle-images.zsxq.com%2FFndRVjzgRU4tRQ0t8OgQ3rOZyrTS" width="1615" height="819"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(After cloning the six repos locally, where the chain breaks. Green is what worked; red is what this machine doesn't have.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The environment first: Python 3.11.15; &lt;code&gt;/usr/local/Ascend&lt;/code&gt; does not exist; &lt;code&gt;import torch_npu&lt;/code&gt; raises ModuleNotFoundError; &lt;code&gt;which bisheng ccec&lt;/code&gt; finds nothing; there is no &lt;code&gt;nvidia-smi&lt;/code&gt; command; and &lt;code&gt;lspci&lt;/code&gt; shows zero NVIDIA, Ascend or Huawei devices. This is a pure CPU machine.&lt;/p&gt;

&lt;p&gt;Then I actually tried to build DeepGEMM-Ascend. The system python3 doesn't even have setuptools, so it stopped at line 5. I made a clean venv, installed the dependencies, and the second attempt got to line 7:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="nb"&gt;ModuleNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;torch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Ascend part only starts at line 28: &lt;code&gt;ASCEND_HOME&lt;/code&gt; defaults to &lt;code&gt;/usr/local/Ascend/ascend-toolkit/latest&lt;/code&gt;, then it imports &lt;code&gt;torch_npu&lt;/code&gt; and links against ascendcl, hccl_fwk, torch_npu, opapi and dl. DeepEP-Ascend's CMakeLists is even more direct — under &lt;code&gt;${ASCEND_HOME_PATH}/aarch64-linux/&lt;/code&gt; alone there are dozens of header directories, plus the clang/15.0.5 headers from the bisheng compiler.&lt;/p&gt;

&lt;p&gt;The conclusion is that I never even reached the doorstep. Reproducing any of this yourself starts with owning an Ascend machine with CANN installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. What's in the code but not in the announcement
&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%2Farticle-images.zsxq.com%2FFoul2LJkQWF9q38VrpFqg_pTIkOT" 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%2Farticle-images.zsxq.com%2FFoul2LJkQWF9q38VrpFqg_pTIkOT" width="1747" height="853"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Distribution of Ascend-specific kernels in TileKernels. The &lt;code&gt;quant&lt;/code&gt; directory is the largest: 12 files, 3,491 lines.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TileKernels writes two implementations for each kernel.&lt;/strong&gt; Take SwiGLU: the directory contains both &lt;code&gt;swiglu_forward_kernel.py&lt;/code&gt; and &lt;code&gt;swiglu_forward_asc.py&lt;/code&gt;. The Ascend one starts with &lt;code&gt;import tilelang.ascend.language as T&lt;/code&gt; and &lt;code&gt;from tilelang.ascend.language import simd as S&lt;/code&gt;, and its pipeline synchronization is hand-written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ascend_set_flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MTE3_MTE2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ascend_wait_flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MTE2_V&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_slot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The README's "backend selected automatically at runtime" is true, and the mechanism is an &lt;code&gt;is_ascend()&lt;/code&gt; check in &lt;code&gt;tile_kernels/config.py&lt;/code&gt;; the package can carry both backends in one environment. But a backend is not automatic translation — the Ascend implementation is written by hand. I counted the Ascend-specific files: &lt;code&gt;quant&lt;/code&gt; 12 files / 3,491 lines, &lt;code&gt;mhc&lt;/code&gt; 9 / 2,375, &lt;code&gt;engram&lt;/code&gt; 6 / 969, &lt;code&gt;moe&lt;/code&gt; 4 / 652, plus one each in &lt;code&gt;transform&lt;/code&gt; and &lt;code&gt;rand&lt;/code&gt; — 33 files, 7,729 lines in total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints scattered through asserts.&lt;/strong&gt; A few: per-token quantization only supports groups of 32 channels; the E4M3 scale factor is CUDA-only; SwiGLU only supports &lt;code&gt;sf_block=(1, 32)&lt;/code&gt;; hyper-connections only support 4 streams; &lt;code&gt;randn&lt;/code&gt; doesn't support graph capture on NPU because replay would reuse the same random seed; and &lt;code&gt;norm&lt;/code&gt;'s hidden dimension must be divisible by 128. The announcement's line that "every TileLang kernel has a corresponding high-performance implementation" holds — the price is that list of shapes and preconditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FlashMLA's Ascend kernel covers only 6 combinations.&lt;/strong&gt; Under &lt;code&gt;csrc/ascend_kernels/prefill/sparse&lt;/code&gt; there are 4 headers plus 6 script-generated instantiation files: V4.1, h64, prefill and decode, with and without sink, fp8 and fp4. Change the shape and you regenerate. This is not a general-purpose library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no kernel file in DeepEP-Ascend with "ascend" in its name.&lt;/strong&gt; Its kernels are organized by function under &lt;code&gt;csrc/kernels/ep&lt;/code&gt;, &lt;code&gt;comm&lt;/code&gt;, &lt;code&gt;bucket&lt;/code&gt;, &lt;code&gt;pp&lt;/code&gt; and &lt;code&gt;engram&lt;/code&gt; — all &lt;code&gt;.hpp&lt;/code&gt;, 47 files, 6,335 lines in total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same batch of commits carries a breaking change.&lt;/strong&gt; FlashMLA removed Hopper support and V3, V3.2 and V4.0 in this release, changed the KV cache format, and requires switching back to the 15 September commit if you need older models or formats. The Ascend kernels here only serve V4.1. This version of the attention library moves with V4.1.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. What this post doesn't do
&lt;/h2&gt;

&lt;p&gt;I reproduced none of the performance numbers — I have no Ascend card. I did no independent verification of second-hand items like "160,000 chips" or the supernode timelines; each is marked with its source in the text. When the commercial HDK lands in mid-October, there will be a version a third party can actually check, and I'll come back and fill this in then.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;DeepSeek's announcement, "DeepSeek open-sources Ascend foundation components" (in Chinese): &lt;a href="https://mp.weixin.qq.com/s/X41mKH4Ds-VXUAnK6M8Eww" rel="noopener noreferrer"&gt;https://mp.weixin.qq.com/s/X41mKH4Ds-VXUAnK6M8Eww&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;FlashMLA Ascend technical report (Chinese): &lt;a href="https://github.com/deepseek-ai/FlashMLA/blob/20260930-ascend-open-source/docs/20260930-ascend-prefill-deep-dive-zh.md" rel="noopener noreferrer"&gt;https://github.com/deepseek-ai/FlashMLA/blob/20260930-ascend-open-source/docs/20260930-ascend-prefill-deep-dive-zh.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DeepGEMM-Ascend, DeepEP-Ascend, TileKernels, FlashMLA, DeepSelect: the same-named repositories under github.com/deepseek-ai&lt;/li&gt;
&lt;li&gt;TileLang main repo PR #3308: &lt;a href="https://github.com/tile-ai/tilelang/pull/3308" rel="noopener noreferrer"&gt;https://github.com/tile-ai/tilelang/pull/3308&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;TileLang Ascend backend docs: &lt;a href="https://github.com/tile-ai/tilelang/blob/main/tilelang/ascend/README.md" rel="noopener noreferrer"&gt;https://github.com/tile-ai/tilelang/blob/main/tilelang/ascend/README.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Huawei, "Ascend 960 supernode launch" (2026-09-17): &lt;a href="https://www.huawei.com/cn/news/2026/9/hc-ascend960-supernode" rel="noopener noreferrer"&gt;https://www.huawei.com/cn/news/2026/9/hc-ascend960-supernode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Huawei supernode interconnect keynote transcript (2025-09, Atlas 950 specs): &lt;a href="https://www.huawei.com/cn/news/2025/9/hc-xu-keynote-speech" rel="noopener noreferrer"&gt;https://www.huawei.com/cn/news/2025/9/hc-xu-keynote-speech&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ascend community, "TileLang AscendNPU IR from beginner to expert" (2026-05-08): &lt;a href="https://www.hiascend.com/developer/techArticles/20260506-1" rel="noopener noreferrer"&gt;https://www.hiascend.com/developer/techArticles/20260506-1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cailian Press, "Huawei's Atlas 950 hardware to appear at WAIC" (2026-07-07): &lt;a href="https://www.cls.cn/detail/2418935" rel="noopener noreferrer"&gt;https://www.cls.cn/detail/2418935&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Guancha, "DeepSeek reportedly to buy 160,000 Huawei Ascend 950DT chips" (2026-09-05): &lt;a href="https://www.guancha.cn/economy/2026_09_05_830131.shtml" rel="noopener noreferrer"&gt;https://www.guancha.cn/economy/2026_09_05_830131.shtml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Economic Observer, "Huawei computing power opens its chain" (2026-05-01): &lt;a href="http://www.eeo.com.cn/2026/0501/860610.shtml" rel="noopener noreferrer"&gt;http://www.eeo.com.cn/2026/0501/860610.shtml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SemiAnalysis InferenceX, "DeepSeekV4 1.6T Day 0 to Day 43": &lt;a href="https://inferencex.semianalysis.com/blog/deepseekv4-16t-day-0-to-day-43-performance" rel="noopener noreferrer"&gt;https://inferencex.semianalysis.com/blog/deepseekv4-16t-day-0-to-day-43-performance&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every number, error message and source in this post is in the list above; nothing here is repeated from somewhere else. I write one of these deep dives a week, published first in my own community in Chinese — this is the cleaned-up public version.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I also keep a small paid community where I write these engineering logs up in full, one deep dive a week. Its content is in Chinese — I mention it in case you read Chinese: &lt;a href="https://t.zsxq.com/fx2y5" rel="noopener noreferrer"&gt;AI落地实录&lt;/a&gt; (¥25/year with the current new-member coupon, ¥50 list price).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: this English version was translated and edited with an AI assistant from my own write-up. Every figure, error message and file count comes from my own runs or the linked primary sources — which is exactly why the parts I could not verify myself are marked as vendor claims.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally written in Chinese for my own notes; this English version is a translation.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read next&lt;/strong&gt;, from the same notebook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/chinese-open-weights-timeline/" rel="noopener noreferrer"&gt;The Chinese open-weights release timeline, dated&lt;/a&gt; — The release timeline these repos sit on, dated.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notebookfield.com/posts/llm-api-pricing-per-task/" rel="noopener noreferrer"&gt;From token prices to task prices: three jobs, seven APIs&lt;/a&gt; — What the models cost per task, not per token.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>hardware</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
