<?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: Khadim Hussain</title>
    <description>The latest articles on DEV Community by Khadim Hussain (@khadimhussain).</description>
    <link>https://dev.to/khadimhussain</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%2F4177831%2F811b7b78-5d73-4e79-8cc3-6182008711d7.jpg</url>
      <title>DEV Community: Khadim Hussain</title>
      <link>https://dev.to/khadimhussain</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/khadimhussain"/>
    <language>en</language>
    <item>
      <title>AI Bubble Burst in China: What It Means for 2027</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Sun, 01 Nov 2026 09:01:35 +0000</pubDate>
      <link>https://dev.to/khadimhussain/ai-bubble-burst-in-china-what-it-means-for-2027-3dc2</link>
      <guid>https://dev.to/khadimhussain/ai-bubble-burst-in-china-what-it-means-for-2027-3dc2</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/ai-bubble-burst-china" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; China's AI stock bubble burst in 2026: the STAR50 index fell 40% in under four months, erasing about $480 billion, while Chinese AI firms were rationing compute and raising prices. In the US, Microsoft, Alphabet, Amazon and Meta spent $201.9 billion on property and equipment in the third quarter of 2026, $26.4 billion more than their combined operating cash flow. A few forecasters put a US market fall in 2027 or 2028, while the other strategists Bloomberg tracks see about 14% upside on average. For a company using AI, the main risk in a correction is a vendor failing, so keep a second provider, an open-weight model you can run yourself, and a measured return for each use case.&lt;/p&gt;

&lt;p&gt;The AI bubble burst in China in 2026, on the stock market. China's STAR50 technology index fell 40% in under four months, at a time when Chinese AI companies were short of computing power and raising their prices. Valuations collapsed, and demand for AI did not.&lt;/p&gt;

&lt;p&gt;In the US the argument is about money. Microsoft, Alphabet, Amazon and Meta now spend more on data centres and equipment than their businesses generate in cash, and they are borrowing and selling shares to cover the gap. A minority of forecasters name 2027 or 2028 for a correction, and the strategists Bloomberg tracks still see US stocks rising on average.&lt;/p&gt;

&lt;p&gt;For a company that uses AI, the useful question is what a fall would do to the models and vendors it depends on. The market predictions below are the forecasters' own, and none of this is investment advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did the AI bubble burst in China?
&lt;/h2&gt;

&lt;p&gt;China's AI stock bubble did. On October 27, 2026, &lt;a href="https://kathmandupost.com/world/2026/10/27/in-china-the-ai-bubble-has-already-burst" rel="noopener noreferrer"&gt;Reuters reported&lt;/a&gt; that the STAR50, the main index of Shanghai's technology board, had fallen 40% in less than four months, wiping out about $480 billion of market value. At its peak it traded at nearly 250 times the previous 12 months' earnings, and after the fall it was still around 100 times, against 30 times for the Nasdaq 100.&lt;/p&gt;

&lt;p&gt;By Goldman Sachs' count, AI stocks are 42% of China's market value and 57% of its trading turnover. The broader CSI 300 was about 20% below its June peak, while the Nasdaq was about 6% below its record.&lt;/p&gt;

&lt;p&gt;Beijing helped let the air out. Regulators cracked down on leveraged and high-frequency trading, state-backed investors sold into rallies, and October guidelines from the Communist Party and the State Council &lt;a href="https://www.thestandard.com.hk/innovation/article/345071/China-vows-to-curb-tech-bubbles-keep-AI-risks-in-check" rel="noopener noreferrer"&gt;set out&lt;/a&gt; to "curb investment bubbles" while keeping the "AI Plus" push to use AI in traditional industries.&lt;/p&gt;

&lt;p&gt;When MiniMax listed in Hong Kong in January, its retail tranche was &lt;a href="https://www.caixinglobal.com/2026-01-06/minimaxs-hong-kong-ipo-oversubscribed-1848-times-as-ai-frenzy-builds-102400879.html" rel="noopener noreferrer"&gt;oversubscribed 1,848 times&lt;/a&gt;, and by late February it and Zhipu, which listed a day earlier, were worth $63 billion together, with enterprise values of more than 100 times the revenue analysts forecast for the next 12 months. A &lt;a href="https://www.itiger.com/news/2614307768" rel="noopener noreferrer"&gt;Reuters Breakingviews column&lt;/a&gt; compared them with the electric-car maker Nio, whose shares rose tenfold and then fell more than 90%.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happened to China's AI startups before the crash?
&lt;/h3&gt;

&lt;p&gt;In 2024 more than 144 companies registered large language models with the Cyberspace Administration of China. By the end of that year only about 10% were still investing in large-scale training, according to the Economic Observer, as cited by &lt;a href="https://www.technologyreview.com/2025/03/26/1113802/china-ai-data-centers-unused/" rel="noopener noreferrer"&gt;MIT Technology Review&lt;/a&gt;. A price war in May 2024 squeezed them further: ByteDance priced its Doubao Pro model at what it said was 99% below the industry average, and Alibaba cut some Qwen prices by up to 97% (&lt;a href="https://fortune.com/asia/2024/05/22/china-ai-llm-price-war-discounts-bytedance-alibaba-baidu-iflytek" rel="noopener noreferrer"&gt;Fortune&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Why did China's AI data centres sit idle?
&lt;/h3&gt;

&lt;p&gt;More than 500 data-centre projects were announced in 2023 and 2024. The Chinese outlets Jiazi Guangnian and 36Kr reported that up to 80% of the newly built computing capacity was unused, and the monthly rent for a server with eight Nvidia H100 GPUs fell to 75,000 yuan from highs of around 180,000 (&lt;a href="https://www.technologyreview.com/2025/03/26/1113802/china-ai-data-centers-unused/" rel="noopener noreferrer"&gt;MIT Technology Review&lt;/a&gt;, March 2025). Many were remote sites built for training, and they lost their appeal when demand moved to serving reasoning models close to users. In May 2026 the Australian Strategic Policy Institute &lt;a href="https://www.aspistrategist.org.au/abundant-electricity-isnt-enough-chinas-overbuilt-ai-computing-power-is-underused/" rel="noopener noreferrer"&gt;still reported&lt;/a&gt; utilisation as low as 20% to 30% at some western data centres, against a goal of more than 60%.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why did AI demand in China keep growing?
&lt;/h3&gt;

&lt;p&gt;By early 2026 the shortage ran the other way. In January &lt;a href="https://www.bloomberg.com/news/articles/2026-01-21/china-ai-firm-zhipu-limits-coding-agent-access-as-demand-soars" rel="noopener noreferrer"&gt;Bloomberg reported&lt;/a&gt; that Zhipu was limiting access to its coding agent, cutting the daily quota of new coding-plan sales to 20% of its previous level. In April, &lt;a href="https://www.caixinglobal.com/2026-04-21/computing-shortage-forces-chinese-ai-firms-to-ration-services-102436452.html" rel="noopener noreferrer"&gt;Caixin reported&lt;/a&gt; that a shortage of computing power was forcing Chinese AI firms to "throttle services, ration access and hike prices". Alibaba Cloud raised AI compute prices by 5% to 34% and Baidu by about 5% to 30%, both from April 18 (&lt;a href="https://www.trendforce.com/news/2026/04/10/news-ai-compute-prices-rise-across-china-tencent-joins-alibaba-baidu-in-hikes-zhipu-raises-prices-again/" rel="noopener noreferrer"&gt;TrendForce&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The large platforms kept building: Alibaba, Tencent and Baidu spent about 131.8 billion yuan on capital projects in the second quarter of 2026, about 2.1 times as much as a year earlier (&lt;a href="https://eu.36kr.com/en/p/3962911086623874" rel="noopener noreferrer"&gt;36Kr&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;China is an imperfect template for the US: the Chinese state actively cooled the market, and the AI crash came on top of a wider fall in Chinese shares.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is AI a bubble in the US?
&lt;/h2&gt;

&lt;p&gt;In the US, the question is whether spending has outrun cash. &lt;a href="https://www.sec.gov/Archives/edgar/data/0000789019/000119312526436122/msft-ex99_1.htm" rel="noopener noreferrer"&gt;Microsoft&lt;/a&gt;, &lt;a href="https://www.sec.gov/Archives/edgar/data/0001652044/000165204426000080/googexhibit991q32026.htm" rel="noopener noreferrer"&gt;Alphabet&lt;/a&gt;, &lt;a href="https://www.sec.gov/Archives/edgar/data/0001018724/000101872426000038/amzn-20260930xex991.htm" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt; and &lt;a href="https://investor.atmeta.com/investor-news/press-release-details/2026/Meta-Reports-Third-Quarter-2026-Results/default.aspx" rel="noopener noreferrer"&gt;Meta&lt;/a&gt; spent $201.9 billion on property and equipment in the July-to-September quarter of 2026, against $175.5 billion of operating cash flow. A year earlier they spent $97.3 billion against $159.0 billion. Their spending roughly doubled while their cash from operations rose about 10%.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chart: &lt;a href="https://khadim.tech/blog/ai-bubble-burst-china" rel="noopener noreferrer"&gt;Q3 2026 capital spending vs operating cash flow for Microsoft, Alphabet, Amazon and Meta&lt;/a&gt;, from each company's results.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The figures are reported purchases of property and equipment and net cash from operations for the quarter ended September 30, 2026 (Microsoft's fiscal first quarter of 2027). None of the four reports an AI-only figure, and Oracle is left out because its quarter ends in August.&lt;/p&gt;

&lt;h3&gt;
  
  
  The case for a bubble
&lt;/h3&gt;

&lt;p&gt;Three of the four are spending more cash than they make. Alphabet's free cash flow was -$9.1 billion in the quarter, and Meta's was -$2.1 billion, against +$10.6 billion a year earlier. Amazon's was -$42.4 billion over the 12 months to September, against +$14.8 billion a year before. Microsoft had about $18.6 billion of operating cash left after its capital spending.&lt;/p&gt;

&lt;p&gt;Outside money is filling the gap. Meta announced a $95 billion share offering, its first new stock since 2013 (&lt;a href="https://www.semafor.com/article/10/26/2026/meta-announces-95b-offering-to-fund-ai" rel="noopener noreferrer"&gt;Semafor&lt;/a&gt;). A Bank of Korea report found that Alphabet, Microsoft, Amazon, Meta and Oracle issued $230 billion of bonds in the first nine months of 2026, more than double their total for 2025 (&lt;a href="https://biz.heraldcorp.com/article/10916568" rel="noopener noreferrer"&gt;Herald Business&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.imf.org/en/blogs/articles/2026/10/12/global-financial-markets-face-rising-risks-from-ai-bond-yields-and-cyberattacks" rel="noopener noreferrer"&gt;IMF&lt;/a&gt; warned in October 2026 that "faltering AI investment and profitability could challenge optimistic expectations and trigger a correction in asset prices." It reviewed 14 circular financing deals since 2023, in which AI companies' investors are also their suppliers or customers: about $300 billion of equity and $650 billion of debt, tied to $1.4 trillion of future revenue commitments. Trouble at one recipient could spread along that chain, though the IMF presents this as a vulnerability, not a forecast.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bain.com/about/media-center/press-releases/20252/$2-trillion-in-new-revenue-needed-to-fund-ais-scaling-trend---bain--companys-6th-annual-global-technology-report/" rel="noopener noreferrer"&gt;Bain&lt;/a&gt; estimated in September 2025 that AI needs $2 trillion of annual revenue by 2030 to pay for the computing it expects to use, and that the world is still $800 billion short after counting AI-driven savings. Citadel's Ken Griffin said on October 20, 2026 that investors are "almost certainly" in an AI bubble, and &lt;a href="https://decrypt.co/381578/ken-griffin-ai-bubble-gpus-airline-seats" rel="noopener noreferrer"&gt;compared GPUs to airline seats&lt;/a&gt;: once supply exceeds demand, prices can collapse.&lt;/p&gt;

&lt;h3&gt;
  
  
  The case against a bubble
&lt;/h3&gt;

&lt;p&gt;The demand shows up in revenue. In the same quarter Azure revenue grew 48%, Google Cloud grew 103% to $30.7 billion with $11.1 billion of operating income, and AWS grew 39% to $46.0 billion. Microsoft's commercial remaining performance obligation, the revenue under contract that it has not yet recognised, rose 73% to $677 billion. Nvidia's data-centre revenue was $89.0 billion in the quarter to July 26, 2026, up 117% (&lt;a href="https://nvidianews.nvidia.com/news/nvidia-announces-financial-results-for-second-quarter-fiscal-2027" rel="noopener noreferrer"&gt;Nvidia&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Compute is still scarce. AWS raised prices for EC2 Capacity Blocks for ML, its reserved GPU capacity, by about 15% in early January 2026, saying the change "reflects the supply/demand patterns we expect this quarter" (&lt;a href="https://www.theregister.com/2026/01/05/aws_price_increase/" rel="noopener noreferrer"&gt;The Register&lt;/a&gt;). Jeff Bezos, who called AI "a kind of industrial bubble" in October 2025, said such bubbles are "not nearly as bad" as financial ones (&lt;a href="https://www.fortune.com/2025/10/04/jeff-bezos-amazon-openai-sam-altman-ai-bubble-tech-stocks-investing" rel="noopener noreferrer"&gt;Fortune&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Will the AI bubble burst in 2027?
&lt;/h2&gt;

&lt;p&gt;A handful of forecasters name 2027, and they predict different things:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Who&lt;/th&gt;
&lt;th&gt;What they predict&lt;/th&gt;
&lt;th&gt;By when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://fortune.com/2026/09/13/stock-market-outlook-late-stage-bubble-crash-2027-treasury-yields-5-percent/" rel="noopener noreferrer"&gt;Capital Economics&lt;/a&gt; (James Reilly)&lt;/td&gt;
&lt;td&gt;A "late-stage bubble": the S&amp;amp;P 500 ends 2026 at 8,250, then falls 21% to 6,500&lt;/td&gt;
&lt;td&gt;End of 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://www.insurancejournal.com/news/international/2026/10/09/888427.htm" rel="noopener noreferrer"&gt;Joachim Klement&lt;/a&gt;, Panmure Liberum&lt;/td&gt;
&lt;td&gt;The AI bubble "will either burst in 2027 or in 2028"; the S&amp;amp;P 500 at 5,000, about 36% below its October 2026 level&lt;/td&gt;
&lt;td&gt;End of 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://fortune.com/2026/10/07/ai-cash-flow-negative-inflection-point-capex-slow/" rel="noopener noreferrer"&gt;Ohsung Kwon&lt;/a&gt;, Wells Fargo&lt;/td&gt;
&lt;td&gt;Markets start treating 2027 as a potential peak year for AI capital spending, which will "likely slow meaningfully in 2028"&lt;/td&gt;
&lt;td&gt;2027 to 2028&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://fortune.com/2026/10/07/ai-cash-flow-negative-inflection-point-capex-slow/" rel="noopener noreferrer"&gt;Pantheon Macroeconomics&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Hyperscaler capital spending growth "looks set to moderate"&lt;/td&gt;
&lt;td&gt;2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.fool.com/investing/2026/10/04/michael-burry-thinks-the-ai-bubble-will-burst-soon/" rel="noopener noreferrer"&gt;Michael Burry&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Reportedly bought put options, which pay off if a share falls, on Micron and Nebius&lt;/td&gt;
&lt;td&gt;Expiring June 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.gartner.com/en/newsroom/press-releases/2025-06-25-gartner-predicts-over-40-percent-of-agentic-ai-projects-will-be-canceled-by-end-of-2027" rel="noopener noreferrer"&gt;Gartner&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;More than 40% of agentic AI projects cancelled&lt;/td&gt;
&lt;td&gt;End of 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Only the first two forecast a market fall in 2027, and Burry's positions bet the same way; the other calls are about slower spending or cancelled projects.&lt;/p&gt;

&lt;p&gt;Most strategists disagree: the seven others Bloomberg tracks alongside Klement see 14% upside for US stocks on average. Ruchir Sharma of Rockefeller International names a trigger rather than a date: in a Financial Times column he wrote that the bubble could pop if the 10-year US Treasury yield "decisively breaches" 5%. In mid-September it reached 4.97%.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens to companies using AI when the AI bubble bursts?
&lt;/h2&gt;

&lt;p&gt;A correction is a fall in share prices and spending plans. Trained models and finished data centres keep running. What changes for a company using AI is the price of compute, which vendors survive, and how budgets are judged.&lt;/p&gt;

&lt;p&gt;On price, the evidence points both ways. Griffin's airline-seat comparison implies that a glut of GPUs would push compute prices down, and the price of a fixed level of LLM capability has fallen between 9 and 900 times a year depending on the task, according to &lt;a href="https://epoch.ai/data-insights/llm-inference-price-trends" rel="noopener noreferrer"&gt;Epoch AI&lt;/a&gt;. But shortages pushed prices up in 2026 in both the US and China, so an AI budget should allow for movement in either direction.&lt;/p&gt;

&lt;p&gt;The bigger exposure is vendors. Builder.ai, once valued at over $1 billion, backed by Microsoft and funded with more than $450 million, &lt;a href="https://techcrunch.com/2025/05/20/once-worth-over-1b-microsoft-backed-builder-ai-is-running-out-of-money" rel="noopener noreferrer"&gt;entered insolvency proceedings&lt;/a&gt; in May 2025. Gartner estimates that only about 130 of the thousands of companies selling "agentic AI" are real, and calls the relabelling of chatbots and automation tools "agent washing".&lt;/p&gt;

&lt;p&gt;Gartner's cancellation forecast names rising costs, unclear business value and weak risk controls as the causes. MIT's Project NANDA, based on 52 interviews and 153 survey responses, found that just 5% of integrated generative AI pilots were extracting millions in value, while the large majority had no measurable effect on profit and loss (&lt;a href="https://virtualizationreview.com/articles/2025/08/19/mit-report-finds-most-ai-business-investments-fail-reveals-genai-divide" rel="noopener noreferrer"&gt;Virtualization Review&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you protect an AI project from a bubble burst?
&lt;/h2&gt;

&lt;p&gt;I recommend these engineering hedges whatever the market does, because providers change prices, models and terms in good years too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep the model behind one interface
&lt;/h3&gt;

&lt;p&gt;Call the model through one thin layer in your own code, keep prompts and evaluation sets in your repository, and run the same evaluation against a second provider on a schedule. Switching then takes a configuration change and a test run. For agents, tools built as MCP servers can be called by any compatible model client, so the model can change without rebuilding them. That is one reason MCP servers are part of my &lt;a href="https://khadim.tech/services/ai-agent-consulting" rel="noopener noreferrer"&gt;AI agent consulting&lt;/a&gt; work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep an open-weight model you can run yourself
&lt;/h3&gt;

&lt;p&gt;An open-weight model you host stays available if its maker fails or changes its terms. DeepSeek V4, released in April 2026 under the MIT licence, can be served by any provider or on your own hardware, and its Flash version costs $0.14 per million input tokens through DeepSeek's API (&lt;a href="https://simonwillison.net/2026/apr/24/deepseek-v4" rel="noopener noreferrer"&gt;Simon Willison&lt;/a&gt;). Few companies have this fallback ready: &lt;a href="https://menlovc.com/perspective/2025-mid-year-llm-market-update/" rel="noopener noreferrer"&gt;Menlo Ventures&lt;/a&gt; found open-source models handled 13% of enterprise AI workloads in mid-2025, down from 19% six months earlier, partly because they trail the best closed models by nine to 12 months. For one narrow task, test whether a smaller open model fine-tuned on your own examples meets your accuracy bar, which is the first step in &lt;a href="https://khadim.tech/services/llm-fine-tuning" rel="noopener noreferrer"&gt;LLM fine-tuning and consulting&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Running a model yourself means owning the runtime as well. When Ollama 0.34.1 dropped support for LoRA adapters in September 2026, fine-tunes served that way needed a new route, and I tested the two that work in &lt;a href="https://khadim.tech/blog/ollama-lora-adapters" rel="noopener noreferrer"&gt;Ollama no longer supports LoRA adapters&lt;/a&gt;. Pin the versions you depend on and test upgrades before they reach production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measure the return of each use case
&lt;/h3&gt;

&lt;p&gt;Give every AI use case a baseline and a number, such as minutes per task, error rate or cost per thousand requests, and measure before and after. Unclear business value is one of the causes Gartner gives for its cancellation forecast, and a measured result answers it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write the exit into the contract
&lt;/h3&gt;

&lt;p&gt;Avoid long prepaid commitments to loss-making start-ups, or keep them small enough to write off. In any AI contract, ask for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;export of your data, prompts, logs and any fine-tuned weights in a standard format, at any time;&lt;/li&gt;
&lt;li&gt;notice before price changes (AWS raised its reserved GPU prices by about 15% on a Saturday in January 2026);&lt;/li&gt;
&lt;li&gt;a termination right and the return of your data if the vendor becomes insolvent or is acquired;&lt;/li&gt;
&lt;li&gt;clear ownership of anything trained on your data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Did the AI bubble burst in China?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;China's AI stock bubble did. The STAR50 index fell 40% in under four months to late October 2026, wiping out about $480 billion, according to Reuters. Demand for AI computing held up: earlier in 2026, Chinese cloud providers raised AI compute prices by 5% to 34% and some AI firms rationed access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is AI a bubble?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Forecasters disagree. On the spending side, Microsoft, Alphabet, Amazon and Meta put $26.4 billion more into property and equipment than they generated in operating cash in the third quarter of 2026, and the IMF warns that faltering AI investment could trigger a correction in asset prices. On the revenue side, Google Cloud grew 103% in the same quarter and Microsoft reported $677 billion of contracted commercial revenue not yet recognised.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will the AI bubble burst in 2027?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A few forecasters say so. Capital Economics expects the S&amp;amp;P 500 to fall 21% to 6,500 by the end of 2027, and Panmure Liberum's Joachim Klement says the AI bubble will burst in 2027 or 2028. They are in the minority: the seven other strategists Bloomberg tracks see about 14% upside for US stocks on average.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when the AI bubble bursts?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Share prices and spending plans fall, but models already trained keep running. For a company using AI, the larger exposure is its vendors: Microsoft-backed Builder.ai, once valued at over $1 billion, entered insolvency proceedings in May 2025. Projects with a measured return are the easiest to defend when budgets tighten.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are big tech companies spending more than they earn on AI?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In cash terms, three of them were in the third quarter of 2026. Alphabet, Amazon and Meta each spent more on property and equipment than their operating cash flow, while Microsoft spent $39.4 billion against $58.0 billion. The filings report total capital spending, with no AI-only figure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How can a company protect its AI projects if the bubble bursts?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Call models through one interface so you can switch providers, and keep a tested open-weight model you can run yourself. Measure each use case against a baseline, and avoid long prepaid commitments to loss-making start-ups. Contracts should cover data export, notice of price changes and what happens if the vendor becomes insolvent.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: written with AI assistance, from my own research and notes. Not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>business</category>
      <category>china</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Claude Code on Windows: Install and Setup for Beginners</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Tue, 27 Oct 2026 09:17:25 +0000</pubDate>
      <link>https://dev.to/khadimhussain/claude-code-on-windows-install-and-setup-for-beginners-5d04</link>
      <guid>https://dev.to/khadimhussain/claude-code-on-windows-install-and-setup-for-beginners-5d04</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/claude-code-windows" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; To install Claude Code on Windows, open PowerShell (no admin rights needed), run irm &lt;a href="https://claude.ai/install.ps1" rel="noopener noreferrer"&gt;https://claude.ai/install.ps1&lt;/a&gt; | iex, then open a new terminal and type claude --version. You need Windows 10 version 1809 or later, 4 GB of RAM and a paid Claude plan: the free plan doesn't include Claude Code, and Pro costs $20 a month, or $17 a month billed yearly. If Windows says claude is not recognized, add %USERPROFILE%.local\bin to your user PATH and open a new terminal.&lt;/p&gt;

&lt;p&gt;Claude Code on Windows takes one command to install, and you don't need to know how to code to use it. I filmed the whole setup on a fresh Windows 11 install for my YouTube channel, &lt;a href="https://www.youtube.com/@khadimlabs" rel="noopener noreferrer"&gt;Khadim Labs&lt;/a&gt;: the install, the PATH warning the installer gave me, the sign-in, and two real jobs done in plain English. This page has every command from the video, plus the fixes Anthropic's documentation lists for the errors beginners run into. Steps and prices are as of 27 October 2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Video:&lt;/strong&gt; &lt;a href="https://youtu.be/89MrDJHG8sI" rel="noopener noreferrer"&gt;Claude Code on Windows: Full Beginner Setup (No Coding Needed)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Claude Code?
&lt;/h2&gt;

&lt;p&gt;Claude Code is Anthropic's AI assistant that works on the files on your computer. The &lt;a href="https://code.claude.com/docs/en/overview" rel="noopener noreferrer"&gt;official overview&lt;/a&gt; calls it "an agentic coding tool that reads your codebase, edits files, runs commands, and integrates with your development tools."&lt;/p&gt;

&lt;p&gt;A chatbot like ChatGPT answers in a browser tab. Claude Code runs in the terminal, the window where you type commands, inside a folder you choose, where it can read, create and change files and run commands. You describe the result, it works out the steps, and you check what it did. Developers use it to write software, but the two jobs in my video were a messy folder and a bakery website. It also runs in the Claude desktop app, in VS Code and on the web.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Claude Code free? What does it cost?
&lt;/h2&gt;

&lt;p&gt;No. Claude Code needs a paid plan, and the free Claude plan doesn't include it. These are the individual plans on &lt;a href="https://claude.com/pricing" rel="noopener noreferrer"&gt;claude.com/pricing&lt;/a&gt; as of 27 October 2026, in US dollars and before tax:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Claude Code&lt;/th&gt;
&lt;th&gt;Usage per 5-hour session&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Everyday questions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;$20/month, or $17/month billed yearly ($200 up front)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;At least 5x Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max 5x&lt;/td&gt;
&lt;td&gt;$100/month&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;5x Pro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max 20x&lt;/td&gt;
&lt;td&gt;$200/month&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;20x Pro&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Limits reset on a rolling five-hour window, paid plans add weekly limits on top, and the Claude app and Claude Code share one pool. There's no fixed message count: usage depends on how long and complex the work is and which model you pick. At a limit you can wait, upgrade, or turn on usage credits billed at API rates. Type &lt;code&gt;/usage&lt;/code&gt; in Claude Code to see where you stand.&lt;/p&gt;

&lt;p&gt;Start with Pro. Anthropic's &lt;a href="https://support.claude.com/en/articles/11145838-use-claude-code-with-your-pro-or-max-plan" rel="noopener noreferrer"&gt;help article for Pro and Max&lt;/a&gt; suggests Max 5x if you "consistently hit limits." To pay per use instead, a Claude Console account with prepaid API credits works too. One trap: if an &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; environment variable is set on your PC and you approve it when Claude Code asks, it bills that API account instead of your plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you need to run Claude Code on Windows?
&lt;/h2&gt;

&lt;p&gt;From Anthropic's &lt;a href="https://code.claude.com/docs/en/setup#system-requirements" rel="noopener noreferrer"&gt;system requirements&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows 10 version 1809 or later, or Windows Server 2019 or later, 64-bit&lt;/li&gt;
&lt;li&gt;4 GB of RAM or more, with an x64 or ARM64 processor&lt;/li&gt;
&lt;li&gt;An internet connection, in a &lt;a href="https://www.anthropic.com/supported-countries" rel="noopener noreferrer"&gt;supported country&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Pro, Max, Team, Enterprise or Console account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need admin rights, Node.js or WSL. &lt;a href="https://git-scm.com/downloads/win" rel="noopener noreferrer"&gt;Git for Windows&lt;/a&gt; is optional: it adds Git Bash, which Claude Code uses for its Bash tool, and without it Claude Code runs commands through PowerShell. Choose WSL 2 only if you want Linux tools or Claude Code's sandbox, which native Windows doesn't support.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I install Claude Code on Windows?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Right-click the Start button (or press &lt;code&gt;Win + X&lt;/code&gt;) and choose Terminal or Windows PowerShell. Check that the prompt starts with &lt;code&gt;PS&lt;/code&gt;. Without &lt;code&gt;PS&lt;/code&gt; you're in Command Prompt, which needs a different command (below).&lt;/li&gt;
&lt;li&gt;Paste this line and press Enter:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;irm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://claude.ai/install.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;irm&lt;/code&gt; downloads the installer and &lt;code&gt;iex&lt;/code&gt; runs it. It shows no progress while it downloads, and it took a few minutes for me. Anthropic's &lt;a href="https://code.claude.com/docs/en/terminal-guide" rel="noopener noreferrer"&gt;terminal guide&lt;/a&gt; says it ends with "Claude Code successfully installed!" Read the last lines anyway: mine also said the install folder wasn't on my PATH, which the next section fixes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Close the terminal, open a new one, and check the install:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;claude&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A working install prints a version number followed by &lt;code&gt;(Claude Code)&lt;/code&gt;. For a fuller check, &lt;code&gt;claude doctor&lt;/code&gt; prints read-only diagnostics without starting a session.&lt;/p&gt;

&lt;p&gt;The installer puts &lt;code&gt;claude.exe&lt;/code&gt; in &lt;code&gt;%USERPROFILE%\.local\bin&lt;/code&gt; and keeps it updated in the background. If you're in Command Prompt instead of PowerShell, use the CMD installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;curl&lt;/span&gt; &lt;span class="na"&gt;-fsSL &lt;/span&gt;&lt;span class="kd"&gt;https&lt;/span&gt;://claude.ai/install.cmd &lt;span class="na"&gt;-o &lt;/span&gt;&lt;span class="kd"&gt;install&lt;/span&gt;.cmd &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="kd"&gt;install&lt;/span&gt;.cmd &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;del&lt;/span&gt; &lt;span class="kd"&gt;install&lt;/span&gt;.cmd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WinGet works too (&lt;code&gt;winget install Anthropic.ClaudeCode&lt;/code&gt;), but it doesn't update itself, so run &lt;code&gt;winget upgrade Anthropic.ClaudeCode&lt;/code&gt; now and then. To skip the terminal entirely, the &lt;a href="https://code.claude.com/docs/en/desktop-quickstart" rel="noopener noreferrer"&gt;Claude desktop app&lt;/a&gt; for Windows (x64 and ARM64) has a Code tab that runs Claude Code in a normal window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does Windows say 'claude' is not recognized?
&lt;/h2&gt;

&lt;p&gt;PowerShell shows &lt;code&gt;claude : The term 'claude' is not recognized as the name of a cmdlet&lt;/code&gt;, and Command Prompt shows &lt;code&gt;'claude' is not recognized as an internal or external command&lt;/code&gt;. Both mean &lt;code&gt;claude.exe&lt;/code&gt; is installed but its folder isn't on your PATH, the list of folders Windows searches for programs. The installer prints this under &lt;code&gt;Setup notes:&lt;/code&gt; as &lt;code&gt;Native installation exists but C:\Users\you\.local\bin is not in your PATH.&lt;/code&gt; and leaves PATH alone.&lt;/p&gt;

&lt;p&gt;This is the fix I used in the video. It takes about 30 seconds, once:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Start and search for "environment variables for your account". Open it.&lt;/li&gt;
&lt;li&gt;Under the user variables, select Path and click Edit.&lt;/li&gt;
&lt;li&gt;Click New and type &lt;code&gt;%USERPROFILE%\.local\bin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click OK, then OK again.&lt;/li&gt;
&lt;li&gt;Close the terminal, open a new one, and run &lt;code&gt;claude --version&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or paste these two lines into PowerShell, from Anthropic's &lt;a href="https://code.claude.com/docs/en/troubleshoot-install#verify-your-path" rel="noopener noreferrer"&gt;PATH troubleshooting&lt;/a&gt;, then open a new terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$currentPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PATH'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'User'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;SetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PATH'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$currentPath&lt;/span&gt;&lt;span class="s2"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\.local\bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'User'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still not recognized in a new window? A terminal inside an editor such as VS Code takes its PATH from the editor, so quit and reopen the editor. If the error started right after an update, the docs explain how to &lt;a href="https://code.claude.com/docs/en/troubleshoot-install#claude-exe-missing-after-an-update-on-windows" rel="noopener noreferrer"&gt;restore claude.exe from its backup&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Claude Code install errors on Windows, and their fixes
&lt;/h2&gt;

&lt;p&gt;These are the Windows errors in Anthropic's &lt;a href="https://code.claude.com/docs/en/troubleshoot-install" rel="noopener noreferrer"&gt;installation troubleshooting&lt;/a&gt; page.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you see&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'irm' is not recognized&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You're in Command Prompt&lt;/td&gt;
&lt;td&gt;Open PowerShell, or use the CMD installer above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;The token '&amp;amp;&amp;amp;' is not a valid statement separator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You ran the CMD command in PowerShell&lt;/td&gt;
&lt;td&gt;Use the &lt;code&gt;irm&lt;/code&gt; command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;A parameter cannot be found that matches parameter name 'fsSL'&lt;/code&gt; or &lt;code&gt;'bash' is not recognized&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;You ran the macOS/Linux command&lt;/td&gt;
&lt;td&gt;Use the &lt;code&gt;irm&lt;/code&gt; command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The script's text prints and nothing installs&lt;/td&gt;
&lt;td&gt;You ran &lt;code&gt;irm&lt;/code&gt; without `\&lt;/td&gt;
&lt;td&gt;iex`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Claude Code does not support 32-bit Windows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You opened Windows PowerShell (x86)&lt;/td&gt;
&lt;td&gt;Open the Start entry without "(x86)"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;The process cannot access the file ... because it is being used by another process&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An earlier install is still running, or antivirus is scanning the download&lt;/td&gt;
&lt;td&gt;Close other installer windows, delete &lt;code&gt;%USERPROFILE%\.claude\downloads&lt;/code&gt;, run the installer again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Could not create SSL/TLS secure channel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Usually an older Windows 10&lt;/td&gt;
&lt;td&gt;Run the TLS line below, then the installer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typing &lt;code&gt;claude&lt;/code&gt; opens the desktop app&lt;/td&gt;
&lt;td&gt;An older Claude Desktop registered its own &lt;code&gt;Claude.exe&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Update Claude Desktop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;App unavailable in region&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Claude Code isn't offered in your country&lt;/td&gt;
&lt;td&gt;No workaround&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The TLS fix for older Windows 10:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Net.ServicePointManager&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;SecurityProtocol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Net.SecurityProtocolType&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Tls12&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;irm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://claude.ai/install.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How do I start Claude Code for the first time?
&lt;/h2&gt;

&lt;p&gt;Claude Code works inside a folder, so make one for your first project, move into it, and start Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Documents\my-first-project&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Documents\my-first-project&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;claude&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time, it asks you to pick a text style, then how to sign in: pick your Claude subscription. Your browser opens, you sign in with the account that has your plan and approve, and the terminal shows "Login successful" and asks you to press Enter. If the browser doesn't open, press &lt;code&gt;c&lt;/code&gt; to copy the login link. If it shows a code instead of sending you back, paste it at "Paste code here if prompted."&lt;/p&gt;

&lt;p&gt;Next it asks whether you trust this folder. The highlighted answer was "No, exit", so I pressed the down arrow, then Enter. Say yes only for folders you made. Avoid starting it in your user folder itself: the &lt;a href="https://code.claude.com/docs/en/security" rel="noopener noreferrer"&gt;security docs&lt;/a&gt; say trust given there isn't saved, so the question comes back every launch.&lt;/p&gt;

&lt;p&gt;Then you're in. You type in the box at the bottom, and the welcome screen shows the version, the model and your folder. The line under the box says auto mode is on, the starting mode for terminal sessions since Claude Code 2.1.283. In &lt;a href="https://code.claude.com/docs/en/permission-modes" rel="noopener noreferrer"&gt;auto mode&lt;/a&gt;, reads and file edits inside your folder just happen. Everything else goes to a second AI model, the classifier, which blocks risky actions such as downloading and running code or permanently destroying files that existed before the session. After 3 blocks in a row, or 20 in a session, it pauses and Claude Code asks you instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Claude Code with no coding: two tasks I ran
&lt;/h2&gt;

&lt;p&gt;I recorded on Claude Code 2.1.310. For the first task I put 16 files in the project folder, photos, PDFs, spreadsheets and a voice memo all mixed together, and typed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sort these files into folders by type, and don't delete anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It looked at the files, made the folders and moved each file. About two minutes later it was done: six folders, with all 16 files still there. It also offered to put the screenshots in a folder of their own.&lt;/p&gt;

&lt;p&gt;For the second task I asked for a website:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make a simple one-page website for a bakery called Sunrise Bakery, with a menu, prices, opening hours and a contact section. Open it in my browser when it's done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It took a couple of minutes to write the page, then opened it in Edge: a menu with prices, the opening hours and a contact section. The address and phone number were placeholders, so you'd swap in real ones. Changes are one more sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make the colors warmer and add a photo gallery.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;About two minutes later it opened the new version, in oranges and creams, with a gallery that has empty spaces ready for your own photos. Sorting the files and building the site raised no permission prompts in auto mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to get better results from Claude Code
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Describe the result you want, not the steps, and add your rules, like "don't delete anything." Both tasks above were one sentence of this kind.&lt;/li&gt;
&lt;li&gt;Use plan mode for bigger jobs. &lt;code&gt;Shift+Tab&lt;/code&gt; cycles the modes, from auto to manual, accept edits, then plan, so press it three times until the line under the box says plan mode, or type &lt;code&gt;/plan&lt;/code&gt; before one request. Claude then reads and proposes but doesn't edit. When I asked it to "Add an online order form to the website," it asked clarifying questions with suggested answers first, then showed the plan and waited for my okay. The &lt;a href="https://code.claude.com/docs/en/permission-modes#review-and-approve-a-plan" rel="noopener noreferrer"&gt;docs&lt;/a&gt; list the choices as "Yes, and use auto mode", "Yes, manually approve edits" and "No, keep planning."&lt;/li&gt;
&lt;li&gt;Give it a memory. &lt;code&gt;/init&lt;/code&gt; writes a &lt;code&gt;CLAUDE.md&lt;/code&gt; file with notes about your project, which Claude reads at the start of every session. Add your own rules there, and keep it under 200 lines, as the &lt;a href="https://code.claude.com/docs/en/memory" rel="noopener noreferrer"&gt;memory docs&lt;/a&gt; advise.&lt;/li&gt;
&lt;li&gt;Rewind when it goes wrong. Press &lt;code&gt;Esc&lt;/code&gt; twice with an empty input box, or type &lt;code&gt;/rewind&lt;/code&gt;, to restore your files, the conversation, or both to an earlier point. &lt;a href="https://code.claude.com/docs/en/checkpointing" rel="noopener noreferrer"&gt;Rewind&lt;/a&gt; only tracks changes made with Claude's own file-editing tool, so files moved or deleted by commands may not come back. Keep backups.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;/clear&lt;/code&gt; when you switch tasks. It starts a fresh conversation, and the &lt;a href="https://code.claude.com/docs/en/costs" rel="noopener noreferrer"&gt;cost docs&lt;/a&gt; note that stale context "wastes tokens on every subsequent message."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Also useful: &lt;code&gt;Esc&lt;/code&gt; once stops Claude mid-task, &lt;code&gt;Shift+Enter&lt;/code&gt; adds a new line in Windows Terminal, &lt;code&gt;Alt+V&lt;/code&gt; pastes an image, and &lt;code&gt;@&lt;/code&gt; picks a file by name. A procedure you repeat can become a &lt;a href="https://khadim.tech/blog/claude-code-skills" rel="noopener noreferrer"&gt;Claude Code skill&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Claude Code safe to use on Windows?
&lt;/h2&gt;

&lt;p&gt;It's safe enough to learn on if you start it in a folder you made and read what it does. Auto mode approves file edits in your folder without asking, and Anthropic's own warning is that it "reduces permission prompts but does not guarantee safety." To approve every step yourself, press &lt;code&gt;Shift+Tab&lt;/code&gt; once from auto to reach manual mode: Claude Code then asks before it edits files or runs commands, apart from read-only ones such as &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;git status&lt;/code&gt;. The security docs put it plainly: "You're responsible for reviewing proposed code and commands for safety before approval."&lt;/p&gt;

&lt;p&gt;Native Windows also lacks Claude Code's sandbox, which only WSL 2 supports, so never start it in your whole user folder, and never paste passwords or API keys into it.&lt;/p&gt;

&lt;p&gt;If you want Claude Code set up for your team's real work, that's part of my &lt;a href="https://khadim.tech/services/ai-agent-consulting" rel="noopener noreferrer"&gt;AI agent consulting&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is Claude Code free?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. The free Claude plan doesn't include Claude Code. The cheapest plan that does is Pro, at $20 a month or $17 a month billed yearly, and its usage limits are shared with the Claude app on web, desktop and mobile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need to know how to code to use Claude Code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. You type what you want in plain English. In my beginner video it sorted a folder of 16 files and built a one-page bakery website, and I didn't write any code. You still need to read what it does and check the result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need admin rights to install Claude Code on Windows?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Anthropic's setup guide says you don't need to run the installer as Administrator. It puts claude.exe in %USERPROFILE%.local\bin, inside your own user folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need WSL or Git to use Claude Code on Windows?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Neither. Claude Code runs natively on Windows 10 version 1809 or later. Git for Windows is optional: it adds Git Bash, and without it Claude Code runs commands through PowerShell. WSL 2 matters only if you want Claude Code's sandbox, which native Windows doesn't support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I use Claude Code without the terminal?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The Claude desktop app for Windows has a Code tab that runs Claude Code with a graphical interface, and it needs the same paid plan. Anthropic's help center now lists it as the recommended way for Pro and Max subscribers to open Claude Code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I update or uninstall Claude Code on Windows?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The PowerShell install updates itself in the background, and claude update applies an update right away. A WinGet install doesn't update itself, so run winget upgrade Anthropic.ClaudeCode. To remove the PowerShell install, delete %USERPROFILE%.local\bin\claude.exe and the %USERPROFILE%.local\share\claude folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is Claude Code using API credits instead of my Pro plan?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An ANTHROPIC_API_KEY environment variable is set on your PC and you approved it when Claude Code asked. Claude Code then bills that key's API account instead of your subscription. Turn the key off with the Use custom API key toggle in /config, or remove the variable, and check /status to see which login is active.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: written with AI assistance, from my own recording and notes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>windows</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Claude Code Skills: How They Work and When They Trigger</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Sun, 25 Oct 2026 20:51:46 +0000</pubDate>
      <link>https://dev.to/khadimhussain/claude-code-skills-how-they-work-and-when-they-trigger-26dk</link>
      <guid>https://dev.to/khadimhussain/claude-code-skills-how-they-work-and-when-they-trigger-26dk</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/claude-code-skills" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; A Claude Code skill is a folder with a SKILL.md file: a short YAML header whose name and description tell Claude when to use it, then instructions that load only when the skill runs. Claude picks a skill up by itself when your request matches, or you run it as /skill-name. In my 108-run test on Claude Code 2.1.310, a skill loaded on 15 of 15 matching requests when either its name or its description said what it does, on 5 of 15 when neither did, and never with disable-model-invocation: true, in which case Claude did the job without the skill's rules.&lt;/p&gt;

&lt;p&gt;I run Claude Code with 71 skills in my own agent repo, and I make short Claude Code tutorials on my YouTube channel, &lt;a href="https://www.youtube.com/@khadimlabs" rel="noopener noreferrer"&gt;Khadim Labs&lt;/a&gt;. For the skills episode I had Claude Code write a skill from one sentence, and it made a decision I hadn't asked for. Then I measured when Claude actually loads a skill by itself, because every guide says "write a good description" and none of them shows what that changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Claude Code skill?
&lt;/h2&gt;

&lt;p&gt;A skill is a folder with a &lt;code&gt;SKILL.md&lt;/code&gt; file that teaches Claude Code a procedure. The file has two parts: YAML frontmatter between &lt;code&gt;---&lt;/code&gt; lines, then Markdown instructions. Anthropic's &lt;a href="https://code.claude.com/docs/en/skills" rel="noopener noreferrer"&gt;skills documentation&lt;/a&gt; puts the frontmatter rule plainly: "All fields are optional. Only &lt;code&gt;description&lt;/code&gt; is recommended so Claude knows when to use the skill." The &lt;code&gt;name&lt;/code&gt; defaults to the folder's name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tidy-folder&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sort the loose files in the current folder into subfolders by type. Use when the user asks to tidy, clean up, organize or sort a folder.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# tidy-folder&lt;/span&gt;

Only move files. Never delete or overwrite anything. Finish with a table of what moved.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Skills are cheap because of when they load. Claude Code keeps only the names and descriptions in context, and the "full skill content only loads when invoked." A 400-line procedure costs almost nothing until a request needs it, while &lt;code&gt;CLAUDE.md&lt;/code&gt; loads in full every session. Skills follow the &lt;a href="https://agentskills.io" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt; open standard, and Claude Code adds its own fields on top, such as invocation control and running a skill in a subagent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where do Claude Code skills go?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Who gets it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Personal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude/skills/&amp;lt;name&amp;gt;/SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You, in every project on this machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.claude/skills/&amp;lt;name&amp;gt;/SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Everyone who clones the repo, once you commit it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plugin&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;plugin&amp;gt;/skills/&amp;lt;name&amp;gt;/SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wherever the plugin is enabled, as &lt;code&gt;/plugin-name:skill-name&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;the managed settings directory&lt;/td&gt;
&lt;td&gt;Every machine your organisation deploys it to&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When two skills share a name, enterprise beats personal and personal beats project. Old &lt;code&gt;.claude/commands/deploy.md&lt;/code&gt; files keep working, since "custom commands have been merged into skills", and a skill wins over a command file with the same name. &lt;code&gt;/skills&lt;/code&gt; lists what Claude Code found. It also watches these folders, so a new or edited &lt;code&gt;SKILL.md&lt;/code&gt; is live in the running session without a restart.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to create a skill: I asked Claude Code to write one
&lt;/h2&gt;

&lt;p&gt;You can write &lt;code&gt;SKILL.md&lt;/code&gt; by hand, but the fastest way is to describe what you want. In a clean Windows VM running Claude Code 2.1.310, I typed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a personal skill called tidy-folder. It sorts the files in the current folder into subfolders by type, puts screenshots in their own Screenshots folder, never deletes anything, and ends with a short table of what moved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fifty seconds later it had written &lt;code&gt;~/.claude/skills/tidy-folder/SKILL.md&lt;/code&gt;, 2,936 characters long. This is its frontmatter, unedited:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tidy-folder&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sort the loose files in the current folder into subfolders by type (Images, Documents, Spreadsheets, Audio, Video, Archives, Code, Other), put screenshots in a Screenshots folder, never delete anything, and finish with a table of what moved. Use when the user asks to tidy, clean up, organize or sort a folder.&lt;/span&gt;
&lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The body added rules I hadn't spelled out. It works only on files directly in the folder and skips hidden and system files. On a name clash it keeps both files by adding (1), (2) and so on, and it moves files with PowerShell's &lt;code&gt;Move-Item&lt;/code&gt; without &lt;code&gt;-Force&lt;/code&gt;. If the folder looks like a project (a &lt;code&gt;.git&lt;/code&gt; folder, a solution file), it stops and asks first, because moving files could break it.&lt;/p&gt;

&lt;p&gt;I never asked for the last line of the frontmatter. &lt;code&gt;disable-model-invocation: true&lt;/code&gt; makes a skill manual-only: Claude won't load it by itself, and it runs only when you type &lt;code&gt;/tidy-folder&lt;/code&gt;. Claude set it because the skill moves files. The docs describe the field the same way, for "workflows you want to trigger manually", and their own example is a skill that makes git commits.&lt;/p&gt;

&lt;p&gt;Run by hand on a folder of 16 messy files, it reported "16 moved, 0 skipped, 0 deleted". Then I asked: "Change my tidy-folder skill so you can use it on your own whenever I ask you to tidy a folder." It deleted that one line. In a fresh folder of 16 files, a plain "Tidy up this folder." loaded the skill without any slash command, followed the same rules, and again moved 16, skipped 0 and deleted 0.&lt;/p&gt;

&lt;h2&gt;
  
  
  When does Claude use a skill by itself? I measured it
&lt;/h2&gt;

&lt;p&gt;I gave Claude Code the same skill body under six frontmatters. Two things varied: whether the name says what the skill does (&lt;code&gt;tidy-folder&lt;/code&gt; or &lt;code&gt;helper&lt;/code&gt;), and whether the description does (the long one Claude wrote above, &lt;code&gt;Folder utility.&lt;/code&gt; or &lt;code&gt;General helper.&lt;/code&gt;). The sixth version is the specific one with &lt;code&gt;disable-model-invocation: true&lt;/code&gt; added.&lt;/p&gt;

&lt;p&gt;Each version got six requests: five that want the folder tidied, in different words, and one that only asks what's in it. I ran the set three times, 108 runs in all, on 26 October 2026. Every run started in a fresh folder with six dummy files and the skill in &lt;code&gt;.claude/skills/&lt;/code&gt;, headless with &lt;code&gt;claude -p&lt;/code&gt; on Claude Code 2.1.310 and Opus 5.5 (&lt;code&gt;claude-opus-5-5&lt;/code&gt;). The skill listing also held the nine skills synced to my claude.ai account (PDF, Word, Excel and others), none of them about folders. A run counts as a trigger when Claude called its Skill tool. File commands were blocked, so nothing actually moved.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Tidy requests that loaded the skill&lt;/th&gt;
&lt;th&gt;"What files are in this folder?"&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tidy-folder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the specific one Claude wrote&lt;/td&gt;
&lt;td&gt;15 of 15&lt;/td&gt;
&lt;td&gt;0 of 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tidy-folder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Folder utility.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;15 of 15&lt;/td&gt;
&lt;td&gt;0 of 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;helper&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the specific one Claude wrote&lt;/td&gt;
&lt;td&gt;15 of 15&lt;/td&gt;
&lt;td&gt;0 of 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;helper&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Folder utility.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;13 of 15&lt;/td&gt;
&lt;td&gt;0 of 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;helper&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;General helper.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5 of 15&lt;/td&gt;
&lt;td&gt;0 of 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tidy-folder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;specific, plus &lt;code&gt;disable-model-invocation: true&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0 of 15&lt;/td&gt;
&lt;td&gt;0 of 3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Claude reads the name as well as the description, and one clear signal was enough. A two-word description under a telling name triggered every time, and so did a meaningless name with a telling description. With neither, it fell to 5 of 15. The five tidy wordings made no real difference, and the request that only asked what was in the folder never loaded the skill, in any version.&lt;/p&gt;

&lt;p&gt;The manual-only row needs a second look. Claude never loaded that skill, but it didn't refuse the job either: in all 15 runs it went straight to PowerShell or Bash to sort the files itself, without the skill's rules. If those rules matter (mine say never delete anything), type &lt;code&gt;/tidy-folder&lt;/code&gt;, or let Claude load the skill on its own.&lt;/p&gt;

&lt;p&gt;This test pits one skill against about a dozen others on one model. With many more skills, Claude Code starts dropping descriptions from the listing while keeping every name (next section), which is one more reason to name a skill after what it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why isn't my Claude Code skill triggering?
&lt;/h2&gt;

&lt;p&gt;Check these in order. I've hit the first two myself.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Neither the name nor the description says what the skill does. Use the words a person would type ("tidy, clean up, organize or sort a folder"), and put that use case first, because each description is cut at 1,536 characters in the listing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;disable-model-invocation: true&lt;/code&gt; is set. It can be there without you writing it, as my tidy-folder skill shows, so read the frontmatter before rewriting the description.&lt;/li&gt;
&lt;li&gt;The frontmatter didn't parse. Claude Code reads frontmatter only when the opening &lt;code&gt;---&lt;/code&gt; is the file's first line. If the YAML between the markers is malformed, the skill loads with no fields, so &lt;code&gt;/skill-name&lt;/code&gt; still works but Claude has no description to match. Field names must match exactly, hyphens included, and Claude Code ignores an unknown field without an error. &lt;code&gt;claude --debug&lt;/code&gt; shows the parse error.&lt;/li&gt;
&lt;li&gt;You have too many skills. The listing of names and descriptions gets about 1% of the model's context window, and when it overflows, Claude Code drops the descriptions of the skills you use least. &lt;code&gt;/doctor&lt;/code&gt; estimates the listing's cost, and the &lt;code&gt;skillListingBudgetFraction&lt;/code&gt; setting raises the budget.&lt;/li&gt;
&lt;li&gt;The skill sets &lt;code&gt;paths&lt;/code&gt;. It then activates only for matching files inside the current working directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The quickest check is to ask Claude "What skills are available?" If the skill isn't in the answer, Claude can't choose it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code skills vs CLAUDE.md, slash commands, subagents and hooks
&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;When it loads&lt;/th&gt;
&lt;th&gt;Use it for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;In full, every session&lt;/td&gt;
&lt;td&gt;Facts and rules that always apply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skill&lt;/td&gt;
&lt;td&gt;Name and description always, body only when used&lt;/td&gt;
&lt;td&gt;Procedures, checklists, reference material&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slash command (&lt;code&gt;.claude/commands/&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Same as a skill; the two were merged&lt;/td&gt;
&lt;td&gt;Existing commands; prefer a skill for new work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subagent&lt;/td&gt;
&lt;td&gt;In its own context window&lt;/td&gt;
&lt;td&gt;Work that shouldn't fill your conversation; a skill with &lt;code&gt;context: fork&lt;/code&gt; hands its instructions to one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hook&lt;/td&gt;
&lt;td&gt;Runs on every matching event&lt;/td&gt;
&lt;td&gt;Rules that must hold every time, with no judgement involved&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The docs give a simple test for moving something out of &lt;code&gt;CLAUDE.md&lt;/code&gt;: "Create a skill when you keep pasting the same instructions, checklist, or multi-step procedure into chat, or when a section of CLAUDE.md has grown into a procedure rather than a fact." When Claude skips a rule that must hold every time, they recommend moving that rule into a &lt;a href="https://code.claude.com/docs/en/hooks-guide" rel="noopener noreferrer"&gt;hook&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What 71 skills in one repo taught me
&lt;/h2&gt;

&lt;p&gt;My own agent repo holds 71 skills, covering how I review merge requests, publish this site and run the YouTube channel, among other jobs. Two lessons cost me real time.&lt;/p&gt;

&lt;p&gt;Don't name a skill after a built-in command. I had a CV skill called &lt;code&gt;resume&lt;/code&gt;, and Claude Code's built-in &lt;code&gt;/resume&lt;/code&gt;, which reopens an earlier session, stopped working. That's documented behaviour: "In a local terminal session, your skill replaces the built-in command, but not its aliases." I renamed mine &lt;code&gt;resume-cv&lt;/code&gt;, and the script that indexes my skills now refuses built-in names.&lt;/p&gt;

&lt;p&gt;Keep one copy of each skill and point to it. My skills live in a plain &lt;code&gt;skills/&lt;/code&gt; folder that Claude Code doesn't scan. A script writes a one-line pointer for each into &lt;code&gt;.claude/commands/&amp;lt;name&amp;gt;.md&lt;/code&gt; ("read &lt;code&gt;skills/&amp;lt;name&amp;gt;/SKILL.md&lt;/code&gt; and follow it"), so &lt;code&gt;/&amp;lt;name&amp;gt;&lt;/code&gt; works while the instructions stay in one place, and the same script fails the build when a pointer is missing or stale. When I first generated them, all 55 pointers worked in the already-running session, with no restart.&lt;/p&gt;

&lt;p&gt;The skills episode, with the tidy-folder runs, is on &lt;a href="https://www.youtube.com/@khadimlabs" rel="noopener noreferrer"&gt;my YouTube channel&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does a vague description stop Claude from using a skill?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not if the name is clear. In my test a skill called tidy-folder with the description "Folder utility." loaded on 15 of 15 tidy requests. A skill called helper with "General helper." loaded on only 5 of 15, so give Claude at least one clear signal, and preferably both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do Claude Code skills use up context?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A little. Claude Code keeps every skill's name and description in context and loads the full SKILL.md only when the skill runs. Each description is cut at 1,536 characters, and the whole listing gets about 1% of the model's context window; past that, the least-used skills lose their descriptions but keep their names.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need to restart Claude Code after adding a skill?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Claude Code watches the skill folders and picks up a new or edited SKILL.md in the running session. The exception is a top-level skills folder that didn't exist when the session started: run /reload-skills for that one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between a skill and a slash command?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Very little now. Custom commands were merged into skills, so .claude/commands/deploy.md and .claude/skills/deploy/SKILL.md both create /deploy. A skill adds a folder for supporting files and frontmatter that controls who can invoke it, and it wins if both exist with the same name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop Claude from using a skill on its own?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add disable-model-invocation: true to the frontmatter, and Claude Code runs the skill only when you type /skill-name. Claude still does the task if you ask for it in plain words: in my test it never loaded such a skill and sorted the files itself every time, without the skill's rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can a skill run a script?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Put the script in the skill's folder and tell Claude in SKILL.md to run it, with ${CLAUDE_SKILL_DIR} in the path so it resolves wherever the skill is installed. Claude Code runs the script rather than loading its code into the conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do Claude Code skills work in other AI tools?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The format is the Agent Skills open standard, so the folder works in other tools that support it. Outside Claude Code only the standard's fields count: name, description, license, compatibility, metadata and allowed-tools. Claude Code's extras, such as disable-model-invocation, don't carry over.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: written with AI assistance, from my own measurements and notes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>TensorRT INT8 vs FP16 on RF-DETR, Measured on an RTX 5090</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Fri, 23 Oct 2026 21:01:04 +0000</pubDate>
      <link>https://dev.to/khadimhussain/tensorrt-int8-vs-fp16-on-rf-detr-measured-on-an-rtx-5090-3med</link>
      <guid>https://dev.to/khadimhussain/tensorrt-int8-vs-fp16-on-rf-detr-measured-on-an-rtx-5090-3med</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/tensorrt-int8-vs-fp16-rf-detr" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; On an RTX 5090 with TensorRT 10.12, a calibrated INT8 build of RF-DETR put zero layers in INT8. TensorRT's own timing chose FP16 for every layer, so the INT8 engine was the FP16 engine: about 5x faster than the PyTorch FP32 forward, with no extra gain. Forcing INT8 through Q/DQ nodes made it 17% slower and wrecked accuracy. On a CPU without VNNI, only PyTorch dynamic INT8 sped the forward up (1.3-2x), and it changed some detection decisions. Distilling RF-DETR Medium into Nano with pseudo-labels lowered accuracy on every seed. For RF-DETR on an RTX 5090, TensorRT FP16 was the answer, and NVIDIA's own release notes point the same way for DETR models.&lt;/p&gt;

&lt;p&gt;I maintain a training and serving engine for RF-DETR defect-detection models. When a model has to meet a cycle time on a given device, the first question is whether it can run faster, so I added a job that builds every faster variant of a trained model (TensorRT FP16 and INT8, CPU INT8 in two runtimes, and a distilled smaller student) and measures each one against the original on the same hardware and the same test images.&lt;/p&gt;

&lt;p&gt;Most of the variants that are supposed to be faster were not. These are the measurements, the reasons, and what I recommend instead. The models and their training data stay private; the setup, the method and the ratios below carry over to any RF-DETR model.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the INT8, FP16 and distillation variants were measured
&lt;/h2&gt;

&lt;p&gt;Measured on 12 October 2026:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;NVIDIA RTX 5090&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;12-vCPU QEMU virtual CPU, AVX2 only (no VNNI, AVX-512 or AMX)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Software&lt;/td&gt;
&lt;td&gt;rfdetr 1.11.2, PyTorch 2.9.0 with CUDA 12.8, TensorRT 10.12, ONNX Runtime 1.23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Models&lt;/td&gt;
&lt;td&gt;RF-DETR Nano detect at 384 px, RF-DETR Seg Nano at 312 px, RF-DETR Medium detect at 576 px, each fine-tuned on an industrial defect-detection set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timing&lt;/td&gt;
&lt;td&gt;median of 100 timed calls after 10 warm-up calls, at batch 1 and batch 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accuracy&lt;/td&gt;
&lt;td&gt;mAP50:95 change against the FP32 original on a 10-image test set, plus decisions at a 0.5 threshold that changed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each variant replaces only the network forward. rfdetr calls &lt;code&gt;model.model.inference_model&lt;/code&gt; when a model is marked as optimized, so preprocessing, the decoder and the postprocessing are the same code for every variant, and any difference comes from the forward alone. The Nano measurements were repeated three times: forward medians stayed within about 6% on the GPU and 10% on the CPU, and accuracy was identical every time.&lt;/p&gt;

&lt;p&gt;The test set is small, so read the accuracy changes as a direction, not a precise figure. The speed numbers are solid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is TensorRT INT8 not faster than FP16 for RF-DETR?
&lt;/h2&gt;

&lt;p&gt;Because TensorRT didn't use INT8. I built the INT8 engine the standard way: INT8 flag, an entropy calibrator over 64 training images, FP16 allowed as the fallback. Then I counted the precision of every layer in the finished engine:&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;Layers in INT8&lt;/th&gt;
&lt;th&gt;TensorRT FP16 vs PyTorch FP32 (batch 1)&lt;/th&gt;
&lt;th&gt;TensorRT INT8 vs PyTorch FP32 (batch 1)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nano detect @384&lt;/td&gt;
&lt;td&gt;0 of 193&lt;/td&gt;
&lt;td&gt;5.3x faster&lt;/td&gt;
&lt;td&gt;5.3x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seg Nano @312&lt;/td&gt;
&lt;td&gt;0 of 259&lt;/td&gt;
&lt;td&gt;5.7x faster&lt;/td&gt;
&lt;td&gt;5.7x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium detect @576&lt;/td&gt;
&lt;td&gt;0 of 225&lt;/td&gt;
&lt;td&gt;4.8x faster&lt;/td&gt;
&lt;td&gt;4.8x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With INT8 allowed, TensorRT times the candidate kernels for each layer and keeps the fastest. It kept FP16 every time, so the "INT8" engine was the FP16 engine with a different name. FP16 cost almost nothing in accuracy: mAP50:95 moved by 0.002 or less, and one decision changed on the segmentation model.&lt;/p&gt;

&lt;p&gt;This is a known pattern for DETR models, not an RF-DETR quirk. NVIDIA's &lt;a href="https://docs.nvidia.com/deeplearning/tensorrt/10.x.x/getting-started/release-notes-10/10.2.0.html" rel="noopener noreferrer"&gt;TensorRT 10.2 release notes&lt;/a&gt; list it as a known issue: "For some Transformer models, including ViT, Swin-Transformer, and DETR, there is a performance drop in INT8 precision (including both explicit and implicit quantization) compared to FP16 precision."&lt;/p&gt;

&lt;p&gt;Forcing the issue didn't help. With explicit Q/DQ nodes in the ONNX graph, TensorRT put 166 layers in INT8. That engine was 17% slower than FP16, and mAP50:95 dropped by more than three quarters. I left it out of the job.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The calibrator route is going away&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same release notes deprecate implicit INT8 quantization and the calibrator APIs, &lt;code&gt;IInt8EntropyCalibrator2&lt;/code&gt; included, and NVIDIA's &lt;a href="https://docs.nvidia.com/deeplearning/tensorrt/latest/api/migration/tensorrt-10x-to-11x.html" rel="noopener noreferrer"&gt;10.x-to-11.x migration guide&lt;/a&gt; says implicit quantization is not supported in TensorRT 11. On TensorRT 11, INT8 means explicit quantization with Q/DQ nodes, the route that was slower here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Check the precision TensorRT actually chose
&lt;/h3&gt;

&lt;p&gt;An engine that builds with the INT8 flag is not proof that anything runs in INT8. Build with detailed profiling verbosity, then ask the engine inspector:&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;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tensorrt&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;trt&lt;/span&gt;

&lt;span class="c1"&gt;# at build time: config.profiling_verbosity = trt.ProfilingVerbosity.DETAILED
&lt;/span&gt;&lt;span class="n"&gt;inspector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_engine_inspector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inspector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_engine_information&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LayerInformationFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&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;layer&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Layers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Outputs&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Format/Datatype&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without detailed verbosity the inspector returns layer names only, which looks like an empty result rather than an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does the network forward even matter on the GPU?
&lt;/h2&gt;

&lt;p&gt;Barely, in this pipeline. The test frames are 16 megapixels, and preprocessing them for the model took most of each request: the TensorRT forward was between 1% and 5% of a request's time on the GPU host, decoding not even counted. Shrinking the network further would change nothing a user of the line notices. Profile the whole request before optimizing the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does INT8 speed up RF-DETR on a CPU?
&lt;/h2&gt;

&lt;p&gt;One method did, with a cost. I tried two:&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;PyTorch dynamic INT8 vs FP32 forward&lt;/th&gt;
&lt;th&gt;ONNX Runtime static INT8 vs PyTorch FP32 forward&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nano detect @384&lt;/td&gt;
&lt;td&gt;1.3x faster (1.5x at batch 4), 1 decision changed&lt;/td&gt;
&lt;td&gt;19% slower, 2 decisions changed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seg Nano @312&lt;/td&gt;
&lt;td&gt;1.6x faster (2.0x at batch 4), 4 decisions changed&lt;/td&gt;
&lt;td&gt;9% slower, mAP50:95 −0.131, 32 decisions changed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium detect @576&lt;/td&gt;
&lt;td&gt;1.4x faster (1.5x at batch 4), 3 decisions changed&lt;/td&gt;
&lt;td&gt;36% slower, 3 decisions changed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PyTorch dynamic INT8 (&lt;code&gt;torch.ao.quantization.quantize_dynamic&lt;/code&gt; on every &lt;code&gt;nn.Linear&lt;/code&gt;) is a load-time transform with nothing extra to ship. It sped up the network forward by 1.3 to 2.0 times and the whole request by 1.2 to 1.3 times. Accuracy moved only slightly, between −0.014 and +0.009 mAP50:95, but 1 to 4 detections per model crossed the 0.5 threshold one way or the other on 10 images. In inspection, a changed decision is a missed defect or a false reject, so that matters more than the mAP change.&lt;/p&gt;

&lt;p&gt;ONNX Runtime static INT8 (per-channel weights, MinMax calibration) was slower than plain PyTorch on this CPU, and its FP32 path was no faster than PyTorch either. It broke the segmentation model whatever the calibration. Percentile calibration helped the detection model (−0.011 instead of −0.026 mAP50:95 on the same images) but needed 30 GB of memory for the Medium model, because ONNX Runtime's histogram calibrators keep every activation of every calibration image in memory.&lt;/p&gt;

&lt;p&gt;This CPU has no VNNI, AVX-512 or AMX. INT8 kernels depend on those instructions, so a server CPU that has them could give a different answer; I haven't measured one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does distilling RF-DETR Medium into Nano help?
&lt;/h2&gt;

&lt;p&gt;Not with clean labels. The distillation was the simple, hard-label kind. The Medium model predicted on the training images. Every detection at 0.5 confidence or higher that no existing label of the same class already covered (IoU 0.5 or more) became an extra training box. Then two Nano students trained with the same configuration and the same three seeds: one on the labels alone, one on the labels plus the teacher's boxes. Each ran 20 epochs, with no early stopping and no augmentation, and both were scored on the untouched test set.&lt;/p&gt;

&lt;p&gt;The student trained with pseudo-labels scored lower on every seed, by 0.025 mAP50:95 on average, and varied more between seeds (standard deviation 0.022 against 0.010). The reason is in what the teacher added. On a fully labelled training set it mostly re-found the existing labels, and it added two boxes across the whole set, both false positives on its weakest class. This kind of distillation can only add value through objects the annotators missed. It is worth trying on incompletely labelled data, and it has nothing to offer on clean labels.&lt;/p&gt;

&lt;p&gt;The student was faster: 2.5 times on the CPU and 1.3 times on the GPU. But the Nano and Medium variants have 29.5M and 32.7M parameters, so the speed came from the input resolution (384 px against 576 px), not from a smaller network. If speed is what you need, retrain at a lower resolution and measure that first.&lt;/p&gt;

&lt;p&gt;Feature- and logit-based distillation, which train the student on the teacher's internal outputs, weren't part of this test.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to compare model compression methods fairly
&lt;/h2&gt;

&lt;p&gt;Most of what I learned is about measuring, and it applies to any detector:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Swap only the forward. Run the same preprocessing, decoder and postprocessing for every variant, or you measure your pipeline changes rather than the compression.&lt;/li&gt;
&lt;li&gt;Report medians. On a shared or virtual host, one stalled call in a hundred can triple a mean.&lt;/li&gt;
&lt;li&gt;Record the host load. One run next to a training job measured a CPU forward more than 50 times slower than its real speed.&lt;/li&gt;
&lt;li&gt;Calibrate on training images, never on the test set. Calibrating on the images you score flatters INT8.&lt;/li&gt;
&lt;li&gt;Count changed decisions at your operating threshold. A small mAP change can hide detections that flip between found and missed.&lt;/li&gt;
&lt;li&gt;Repeat anything that involves training, with three or more paired seeds, before reading a difference.&lt;/li&gt;
&lt;li&gt;Check which precision actually ran, with the inspector above.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What should you use for RF-DETR?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NVIDIA GPU (measured on an RTX 5090)&lt;/td&gt;
&lt;td&gt;TensorRT FP16&lt;/td&gt;
&lt;td&gt;About 5x the PyTorch FP32 forward, accuracy unchanged; INT8 gave no further gain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU without VNNI, AVX-512 or AMX&lt;/td&gt;
&lt;td&gt;PyTorch FP32, or dynamic INT8 if your decisions survive it&lt;/td&gt;
&lt;td&gt;1.3-2x faster forward, but check the flips at your threshold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Any, and still too slow&lt;/td&gt;
&lt;td&gt;A lower input resolution, retrained&lt;/td&gt;
&lt;td&gt;Where the distilled student's speed actually came from&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incomplete labels&lt;/td&gt;
&lt;td&gt;Pseudo-label distillation, measured with seeds&lt;/td&gt;
&lt;td&gt;The only case where the teacher adds real information&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This covers one GPU, one CPU class and three RF-DETR variants. Jetson devices, CPUs with VNNI or AMX, and quantization-aware training for TensorRT 11's explicit INT8 are open questions here. They are the next things I would measure for a customer who needs one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why is TensorRT INT8 not faster than FP16?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With a calibrator and the INT8 flag, TensorRT times INT8 and FP16 kernels per layer and keeps the faster one. For RF-DETR on an RTX 5090 it chose FP16 for every layer, so the INT8 engine was the FP16 engine. NVIDIA's TensorRT 10.2 release notes list INT8 as slower than FP16 for some Transformer models, DETR among them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can RF-DETR run in INT8 with TensorRT?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can force it with Q/DQ nodes in the ONNX graph. In my test that put 166 layers in INT8, ran 17% slower than FP16 and cut mAP50:95 by more than three quarters. A calibrated build with FP16 allowed put no layer in INT8 at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the TensorRT INT8 calibrator deprecated?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. TensorRT 10.2 deprecated implicit INT8 quantization and the calibrator APIs, IInt8EntropyCalibrator2 included, and NVIDIA's 10.x-to-11.x migration guide says implicit quantization is not supported in TensorRT 11. INT8 there means explicit quantization with Q/DQ nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does PyTorch dynamic quantization speed up object detection on CPU?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For RF-DETR on an AVX2-only CPU it made the network forward 1.3 to 2.0 times faster and the whole request 1.2 to 1.3 times faster. It also flipped 1 to 4 detection decisions at a 0.5 threshold on a 10-image test set, so check decisions, not only mAP, before shipping it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does knowledge distillation make RF-DETR faster?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The distilled Nano student was 2.5 times faster than the Medium teacher on CPU and 1.3 times on GPU, but nearly all of that came from its lower input resolution: the two models have 29.5M and 32.7M parameters. Training it on the teacher's pseudo-labels lowered accuracy on every seed compared with labels alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I see which layers TensorRT ran in INT8?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Build the engine with detailed profiling verbosity, then read its engine inspector as JSON and look at each layer's output data type. Without detailed verbosity the inspector lists only layer names.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: written with AI assistance, from my own measurements and notes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tensorrt</category>
      <category>computervision</category>
      <category>machinelearning</category>
      <category>deeplearning</category>
    </item>
    <item>
      <title>Ollama LoRA Adapters No Longer Supported: What to Do</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Wed, 21 Oct 2026 14:19:22 +0000</pubDate>
      <link>https://dev.to/khadimhussain/ollama-lora-adapters-no-longer-supported-what-to-do-1m78</link>
      <guid>https://dev.to/khadimhussain/ollama-lora-adapters-no-longer-supported-what-to-do-1m78</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/ollama-lora-adapters" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; Since version 0.34.1 (September 2026), Ollama rejects every Modelfile with an ADAPTER line: "Error: LoRA adapters are no longer supported". The same release stopped converting safetensors to GGUF and stopped quantizing GGUF files. You have two ways to run a LoRA fine-tune now. One is to merge the adapter into the model and import a single GGUF into Ollama. The other is to keep the adapter separate and serve it with llama.cpp's llama-server --lora. I tested both with my Qwen3-14B adapter, on Ollama 0.40.7 and llama.cpp; on CPU the merged file generated about 13% faster.&lt;/p&gt;

&lt;p&gt;I publish my &lt;a href="https://huggingface.co/khadim-hussain/qwen3-14b-stem-qa" rel="noopener noreferrer"&gt;Qwen3-14B STEM fine-tune&lt;/a&gt; three ways: as a LoRA adapter, as a merged model and as GGUF files. For a long time, the adapter route in Ollama was two lines in a Modelfile, a &lt;code&gt;FROM&lt;/code&gt; for the base model and an &lt;code&gt;ADAPTER&lt;/code&gt; for the fine-tune. On a current Ollama, that second line makes &lt;code&gt;ollama create&lt;/code&gt; fail before it touches the model files. I tested what still works with the same adapter, end to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in Ollama 0.34.1?
&lt;/h2&gt;

&lt;p&gt;Ollama removed LoRA adapter support in &lt;a href="https://github.com/ollama/ollama/pull/14969" rel="noopener noreferrer"&gt;PR #14969&lt;/a&gt;, merged on 15 September 2026 and shipped in &lt;a href="https://github.com/ollama/ollama/releases/tag/v0.34.1" rel="noopener noreferrer"&gt;v0.34.1&lt;/a&gt;. The PR limits &lt;code&gt;ollama create&lt;/code&gt; to "wrapping existing GGUF inputs into Ollama manifests" and removes the built-in safetensors-to-GGUF converter and the server-side quantizer, "so GGUF conversion and quantization stay in llama.cpp tooling". Adapters went with the converter: the CLI and the server both reject a Modelfile that contains &lt;code&gt;ADAPTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The release notes give this one line: "GGUF model creation now requires using llama.cpp tooling for safetensor conversion and quantization." The word adapter isn't in them. The &lt;code&gt;ADAPTER&lt;/code&gt; section and the "Importing a fine tuned adapter" section have since been removed from the &lt;a href="https://github.com/ollama/ollama/blob/v0.34.0/docs/modelfile.mdx#adapter" rel="noopener noreferrer"&gt;Modelfile&lt;/a&gt; and &lt;a href="https://github.com/ollama/ollama/blob/v0.34.0/docs/import.mdx" rel="noopener noreferrer"&gt;import&lt;/a&gt; docs, so the old instructions now live on mostly in blog posts and tutorials. Nothing has changed since: &lt;a href="https://github.com/ollama/ollama/releases/tag/v0.40.7" rel="noopener noreferrer"&gt;v0.40.7&lt;/a&gt;, released 18 October 2026, rejects adapters the same way.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you do&lt;/th&gt;
&lt;th&gt;Ollama 0.34.0 and earlier&lt;/th&gt;
&lt;th&gt;Ollama 0.34.1 and later&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ADAPTER&lt;/code&gt; line in a Modelfile (GGUF or safetensors adapter)&lt;/td&gt;
&lt;td&gt;Applied on top of the &lt;code&gt;FROM&lt;/code&gt; model&lt;/td&gt;
&lt;td&gt;Rejected before anything is uploaded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;FROM&lt;/code&gt; a GGUF file&lt;/td&gt;
&lt;td&gt;Imported&lt;/td&gt;
&lt;td&gt;Imported (unchanged)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ollama create --quantize q4_K_M&lt;/code&gt; on an F16 or F32 GGUF&lt;/td&gt;
&lt;td&gt;Quantized by Ollama&lt;/td&gt;
&lt;td&gt;Rejected; quantize with llama.cpp first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;FROM&lt;/code&gt; a safetensors model directory&lt;/td&gt;
&lt;td&gt;Converted to GGUF&lt;/td&gt;
&lt;td&gt;Imported only through the MLX engine, which needs the MLX runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Models created with an adapter before the change&lt;/td&gt;
&lt;td&gt;Ran with the adapter&lt;/td&gt;
&lt;td&gt;The adapter layer is kept, with a deprecation warning in the server log&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last row comes from Ollama's own source: when it loads an older model that has an adapter layer, it logs "LoRA adapters are deprecated; the adapter layer is carried over but new adapters cannot be created" (&lt;a href="https://github.com/ollama/ollama/blob/v0.40.7/server/model.go" rel="noopener noreferrer"&gt;server/model.go&lt;/a&gt;). I didn't have such a model to test.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "LoRA adapters are no longer supported" mean?
&lt;/h2&gt;

&lt;p&gt;It means the Modelfile has an &lt;code&gt;ADAPTER&lt;/code&gt; line, and Ollama 0.34.1 or later refuses to build any model that has one. I ran the variants against Ollama 0.40.7 on Windows. With &lt;code&gt;ADAPTER&lt;/code&gt; pointing at the GGUF LoRA, or at the PEFT folder with &lt;code&gt;adapter_model.safetensors&lt;/code&gt;, &lt;code&gt;ollama create&lt;/code&gt; stops before it reads either file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ollama create stem-qa -f Modelfile
Error: LoRA adapters are no longer supported
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pointing &lt;code&gt;FROM&lt;/code&gt; at the GGUF LoRA itself gets one step further. Ollama copies the file, reads its header and refuses it there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: LoRA adapters are no longer supported: qwen3-14b-stem-qa-lora-f16.gguf is a LoRA adapter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Asking Ollama to quantize a GGUF fails too, because quantization moved to llama.cpp as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ollama create stem-qa --quantize q4_K_M -f Modelfile
Error: create-time quantization is only supported for safetensors imports; quantize GGUF models before importing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Getting the error for a vision projector?
&lt;/h3&gt;

&lt;p&gt;Some Modelfiles for vision models put the multimodal projector, the &lt;code&gt;mmproj&lt;/code&gt; GGUF, on the &lt;code&gt;ADAPTER&lt;/code&gt; line. Ollama rejects those now too, with the same message, even though a projector isn't a LoRA. The projector goes on a second &lt;code&gt;FROM&lt;/code&gt; line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ./SmolVLM-256M-Instruct-Q8_0.gguf&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ./mmproj-SmolVLM-256M-Instruct-Q8_0.gguf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tested this with &lt;a href="https://huggingface.co/ggml-org/SmolVLM-256M-Instruct-GGUF" rel="noopener noreferrer"&gt;SmolVLM-256M&lt;/a&gt; because it is small: with &lt;code&gt;ADAPTER&lt;/code&gt; the create failed, and with two &lt;code&gt;FROM&lt;/code&gt; lines it succeeded and &lt;code&gt;ollama show&lt;/code&gt; listed the projector and the vision capability.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I use a LoRA fine-tune in Ollama now?
&lt;/h2&gt;

&lt;p&gt;Merge the adapter into the base model, convert the result to GGUF, quantize it with llama.cpp, and point &lt;code&gt;FROM&lt;/code&gt; at that file. Ollama still imports any GGUF; it just won't build one for you anymore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merge the adapter, then convert and quantize
&lt;/h3&gt;

&lt;p&gt;If you trained with Unsloth, the model object does the merge and the GGUF export itself. These are the calls from my &lt;a href="https://khadim.tech/blog/fine-tune-gpt-oss-20b-unsloth-ollama" rel="noopener noreferrer"&gt;gpt-oss fine-tuning walkthrough&lt;/a&gt;, with this model's names:&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="c1"&gt;# merged 16-bit model, for Transformers or vLLM, and the input for a GGUF
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained_merged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3-14b-stem-qa-merged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;merged_16bit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# or merge, convert and quantize in one call
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained_gguf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3-14b-stem-qa-gguf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantization_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q4_k_m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With plain PEFT, load the base model in 16-bit, not 4-bit, so the merged weights aren't rounded to the 4-bit grid:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;

&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Qwen/Qwen3-14B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;khadim-hussain/qwen3-14b-stem-qa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;merge_and_unload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3-14b-stem-qa-merged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Qwen/Qwen3-14B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3-14b-stem-qa-merged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then convert and quantize with llama.cpp, the two steps Ollama dropped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python convert_hf_to_gguf.py qwen3-14b-stem-qa-merged &lt;span class="nt"&gt;--outtype&lt;/span&gt; f16 &lt;span class="nt"&gt;--outfile&lt;/span&gt; qwen3-14b-stem-qa-f16.gguf
llama-quantize qwen3-14b-stem-qa-f16.gguf qwen3-14b-stem-qa-q4_k_m.gguf Q4_K_M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My published &lt;a href="https://huggingface.co/khadim-hussain/qwen3-14b-stem-qa-gguf" rel="noopener noreferrer"&gt;Qwen3-14B GGUF&lt;/a&gt; came out of this path. Its metadata calls the model "Qwen3 14b Finetuned Merged", the name llama.cpp's converter takes from the merged model's folder, and the Q4_K_M file is 9.0 GB. Merging needs the whole 16-bit model in memory, and my F16 GGUF of it is 29.5 GB, so it is a step for the training machine rather than a laptop.&lt;/p&gt;

&lt;p&gt;llama.cpp also has a merge tool that works on GGUF files directly, &lt;code&gt;llama-export-lora&lt;/code&gt;, which applies a GGUF LoRA to a GGUF base and writes a merged file. Don't look for it in the release download: the Windows CPU zip I used (build b11793) loads its CPU backends dynamically, and llama.cpp's build file skips &lt;code&gt;export-lora&lt;/code&gt; in that configuration (&lt;a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/CMakeLists.txt" rel="noopener noreferrer"&gt;tools/CMakeLists.txt&lt;/a&gt;). You would have to build it from source with &lt;code&gt;GGML_BACKEND_DL&lt;/code&gt; off. I didn't, because the merged GGUF from training already existed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Import the merged GGUF into Ollama
&lt;/h3&gt;

&lt;p&gt;The Modelfile loses its &lt;code&gt;ADAPTER&lt;/code&gt; line, and &lt;code&gt;FROM&lt;/code&gt; points at the merged file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ./qwen3-14b-stem-qa-q4_k_m.gguf&lt;/span&gt;

TEMPLATE """{{- if .System }}&amp;lt;|im_start|&amp;gt;system
{{ .System }}&amp;lt;|im_end|&amp;gt;
{{ end }}
{{- range .Messages }}
{{- if or (eq .Role "user") (eq .Role "assistant") }}&amp;lt;|im_start|&amp;gt;{{ .Role }}
{{ .Content }}&amp;lt;|im_end|&amp;gt;
{{ end }}
{{- end }}&amp;lt;|im_start|&amp;gt;assistant
"""

PARAMETER stop "&amp;lt;|im_end|&amp;gt;"
PARAMETER num_ctx 4096
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama create qwen3-14b-stem &lt;span class="nt"&gt;-f&lt;/span&gt; Modelfile
ollama run qwen3-14b-stem &lt;span class="s2"&gt;"What is DNA?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Ollama 0.40.7 the create took 20 seconds, most of it spent copying the 9.0 GB file into Ollama's model store, and the first request loaded the model in 9.6 seconds. It answered the DNA question directly, in 49 tokens and without a &lt;code&gt;&amp;lt;think&amp;gt;&lt;/code&gt; block, which is how the adapter behaves in llama.cpp below. The wording wasn't identical ("two long chains of nucleotides" where the adapter run says "two polynucleotide chains"). That is expected: this file was merged first and quantized afterwards, while llama.cpp adds the 16-bit adapter on top of an already quantized base, so the two setups don't compute exactly the same numbers.&lt;/p&gt;

&lt;p&gt;The template loops over &lt;code&gt;.Messages&lt;/code&gt; so that earlier turns of a chat keep their &lt;code&gt;&amp;lt;|im_end|&amp;gt;&lt;/code&gt; and a &lt;code&gt;SYSTEM&lt;/code&gt; line is used. I first published it with &lt;code&gt;{{ .Prompt }}&lt;/code&gt; alone, which renders one question byte for byte the same (the runs above used it) but drops &lt;code&gt;&amp;lt;|im_end|&amp;gt;&lt;/code&gt; after every earlier answer once a chat has history, and ignores &lt;code&gt;SYSTEM&lt;/code&gt;. The &lt;a href="https://khadim.tech/blog/fine-tune-gpt-oss-20b-unsloth-ollama#why-a-template-with-only-prompt-breaks-the-second-message" rel="noopener noreferrer"&gt;gpt-oss post&lt;/a&gt; shows the rendered difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I run a LoRA adapter without Ollama?
&lt;/h2&gt;

&lt;p&gt;llama.cpp still loads LoRA adapters, and it can apply one on top of a quantized base at run time. It needs the adapter as a GGUF file, so the first step is a conversion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Convert the PEFT adapter to a GGUF LoRA
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/ggml-org/llama.cpp/blob/master/convert_lora_to_gguf.py" rel="noopener noreferrer"&gt;&lt;code&gt;convert_lora_to_gguf.py&lt;/code&gt;&lt;/a&gt; ships with llama.cpp. It reads &lt;code&gt;adapter_config.json&lt;/code&gt; and &lt;code&gt;adapter_model.safetensors&lt;/code&gt; and needs only the base model's config, not its weights:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements/requirements-convert_lora_to_gguf.txt
python convert_lora_to_gguf.py ./qwen3-14b-stem-qa &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--base-model-id&lt;/span&gt; Qwen/Qwen3-14B &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--outtype&lt;/span&gt; f16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--outfile&lt;/span&gt; qwen3-14b-stem-qa-lora-f16.gguf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pass --base-model-id for Unsloth adapters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without it, the script follows &lt;code&gt;base_model_name_or_path&lt;/code&gt; in &lt;code&gt;adapter_config.json&lt;/code&gt;. Unsloth writes the 4-bit training copy there (&lt;code&gt;unsloth/qwen3-14b-unsloth-bnb-4bit&lt;/code&gt; for my adapter), and the conversion stops with &lt;code&gt;NotImplementedError: Quant method is not yet supported: 'bitsandbytes'&lt;/code&gt;. Point it at the original model instead. The base model ID only supplies the architecture and layer shapes; the LoRA weights come from the adapter file either way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My adapter is rank 32 on all seven attention and MLP projections of the 40 layers, which makes 560 tensors. Each conversion took under a minute on my laptop's CPU:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Adapter file&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;adapter_model.safetensors&lt;/code&gt; (as trained)&lt;/td&gt;
&lt;td&gt;F32&lt;/td&gt;
&lt;td&gt;513.9 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen3-14b-stem-qa-lora-f16.gguf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;F16&lt;/td&gt;
&lt;td&gt;256.9 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen3-14b-stem-qa-lora-q8_0.gguf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Q8_0&lt;/td&gt;
&lt;td&gt;136.5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Serve the base model with the adapter on top
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;llama-server&lt;/code&gt; takes the base GGUF with &lt;code&gt;-m&lt;/code&gt; and the adapter with &lt;code&gt;--lora&lt;/code&gt;, and serves an OpenAI-compatible API plus its own &lt;code&gt;/completion&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llama-server &lt;span class="nt"&gt;-m&lt;/span&gt; Qwen3-14B-Q4_K_M.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lora&lt;/span&gt; qwen3-14b-stem-qa-lora-f16.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; 4096 &lt;span class="nt"&gt;--port&lt;/span&gt; 8090
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my laptop (Intel Core Ultra 9 285H, 32 GB RAM, no GPU) the server was ready about 15 seconds after start. The log prints one &lt;code&gt;cannot use buft 'CPU_REPACK', fallback to CPU&lt;/code&gt; warning per adapter tensor, 240 of them here. They mean the adapter weights sit in a plain CPU buffer rather than a repacked one, and generation still works.&lt;/p&gt;

&lt;p&gt;Each request can set the adapter's strength through a &lt;code&gt;lora&lt;/code&gt; field, so one server answers as the base model and as the fine-tune:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:8090/completion &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
  "prompt": "&amp;lt;|im_start|&amp;gt;user\nWhat is DNA?&amp;lt;|im_end|&amp;gt;\n&amp;lt;|im_start|&amp;gt;assistant\n",
  "n_predict": 160, "temperature": 0,
  "lora": [{"id": 0, "scale": 1.0}]
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the scale at 0, Qwen3-14B opened a &lt;code&gt;&amp;lt;think&amp;gt;&lt;/code&gt; block and was still reasoning when it reached the 160-token limit. With the scale at 1, the fine-tune answered in 74 tokens and stopped:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deoxyribonucleic acid is a polymer composed of two polynucleotide chains that coil around each other to form a double helix carrying genetic instructions for the development, functioning, growth and reproduction of all known organisms and many viruses.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the adapter was applied: the fine-tune skips Qwen3's reasoning block and answers directly. &lt;code&gt;GET /lora-adapters&lt;/code&gt; lists the loaded adapters and their ids, and &lt;code&gt;POST /lora-adapters&lt;/code&gt; sets the scale for requests that don't send one. Start the server with &lt;code&gt;--lora-init-without-apply&lt;/code&gt; if the adapter should be off by default.&lt;/p&gt;

&lt;p&gt;The Q8_0 adapter loads the same way. At temperature 0 its answer to the DNA question differed from the F16 adapter's by a few words ("development, survival and reproduction of all known living organisms" instead of "development, functioning, growth and reproduction of all known organisms"). At half the file size, that makes Q8_0 a reasonable default for this adapter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a runtime LoRA slower than a merged model?
&lt;/h3&gt;

&lt;p&gt;A little. I loaded the merged GGUF and the base-plus-adapter setup side by side, then alternated requests between them: 128 tokens each, temperature 0, four rounds, CPU only with 16 threads.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Prompt processing (tokens/s)&lt;/th&gt;
&lt;th&gt;Generation (tokens/s)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Merged Q4_K_M GGUF&lt;/td&gt;
&lt;td&gt;21.1&lt;/td&gt;
&lt;td&gt;5.33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base Q4_K_M + F16 LoRA, adapter off (scale 0)&lt;/td&gt;
&lt;td&gt;22.0&lt;/td&gt;
&lt;td&gt;5.23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base Q4_K_M + F16 LoRA, adapter on (scale 1)&lt;/td&gt;
&lt;td&gt;19.6&lt;/td&gt;
&lt;td&gt;4.66&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are medians. With the adapter on, generation was about 13% slower than the merged file, and prompt processing about 7% slower. I measured CPU only. Alternating matters on a laptop: an earlier run, with a large download going in the background, varied more between identical requests than these setups vary from each other.&lt;/p&gt;

&lt;p&gt;For one fine-tune that should always be on, the merged file is faster. The adapter route pays off when one base model serves several adapters, or the plain base model as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which route should you use?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Route&lt;/th&gt;
&lt;th&gt;You ship&lt;/th&gt;
&lt;th&gt;Runs in&lt;/th&gt;
&lt;th&gt;Use it when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Merged GGUF&lt;/td&gt;
&lt;td&gt;One full model file (9.0 GB at Q4_K_M for a 14B model)&lt;/td&gt;
&lt;td&gt;Ollama, llama.cpp, LM Studio&lt;/td&gt;
&lt;td&gt;You want &lt;code&gt;ollama run&lt;/code&gt; to keep working, or you hand the model to people who use Ollama&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GGUF LoRA + base GGUF&lt;/td&gt;
&lt;td&gt;A small adapter file plus a base model the user may already have&lt;/td&gt;
&lt;td&gt;llama.cpp (&lt;code&gt;llama-server&lt;/code&gt;, &lt;code&gt;llama-cli&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;You have several fine-tunes of one base, or you want to switch an adapter on and off per request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safetensors adapter&lt;/td&gt;
&lt;td&gt;The PEFT adapter as trained&lt;/td&gt;
&lt;td&gt;vLLM (&lt;code&gt;--enable-lora&lt;/code&gt;), Transformers, Unsloth&lt;/td&gt;
&lt;td&gt;You serve on a GPU and want many adapters on one base (&lt;a href="https://docs.vllm.ai/en/latest/features/lora/" rel="noopener noreferrer"&gt;vLLM LoRA docs&lt;/a&gt;); I didn't test this route here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stay on Ollama 0.34.0&lt;/td&gt;
&lt;td&gt;Your existing Modelfile&lt;/td&gt;
&lt;td&gt;Ollama 0.34.0&lt;/td&gt;
&lt;td&gt;Only as a stopgap: you give up every Ollama release since September 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For most people who were using &lt;code&gt;ADAPTER&lt;/code&gt; in Ollama, the merged GGUF is the shortest path back: the Modelfile loses one line and points &lt;code&gt;FROM&lt;/code&gt; at the merged file. Keep the adapter as well, because it is the file you build every other format from.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can Ollama load a LoRA adapter?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not since version 0.34.1, released in September 2026. Any Modelfile with an ADAPTER line fails with "Error: LoRA adapters are no longer supported", whether the adapter is a GGUF file or a safetensors folder. Merge the adapter into the model and import the merged GGUF instead, or serve the adapter with llama.cpp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which Ollama version removed LoRA support?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Version 0.34.1. Version 0.34.0, released on 5 September 2026, was the last one that applied an ADAPTER. The change came in pull request #14969, which also removed Ollama's built-in safetensors-to-GGUF converter and its quantizer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I still use a model I created with an adapter before 0.34.1?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ollama's source keeps the adapter layer of models that already exist and logs a deprecation warning when it loads one. You can't create a new one, and re-creating that model from its Modelfile will fail on the ADAPTER line. I didn't have an older adapter model to test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I convert a LoRA adapter to GGUF?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run llama.cpp's convert_lora_to_gguf.py on the folder that holds adapter_config.json and adapter_model.safetensors. If the adapter's config names a quantized training copy of the base (Unsloth writes a bnb-4bit repo there), pass --base-model-id with the original model. The converter needs only the base model's config, not its weights.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does llama.cpp still support LoRA adapters?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. llama-server and llama-cli load a GGUF LoRA with --lora on top of a base GGUF, including a quantized one, and llama-server can change an adapter's scale per request through a lora field. llama.cpp refuses an adapter whose architecture or tensor shapes don't match the base, but it can't tell two same-sized bases apart, so pairing the right base is still on you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does ollama create --quantize fail on my GGUF?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same 0.34.1 change moved quantization out of Ollama for GGUF models. Ollama 0.40.7 answers with "Error: create-time quantization is only supported for safetensors imports; quantize GGUF models before importing". Quantize the F16 file with llama.cpp's llama-quantize, then import the result.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: written with AI assistance, from my own notes and test runs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ollama</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>ai</category>
    </item>
    <item>
      <title>BLUF Meaning: Examples for ML Engineers and Data Scientists</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Sun, 18 Oct 2026 20:45:14 +0000</pubDate>
      <link>https://dev.to/khadimhussain/bluf-meaning-examples-for-ml-engineers-and-data-scientists-4m4k</link>
      <guid>https://dev.to/khadimhussain/bluf-meaning-examples-for-ml-engineers-and-data-scientists-4m4k</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/bluf-examples-ml-engineers" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; BLUF stands for bottom line up front. The first sentence of a message says what you need or what you concluded, and everything after it supports that sentence. In ML work, that means opening with the decision, the date or the business impact, and keeping metrics like ROC-AUC, the debugging story and tool names for later, or for the engineers who ask. Below are before-and-after examples for a production rollback, a delayed project, a GPU budget request, an A/B test, a client question, a chat message and an interview answer.&lt;/p&gt;

&lt;p&gt;I recently made BLUF the rule for everything I write at work: status updates, emails, merge request descriptions and chat replies. I work remotely, in a different time zone from most of the people who read my messages, so a message is usually read once, quickly, between other things. When the ask sits in the third paragraph, it waits until the next day.&lt;/p&gt;

&lt;p&gt;ML updates are especially easy to get wrong this way. The investigation is the part we find interesting, so that is where we start, and the reader has to get through the ROC-AUC numbers and the drift theory before finding out what we want from them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does BLUF mean?
&lt;/h2&gt;

&lt;p&gt;BLUF stands for &lt;strong&gt;bottom line up front&lt;/strong&gt;: put the conclusion, the recommendation or the request in the first sentence, and the supporting detail after it. The phrase comes from the US Army's correspondence regulation, AR 25-50, which tells writers to put the main point at the beginning and to use the active voice (&lt;a href="https://en.wikipedia.org/wiki/BLUF_(communication)" rel="noopener noreferrer"&gt;Wikipedia summarises the history&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Kabir Sehgal, writing about military email for &lt;a href="https://hbr.org/2016/11/how-to-write-email-with-military-precision" rel="noopener noreferrer"&gt;Harvard Business Review&lt;/a&gt;, describes the BLUF as a short opening statement that "declares the purpose of the email and action required." His example from the US Air Force:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;BLUF: Effective 29 Oct 13, all Air Force Doctrine Documents (AFDDs) have been rescinded and replaced by core doctrine volumes and doctrine annexes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A reader who stops after that sentence knows what changed, from when, and that they need to switch documents. Anything after it is optional reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  The BLUF + SCR structure
&lt;/h2&gt;

&lt;p&gt;BLUF tells you where the point goes. For what comes after it, I use a short version of the Situation, Complication, Question framework from &lt;a href="https://www.barbaraminto.com/" rel="noopener noreferrer"&gt;Barbara Minto's Pyramid Principle&lt;/a&gt;, with the question replaced by the answer you are proposing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLUF:          what you need, or what you concluded, in one sentence a reader could act on
Situation:     one line of context the reader already agrees with
Complication:  what changed or what is blocking, in business terms
Resolution:    what you propose, by when, and what it gets them
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A short reply is just the first line. A decision request to a manager or client gets all four, and each one stays at a sentence or two. Good news has no complication, so you skip it.&lt;/p&gt;

&lt;h2&gt;
  
  
  ML terms to cut, and what to say instead
&lt;/h2&gt;

&lt;p&gt;Executives and clients read for impact, timeline, cost, risk and the decision they need to make. Tool names and metrics belong in the message only when the reader asked for them or is an engineer who needs them. These are the translations I reach for most:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instead of&lt;/th&gt;
&lt;th&gt;Say&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ROC-AUC fell from 0.88 to 0.61&lt;/td&gt;
&lt;td&gt;The model is now wrong on far more cases, and here is who that affects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precision is 0.74&lt;/td&gt;
&lt;td&gt;About 1 in 4 cases we flag is a false alarm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data drift&lt;/td&gt;
&lt;td&gt;Customer behaviour has changed since the model was trained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overfitting&lt;/td&gt;
&lt;td&gt;It does well on the data it learned from and worse on new cases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hyperparameter tuning&lt;/td&gt;
&lt;td&gt;A week of tuning to improve accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p &amp;lt; 0.01&lt;/td&gt;
&lt;td&gt;The result is very unlikely to be chance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CUDA out of memory on the current GPUs&lt;/td&gt;
&lt;td&gt;Training needs a bigger machine, at a cost of X per month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrain on fresh data&lt;/td&gt;
&lt;td&gt;Update the model with recent data, which takes N days&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  BLUF examples for ML engineers and data scientists
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;These scenarios are illustrative&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The situations and numbers in examples 1 to 6 are made up to show the shape. Swap in your real figures. Example 7 uses a real result from my own work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1. A model is failing in production
&lt;/h3&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I was looking at the logs for the fraud model and noticed ROC-AUC dropped from 0.88 to 0.61. I think it might be data drift, because marketing launched a new campaign that changed the input distributions...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;BLUF:&lt;/strong&gt; I need approval to roll the fraud model back to version 2.1 by 2:00 PM to stop legitimate customers being blocked at checkout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation:&lt;/strong&gt; Version 3.0 has been live for 48 hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complication:&lt;/strong&gt; The new marketing campaign changed how customers behave, and accuracy has fallen 27%, so good transactions are being rejected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution:&lt;/strong&gt; Rolling back restores normal checkout immediately. We then retrain 3.0 on the new traffic, which takes 3 days.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. A project is delayed by a data problem
&lt;/h3&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We're blocked on the churn model. The upstream pipeline is broken and the schemas don't match, so my dataframe keeps throwing errors...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;BLUF:&lt;/strong&gt; The churn prediction dashboard will slip one week, to November 15, unless the data pipeline is fixed today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation:&lt;/strong&gt; We are building the model that tells sales which accounts are likely to leave.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complication:&lt;/strong&gt; Last week's database migration dropped the timestamps from the activity logs, and training cannot run without them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution:&lt;/strong&gt; I have asked Data Engineering for a fix. If it lands today, we still deliver on November 8.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Asking for a GPU budget
&lt;/h3&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Our workstations are too slow. Training takes four days because we're on older consumer GPUs and the batch size is capped. We should move to cloud GPU instances...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;BLUF:&lt;/strong&gt; I'm requesting $3,500 a month for cloud GPUs, which brings the support assistant's launch forward by three weeks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation:&lt;/strong&gt; We are training a support model to cut call-centre volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complication:&lt;/strong&gt; Each training run takes 4 days on our current hardware, which pushes the launch past the holiday season.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution:&lt;/strong&gt; Cloud GPUs cut a run to 3 hours. We launch three weeks earlier and save an estimated $20,000 in support costs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The before version argues for convenience. The after version prices the delay, which is what the person approving the money is weighing.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Reporting an A/B test win
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;BLUF:&lt;/strong&gt; I recommend moving the new recommendation model to 100% of traffic. In a two-week A/B test it lifted checkout conversion by 4.5%, at 99% confidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation:&lt;/strong&gt; The test split traffic evenly between the current model and the new one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution:&lt;/strong&gt; With your go-ahead, we deploy tomorrow morning. The projected gain is $12,000 a week.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is no complication line, because nothing went wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. A client asks "is the model ready?"
&lt;/h3&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Validation mAP is at 0.91 now and recall on the defect class is 0.97. We still see some false negatives on small scratches, mostly under low lighting, so we're adding augmentation...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not for fully automatic rejection yet, but it is ready to flag parts for a human to check. In testing it catches 97 of every 100 defects, and the line needs 99 before it can run without a person. The misses are mostly small scratches in poor lighting. Two more weeks of data collection should close that gap, and I'll send the next numbers on the 30th.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A client question gets an answer in the first word. "Not yet" with a usable alternative beats a page of metrics they have to interpret themselves.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. A chat message
&lt;/h3&gt;

&lt;p&gt;Labels look stiff in chat, so keep the order and write it as plain sentences:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can't make Thursday 3 PM. Can we do Friday 11 AM instead? The GPU delivery moved to Thursday afternoon and I need to be there to sign for it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The request comes first, the alternative second, the reason last.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. An interview answer
&lt;/h3&gt;

&lt;p&gt;Question: &lt;em&gt;"Tell me about a time you improved a model in production."&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I improved our inference speed by 70% by moving model serving to TensorRT, ONNX and Triton. The context was an internal inference engine used across the ML team...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The result is in the first sentence, and the story after it explains how. If the interviewer cuts you off after ten seconds, they already have the part that matters. Nobody says "BLUF" out loud in an interview; you just answer first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you write "BLUF:" in the message?
&lt;/h2&gt;

&lt;p&gt;In formal writing, such as status reports, escalations and decision requests, the label helps, as long as the readers know what it means. Sehgal drops the acronym in his corporate job because it would be unclear to his recipients, and starts with a bold "Bottom line:" instead. In chat, client messages and proposals I leave labels out entirely. The first sentence carries the bottom line and the rest is ordinary prose, because a message full of bold labels reads like a form.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I use BLUF in my own writing
&lt;/h2&gt;

&lt;p&gt;Every page on this site opens with an "In short" box that answers the page's question before the article starts. The &lt;a href="https://khadim.tech/blog/is-synthid-reliable" rel="noopener noreferrer"&gt;SynthID test&lt;/a&gt; begins "Mostly, for what it is built to do," and then spends the rest of the page on the evidence. Search engines and AI answer tools quote passages, so a page with its conclusion in the first two sentences tends to be quoted with that conclusion attached.&lt;/p&gt;

&lt;p&gt;I also wrote the rule into the instructions my coding agent follows, so any message it drafts for me starts with the bottom line, and so do its reports back to me. The test I use is the same everywhere: if the reader stopped after the first sentence, would they know what I need from them?&lt;/p&gt;

&lt;p&gt;If you're preparing results for a stakeholder, or deciding whether a &lt;a href="https://khadim.tech/services/llm-fine-tuning" rel="noopener noreferrer"&gt;fine-tuned model&lt;/a&gt; is worth the effort, I'm happy to look at it with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What does BLUF stand for?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bottom line up front. The term comes from US Army correspondence rules, which put the main point or recommendation at the start of a memo instead of the end. Outside the military it is used for email, status reports and any message where the reader may only read the first line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I write "BLUF:" at the top of the email?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only if your readers know the term. Kabir Sehgal, who wrote about military-style email for Harvard Business Review, starts his corporate notes with a bold "Bottom line:" instead, because the acronym would be unclear to his recipients. The order matters more than the label.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between BLUF and SCR?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;BLUF says where the main point goes: first. SCR (situation, complication, resolution) is a way to arrange the context that follows it. Together they give one sentence with the ask or conclusion, then a line each on the background, what changed, and what you propose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I explain model performance to non-technical stakeholders?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Say what the metric changes for them: customers affected, revenue, cost, time or risk. "About 1 in 4 transactions we block belongs to a real customer" lands where "precision is 0.74" does not. Keep the metric ready for anyone who asks for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does BLUF work for good news?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Lead with the decision the good news supports, such as moving a new model to all traffic, then give the result that justifies it. A good result sent without an ask often gets a thumbs-up and no decision.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: written with AI assistance, from my own notes and examples.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>machinelearning</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Training FLUX.2 and Kolors LoRAs on a space dataset</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Tue, 13 Oct 2026 07:26:26 +0000</pubDate>
      <link>https://dev.to/khadimhussain/training-flux2-and-kolors-loras-on-a-space-dataset-3p2p</link>
      <guid>https://dev.to/khadimhussain/training-flux2-and-kolors-loras-on-a-space-dataset-3p2p</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/work/kiffusion-spacevision" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; A space-themed image generation project: a dataset of 1,964 image-caption pairs from NASA, Wallhaven and LAION-Aesthetics, auto-captioned with JoyCaption, and LoRA adapters trained on FLUX.2-klein (9B) and Kolors. The dataset has 3,300+ downloads on Hugging Face.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dataset
&lt;/h2&gt;

&lt;p&gt;Kiffusion is 1,964 image-caption pairs of space and sci-fi imagery, collected from NASA, Wallhaven and LAION-Aesthetics. Every image was captioned automatically with JoyCaption, so the captions describe composition and lighting in the detail a diffusion model needs, instead of relying on short alt text.&lt;/p&gt;

&lt;h2&gt;
  
  
  The LoRAs
&lt;/h2&gt;

&lt;p&gt;SpaceVision is the set of LoRA adapters trained on that dataset, using SimpleTuner and Diffusers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FLUX.2-klein (9B)&lt;/strong&gt;, a 38 MB adapter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kolors&lt;/strong&gt;, a 46.6 MB adapter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A LoRA trains a small set of extra weights instead of the whole model, so the adapters stay small enough to download and swap in seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What got used
&lt;/h2&gt;

&lt;p&gt;Download counts from Hugging Face, all time, as of October 2026:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Artifact&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Downloads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;kiffusion-space-scifi&lt;/td&gt;
&lt;td&gt;Dataset&lt;/td&gt;
&lt;td&gt;3,306&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;spacevision-flux2-lora&lt;/td&gt;
&lt;td&gt;LoRA for FLUX.2-klein&lt;/td&gt;
&lt;td&gt;204&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;spacevision-kolors-lora&lt;/td&gt;
&lt;td&gt;LoRA for Kolors&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The dataset is used far more than the adapters, which suggests that a well-captioned niche dataset is the more reusable artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/datasets/khadim-hussain/kiffusion-space-scifi" rel="noopener noreferrer"&gt;Kiffusion dataset on Hugging Face&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/khadim-hussain/spacevision-flux2-lora" rel="noopener noreferrer"&gt;SpaceVision FLUX.2-klein LoRA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/khadim-hussain/spacevision-kolors-lora" rel="noopener noreferrer"&gt;SpaceVision Kolors LoRA&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>stablediffusion</category>
      <category>huggingface</category>
    </item>
    <item>
      <title>Is SynthID reliable? I tested Google's AI image check on screenshots, stripped files and real photos</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Mon, 12 Oct 2026 18:40:48 +0000</pubDate>
      <link>https://dev.to/khadimhussain/is-synthid-reliable-i-tested-googles-ai-image-check-on-screenshots-stripped-files-and-real-photos-297o</link>
      <guid>https://dev.to/khadimhussain/is-synthid-reliable-i-tested-googles-ai-image-check-on-screenshots-stripped-files-and-real-photos-297o</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/is-synthid-reliable" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; Mostly, for what it is built to do. In my tests on October 12, 2026, Google's SynthID check in the Gemini app flagged a Gemini-generated image after its metadata was removed and again after a screenshot, and found nothing on a real photo. It missed the same AI image once it was pasted small into a real photo, and the new SynthID Detector website failed on every upload that day. A clean result only means no watermark from Google or its partners was found.&lt;/p&gt;

&lt;p&gt;Google opened the &lt;a href="https://synthid.com" rel="noopener noreferrer"&gt;SynthID Detector&lt;/a&gt; to everyone on October 7, 2026. Most of the coverage so far repeats the launch post, so I generated some images and tested the questions people search for. Does it survive a screenshot? Does it need the file's metadata? Will it flag a real photo?&lt;/p&gt;

&lt;h2&gt;
  
  
  What SynthID Detector is
&lt;/h2&gt;

&lt;p&gt;SynthID is Google DeepMind's invisible watermark for AI-generated images, video and audio. &lt;a href="https://blog.google/innovation-and-ai/models-and-research/google-deepmind/synth-id-ai-content/" rel="noopener noreferrer"&gt;Google's launch post&lt;/a&gt; says it has watermarked over 180 billion images and videos and 240,000 years of audio. The Detector reads watermarks from Google and its partners: OpenAI, NVIDIA, Kakao and, soon, Apple. You sign in with a Google, Apple or OpenAI account, then upload a file.&lt;/p&gt;

&lt;p&gt;It is not a general AI detector. It answers one question: does this file carry a SynthID watermark? The same check is built into Search, Chrome and the Gemini app, which together handle over a million verification requests a day, according to Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I tested it
&lt;/h2&gt;

&lt;p&gt;I generated two photorealistic images with Gemini 3 Pro Image through the Gemini API (2400 × 1792 px). Google's &lt;a href="https://ai.google.dev/gemini-api/docs/image-generation" rel="noopener noreferrer"&gt;image generation docs&lt;/a&gt; say "All generated images include a SynthID watermark." From one of them I made these test files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the original file, untouched&lt;/li&gt;
&lt;li&gt;the same pixels with the metadata removed (a pixel diff against the original is exactly zero)&lt;/li&gt;
&lt;li&gt;a real screenshot: the image shown in Chrome, captured from the screen and saved as a 2048 × 1523 PNG&lt;/li&gt;
&lt;li&gt;a real photo from NASA's public image library, taken at Kennedy Space Center in 2023&lt;/li&gt;
&lt;li&gt;a second NASA photo with only the sky changed by Gemini&lt;/li&gt;
&lt;li&gt;the AI image pasted small into the corner of the real photo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Detector was the plan, but on October 12 all four of my uploads, between 4:36 and 5:33 pm UTC, ended in "Something went wrong! Please wait and try again later." That included a 166 KB file, so it wasn't file size. So I used the SynthID check built into the Gemini app: a fresh chat for each file, my personal account, and the question "Is this AI-generated?"&lt;/p&gt;

&lt;p&gt;I kept to things a normal user would check. The Detector's terms forbid using its results to work out which transformations break the watermark, so I didn't build a grid of crops, filters and compression levels.&lt;/p&gt;

&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;p&gt;Content Credentials (the C2PA standard) are signed metadata that record how a file was made. SynthID is the watermark in the pixels. Here is what Gemini said about each file:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Content Credentials in file&lt;/th&gt;
&lt;th&gt;Gemini's answer&lt;/th&gt;
&lt;th&gt;What it used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemini image, untouched&lt;/td&gt;
&lt;td&gt;Yes, signed by Google LLC&lt;/td&gt;
&lt;td&gt;"created using artificial intelligence by Google LLC, followed by subsequent editing using AI tools"&lt;/td&gt;
&lt;td&gt;C2PA metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same pixels, metadata removed&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;"Most or all of this image was edited or generated with Google AI." (2 of 2 runs)&lt;/td&gt;
&lt;td&gt;SynthID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screenshot of it (PNG)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;"Most or all of this image was edited or generated with Google AI." (2 of 2 runs)&lt;/td&gt;
&lt;td&gt;SynthID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real photo (NASA, 2023)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;"No reliable signals were detected indicating how the content was created"&lt;/td&gt;
&lt;td&gt;Nothing found&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real photo, sky edited in Gemini&lt;/td&gt;
&lt;td&gt;Yes, signed by Google&lt;/td&gt;
&lt;td&gt;"created as a generative AI creation and subsequently edited using generative AI tools"&lt;/td&gt;
&lt;td&gt;C2PA metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI image pasted small into the real photo&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;"No reliable signals were detected"&lt;/td&gt;
&lt;td&gt;Missed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fethze9crcvtydfb2t6q9.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%2Fethze9crcvtydfb2t6q9.png" alt="Gemini answering 'Most or all of this image was edited or generated with Google AI' for an image whose metadata had been removed" width="800" height="346"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Metadata removed, pixels untouched: Gemini still finds Google's watermark.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Does SynthID work on screenshots?
&lt;/h2&gt;

&lt;p&gt;It did in mine. A screenshot changes the image: it's rescaled to fit the screen, re-rendered by the browser and saved in a different format. Gemini flagged it both times. The second answer also said "No reliable C2PA content credentials were detected for this file", so the detection came from the pixels.&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%2Fu7wcba07u9qejuffxswo.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%2Fu7wcba07u9qejuffxswo.png" alt="Gemini detecting Google AI in a PNG screenshot of a generated image and noting that no C2PA content credentials were found" width="800" height="558"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A screenshot of the image, saved as PNG: flagged, with no C2PA credentials left in the file.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not every screenshot test agrees. On October 8, &lt;a href="https://www.digitalinformationworld.com/2026/10/google-expands-synthid-detector.html" rel="noopener noreferrer"&gt;Digital Information World&lt;/a&gt; reported that the Detector website flagged their image as a standalone file but not as a screenshot. Mine was cropped to the image and checked in Gemini, and theirs used the Detector, so the two aren't directly comparable. Either way, a screenshot is a weaker test than the original file.&lt;/p&gt;

&lt;p&gt;Google's own claim is narrower. Its &lt;a href="https://deepmind.google/models/synthid/" rel="noopener noreferrer"&gt;SynthID page&lt;/a&gt; says the image watermark is "designed to stand up to modifications like cropping, adding filters, changing frame rates, or lossy compression." Screenshots aren't on that list.&lt;/p&gt;
&lt;h2&gt;
  
  
  SynthID vs C2PA: the two labels inside a Gemini image
&lt;/h2&gt;

&lt;p&gt;Every Gemini image I made carried two separate labels, and the check reads both.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;C2PA Content Credentials&lt;/th&gt;
&lt;th&gt;SynthID&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where it lives&lt;/td&gt;
&lt;td&gt;Signed metadata in the file (about 6 KB here)&lt;/td&gt;
&lt;td&gt;In the pixels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What it records&lt;/td&gt;
&lt;td&gt;Who made it, which tool, edit history&lt;/td&gt;
&lt;td&gt;Only that a Google or partner model made it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Survives a re-save or screenshot&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, in my tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who can read it&lt;/td&gt;
&lt;td&gt;Anyone, with open tools&lt;/td&gt;
&lt;td&gt;Google's tools only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Removing the metadata, or re-saving the image as a 1080 px JPEG, deleted the C2PA manifest completely. The file then relies on SynthID alone. You can read the credentials yourself with &lt;a href="https://github.com/contentauth/c2pa-python" rel="noopener noreferrer"&gt;c2pa-python&lt;/a&gt; (version 0.38.0 here):&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;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;c2pa&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Reader&lt;/span&gt;

&lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Reader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-image.jpg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;manifests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active_manifest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signature_info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issuer&lt;/span&gt;&lt;span class="sh"&gt;"&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;assertion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assertions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;assertion&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;c2pa.actions&lt;/span&gt;&lt;span class="sh"&gt;"&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;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;assertion&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;actions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;digitalSourceType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Google LLC
c2pa.created http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia
c2pa.edited http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;trainedAlgorithmicMedia&lt;/code&gt; is the &lt;a href="https://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia" rel="noopener noreferrer"&gt;IPTC term&lt;/a&gt; for media created by a generative model. If you'd rather not run code, &lt;a href="https://contentcredentials.org/verify" rel="noopener noreferrer"&gt;contentcredentials.org/verify&lt;/a&gt; reads the same manifest in the browser.&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%2Fes913g2vo8qt2lqvxm05.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%2Fes913g2vo8qt2lqvxm05.png" alt="Gemini reporting C2PA Content Credentials showing the image was created and edited with AI by Google LLC" width="799" height="515"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;With the metadata intact, Gemini answers from the C2PA credentials and doesn't mention SynthID.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Is SynthID reliable? What a result tells you
&lt;/h2&gt;

&lt;p&gt;A positive result is strong evidence. Gemini identified the untouched file from its metadata, and the stripped copy and the screenshot from the watermark, while the real photo came back clean. One photo isn't a false-positive rate, though.&lt;/p&gt;

&lt;p&gt;A negative result is weak evidence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It only knows its partners. An image from a generator outside the SynthID scheme will always come back clean.&lt;/li&gt;
&lt;li&gt;It missed a composite. The same AI image, pasted small into the corner of a real photo, came back "No reliable signals were detected." &lt;a href="https://www.digitalinformationworld.com/2026/10/google-expands-synthid-detector.html" rel="noopener noreferrer"&gt;Digital Information World&lt;/a&gt; saw the same on the Detector website: an AI image covering more than half of a new image came back as unlikely to be from a SynthID partner.&lt;/li&gt;
&lt;li&gt;The check has slipped before. In July, &lt;a href="https://leadstories.com/analysis/2026/07/google-gemini-synthid-detector-confuses-results-within-same-chat.html" rel="noopener noreferrer"&gt;Lead Stories found&lt;/a&gt; that within one chat, Gemini repeated its verdict on the first file when asked about a second one, for images and videos. Google said on July 16, 2026 that a fix had rolled out globally. I still used a fresh chat for every file.&lt;/li&gt;
&lt;/ul&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%2Fhs2rob9zhx5q8w4c03a9.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%2Fhs2rob9zhx5q8w4c03a9.png" alt="Gemini answering 'No reliable signals were detected' for a real photo with a small AI-generated image pasted into its corner" width="799" height="439"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;An AI image pasted small into a real photo (background: NASA, public domain): not detected.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How you ask matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My first prompt was "Was this image created or edited with Google AI? Please check it for a SynthID watermark." On the metadata-stripped file, Gemini answered that it couldn't check for a watermark at all. Asked "Is this AI-generated?", which is Google's own suggested wording, it found the watermark both times. That was one run against two, but if Gemini says it can't check, ask again with the plain question in a new chat.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to check an image yourself
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In the Gemini app, upload the image in a new chat and ask "Is this AI-generated?" In my tests it answered from C2PA when the file had it, and from SynthID when it didn't.&lt;/li&gt;
&lt;li&gt;On the SynthID Detector, sign in at &lt;a href="https://synthid.com" rel="noopener noreferrer"&gt;synthid.com&lt;/a&gt; and upload the file. It covers Google's partners as well, and shows which parts of the image are most likely watermarked.&lt;/li&gt;
&lt;li&gt;For Content Credentials, use &lt;a href="https://contentcredentials.org/verify" rel="noopener noreferrer"&gt;contentcredentials.org/verify&lt;/a&gt;, or the code above, to see the signed history, if the file still has it.&lt;/li&gt;
&lt;li&gt;Read a clean result as "unknown", not "real".&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What about AI-generated text?
&lt;/h2&gt;

&lt;p&gt;The image watermark is closed, but &lt;a href="https://ai.google.dev/responsible/docs/safeguards/synthid" rel="noopener noreferrer"&gt;SynthID Text&lt;/a&gt; is open source. It ships in Hugging Face Transformers 4.46 and later as a watermarking config passed to &lt;code&gt;generate()&lt;/code&gt;, with a Bayesian detector. Text detection is probabilistic, and I didn't test it here.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you build image pipelines
&lt;/h2&gt;

&lt;p&gt;Any step that decodes and re-encodes an image (resizing, thumbnails, format conversion) drops the C2PA manifest, as the 1080 px re-save above showed. If provenance matters to your product, carry the manifest through with the C2PA SDK rather than relying on the watermark alone. SynthID survived the re-save and the screenshot in my tests, but only Google's tools can read it.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does SynthID work on screenshots?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes. My screenshot of a Gemini image, cropped to the image and saved as a PNG, was flagged by Gemini's check in both runs, from the watermark in the pixels. Digital Information World's screenshot test on the SynthID Detector website failed on October 8, 2026. Use the original file whenever you can get it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is SynthID the same as C2PA Content Credentials?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. C2PA Content Credentials are signed metadata stored in the file, and a normal re-save or upload removes them. SynthID is a watermark in the pixels themselves. Gemini images carry both, and Gemini's check reads whichever one it finds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can SynthID detect Midjourney or Stable Diffusion images?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only if the generator embeds SynthID. Google lists itself, OpenAI, NVIDIA and Kakao as the companies whose watermarks the Detector reads, with Apple to follow. Anything else comes back as no watermark found, which says nothing either way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does SynthID have false positives?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A real photo came back clean in my test, but that is one photo, not a false-positive rate. Treat a positive result as strong evidence the image passed through Google's or a partner's AI tools, and a negative one as unknown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is SynthID open source?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The text watermark is: SynthID Text ships in Hugging Face Transformers 4.46 and later, with a reference implementation on GitHub. Image, video and audio detection is only available through Google's tools, such as the SynthID Detector and the Gemini app.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>security</category>
      <category>google</category>
    </item>
    <item>
      <title>Fine-tuning gpt-oss-20b with Unsloth and running it in Ollama</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Sun, 11 Oct 2026 21:08:28 +0000</pubDate>
      <link>https://dev.to/khadimhussain/fine-tuning-gpt-oss-20b-with-unsloth-and-running-it-in-ollama-1nfh</link>
      <guid>https://dev.to/khadimhussain/fine-tuning-gpt-oss-20b-with-unsloth-and-running-it-in-ollama-1nfh</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/fine-tune-gpt-oss-20b-unsloth-ollama" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; gpt-oss-20b fine-tunes with QLoRA on a single consumer GPU (Unsloth puts it at about 14 GB of VRAM). Shipping it is where the chat template matters: gpt-oss uses OpenAI's Harmony format, so the GGUF needs a Harmony template with &amp;lt;|return|&amp;gt; as a stop token, or Ollama gives gibberish or never stops.&lt;/p&gt;

&lt;p&gt;I fine-tuned &lt;a href="https://huggingface.co/openai/gpt-oss-20b" rel="noopener noreferrer"&gt;gpt-oss-20b&lt;/a&gt; for STEM reasoning and published it three ways: a LoRA adapter, a merged 16-bit model and GGUF files. Below are the settings and versions behind it, the export code, and the Modelfile that makes the GGUF behave in Ollama.&lt;/p&gt;

&lt;h2&gt;
  
  
  QLoRA settings for gpt-oss-20b
&lt;/h2&gt;

&lt;p&gt;The published run, as recorded on the &lt;a href="https://huggingface.co/khadim-hussain/gpt-oss-20b-stem-reasoning" rel="noopener noreferrer"&gt;model card&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;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base model&lt;/td&gt;
&lt;td&gt;openai/gpt-oss-20b (21B parameters, mixture of experts)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;QLoRA, 4-bit base weights&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA rank / alpha&lt;/td&gt;
&lt;td&gt;32 / 64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning rate&lt;/td&gt;
&lt;td&gt;1e-4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch size&lt;/td&gt;
&lt;td&gt;1, with gradient accumulation 16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optimizer&lt;/td&gt;
&lt;td&gt;AdamW 8-bit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Epochs&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;4,260 training and 474 evaluation examples of STEM Q&amp;amp;A with chain-of-thought&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result&lt;/td&gt;
&lt;td&gt;train loss 1.087, eval loss 0.837&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;Unsloth + TRL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The full training config, with target modules and scheduler, is in &lt;a href="https://github.com/khadimhussain0/kllm/blob/main/configs/gpt_oss_20b.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;configs/gpt_oss_20b.yaml&lt;/code&gt;&lt;/a&gt; in &lt;a href="https://github.com/khadimhussain0/kllm" rel="noopener noreferrer"&gt;kllm&lt;/a&gt;, the small toolkit I use for these runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much VRAM does fine-tuning gpt-oss-20b need?
&lt;/h2&gt;

&lt;p&gt;QLoRA keeps the base weights in 4-bit and trains only a small adapter, so a 20B model fits on one consumer card. &lt;a href="https://docs.unsloth.ai/new/gpt-oss-how-to-run-and-fine-tune/tutorial-how-to-fine-tune-gpt-oss" rel="noopener noreferrer"&gt;Unsloth's gpt-oss guide&lt;/a&gt; lists about 14 GB of VRAM for gpt-oss-20b with QLoRA and recommends at least 16 GB for stable runs. LoRA on unquantized BF16 weights needs about 44 GB, which is why QLoRA is the practical choice on one card.&lt;/p&gt;

&lt;p&gt;The environment kllm is verified on:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;RTX 5090 (32 GB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;Ubuntu 24.04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CUDA&lt;/td&gt;
&lt;td&gt;12.8 toolkit (13.0 driver)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;3.12.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PyTorch&lt;/td&gt;
&lt;td&gt;2.9.1+cu128&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unsloth&lt;/td&gt;
&lt;td&gt;2026.1.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;transformers&lt;/td&gt;
&lt;td&gt;4.57.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vLLM&lt;/td&gt;
&lt;td&gt;0.15.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pin transformers.&lt;/strong&gt; vLLM 0.15.0 needs exactly transformers 4.57.3, and other versions fail with import errors. Install in this order, and force the pin last.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; vllm &lt;span class="nt"&gt;--torch-backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cu128   &lt;span class="c"&gt;# cu121 for RTX 40 series&lt;/span&gt;
uv pip &lt;span class="nb"&gt;install &lt;/span&gt;unsloth unsloth_zoo bitsandbytes
uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--force-reinstall&lt;/span&gt; &lt;span class="s2"&gt;"transformers==4.57.3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Export to LoRA, merged and GGUF with Unsloth
&lt;/h2&gt;

&lt;p&gt;After training, the same model object gives all three formats. These are the calls kllm makes:&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="c1"&gt;# 1. LoRA adapter (~61 MB): for people who already run the base model
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Merged 16-bit model (~41 GB): for Transformers or vLLM, no adapter handling
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained_merged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-merged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;merged_16bit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. GGUF for llama.cpp, Ollama and LM Studio, one call per quantization
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained_gguf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gguf/gpt-oss-20b-stem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantization_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q4_k_m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;quantization_method&lt;/code&gt; takes &lt;code&gt;f16&lt;/code&gt;, &lt;code&gt;q8_0&lt;/code&gt;, &lt;code&gt;q4_k_m&lt;/code&gt; and the other llama.cpp types listed in &lt;a href="https://unsloth.ai/docs/basics/inference-and-deployment/saving-to-gguf" rel="noopener noreferrer"&gt;Unsloth's GGUF docs&lt;/a&gt;. The GGUF files for this model came out at:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;f16&lt;/td&gt;
&lt;td&gt;41.9 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q8_0&lt;/td&gt;
&lt;td&gt;22.3 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q4_K_M&lt;/td&gt;
&lt;td&gt;15.8 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the GGUF step runs out of memory, Unsloth's docs suggest lowering &lt;code&gt;maximum_memory_usage&lt;/code&gt; from its 0.75 default to around 0.5.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does &lt;code&gt;save_pretrained_gguf&lt;/code&gt; include the LoRA adapter?
&lt;/h2&gt;

&lt;p&gt;The three save calls above produce different artifacts. &lt;code&gt;model.save_pretrained(output_dir)&lt;/code&gt; writes the adapter and its configuration; the adapter from this run is about 61 MB and needs the base model to run. &lt;code&gt;model.save_pretrained_merged(...)&lt;/code&gt; writes a full model with the adapter merged. &lt;code&gt;model.save_pretrained_gguf(...)&lt;/code&gt; exports a runnable GGUF, not another small adapter file. The Q4_K_M GGUF here is 15.8 GB.&lt;/p&gt;

&lt;p&gt;Check the export with prompts held out from training. A &lt;a href="https://github.com/unslothai/unsloth/issues/11698" rel="noopener noreferrer"&gt;reported Unsloth PEFT wrapper export issue&lt;/a&gt; describes a path where a GGUF file was produced from base weights without the trained adapter. That report does not establish that this happened to my published model. It is a reason to compare the adapter and GGUF runs before handing either to users.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Harmony chat template is what breaks
&lt;/h2&gt;

&lt;p&gt;gpt-oss doesn't use ChatML. It was trained on OpenAI's &lt;a href="https://developers.openai.com/cookbook/articles/openai-harmony" rel="noopener noreferrer"&gt;Harmony format&lt;/a&gt;, where every message is wrapped in special tokens and the assistant writes on separate channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;analysis&lt;/code&gt; for its chain of thought, which end users shouldn't see&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;final&lt;/code&gt; for the answer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;commentary&lt;/code&gt; for tool calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A final answer ends with &lt;code&gt;&amp;lt;|return|&amp;gt;&lt;/code&gt;, which OpenAI's docs call "a valid stop token indicating that you should stop inference." Messages kept in the conversation history end with &lt;code&gt;&amp;lt;|end|&amp;gt;&lt;/code&gt; instead, while supervised training targets should end with &lt;code&gt;&amp;lt;|return|&amp;gt;&lt;/code&gt;. OpenAI's &lt;a href="https://huggingface.co/openai/gpt-oss-20b" rel="noopener noreferrer"&gt;model card&lt;/a&gt; is blunt about it: the models "should only be used with the harmony format as it will not work correctly otherwise."&lt;/p&gt;

&lt;p&gt;So the template you ship has to match the one used in training. &lt;a href="https://unsloth.ai/docs/basics/inference-and-deployment/saving-to-gguf" rel="noopener noreferrer"&gt;Unsloth's docs&lt;/a&gt; name a mismatch as the most common reason a model gives gibberish or endless output on other platforms. And &lt;code&gt;&amp;lt;|return|&amp;gt;&lt;/code&gt; has to be a stop token, or generation runs past the answer.&lt;/p&gt;

&lt;p&gt;This exact question, how to template a fine-tuned gpt-oss for Ollama, is &lt;a href="https://community.openai.com/t/guidance-needed-gpt-oss-20b-fine-tuning-with-unsloth-gguf-ollama-triton-vllm-tensorrt-llm/1373515" rel="noopener noreferrer"&gt;still open on OpenAI's forum&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A working Ollama Modelfile for fine-tuned gpt-oss
&lt;/h2&gt;

&lt;p&gt;This is the Modelfile that ships with the &lt;a href="https://huggingface.co/khadim-hussain/gpt-oss-20b-stem-reasoning-GGUF" rel="noopener noreferrer"&gt;GGUF repo&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ./gpt-oss-20b-finetuned-q8_0.gguf

TEMPLATE """&amp;lt;|start|&amp;gt;system&amp;lt;|message|&amp;gt;You are a helpful assistant trained by OpenAI.

Reasoning: medium

# Valid channels: analysis, commentary, final. Channel must be included for every message.&amp;lt;|end|&amp;gt;
{{- if .System }}&amp;lt;|start|&amp;gt;developer&amp;lt;|message|&amp;gt;# Instructions

{{ .System }}&amp;lt;|end|&amp;gt;
{{- end }}
{{- range .Messages }}
{{- if eq .Role "user" }}&amp;lt;|start|&amp;gt;user&amp;lt;|message|&amp;gt;{{ .Content }}&amp;lt;|end|&amp;gt;
{{- else if eq .Role "assistant" }}&amp;lt;|start|&amp;gt;assistant&amp;lt;|channel|&amp;gt;final&amp;lt;|message|&amp;gt;{{ .Content }}&amp;lt;|end|&amp;gt;
{{- end }}
{{- end }}&amp;lt;|start|&amp;gt;assistant"""

PARAMETER stop "&amp;lt;|return|&amp;gt;"
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 4096
PARAMETER num_predict 2048
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The template loops over &lt;code&gt;.Messages&lt;/code&gt; so that a conversation keeps Harmony's structure: each earlier answer goes back in as a &lt;code&gt;final&lt;/code&gt;-channel message closed with &lt;code&gt;&amp;lt;|end|&amp;gt;&lt;/code&gt;, and a &lt;code&gt;SYSTEM&lt;/code&gt; line becomes a Harmony &lt;code&gt;developer&lt;/code&gt; message. Ollama's own gpt-oss template does the same and also handles tools and the think level; &lt;code&gt;ollama show gpt-oss:20b --template&lt;/code&gt; prints it if you need those.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why a TEMPLATE with only .Prompt breaks the second message
&lt;/h3&gt;

&lt;p&gt;The first version of this Modelfile used &lt;code&gt;{{ .Prompt }}&lt;/code&gt; alone. A reader, Mihai_LeanZero, pointed out on the Hashnode copy of this post that Ollama treats such a template as a legacy one. Ollama's &lt;code&gt;template.go&lt;/code&gt; appends &lt;code&gt;{{ .Response }}&lt;/code&gt; to any template that mentions neither &lt;code&gt;.Messages&lt;/code&gt; nor &lt;code&gt;.Response&lt;/code&gt;, then renders the whole template once for every earlier user and assistant pair. I checked it on Ollama 0.40.7 with the API's &lt;code&gt;_debug_render_only&lt;/code&gt; option, which returns the prompt without running the model. For a second question, the old template produced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;|start|&amp;gt;system&amp;lt;|message|&amp;gt;…&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;user&amp;lt;|message|&amp;gt;What is DNA?&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;assistantDNA is the molecule that carries genetic information.&amp;lt;|start|&amp;gt;system&amp;lt;|message|&amp;gt;…&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;user&amp;lt;|message|&amp;gt;And RNA?&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The earlier answer has no channel header and no &lt;code&gt;&amp;lt;|end|&amp;gt;&lt;/code&gt;, the system block repeats, and a &lt;code&gt;SYSTEM&lt;/code&gt; message disappears. The template above renders the same conversation as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;|start|&amp;gt;system&amp;lt;|message|&amp;gt;…&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;user&amp;lt;|message|&amp;gt;What is DNA?&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;assistant&amp;lt;|channel|&amp;gt;final&amp;lt;|message|&amp;gt;DNA is the molecule that carries genetic information.&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;user&amp;lt;|message|&amp;gt;And RNA?&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single question renders byte for byte the same with both templates, so &lt;code&gt;ollama run&lt;/code&gt; with one prompt looked fine either way. Chat clients that send the whole history are where the old one went wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting reasoning effort
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Reasoning: medium&lt;/code&gt; in the system message sets how much the model thinks in its &lt;code&gt;analysis&lt;/code&gt; channel before it answers. Harmony accepts &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt; and &lt;code&gt;high&lt;/code&gt;, and medium is the default. To change it, edit that line in the TEMPLATE and run &lt;code&gt;ollama create&lt;/code&gt; again. Ollama shows the analysis channel as "Thinking...", so &lt;code&gt;low&lt;/code&gt; is the setting for short, fast answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  ollama run hf.co or a Modelfile?
&lt;/h2&gt;

&lt;p&gt;With the Modelfile, the template is the one you wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama create gpt-oss-20b-stem &lt;span class="nt"&gt;-f&lt;/span&gt; Modelfile
ollama run gpt-oss-20b-stem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ollama can also pull straight from Hugging Face:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run hf.co/khadim-hussain/gpt-oss-20b-stem-reasoning-GGUF:Q4_K_M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That route doesn't read the Modelfile in the repo. Per &lt;a href="https://huggingface.co/docs/hub/ollama" rel="noopener noreferrer"&gt;Hugging Face's Ollama docs&lt;/a&gt;, it uses the chat template stored in the GGUF's &lt;code&gt;tokenizer.chat_template&lt;/code&gt; metadata, or a file named &lt;code&gt;template&lt;/code&gt; in the repo. The quantization tag is case-insensitive, and without a tag Ollama picks Q4_K_M. If you publish a Harmony model, put a &lt;code&gt;template&lt;/code&gt; file in the repo or point people to the Modelfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which format people actually download
&lt;/h2&gt;

&lt;p&gt;All-time Hugging Face downloads as of October 2026:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;gpt-oss-20b&lt;/th&gt;
&lt;th&gt;Qwen3-14B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GGUF&lt;/td&gt;
&lt;td&gt;513&lt;/td&gt;
&lt;td&gt;274&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merged 16-bit&lt;/td&gt;
&lt;td&gt;70&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA adapter&lt;/td&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most people want a file they can run on their own machine. If you only publish one format, make it a GGUF with a working template. The adapter is worth adding for anyone who wants to keep training.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;The failures documented in kllm's troubleshooting guide:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Training process dies with no error&lt;/td&gt;
&lt;td&gt;Out of memory. Check `dmesg \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;UnboundLocalError&lt;/code&gt; mentioning dropout&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;lora_dropout: 0.0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ImportError: cannot import name ... from 'transformers'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uv pip install --force-reinstall "transformers==4.57.3"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Python.h: No such file or directory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt-get install python3.12-dev&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The full project, with the Qwen3-14B run and the download numbers, is in the &lt;a href="https://khadim.tech/work/stem-reasoning-llms" rel="noopener noreferrer"&gt;STEM reasoning LLMs case study&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you need a model trained and tested for your own task, see my &lt;a href="https://khadim.tech/services/llm-fine-tuning" rel="noopener noreferrer"&gt;LLM fine-tuning service&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much VRAM do you need to fine-tune gpt-oss-20b?
&lt;/h3&gt;

&lt;p&gt;With QLoRA, Unsloth's gpt-oss guide puts gpt-oss-20b at about 14 GB of VRAM and recommends at least 16 GB for stable runs. LoRA on BF16 weights needs about 44 GB. kllm, the toolkit I trained with, is verified on an RTX 5090 with 32 GB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I run a fine-tuned gpt-oss-20b in Ollama?
&lt;/h3&gt;

&lt;p&gt;Yes. Export it to GGUF, then either run ollama create with a Modelfile that carries the Harmony template, or pull it with ollama run hf.co//:Q4_K_M. The second route uses the chat template embedded in the GGUF file, not the Modelfile in the repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does my fine-tuned gpt-oss model output gibberish or never stop?
&lt;/h3&gt;

&lt;p&gt;Usually the chat template. The template at inference has to match the one used in training, and Harmony ends a final answer with &amp;lt;|return|&amp;gt;. If that token isn't a stop token, generation runs past the answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which GGUF quantization should I use for gpt-oss-20b?
&lt;/h3&gt;

&lt;p&gt;Q4_K_M (15.8 GB for this model) is the smallest file and the one Ollama picks by default. Use Q8_0 (22.3 GB) if you have the memory and want output closer to the 16-bit model.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I set reasoning effort for gpt-oss in Ollama?
&lt;/h3&gt;

&lt;p&gt;Harmony reads it from the system message as Reasoning: low, medium or high, and medium is the default. In a Modelfile, change that line inside the TEMPLATE and run ollama create again.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>machinelearning</category>
      <category>ollama</category>
      <category>python</category>
    </item>
  </channel>
</rss>
