<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://thakicloud.com/tech-blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://thakicloud.com/tech-blog/" rel="alternate" type="text/html" /><updated>2026-08-04T03:37:05+09:00</updated><id>https://thakicloud.com/tech-blog/feed.xml</id><title type="html">Thaki Cloud Tech Blog | ThakiCloud | 다키클라우드 기술 블로그</title><subtitle>Thaki Cloud (ThakiCloud, 다키클라우드, thaki cloud, THAKI CLOUD, ثاكي كلاود)는 AI/ML Engineering, LLMOps, DevOps 분야의 최신 기술과 실무 경험을 공유하는 전문 기술 블로그입니다. 머신러닝 모델 운영, 쿠버네티스, 클라우드 인프라, AI 엔지니어링 커리어, 인공지능 기술 블로그, 다키클라우드 개발 팀의 깊이 있는 인사이트를 제공합니다. مدونة تقنية متخصصة في هندسة الذكاء الاصطناعي والحوسبة السحابية.</subtitle><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><entry xml:lang="en"><title type="html">Fifteen Seconds of 2K Video With Stereo Sound: What It Takes to Bring MiniMax-H3 In-House</title><link href="https://thakicloud.com/tech-blog/en/llmops/minimax-h3-omni-modal-onprem-serving/" rel="alternate" type="text/html" title="Fifteen Seconds of 2K Video With Stereo Sound: What It Takes to Bring MiniMax-H3 In-House" /><published>2026-08-04T00:00:00+09:00</published><updated>2026-08-04T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/en/llmops/minimax-h3-omni-modal-onprem-serving</id><content type="html" xml:base="https://thakicloud.com/tech-blog/en/llmops/minimax-h3-omni-modal-onprem-serving/"><![CDATA[<p>When an open-weight model ships, the first sentence going around is usually some version of this: now anyone can run it on their own servers. MiniMax H3, released on 31 July 2026, got the same sentence. It understands text, images, video, and audio in one context, and generates video up to 2K resolution for as long as 15 seconds with native stereo audio. But between the fact that weights are public and the fact that it runs on your cluster lies a distance you only learn by opening the file listing and doing the arithmetic.</p>

<p><img src="/tech-blog/assets/images/minimax-h3-omni-modal-onprem-serving-hero.png" alt="Abstract illustration of layered frame ribbons and a waveform band braided into a single strand" /></p>

<p><em>Producing picture and sound from one sequence rather than making them separately and stitching them is where H3’s design starts.</em></p>

<h2 id="why-this-matters">Why This Matters</h2>

<p>This is for people running an in-house GPU cluster who must decide whether to self-host a video generation model or call an API. The conclusion first: H3-Base is a 33B-parameter single-stream transformer, so on paper it resembles LLM serving, but the real bottleneck is not the weights, it is sequence length. A single 15-second 2K clip produces a sequence of over 325,000 tokens, and the initial release ships without the sparse attention implementation that would make it affordable.</p>

<h2 id="overview">Overview</h2>

<p>MiniMax H3 is a general-purpose omni-modal generative system. It jointly understands multimodal context made of text, images, video, and audio, and generates video with stereo audio from it. Output specifications are 4 to 15 seconds, 24 FPS, 32 kHz stereo audio, with a default short side of 768 pixels. It supports aspect ratios from 21:9 through 9:16, and dialogue in 11 languages with stable support, including Arabic and Korean.</p>

<p>What separates it from earlier video generators is that there is no seam where the modalities were sewn together. Text, vision, and audio tokens do not sit in separate silos and merge at the end; they share one transformer stream. In the model card’s phrasing, neither the attention layers nor the FFN layers contain modality-specific structures, and modality-specific parameters live only in the input and output layers and the AdaLN branches.</p>

<p>The license is the MiniMax H3 Community License. It is not a standard open-source license like Apache or MIT but a bespoke community license, so read the clauses first if you are considering commercial distribution.</p>

<h2 id="what-the-system-is">What the System Is</h2>

<p>H3 is not one model but a system of three modules. This distinction is the first thing to know when planning self-hosting, because only the middle one is open source.</p>

<pre><code class="language-mermaid">flowchart TB
    A["User input&lt;br/&gt;text · up to 9 images&lt;br/&gt;up to 3 video clips · up to 3 audio clips"] --&gt; B
    B["H3-Context-IR&lt;br/&gt;instruction parsing · cross-modal association · temporal understanding&lt;br/&gt;not released, API only"] --&gt; C
    C["Context Intermediate Representation&lt;br/&gt;structured intermediate form"] --&gt; D
    D["H3-Base&lt;br/&gt;H3-Encoder + H3-VisualVAE + H3-AudioVAE&lt;br/&gt;+ H3-Omni-Transformer 33B&lt;br/&gt;open weights released"] --&gt; E
    E["768p output&lt;br/&gt;video + 32 kHz stereo"] --&gt; F
    F["H3-Regenerate-2K&lt;br/&gt;regenerates with the original context&lt;br/&gt;not released, API only"] --&gt; G["Final 2K output"]
    E -.local deployment reaches this far.-&gt; H["Self-hosting boundary"]
</code></pre>

<p>H3-Context-IR is a hosted preprocessing and orchestration system for free-form multimodal input. Because it relies on a multi-stage workflow and multiple hosted models and services, it was not part of this release. MiniMax provides an API that reproduces the official workflow, plus prompting guidance for building your own preprocessing system. But as the model card states explicitly, H3-Context-IR is critical to final output quality, so running H3-Base without it will not match the official demos.</p>

<p>H3-Regenerate-2K is also unreleased. The interesting part is that it is not a separate super-resolution module. It feeds the 768p result back into H3 along with the original multimodal context and regenerates at 2K. The advantage is that small text and fine detail, which conventional super-resolution has to guess at, can be recovered from the original context.</p>

<p>The middle piece, H3-Base, is what was released. Text is encoded by the H3-Encoder, which uses the full pretrained weights of Qwen3-VL-32B and passes hidden states from its 50th layer to the transformer. Visual inputs go through both the H3-Encoder and the H3-VisualVAE; audio goes through the H3-AudioVAE alone. The H3-Omni-Transformer then jointly predicts video and audio latents.</p>

<p>The VAE specifications matter for the arithmetic later. H3-VisualVAE is a temporally causal video autoencoder with 16x spatial compression, 4x temporal compression, and 24 latent channels. Patchification of 1x2x2 along time, height, and width is applied on top, so visual tokens entering the transformer have an effective spatial downsampling factor of 32x. The temporal factor stays at 4x. H3-AudioVAE uses the same encoder and decoder for the left and right channels while processing each independently, compressing 32 kHz audio into latent tokens at 40 Hz per channel.</p>

<h2 id="installation-and-integration">Installation and Integration</h2>

<p>Total weight first. We pulled the file manifest from the HuggingFace API and summed only the safetensors.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> <span class="s2">"https://huggingface.co/api/models/MiniMaxAI/MiniMax-H3?blobs=true"</span> <span class="se">\</span>
  | jq <span class="s1">'[.siblings[] | select(.rfilename|endswith(".safetensors"))
         | {f:.rfilename, b:(.lfs.size // .size)}]'</span>
</code></pre></div></div>

<p>The calculation script lives in the ThakiCloud repository. It sums bytes per module and derives sequence lengths in the same run.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.venv/bin/python scripts/experiments/minimax_h3_serving_budget.py
</code></pre></div></div>

<p>The minimal Diffusers path is in the model card.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install</span> <span class="nt">-U</span> diffusers transformers accelerate
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">torch</span>
<span class="kn">from</span> <span class="n">diffusers</span> <span class="kn">import</span> <span class="n">DiffusionPipeline</span>

<span class="n">pipe</span> <span class="o">=</span> <span class="n">DiffusionPipeline</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span>
    <span class="sh">"</span><span class="s">MiniMaxAI/MiniMax-H3</span><span class="sh">"</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="n">bfloat16</span><span class="p">,</span> <span class="n">device_map</span><span class="o">=</span><span class="sh">"</span><span class="s">cuda</span><span class="sh">"</span>
<span class="p">)</span>
</code></pre></div></div>

<p>To be straightforward about it: we did not load H3-Base and run inference for this piece. We computed the resources it requires below, but we did not attach a node of that size to this task. So there are no numbers here on generation quality or measured latency. What there is comes entirely from published file sizes and published compression factors, derived deterministically.</p>

<h2 id="measured-results">Measured Results</h2>

<p>The safetensors across the whole repository total 464 GiB. Do not read that as the required capacity, though, because the same weights are laid out under FL2VA, Ref2VA, and the repository root. Deploying one variant actually requires this much.</p>

<table>
  <thead>
    <tr>
      <th>Module</th>
      <th>bf16 weights</th>
      <th>Parameters (derived)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>H3-Omni-Transformer (H3-Base)</td>
      <td>61.73 GiB</td>
      <td>33.14B</td>
    </tr>
    <tr>
      <td>H3-Encoder (Qwen3-VL-32B)</td>
      <td>62.13 GiB</td>
      <td>33.36B</td>
    </tr>
    <tr>
      <td>H3-VisualVAE</td>
      <td>9.70 GiB</td>
      <td>5.21B</td>
    </tr>
    <tr>
      <td>H3-AudioVAE</td>
      <td>0.56 GiB</td>
      <td>0.30B</td>
    </tr>
    <tr>
      <td><strong>Single variant total</strong></td>
      <td><strong>134.12 GiB</strong></td>
      <td>71.9B</td>
    </tr>
  </tbody>
</table>

<p>The 33.14B derived from bytes matches the 33B the model card states, which confirms the calculation path.</p>

<p>One sentence in the model card becomes important here. Of the transformer’s 33B, roughly 13B sits in AdaLN-related branches, and because AdaLN modulation outputs can be precomputed and cached, those parameters do not need to be loaded for inference-only deployment. The full weights were released to support downstream development including fine-tuning. So if you only plan to run inference, the transformer side drops to about 20B, or 37.5 GiB in bf16.</p>

<p><img src="/tech-blog/assets/images/minimax-h3-omni-modal-onprem-serving-results.png" alt="Chart of per-module weights measured from the manifest and token sequence lengths per clip configuration" /></p>

<p><em>Left is per-module weights summed from the file manifest; right is sequence length derived from the VAE compression factors.</em></p>

<p>But the real problem is not the weights. It is the sequence. Applying the compression factors above:</p>

<table>
  <thead>
    <tr>
      <th>Clip configuration</th>
      <th>Latent frames</th>
      <th>Video tokens</th>
      <th>Audio tokens</th>
      <th>Total</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>768p 16:9, 4s</td>
      <td>24</td>
      <td>24,768</td>
      <td>320</td>
      <td>25,088</td>
    </tr>
    <tr>
      <td>768p 16:9, 15s</td>
      <td>90</td>
      <td>92,880</td>
      <td>1,200</td>
      <td>94,080</td>
    </tr>
    <tr>
      <td>2K 16:9, 15s</td>
      <td>90</td>
      <td>324,000</td>
      <td>1,200</td>
      <td>325,200</td>
    </tr>
  </tbody>
</table>

<p>Here is how to read it. A 15-second clip is 360 source frames, which 4x temporal compression turns into 90 latent frames. Taking 768p 16:9 as 1376 by 768, the effective 32x spatial compression gives 43 by 24, or 1,032 tokens per latent frame. Multiplied by 90 latent frames that is 92,880, plus 1,200 audio tokens. Going to 2K pushes tokens per latent frame to 3,600, for a total of 325,200.</p>

<p>Token count grew 3.5x, but full attention cost grows quadratically, roughly 12x. And here the model card states something important. Native sparse attention was introduced in the final stage of training to cut the cost of long sequences, but <strong>the initial open-source release provides inference with full attention only</strong>, with the sparse-attention implementation to be published separately later.</p>

<p>That is the single most important sentence for a self-hosting plan. The gap between what the official API produces and what you can produce locally with the weights you just downloaded is not only a quality gap but a compute-cost gap. Processing a 94,000-token sequence with full attention is a completely different budget from having the sparse implementation.</p>

<h2 id="what-this-means-for-thakicloud-products">What This Means for ThakiCloud Products</h2>

<p>ThakiCloud’s ai-platform schedules GPU workloads on K8s and Kueue and runs multi-tenant vLLM-based serving. A model like H3 makes several concrete demands of that structure.</p>

<p>First, the deployment unit changes. At 134 GiB, a single variant nearly fills one H200’s 141 GB with weights alone. Once you account for activations and attention workspace for a 94,000-token sequence, one card is not enough, and even dropping AdaLN for inference-only leaves little headroom. A realistic configuration is two or more H200s, and serving both the FL2VA and Ref2VA variants costs proportionally more. Expressed as a Kueue workload, this model is hard to place in the same queue as LLM serving pods. Per-request occupancy is long and the memory profile differs, so a separate resource flavor is the better split.</p>

<p>Second, queue design changes. LLM serving streams token by token, so requests pass through in short bursts, whereas video generation holds a GPU for a long stretch on a single request. On top of that, the 2K workflow adds a regeneration pass after the 768p generation. Since user-visible response time is the sum of both stages, the queueing policy should be designed around completion time rather than throughput.</p>

<p>Third, it fits on-premise demand well. Video generation material is often sensitive by nature. Raw footage, product imagery, and video containing in-house individuals are assets that are hard to send to an external API. Being able to run an open-weight model inside the customer’s boundary satisfies that requirement on its own. That said, with H3-Context-IR and H3-Regenerate-2K unreleased, a fully on-premise configuration is not currently possible. You must build your own preprocessing system following the prompting guidance, and 2K either goes through the API or you settle for 768p. Explaining that gap accurately to customers is the most honest thing to do at this stage.</p>

<p>There is one thing to add from the Paxis lens. What H3-Context-IR does, namely parsing free-form multimodal input, interpreting cross-modal relationships, and serializing them into a structured intermediate representation, is really an agent orchestration problem. Its being unreleased means, conversely, that the slot is open for you to fill. Using Paxis’s DAG multi-agent composition and policy gates, you can make the input refinement pipeline auditable and trace which prompt enrichment led to which output. For an organization that must answer for generated results, that traceability is a prerequisite ahead of image quality.</p>

<h2 id="limits-and-counterarguments">Limits and Counterarguments</h2>

<p>The numbers here have clear boundaries. We did not load the model and produce video, so generation quality, real latency, and real peak memory are absent. Weight sizes come exactly from the file manifest, but activations and attention workspace vary greatly with implementation. The 134 GiB above is therefore a floor on required VRAM, not the actual requirement.</p>

<p>The sequence length calculation also rests on an assumption. We took 768p 16:9 as 1376 by 768 pixels, but the model card states only that the short side is 768, so the long side may differ slightly by implementation. The effective 32x spatial and 4x temporal compression are published values, however, so the order of magnitude does not move.</p>

<p>The license deserves a note as well. The MiniMax H3 Community License is not a standard open-source license and includes use restrictions. The model card states that user-submitted text, images, and videos, as well as enhanced prompts, are subject to automated moderation, and content suspected of being unlawful, pornographic, or infringing third-party rights may be blocked. It also states explicitly that these guardrails do not replace the licensee’s obligations. If you are planning a commercial service, legal review comes before technical review.</p>

<p>Finally, self-hosting does not always win. Without sparse attention, the cost of producing a 15-second 2K clip locally may not compare favorably to API pricing. If generation is infrequent and the material is not sensitive, the API is the reasonable choice. The case for self-hosting is usually about the data boundary, not unit price.</p>

<h2 id="conclusion">Conclusion</h2>

<p>MiniMax H3 is an omni-modal model that produces picture and sound in one stream, and its core, H3-Base at 33B, has been released with open weights. Three numbers matter when you evaluate self-hosting: 134 GiB of weights for a single variant, 94,000 tokens for a 15-second 768p clip, and 325,000 tokens when you go to 2K. One condition attaches to all of them: the initial release has no sparse attention, so those sequences must be processed with full attention.</p>

<p>If you take one action, take this one. Rather than deciding whether to adopt H3 now, first confirm whether the material you need video generation for genuinely cannot leave your boundary. If it cannot, a local H3-Base 768p deployment with your own preprocessing pipeline is something you can start today. If it can, you are better off waiting for the sparse attention implementation and the other two modules, then running the numbers again. When that time comes, just rerun the script from this post.</p>

<h2 id="sources">Sources</h2>

<ul>
  <li>Model card: <a href="https://huggingface.co/MiniMaxAI/MiniMax-H3">MiniMaxAI/MiniMax-H3 on HuggingFace</a></li>
  <li>Official announcement: <a href="https://www.minimax.io/blog/minimax-h3">MiniMax H3: An Open Model Breaking the Boundaries Between Tasks and Modalities</a></li>
  <li>File manifest API: <code class="language-plaintext highlighter-rouge">https://huggingface.co/api/models/MiniMaxAI/MiniMax-H3?blobs=true</code></li>
  <li>Calculation script for this post: <code class="language-plaintext highlighter-rouge">scripts/experiments/minimax_h3_serving_budget.py</code> (ThakiCloud internal repository)</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="llmops" /><category term="MiniMax-H3" /><category term="omni-modal" /><category term="video-generation" /><category term="on-prem-serving" /><category term="GPU-capacity-planning" /><category term="sparse-attention" /><category term="VAE" /><category term="ai-platform" /><summary type="html"><![CDATA[Between weights are public and it runs on our servers lies a distance you only learn by doing the arithmetic.]]></summary></entry><entry xml:lang="en"><title type="html">Write Skills Down or Bake Them Into Weights? Google’s Third Answer</title><link href="https://thakicloud.com/tech-blog/en/research/skillsmith-parametric-skill-synthesis/" rel="alternate" type="text/html" title="Write Skills Down or Bake Them Into Weights? Google’s Third Answer" /><published>2026-08-04T00:00:00+09:00</published><updated>2026-08-04T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/en/research/skillsmith-parametric-skill-synthesis</id><content type="html" xml:base="https://thakicloud.com/tech-blog/en/research/skillsmith-parametric-skill-synthesis/"><![CDATA[<p>There have long been two ways to give an agent a skill. One is to write it down. You keep a task description, a handful of examples, and a reflection note about why the last attempt failed, then load that document into context when you need it. The other is to bake it into weights. For every recurring sub-goal you train a LoRA or prefix module, file it in an adapter drawer, and pull it out on demand. These two approaches have been discussed in separate conference tracks for over a decade, and in practice teams usually treat them as an either-or decision. A Google DeepMind paper posted to arXiv on 29 July 2026 revisits the choice itself.</p>

<p><img src="/tech-blog/assets/images/skillsmith-parametric-skill-synthesis-hero.png" alt="Abstract illustration of two streams of material meeting at an anvil and being forged into a single crystal" /></p>

<p><em>What happens when you put written-down skills and baked-in skills into the same forge is this paper’s question.</em></p>

<h2 id="why-this-matters">Why This Matters</h2>

<p>This is for people running a skill library behind an agent: you have accumulated hundreds of document skills like SKILL.md files, or you manage per-task LoRA adapters. The conclusion first: the two are not substitutes but complements that cannot cover for each other, and there is a real performance band that appears only when both are fed to one model at once. In the paper’s ablation, the gap between text-only and both-modalities was more decisive than the gap between text-only and weights-only.</p>

<h2 id="overview">Overview</h2>

<p>The paper is titled “SkillSmith: Learning to Compose Parametric Skills and Textual Knowledge,” arXiv 2607.27497. Lucio M. Dery and Benedict Aaron Tjandra are joint first authors, with Adhiguna Kuncoro, Arthur Szlam and others making seven contributors. It is filed under cs.CL, and the authors’ own keywords are model merging, kv-caches, continual learning, and prefix tuning.</p>

<p>The situation the paper describes is this. LLM-driven agents learn from past experience along two routes: one synthesizes knowledge in natural language through self-reflection, structured memory, and prompt optimization; the other freezes the behavior of recurring tasks into module weights via PEFT. There is no bridge between them. The text route is flexible but bounded by inference-time context limits. The weight route is efficient at inference, but merging stays at arithmetic like averaging or concatenation and cannot use the semantic relationships between tasks at all.</p>

<p>The paper’s example is intuitive. Suppose an agent trained a prefix cache for English-to-Twi translation in one session, and in another session compiled notes on analyzing English legal documents. Now a new task arrives: analyze a legal document written in Twi. The agent can state in words that this task is a combination of translation ability, Twi language modeling, and legal document analysis. What it cannot do is turn that statement into actual weights. Knowing in words and doing in weights are disconnected.</p>

<h2 id="what-the-technique-is">What the Technique Is</h2>

<p>SkillSmith’s idea reduces to one sentence. Treat weights as another modality the LLM already knows how to read.</p>

<p>Concretely, parametric skills are instantiated through prefix-tuning. Choosing prefix over LoRA has a clear reason. A snippet of text becomes a K-V cache the moment you run one forward pass through the base model, which puts text-derived caches and trained caches in the same space. For a goal of bridging two modalities, no representation is more natural.</p>

<p>SkillSmith receives source task bundles. Each bundle is one trained prefix K-V cache plus textual metadata describing that task. On top of that comes combination text describing how the source tasks relate to the target capability. These ingredients are woven into a single sequence using control tokens and passed once through a coprocessor LLM.</p>

<pre><code class="language-mermaid">flowchart TB
    A["Source task bundle 1&lt;br/&gt;text metadata + prefix K-V"] --&gt; D
    B["Source task bundle 2&lt;br/&gt;text metadata + prefix K-V"] --&gt; D
    C["Combination text&lt;br/&gt;how sources relate to target&lt;br/&gt;+ target task exemplars"] --&gt; D
    D["Input K-V adapter (MLP)&lt;br/&gt;projects weights into the LM latent space"] --&gt; E
    E["Single sequence woven with control tokens&lt;br/&gt;preamble · interleaved bundles · combination · generation block"] --&gt; F
    F["SkillSmith coprocessor&lt;br/&gt;Gemma 3 4B, one forward pass"] --&gt; G
    G["Extract K-V at placeholder positions&lt;br/&gt;strip position info via inverse RoPE"] --&gt; H
    H["Output K-V adapter (MLP)"] --&gt; I["New prefix weights&lt;br/&gt;length 32"]
    I --&gt; J["Attach to frozen base model&lt;br/&gt;solve the target task"]
</code></pre>

<p>The sequence has rules. A preamble describing the compositional objective comes first. Then, for each source bundle, the text begins with a <code class="language-plaintext highlighter-rouge">&lt;src_start&gt;</code> token and the projected K-V sits between <code class="language-plaintext highlighter-rouge">&lt;kv_start&gt;</code> and <code class="language-plaintext highlighter-rouge">&lt;kv_end&gt;</code>. After the bundles comes the combination text, and after <code class="language-plaintext highlighter-rouge">&lt;gen_start&gt;</code> follows a fixed-length run of placeholder tokens. After the forward pass, only the K-V at those placeholder positions is extracted, stripped of position information via inverse RoPE, and passed through the output adapter to yield the new prefix weights.</p>

<p>Training is end to end. The generated cache is attached to the frozen base model, cross-entropy loss on the target task is computed, and that loss is backpropagated through the base model into SkillSmith. The base model’s weights stay fixed throughout.</p>

<p>The evaluation setup is worth noting. Source task prefix lengths are sampled at random from 32, 64, and 128 to inject diversity, and both the coprocessor and the downstream model are Gemma 3 4B. The number of source tasks is fixed at two. And prefix K-V is trained only on the global attention layers, because a prefix attached to a local attention layer eventually slides out of context.</p>

<h2 id="installation-and-integration">Installation and Integration</h2>

<p>The paper released neither code nor checkpoints, so we could not reproduce SkillSmith itself. Instead we computed what it actually costs to carry one skill under the conditions the paper pinned down: a Gemma 3 4B base, global layers only, prefix length 32. Operationally this number is arguably the one you need before the paper’s Elo figures.</p>

<p>First pull the real Gemma 3 4B settings.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> https://huggingface.co/unsloth/gemma-3-4b-it/raw/main/config.json | jq <span class="s1">'.text_config'</span>
<span class="c"># num_hidden_layers: 34, num_key_value_heads: 4, head_dim: 256,</span>
<span class="c"># sliding_window_pattern: 6, torch_dtype: "bfloat16"</span>
</code></pre></div></div>

<p>Because <code class="language-plaintext highlighter-rouge">sliding_window_pattern</code> is 6, one layer in every six is global attention. Of 34 layers, five are global and the remaining twenty-nine are local. The paper pins prefix training to global layers only, so the parameters actually trained cover five layers.</p>

<p>The calculation script lives in the ThakiCloud repository.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.venv/bin/python scripts/experiments/skillsmith_prefix_budget.py
</code></pre></div></div>

<p>It measures two things together. One is the parameter count and byte size of a single prefix K-V under the settings above. The other is the real size of a local skill corpus. The ThakiCloud workspace currently holds 1,911 SKILL.md files under <code class="language-plaintext highlighter-rouge">.claude/skills/</code>. Loading a text skill into context means those tokens also become resident K-V, which lets us compare both approaches on the same unit.</p>

<h2 id="measured-results">Measured Results</h2>

<p>Our own budget first.</p>

<table>
  <thead>
    <tr>
      <th>Item</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Global layers in Gemma 3 4B</td>
      <td>5 (out of 34)</td>
    </tr>
    <tr>
      <td>Prefix length 32, global layers only</td>
      <td>327,680 parameters</td>
    </tr>
    <tr>
      <td>Same setting in bf16</td>
      <td>640 KiB</td>
    </tr>
    <tr>
      <td>Share of the base model</td>
      <td>0.0076 percent</td>
    </tr>
    <tr>
      <td>At prefix length 128</td>
      <td>1,310,720 parameters (2,560 KiB)</td>
    </tr>
    <tr>
      <td>For reference, all 34 layers</td>
      <td>2,228,224 parameters (4,352 KiB)</td>
    </tr>
  </tbody>
</table>

<p>On the text side: the median of 1,911 local SKILL.md files is 6,173 characters, which at a conservative 4 characters per token is roughly 1,543 tokens. Keeping that resident in a Gemma 3 4B context puts K-V on all 34 layers, about 205 MiB. Against 640 KiB for the same skill carried parametrically, that is a factor of 328. By sequence position it is 1,543 versus 32, a factor of 48.</p>

<p><img src="/tech-blog/assets/images/skillsmith-parametric-skill-synthesis-results.png" alt="Chart comparing the paper's input-ablation Elo with ThakiCloud's computed per-skill residency cost" /></p>

<p><em>Left is the ablation from the paper’s Table 1; right is the residency cost computed by the script above.</em></p>

<p>Now the paper’s numbers. To check whether SkillSmith really uses the K-V caches, the authors removed inputs one at a time. Elo measured on the Composite-SNI meta-evaluation tasks:</p>

<table>
  <thead>
    <tr>
      <th>Input configuration</th>
      <th>Elo</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>No inputs</td>
      <td>1209</td>
    </tr>
    <tr>
      <td>K-V caches only</td>
      <td>1455</td>
    </tr>
    <tr>
      <td>Everything except K-V caches</td>
      <td>1622</td>
    </tr>
    <tr>
      <td>All inputs</td>
      <td>1714</td>
    </tr>
  </tbody>
</table>

<p>How to read this matters. Text only (1622) beats weights only (1455), meaning text is the richer signal. But both together reach 1714, which is 92 points above text alone. Those 92 points earned by adding weights are this paper’s central claim. As the authors note, the K-V-only setting is effectively equivalent to the existing ATTEMPT approach of combining PEFT modules with a parametric function instead of arithmetic. So this table lays the ceiling of existing weight-merging methods along the bottom and shows the lift from adding text.</p>

<p>The authors also tested the obvious objection that the gain merely comes from having more text. They extracted all source text and combination text fed to SkillSmith, prepended it directly to the target task inputs, and trained an ordinary prefix cache. Auxiliary text did improve the baseline, but it did not catch SkillSmith. Text simply being present and text being synthesized together with weights are different things.</p>

<p>There is a generalization check too. The 15 meta-evaluation tasks were re-scored split by whether neither, one, or both parent tasks appeared in meta-training, and SkillSmith led in all three. Winning by a large margin even where it had never seen the parent tasks is the notable part.</p>

<p>It did not win on every dataset, however. On the real SNI dataset, once downstream fine-tuning was allowed, the win rate among the top three methods converged to 0.5. The authors attribute this to SNI having on the order of 1,000 instances per task and, having been built in 2022, consisting mostly of primitive work like sentiment classification and character concatenation that is trivial for contemporary models. On MMLU-ProX, with only about 250 tasks and higher difficulty, the gap reopened. There, the variant trained on MMLU-ProX alone without bootstrapping from a Composite-SNI checkpoint reached only 1736 zero-shot Elo.</p>

<h2 id="what-this-means-for-thakicloud-products">What This Means for ThakiCloud Products</h2>

<p>This paper touches both ThakiCloud products.</p>

<p><strong>The Paxis lens</strong> comes first. Paxis is ThakiCloud’s Agent-Native Cloud control plane, treating Skills, Tools, Policies, and Audit Logs as first-class resources. At its center, the Skill Harness currently selects text skills via BM25 and runs them in an isolated sandbox. This paper touches exactly that structure’s cost curve, and the factor of 328 measured above is the reason. As long as skills are carried as text, context budget decides how many can be active at once. That is why a router is needed, and when the router is wrong the turn simply fails.</p>

<p>Parametric skills move that constraint to a different axis. With a 32-position prefix, eight active skills is 256 positions and 5 MiB. To be honest, though, this is complementary rather than a replacement. The paper’s own ablation says text-only beats weights-only, so abandoning text skills for weights is not a direction this paper supports. What it supports is placing a parametric counterpart alongside the text skill the router selected. Since Paxis’s self-evolving skill pipeline already creates, revises, and evaluates skills, adding a prefix cache to that pipeline’s outputs is an extension of an existing artifact list rather than a new architecture.</p>

<p><strong>The ai-platform lens</strong> is about serving. A prefix cache is a per-request adapter, not a model. Keeping thousands of 640 KiB objects per tenant and attaching one on request is structurally the same as multi-adapter LoRA serving. ThakiCloud’s ai-platform already runs multi-tenant vLLM serving on K8s and Kueue, so sharing one base model while attaching a different skill set per tenant does not depart far from the current design. This matters most in on-premise and sovereign environments. Instead of fine-tuning a separate model per customer and consuming GPUs for each, you share one base model and manage per-customer skills as KiB-scale objects, so GPU occupancy no longer scales with customer count.</p>

<p>The two lenses connect. When skills are cheap to carry (ai-platform), an agent can carry more of them (Paxis), and when it carries more, being wrong about which one to pick costs less.</p>

<h2 id="limits-and-counterarguments">Limits and Counterarguments</h2>

<p>The biggest constraint is reproducibility. Neither code nor checkpoints were released, and SkillSmith requires end-to-end meta-training that backpropagates through the base model. You must first build a per-task prefix library, and the combination text has to be generated with Gemini 2.5. The description of roughly 350K initial synthetic tasks suggests the preparation alone is a substantial investment. It is early to expect the same results in your own domain.</p>

<p>Second, the experimental scale is small. Both the coprocessor and downstream model are a single 4B model, and the meta-evaluation task counts are 15, 10, and 6. Elo is a relative metric that says nothing about absolute performance, and its values shift when the candidate set changes. The number of source tasks is fixed at two, so this paper alone cannot tell us whether the approach extends to the multi-skill compositions real agents face.</p>

<p>Third, the convergence seen on SNI narrows the applicable range. When data is plentiful and tasks are easy, direct training reaches the same place. SkillSmith earns its keep where tasks are hard and instances are scarce. Plenty of in-house domain work fits that description, but there is no reason to route every task through this method.</p>

<p>Finally, the factor of 328 computed above is a difference in storage and residency, not in capability. It absolutely does not mean a 640 KiB prefix does the same job as a 205 MiB text context. The paper’s ablation says precisely the opposite, and the calculation here exists to put both approaches on the same ruler, not to justify a replacement.</p>

<h2 id="conclusion">Conclusion</h2>

<p>For a long time, giving an agent a skill meant choosing between documents and weights. This paper puts a third answer on the table. Make weights readable to an LLM, and the relationship descriptions you wrote down become instructions that produce actual weights. The 92 points in the ablation are evidence that those instructions were not empty talk.</p>

<p>If you are running a skill library today, check one thing. Do your skill documents contain a sentence describing how this skill connects to other skills? What SkillSmith actually consumed was not the task description but the description of relationships between tasks. If that sentence is missing, the most important ingredient will be absent when you later try to attach parametric synthesis. If it is already there, baking one more prefix cache into your skill pipeline is a closer next step than it looks.</p>

<h2 id="sources">Sources</h2>

<ul>
  <li>Paper: <a href="https://arxiv.org/abs/2607.27497">SkillSmith: Learning to Compose Parametric Skills and Textual Knowledge (arXiv:2607.27497)</a></li>
  <li>Authors: Lucio M. Dery, Benedict Aaron Tjandra, Siavash Samiei, Adhiguna Kuncoro, Zohar Yahav, Jiajun Shen, Arthur Szlam (Google DeepMind), submitted 29 July 2026</li>
  <li>Base model config: <a href="https://huggingface.co/unsloth/gemma-3-4b-it/raw/main/config.json">Gemma 3 4B config.json</a></li>
  <li>Calculation script for this post: <code class="language-plaintext highlighter-rouge">scripts/experiments/skillsmith_prefix_budget.py</code> (ThakiCloud internal repository)</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="research" /><category term="SkillSmith" /><category term="prefix-tuning" /><category term="parametric-skills" /><category term="skill-composition" /><category term="PEFT" /><category term="KV-cache" /><category term="Gemma-3" /><category term="Paxis" /><category term="ai-platform" /><summary type="html"><![CDATA[If you have ever weighed whether to store agent skills as documents or as adapter weights, this paper questions the premise that you must choose.]]></summary></entry><entry xml:lang="ko"><title type="html">15초 2K 영상에 스테레오 소리까지, MiniMax-H3를 사내에 들이면 무엇이 필요한가</title><link href="https://thakicloud.com/tech-blog/ko/llmops/minimax-h3-omni-modal-onprem-serving/" rel="alternate" type="text/html" title="15초 2K 영상에 스테레오 소리까지, MiniMax-H3를 사내에 들이면 무엇이 필요한가" /><published>2026-08-04T00:00:00+09:00</published><updated>2026-08-04T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/ko/llmops/minimax-h3-omni-modal-onprem-serving</id><content type="html" xml:base="https://thakicloud.com/tech-blog/ko/llmops/minimax-h3-omni-modal-onprem-serving/"><![CDATA[<p>오픈웨이트 모델이 공개되면 가장 먼저 도는 문장은 대개 이런 형태입니다. 이제 누구나 자기 서버에서 돌릴 수 있습니다. MiniMax가 2026년 7월 31일 공개한 H3에도 같은 문장이 붙었습니다. 텍스트와 이미지, 영상, 오디오를 한 컨텍스트에서 이해하고 최대 2K 해상도로 15초짜리 영상을 네이티브 스테레오 오디오와 함께 생성하는 모델입니다. 그런데 가중치가 공개됐다는 사실과 우리 클러스터에서 돌아간다는 사실 사이에는, 파일 목록을 열어 보고 계산해 봐야 알 수 있는 거리가 있습니다.</p>

<p><img src="/tech-blog/assets/images/minimax-h3-omni-modal-onprem-serving-hero.png" alt="겹겹의 프레임 띠와 파형 띠가 하나의 가닥으로 꼬여 흐르는 모습을 형상화한 추상 이미지" /></p>

<p><em>영상과 소리를 따로 만들어 붙이는 대신 하나의 시퀀스에서 함께 뽑는다는 것이 H3 설계의 출발점입니다.</em></p>

<h2 id="왜-읽어야-하나">왜 읽어야 하나</h2>

<p>사내 GPU 클러스터를 운영하면서 영상 생성 모델을 자체 호스팅할지 API를 쓸지 판단해야 하는 분을 위한 글입니다. 결론을 먼저 말씀드리면, H3-Base는 33B 파라미터 단일 스트림 트랜스포머라 숫자만 보면 LLM 서빙과 비슷해 보이지만 실제 병목은 가중치가 아니라 시퀀스 길이에 있습니다. 15초짜리 2K 클립 하나가 32만 토큰이 넘는 시퀀스를 만들고, 초기 공개본에는 이를 감당할 sparse attention 구현이 빠져 있습니다.</p>

<h2 id="개요">개요</h2>

<p>MiniMax H3는 범용 옴니모달 생성 시스템입니다. 텍스트와 이미지, 영상, 오디오가 뒤섞인 멀티모달 컨텍스트를 통합적으로 이해하고, 거기서 스테레오 오디오가 붙은 영상을 생성합니다. 출력 사양은 4초에서 15초 길이, 24 FPS, 32 kHz 스테레오 오디오이고 기본 짧은 변 해상도는 768픽셀입니다. 21대9부터 9대16까지 다양한 화면비를 지원하고 대사는 아랍어와 한국어를 포함해 11개 언어를 안정적으로 지원합니다.</p>

<p>기존 영상 생성 모델과의 차이는 모달리티를 실로 꿰맨 자리가 없다는 점입니다. 텍스트와 시각, 오디오 토큰이 각자의 사일로에 있다가 마지막에 합쳐지는 것이 아니라 하나의 트랜스포머 스트림을 공유합니다. 모델 카드의 표현을 빌리면 어텐션 레이어에도 FFN 레이어에도 모달리티 전용 구조가 없고, 모달리티별 파라미터는 입출력 레이어와 AdaLN 분기에만 있습니다.</p>

<p>라이선스는 MiniMax H3 Community License입니다. Apache나 MIT 같은 표준 오픈소스 라이선스가 아니라 자체 커뮤니티 라이선스이므로, 상업적 배포를 검토하신다면 조항을 먼저 읽어 보셔야 합니다.</p>

<h2 id="이-기술은-무엇인가">이 기술은 무엇인가</h2>

<p>H3는 하나의 모델이 아니라 세 개의 모듈로 이루어진 시스템입니다. 이 구분이 자체 호스팅을 계획할 때 가장 먼저 알아야 할 사실입니다. 세 모듈 중 오픈소스로 풀린 것은 가운데 하나뿐이기 때문입니다.</p>

<pre><code class="language-mermaid">flowchart TB
    A["사용자 입력&lt;br/&gt;텍스트 · 이미지 최대 9장&lt;br/&gt;영상 최대 3클립 · 오디오 최대 3클립"] --&gt; B
    B["H3-Context-IR&lt;br/&gt;지시 파싱 · 교차모달 연결 · 시간 이해&lt;br/&gt;미공개, API로만 제공"] --&gt; C
    C["Context Intermediate Representation&lt;br/&gt;구조화된 중간 표현"] --&gt; D
    D["H3-Base&lt;br/&gt;H3-Encoder + H3-VisualVAE + H3-AudioVAE&lt;br/&gt;+ H3-Omni-Transformer 33B&lt;br/&gt;오픈웨이트 공개"] --&gt; E
    E["768p 결과물&lt;br/&gt;영상 + 32kHz 스테레오"] --&gt; F
    F["H3-Regenerate-2K&lt;br/&gt;원본 컨텍스트와 함께 재생성&lt;br/&gt;미공개, API로만 제공"] --&gt; G["2K 최종 출력"]
    E -.로컬 배포로 여기까지 가능.-&gt; H["자체 호스팅 도달 범위"]
</code></pre>

<p>H3-Context-IR은 자유 형식 멀티모달 입력을 전처리하고 조율하는 호스팅 시스템입니다. 여러 단계의 워크플로와 여러 호스팅 모델에 의존하기 때문에 이번 공개에 포함되지 않았습니다. MiniMax는 공식 워크플로를 재현할 수 있는 API와 직접 전처리 시스템을 만들 수 있는 프롬프팅 가이드를 함께 제공합니다. 다만 모델 카드가 명시적으로 밝히듯 H3-Context-IR은 최종 출력 품질에 결정적이므로, 이것을 빼고 H3-Base만 돌리면 공식 데모와 같은 결과를 기대하기 어렵습니다.</p>

<p>H3-Regenerate-2K도 미공개입니다. 흥미로운 점은 이것이 별도의 초해상도 모듈이 아니라는 것입니다. 768p 결과물을 원본 멀티모달 컨텍스트와 함께 H3에 다시 넣어 2K로 재생성합니다. 통상적인 초해상도가 추측으로 채워야 하는 작은 글자나 미세한 디테일을 원본 컨텍스트에서 복원할 수 있다는 것이 이 설계의 장점입니다.</p>

<p>가운데의 H3-Base가 공개된 부분입니다. 구성은 이렇습니다. 텍스트는 H3-Encoder가 인코딩하는데, 이것은 Qwen3-VL-32B의 사전학습 가중치를 그대로 쓰고 50번째 레이어의 은닉 상태를 트랜스포머에 넘깁니다. 시각 입력은 H3-Encoder와 H3-VisualVAE 양쪽을 거치고 오디오는 H3-AudioVAE만 거칩니다. 그리고 H3-Omni-Transformer가 영상과 오디오 잠재를 동시에 예측합니다.</p>

<p>VAE 사양이 나중에 계산에 쓰이니 적어 두겠습니다. H3-VisualVAE는 시간적으로 인과적인 영상 오토인코더로 공간 압축 16배, 시간 압축 4배, 잠재 채널 24개입니다. 여기에 시간과 높이, 너비 축으로 1대2대2 패치화가 더해지므로 트랜스포머에 들어가는 시각 토큰의 유효 공간 압축률은 32배가 됩니다. 시간 압축률은 4배 그대로입니다. H3-AudioVAE는 좌우 채널에 같은 인코더와 디코더를 쓰되 각 채널을 독립 처리하며, 32 kHz 오디오를 채널당 40 Hz 잠재 토큰 열로 압축합니다.</p>

<h2 id="설치-및-통합">설치 및 통합</h2>

<p>전체 무게를 먼저 봅니다. HuggingFace API로 파일 매니페스트를 받아 safetensors만 합산했습니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> <span class="s2">"https://huggingface.co/api/models/MiniMaxAI/MiniMax-H3?blobs=true"</span> <span class="se">\</span>
  | jq <span class="s1">'[.siblings[] | select(.rfilename|endswith(".safetensors"))
         | {f:.rfilename, b:(.lfs.size // .size)}]'</span>
</code></pre></div></div>

<p>계산 스크립트는 다키클라우드 저장소에 두었습니다. 모듈별 바이트 합산과 시퀀스 길이 유도를 함께 수행합니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.venv/bin/python scripts/experiments/minimax_h3_serving_budget.py
</code></pre></div></div>

<p>Diffusers로 불러오는 최소 경로는 모델 카드에 나와 있습니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install</span> <span class="nt">-U</span> diffusers transformers accelerate
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">torch</span>
<span class="kn">from</span> <span class="n">diffusers</span> <span class="kn">import</span> <span class="n">DiffusionPipeline</span>

<span class="n">pipe</span> <span class="o">=</span> <span class="n">DiffusionPipeline</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span>
    <span class="sh">"</span><span class="s">MiniMaxAI/MiniMax-H3</span><span class="sh">"</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="n">bfloat16</span><span class="p">,</span> <span class="n">device_map</span><span class="o">=</span><span class="sh">"</span><span class="s">cuda</span><span class="sh">"</span>
<span class="p">)</span>
</code></pre></div></div>

<p>정직하게 밝혀 둡니다. 이번 작업에서 H3-Base를 실제로 로드해 추론하지는 못했습니다. 아래에 필요한 자원을 계산해 두었지만 그만한 단일 노드를 이 작업에 붙이지 않았기 때문입니다. 따라서 이 글에는 생성 품질이나 실측 지연 시간에 관한 수치가 없습니다. 있는 것은 공개된 파일 크기와 공개된 압축 계수에서 결정론적으로 유도한 자원 수치뿐입니다.</p>

<h2 id="실제-실험-결과">실제 실험 결과</h2>

<p>리포지토리 전체의 safetensors 합계는 464 GiB입니다. 다만 이 숫자를 그대로 필요 용량으로 읽으면 안 됩니다. FL2VA와 Ref2VA, 그리고 루트 레벨에 같은 가중치가 여러 레이아웃으로 배치돼 있기 때문입니다. 실제로 한 벌을 배포할 때 필요한 크기는 이렇습니다.</p>

<table>
  <thead>
    <tr>
      <th>모듈</th>
      <th>bf16 가중치</th>
      <th>환산 파라미터</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>H3-Omni-Transformer (H3-Base)</td>
      <td>61.73 GiB</td>
      <td>33.14B</td>
    </tr>
    <tr>
      <td>H3-Encoder (Qwen3-VL-32B)</td>
      <td>62.13 GiB</td>
      <td>33.36B</td>
    </tr>
    <tr>
      <td>H3-VisualVAE</td>
      <td>9.70 GiB</td>
      <td>5.21B</td>
    </tr>
    <tr>
      <td>H3-AudioVAE</td>
      <td>0.56 GiB</td>
      <td>0.30B</td>
    </tr>
    <tr>
      <td><strong>단일 변형 합계</strong></td>
      <td><strong>134.12 GiB</strong></td>
      <td>71.9B</td>
    </tr>
  </tbody>
</table>

<p>바이트에서 환산한 33.14B가 모델 카드가 밝힌 33B와 일치합니다. 계산 경로가 맞다는 확인입니다.</p>

<p>여기서 모델 카드의 한 문장이 중요해집니다. H3-Omni-Transformer의 33B 중 약 13B가 AdaLN 관련 분기에 있는데, AdaLN 변조 출력은 미리 계산해 캐시할 수 있으므로 추론 전용 배포에서는 이 파라미터를 로드할 필요가 없습니다. 전체 가중치를 공개한 것은 파인튜닝을 포함한 후속 개발을 지원하기 위해서입니다. 그러니 추론만 하실 계획이라면 트랜스포머 쪽은 약 20B, bf16 기준 37.5 GiB로 줄어듭니다.</p>

<p><img src="/tech-blog/assets/images/minimax-h3-omni-modal-onprem-serving-results.png" alt="모듈별 가중치 실측과 클립 설정별 토큰 시퀀스 길이 차트" /></p>

<p><em>왼쪽은 파일 매니페스트에서 합산한 모듈별 가중치이고 오른쪽은 VAE 압축 계수로 유도한 시퀀스 길이입니다.</em></p>

<p>그런데 진짜 문제는 가중치가 아닙니다. 시퀀스입니다. 위 압축 계수로 계산해 보면 이렇습니다.</p>

<table>
  <thead>
    <tr>
      <th>클립 설정</th>
      <th>잠재 프레임</th>
      <th>비디오 토큰</th>
      <th>오디오 토큰</th>
      <th>합계</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>768p 16대9, 4초</td>
      <td>24</td>
      <td>24,768</td>
      <td>320</td>
      <td>25,088</td>
    </tr>
    <tr>
      <td>768p 16대9, 15초</td>
      <td>90</td>
      <td>92,880</td>
      <td>1,200</td>
      <td>94,080</td>
    </tr>
    <tr>
      <td>2K 16대9, 15초</td>
      <td>90</td>
      <td>324,000</td>
      <td>1,200</td>
      <td>325,200</td>
    </tr>
  </tbody>
</table>

<p>읽는 법은 이렇습니다. 15초 클립은 원본 360프레임이지만 시간 압축 4배를 거쳐 잠재 프레임 90개가 됩니다. 768p 16대9를 1376 곱하기 768로 잡으면 유효 공간 압축 32배 후 잠재 프레임당 43 곱하기 24, 즉 1,032개 토큰입니다. 90개 잠재 프레임을 곱하면 92,880개이고 여기에 오디오 1,200개가 더해집니다. 2K로 가면 잠재 프레임당 토큰이 3,600개로 뛰어 합계 32만 5천 개가 됩니다.</p>

<p>토큰 수는 3.5배 늘었는데 풀 어텐션 연산량은 제곱으로 늘어 약 12배가 됩니다. 그리고 모델 카드는 이 지점에서 중요한 사실을 밝힙니다. 긴 멀티모달 시퀀스의 연산 비용을 줄이려고 학습 마지막 단계에 네이티브 sparse attention을 도입했지만, <strong>초기 오픈소스 공개본은 풀 어텐션 추론만 제공</strong>하며 sparse attention 구현은 추후 별도로 공개한다는 것입니다.</p>

<p>이것이 자체 호스팅 계획에서 가장 중요한 문장입니다. 공식 API가 내는 결과와 지금 내려받은 가중치로 로컬에서 낼 수 있는 결과 사이에는 품질 차이만 있는 것이 아니라 연산 비용 차이도 있습니다. 9만 4천 토큰짜리 시퀀스를 풀 어텐션으로 처리하는 것은 sparse 구현이 있을 때와는 완전히 다른 예산입니다.</p>

<h2 id="다키클라우드-제품-적용-시사점">다키클라우드 제품 적용 시사점</h2>

<p>다키클라우드의 ai-platform은 K8s와 Kueue 위에서 GPU 워크로드를 스케줄링하고 vLLM 기반 서빙을 멀티테넌트로 운영합니다. H3 같은 모델은 이 구조에 몇 가지 구체적인 요구를 만듭니다.</p>

<p>먼저 배치 단위가 달라집니다. 단일 변형 134 GiB는 H200 한 장의 141 GB에 가중치만으로도 거의 찹니다. 9만 4천 토큰짜리 시퀀스의 활성값과 어텐션 작업 공간까지 생각하면 한 장으로는 부족하고, 추론 전용으로 AdaLN을 덜어내도 여유가 크지 않습니다. 현실적인 구성은 H200 두 장 이상이며, FL2VA와 Ref2VA 두 변형을 모두 서비스하면 그만큼 더 듭니다. Kueue 워크로드로 표현할 때 이 모델은 LLM 서빙 파드와 같은 큐에 놓기 어렵습니다. 요청당 점유 시간이 길고 메모리 프로필이 다르기 때문에 별도 리소스 플레이버로 분리하는 편이 낫습니다.</p>

<p>둘째로 큐 설계가 달라집니다. LLM 서빙은 토큰 단위 스트리밍이라 요청이 짧게 여러 번 지나가지만, 영상 생성은 요청 하나가 GPU를 길게 붙듭니다. 여기에 2K 워크플로는 768p 생성 이후 재생성 단계가 한 번 더 붙습니다. 사용자에게 보이는 응답 시간이 두 단계의 합이 되므로, 대기열 정책은 처리량이 아니라 완료 시간 기준으로 설계해야 합니다.</p>

<p>셋째로 온프렘 수요와 잘 맞습니다. 영상 생성은 소재 자체가 민감한 경우가 많습니다. 촬영 원본, 제품 이미지, 사내 인물이 담긴 영상은 외부 API로 내보내기 어려운 자산입니다. 오픈웨이트 모델을 고객사 경계 안에서 돌릴 수 있다는 점은 그 자체로 요구사항을 만족시킵니다. 다만 H3-Context-IR과 H3-Regenerate-2K가 미공개인 지금 상태에서는 완전한 온프렘 구성이 되지 않습니다. 프롬프팅 가이드를 따라 자체 전처리 시스템을 만들어야 하고, 2K는 API를 타거나 768p로 만족해야 합니다. 이 격차를 고객에게 정확히 설명하는 것이 지금 단계에서 할 수 있는 가장 정직한 일입니다.</p>

<p>Paxis 관점에서 덧붙일 것이 하나 있습니다. H3-Context-IR이 하는 일, 그러니까 자유 형식 멀티모달 입력을 파싱하고 교차모달 관계를 해석해 구조화된 중간 표현으로 직렬화하는 일은 사실 에이전트 오케스트레이션 문제입니다. 이 부분이 미공개라는 것은 반대로 말하면 그 자리를 직접 채울 수 있다는 뜻이기도 합니다. Paxis의 DAG 멀티에이전트 구성과 정책 게이트를 쓰면 입력 정제 파이프라인을 감사 가능한 형태로 만들 수 있고, 어떤 프롬프트 보강이 어떤 출력으로 이어졌는지 추적할 수 있습니다. 생성 결과에 책임을 물어야 하는 조직이라면 이 추적성이 화질보다 먼저 필요한 조건입니다.</p>

<h2 id="한계-및-반론">한계 및 반론</h2>

<p>이 글의 수치에는 분명한 경계가 있습니다. 실제로 모델을 로드해 영상을 뽑아 보지 않았으므로 생성 품질, 실제 지연 시간, 실제 최대 메모리 사용량은 여기 없습니다. 가중치 크기는 파일 매니페스트에서 정확히 나오지만 활성값과 어텐션 작업 공간은 구현에 따라 크게 달라집니다. 따라서 위의 134 GiB는 필요 VRAM의 하한이지 실제 요구량이 아닙니다.</p>

<p>시퀀스 길이 계산도 가정을 하나 깔고 있습니다. 768p 16대9의 실제 픽셀 크기를 1376 곱하기 768로 잡았는데, 모델 카드는 짧은 변이 768이라고만 밝히고 있어 구현에 따라 긴 변이 조금 다를 수 있습니다. 다만 유효 공간 압축 32배와 시간 압축 4배는 공개된 값이므로 자릿수는 흔들리지 않습니다.</p>

<p>라이선스도 짚어야 합니다. MiniMax H3 Community License는 표준 오픈소스 라이선스가 아니고 사용 제한 조항을 포함합니다. 모델 카드는 사용자 제출 텍스트와 이미지, 영상 그리고 보강된 프롬프트가 자동 검열 대상이며 위법하거나 음란하거나 제3자 권리를 침해하는 것으로 의심되는 콘텐츠가 차단될 수 있다고 밝힙니다. 이 안전 가드레일이 라이선스상의 의무를 대신하지 않는다는 점도 명시돼 있습니다. 상업 서비스를 계획하신다면 법무 검토가 기술 검토보다 먼저입니다.</p>

<p>마지막으로 자체 호스팅이 항상 이기는 것은 아닙니다. sparse attention이 빠진 지금 상태에서 15초 2K 클립을 로컬에서 뽑는 비용은 API 호출 단가와 비교했을 때 유리하지 않을 수 있습니다. 생성 빈도가 낮고 소재가 민감하지 않다면 API가 합리적인 선택입니다. 자체 호스팅의 근거는 대개 단가가 아니라 데이터 경계입니다.</p>

<h2 id="정리">정리</h2>

<p>MiniMax H3는 영상과 오디오를 한 스트림에서 함께 만드는 옴니모달 모델이고, 그 중심인 H3-Base 33B가 오픈웨이트로 공개됐습니다. 자체 호스팅을 검토하실 때 확인할 숫자는 세 개입니다. 단일 변형 가중치 134 GiB, 15초 768p 클립의 9만 4천 토큰, 그리고 2K로 갈 때의 32만 5천 토큰입니다. 여기에 조건이 하나 붙습니다. 초기 공개본에는 sparse attention이 없어 이 시퀀스를 풀 어텐션으로 처리해야 합니다.</p>

<p>다음 행동을 하나만 고르신다면 이것입니다. 지금 H3를 도입할지 결정하기보다, 사내에서 영상 생성이 필요한 소재가 실제로 외부로 내보낼 수 없는 것인지부터 확인해 보시기 바랍니다. 그렇다면 H3-Base 768p 로컬 배포와 자체 전처리 파이프라인이 지금 바로 시작할 수 있는 구성입니다. 그렇지 않다면 sparse attention 구현과 나머지 두 모듈이 공개되기를 기다렸다가 다시 계산하는 편이 낫습니다. 그때는 이 글의 스크립트를 그대로 다시 돌리시면 됩니다.</p>

<h2 id="출처">출처</h2>

<ul>
  <li>모델 카드: <a href="https://huggingface.co/MiniMaxAI/MiniMax-H3">MiniMaxAI/MiniMax-H3 on HuggingFace</a></li>
  <li>공식 소개: <a href="https://www.minimax.io/blog/minimax-h3">MiniMax H3: An Open Model Breaking the Boundaries Between Tasks and Modalities</a></li>
  <li>파일 매니페스트 API: <code class="language-plaintext highlighter-rouge">https://huggingface.co/api/models/MiniMaxAI/MiniMax-H3?blobs=true</code></li>
  <li>이 글의 계산 스크립트: <code class="language-plaintext highlighter-rouge">scripts/experiments/minimax_h3_serving_budget.py</code> (다키클라우드 내부 저장소)</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="llmops" /><category term="MiniMax-H3" /><category term="옴니모달" /><category term="비디오-생성" /><category term="온프렘-서빙" /><category term="GPU-용량-산정" /><category term="sparse-attention" /><category term="VAE" /><category term="ai-platform" /><summary type="html"><![CDATA[가중치가 공개됐다는 말과 우리 서버에서 돌릴 수 있다는 말 사이에는 계산해 봐야 알 수 있는 거리가 있습니다.]]></summary></entry><entry xml:lang="ko"><title type="html">스킬을 글로 적어둘 것인가 가중치로 구워둘 것인가, 구글이 내놓은 세 번째 답</title><link href="https://thakicloud.com/tech-blog/ko/research/skillsmith-parametric-skill-synthesis/" rel="alternate" type="text/html" title="스킬을 글로 적어둘 것인가 가중치로 구워둘 것인가, 구글이 내놓은 세 번째 답" /><published>2026-08-04T00:00:00+09:00</published><updated>2026-08-04T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/ko/research/skillsmith-parametric-skill-synthesis</id><content type="html" xml:base="https://thakicloud.com/tech-blog/ko/research/skillsmith-parametric-skill-synthesis/"><![CDATA[<p>에이전트에 스킬을 붙이는 방법은 오랫동안 두 갈래였습니다. 하나는 글로 적어두는 쪽입니다. 태스크 설명과 예시 몇 개, 지난번에 실패한 이유를 적은 반성 노트를 문서로 남겨두고 필요할 때 컨텍스트에 얹습니다. 다른 하나는 가중치로 굽는 쪽입니다. 반복되는 하위 작업마다 LoRA나 prefix 모듈을 하나씩 학습해 어댑터 서랍에 넣어두고 꺼내 씁니다. 두 방식은 십수 년째 서로 다른 학회 세션에서 논의돼 왔고, 실무에서도 대체로 둘 중 하나를 고르는 문제로 취급됩니다. 2026년 7월 29일 arXiv에 올라온 구글 딥마인드 논문은 이 선택지 자체를 다시 봅니다.</p>

<p><img src="/tech-blog/assets/images/skillsmith-parametric-skill-synthesis-hero.png" alt="두 갈래의 재료가 하나의 모루에서 만나 단일한 결정으로 벼려지는 모습을 형상화한 추상 이미지" /></p>

<p><em>글로 적어둔 스킬과 가중치로 구워둔 스킬을 같은 화로에 넣으면 어떻게 되는가가 이 논문의 질문입니다.</em></p>

<h2 id="왜-읽어야-하나">왜 읽어야 하나</h2>

<p>에이전트에 스킬 라이브러리를 붙여 운영하시는 분, 그러니까 SKILL.md 같은 문서 스킬을 수백 개 쌓아두었거나 태스크별 LoRA 어댑터를 관리하고 계신 분을 위한 글입니다. 결론을 먼저 말씀드리면, 두 방식은 대체재가 아니라 서로를 못 채우는 보완재이며, 한 모델에 둘을 동시에 먹였을 때만 나오는 성능 구간이 실제로 존재합니다. 논문의 절단 실험에서 텍스트만 준 경우와 가중치만 준 경우 사이의 격차보다, 텍스트만 준 경우와 둘 다 준 경우 사이의 격차가 더 결정적이었습니다.</p>

<h2 id="개요">개요</h2>

<p>논문 제목은 「SkillSmith: Learning to Compose Parametric Skills and Textual Knowledge」이고 arXiv 번호는 2607.27497입니다. Lucio M. Dery와 Benedict Aaron Tjandra가 공동 1저자이고 Adhiguna Kuncoro, Arthur Szlam 등 일곱 명이 참여했습니다. 분류는 cs.CL이고 저자들이 스스로 붙인 키워드는 model merging, kv-caches, continual learning, prefix tuning 네 개입니다.</p>

<p>논문이 지적하는 현재 상황은 이렇습니다. LLM 기반 에이전트가 과거 경험에서 배우는 경로는 크게 두 가지인데, 하나는 자기반성이나 구조화된 메모리, 프롬프트 최적화처럼 자연어로 지식을 합성하는 경로이고 다른 하나는 PEFT로 반복 작업의 행동을 모듈 가중치에 굳혀 두는 경로입니다. 그런데 이 둘을 잇는 다리가 없습니다. 텍스트 경로는 유연하지만 추론 시점의 컨텍스트 한계에 갇히고, 가중치 경로는 추론 효율은 좋지만 병합 연산이 평균이나 연결 같은 산술에 머물러 태스크 사이의 의미 관계를 전혀 못 씁니다.</p>

<p>논문이 드는 예가 직관적입니다. 어떤 에이전트가 한 세션에서 영어를 트위어로 번역하는 prefix 캐시를 학습했고, 다른 세션에서는 영문 법률 문서 분석에 관한 노트를 정리해 두었다고 합시다. 이제 트위어로 쓰인 법률 문서를 분석하라는 새 작업이 들어옵니다. 에이전트는 이 작업이 번역 능력과 트위어 언어 모델링, 법률 문서 분석의 조합이라는 것을 말로는 설명할 수 있습니다. 그런데 그 설명을 가지고 실제 가중치를 만들어낼 방법이 없습니다. 말로 아는 것과 가중치로 하는 것 사이가 끊겨 있는 것입니다.</p>

<h2 id="이-기술은-무엇인가">이 기술은 무엇인가</h2>

<p>SkillSmith의 발상은 한 문장으로 요약됩니다. 가중치를 LLM이 원래 읽을 줄 아는 또 하나의 모달리티로 취급하자는 것입니다.</p>

<p>구체적으로 파라메트릭 스킬을 prefix-tuning으로 구현합니다. LoRA가 아니라 prefix 방식을 고른 데는 분명한 이유가 있습니다. 텍스트 조각은 베이스 모델에 한 번 순전파를 태우면 그대로 K-V 캐시가 되므로, 텍스트에서 유래한 캐시와 학습으로 만든 캐시가 같은 공간에 놓입니다. 두 모달리티를 잇겠다는 목표에 이보다 자연스러운 표현은 없습니다.</p>

<p>SkillSmith는 소스 태스크 묶음을 받습니다. 각 묶음은 학습된 prefix K-V 캐시 하나와 그 태스크를 설명하는 텍스트 메타데이터 하나로 이루어집니다. 여기에 소스 태스크들이 목표 능력과 어떻게 연결되는지를 서술한 조합 텍스트가 더해집니다. 이 재료들을 제어 토큰으로 엮어 하나의 시퀀스로 만들고 코프로세서 LLM에 한 번 통과시킵니다.</p>

<pre><code class="language-mermaid">flowchart TB
    A["소스 태스크 묶음 1&lt;br/&gt;텍스트 메타데이터 + prefix K-V"] --&gt; D
    B["소스 태스크 묶음 2&lt;br/&gt;텍스트 메타데이터 + prefix K-V"] --&gt; D
    C["조합 텍스트&lt;br/&gt;소스와 목표의 관계 서술&lt;br/&gt;+ 목표 태스크 예시"] --&gt; D
    D["입력 K-V 어댑터 (MLP)&lt;br/&gt;가중치를 언어 모델 잠재 공간으로 사영"] --&gt; E
    E["제어 토큰으로 엮은 단일 시퀀스&lt;br/&gt;서문 · 묶음 교차 배치 · 조합문 · 생성 블록"] --&gt; F
    F["SkillSmith 코프로세서&lt;br/&gt;Gemma 3 4B, 순전파 1회"] --&gt; G
    G["생성 블록 자리표시 토큰의 K-V 추출&lt;br/&gt;역 RoPE로 위치 정보 제거"] --&gt; H
    H["출력 K-V 어댑터 (MLP)"] --&gt; I["새 prefix 가중치&lt;br/&gt;길이 32"]
    I --&gt; J["동결된 베이스 모델에 부착&lt;br/&gt;목표 태스크 수행"]
</code></pre>

<p>시퀀스 구조에는 규칙이 있습니다. 맨 앞에 조합 목표를 설명하는 서문 텍스트가 오고, 그다음 소스 묶음마다 텍스트가 <code class="language-plaintext highlighter-rouge">&lt;src_start&gt;</code> 토큰으로 시작해 사영된 K-V가 <code class="language-plaintext highlighter-rouge">&lt;kv_start&gt;</code>와 <code class="language-plaintext highlighter-rouge">&lt;kv_end&gt;</code> 사이에 놓입니다. 묶음들이 끝나면 조합 텍스트가 붙고, <code class="language-plaintext highlighter-rouge">&lt;gen_start&gt;</code> 이후에 고정 길이의 자리표시 토큰들이 이어집니다. 순전파 뒤 이 자리표시 토큰 위치의 K-V만 뽑아내 역 RoPE로 위치 정보를 벗기고 출력 어댑터를 통과시키면 새 prefix 가중치가 나옵니다.</p>

<p>학습은 종단간입니다. 생성된 캐시를 동결된 베이스 모델에 붙여 목표 태스크의 교차 엔트로피 손실을 계산하고, 그 손실을 베이스 모델을 거쳐 SkillSmith 쪽으로 역전파합니다. 베이스 모델 가중치는 끝까지 고정입니다.</p>

<p>평가 설정도 눈여겨볼 만합니다. 소스 태스크의 prefix 길이는 32, 64, 128 중에서 무작위로 뽑아 다양성을 넣었고, 코프로세서와 다운스트림 모델 모두 Gemma 3 4B를 씁니다. 소스 태스크 개수는 두 개로 고정했습니다. 그리고 prefix K-V는 전역 어텐션 레이어에만 학습합니다. 지역 어텐션 레이어에 붙인 prefix는 슬라이딩 윈도가 지나가면 결국 컨텍스트 밖으로 밀려나기 때문입니다.</p>

<h2 id="설치-및-통합">설치 및 통합</h2>

<p>논문은 코드나 체크포인트를 공개하지 않았습니다. 그래서 SkillSmith 자체를 재현하지는 못했습니다. 대신 논문이 못 박아 둔 설정, 그러니까 Gemma 3 4B 베이스에 전역 레이어만, prefix 길이 32라는 조건에서 스킬 하나를 들고 다니는 비용이 실제로 얼마인지를 계산했습니다. 이 값은 운영 관점에서 논문의 Elo 수치보다 오히려 먼저 알아야 하는 숫자입니다.</p>

<p>Gemma 3 4B의 실제 설정값을 먼저 받아옵니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> https://huggingface.co/unsloth/gemma-3-4b-it/raw/main/config.json | jq <span class="s1">'.text_config'</span>
<span class="c"># num_hidden_layers: 34, num_key_value_heads: 4, head_dim: 256,</span>
<span class="c"># sliding_window_pattern: 6, torch_dtype: "bfloat16"</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">sliding_window_pattern</code>이 6이므로 여섯 개 레이어마다 하나가 전역 어텐션입니다. 34개 레이어 중 전역은 다섯 개, 나머지 스물아홉 개는 지역입니다. 논문이 전역 레이어에만 prefix를 학습한다고 못 박았으니 실제로 학습되는 파라미터는 다섯 개 레이어 몫입니다.</p>

<p>계산 스크립트는 다키클라우드 저장소에 두었습니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.venv/bin/python scripts/experiments/skillsmith_prefix_budget.py
</code></pre></div></div>

<p>이 스크립트는 두 가지를 함께 잽니다. 하나는 위 설정에서 prefix K-V 하나의 파라미터 수와 바이트이고, 다른 하나는 로컬 스킬 코퍼스의 실제 크기입니다. 다키클라우드 워크스페이스에는 현재 <code class="language-plaintext highlighter-rouge">.claude/skills/</code> 아래에 SKILL.md가 1,911개 있습니다. 텍스트 스킬을 컨텍스트에 올리면 그 토큰들도 결국 K-V로 상주하므로, 두 방식을 같은 단위로 놓고 비교할 수 있습니다.</p>

<h2 id="실제-실험-결과">실제 실험 결과</h2>

<p>먼저 우리가 계산한 예산입니다.</p>

<table>
  <thead>
    <tr>
      <th>항목</th>
      <th>값</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Gemma 3 4B 전역 레이어 수</td>
      <td>5개 (전체 34개 중)</td>
    </tr>
    <tr>
      <td>prefix 길이 32, 전역 레이어만</td>
      <td>327,680 파라미터</td>
    </tr>
    <tr>
      <td>같은 조건의 bf16 용량</td>
      <td>640 KiB</td>
    </tr>
    <tr>
      <td>베이스 모델 대비 비중</td>
      <td>0.0076 퍼센트</td>
    </tr>
    <tr>
      <td>prefix 길이 128일 때</td>
      <td>1,310,720 파라미터 (2,560 KiB)</td>
    </tr>
    <tr>
      <td>참고: 34개 레이어 전부에 붙였다면</td>
      <td>2,228,224 파라미터 (4,352 KiB)</td>
    </tr>
  </tbody>
</table>

<p>텍스트 쪽은 이렇습니다. 로컬 SKILL.md 1,911개의 중앙값은 6,173자이고 4자를 1토큰으로 보수적으로 환산하면 약 1,543토큰입니다. 이 분량을 Gemma 3 4B 컨텍스트에 상주시키면 전체 34개 레이어에 K-V가 잡히므로 약 205 MiB가 됩니다. 같은 스킬 하나를 파라메트릭으로 들고 있을 때의 640 KiB와 비교하면 328배 차이입니다. 시퀀스 위치 기준으로 봐도 1,543개 위치와 32개 위치이니 48배입니다.</p>

<p><img src="/tech-blog/assets/images/skillsmith-parametric-skill-synthesis-results.png" alt="논문이 보고한 입력 절단 실험 Elo와 다키클라우드가 계산한 스킬 1개당 상주 비용 비교 차트" /></p>

<p><em>왼쪽은 논문 Table 1의 절단 실험이고 오른쪽은 위 스크립트가 계산한 상주 비용입니다.</em></p>

<p>논문 쪽 수치도 정리해 두겠습니다. 저자들은 SkillSmith가 정말로 K-V 캐시를 쓰는지 확인하려고 입력을 하나씩 지워 봤습니다. Composite-SNI 메타평가 태스크에서 측정한 Elo는 다음과 같습니다.</p>

<table>
  <thead>
    <tr>
      <th>입력 구성</th>
      <th>Elo</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>입력 없음</td>
      <td>1209</td>
    </tr>
    <tr>
      <td>K-V 캐시만</td>
      <td>1455</td>
    </tr>
    <tr>
      <td>K-V 캐시를 뺀 나머지 전부</td>
      <td>1622</td>
    </tr>
    <tr>
      <td>전체 입력</td>
      <td>1714</td>
    </tr>
  </tbody>
</table>

<p>읽는 법이 중요합니다. 텍스트만 준 경우(1622)가 가중치만 준 경우(1455)보다 낫습니다. 텍스트가 더 풍부한 신호라는 뜻입니다. 그런데 둘 다 준 경우는 1714로, 텍스트만 준 경우보다 92점 더 높습니다. 가중치를 더해서 얻은 이 92점이 이 논문의 핵심 주장입니다. 저자들이 지적하듯 K-V 캐시만 쓰는 설정은 PEFT 모듈을 산술 대신 파라메트릭 함수로 합치는 기존 ATTEMPT 방식과 사실상 같습니다. 그러니 이 표는 기존 가중치 병합 계열이 어디까지 갈 수 있는지를 아래쪽에 깔아두고, 텍스트를 더했을 때의 상승 폭을 보여주는 셈입니다.</p>

<p>성능 상승이 단지 텍스트를 더 줘서 생긴 것 아니냐는 반론도 저자들이 직접 검증했습니다. SkillSmith에 들어간 소스 텍스트와 조합 텍스트를 전부 뽑아 목표 태스크 입력 앞에 그대로 붙이고 일반 prefix 캐시를 학습시켜 봤는데, 보조 텍스트가 기준선을 개선하기는 했지만 SkillSmith를 따라잡지는 못했습니다. 텍스트가 그냥 거기 있는 것과 가중치와 함께 합성되는 것은 다르다는 뜻입니다.</p>

<p>일반화 검증도 있습니다. 15개 메타평가 태스크를 부모 태스크가 메타학습에 전혀 등장하지 않은 경우, 하나만 등장한 경우, 둘 다 등장한 경우로 나눠 다시 재보니 세 구간 모두에서 SkillSmith가 앞섰습니다. 부모 태스크를 한 번도 못 본 구간에서도 큰 격차로 이겼다는 점이 특히 눈에 띕니다.</p>

<p>다만 모든 데이터셋에서 이긴 것은 아닙니다. 실제 SNI 데이터셋에서는 다운스트림 미세조정을 허용하자 상위 세 방법의 승률이 0.5로 수렴했습니다. 저자들은 SNI 태스크가 태스크당 인스턴스가 1,000개 수준으로 많고, 2022년에 만들어져 감성 분류나 문자 연결 같은 원시적 작업 위주라 요즘 모델에게는 너무 쉽기 때문이라고 봅니다. 반대로 태스크가 250개뿐이고 난도가 높은 MMLU-ProX에서는 격차가 다시 벌어졌습니다. 여기서 Composite-SNI 체크포인트로 부트스트랩하지 않고 MMLU-ProX만으로 학습한 변형은 제로샷 Elo 1736에 그쳤습니다.</p>

<h2 id="다키클라우드-제품-적용-시사점">다키클라우드 제품 적용 시사점</h2>

<p>이 논문은 다키클라우드의 두 제품 모두에 걸칩니다.</p>

<p><strong>Paxis 관점</strong>이 먼저입니다. Paxis는 다키클라우드의 Agent-Native Cloud 제어 평면으로, Skills와 Tools, Policies, Audit Logs를 일급 리소스로 다룹니다. 그 중심에 있는 Skill Harness는 현재 텍스트 스킬을 BM25로 골라 격리 샌드박스에서 실행하는 구조입니다. 이 논문이 정확히 그 구조의 비용 곡선을 건드립니다. 위에서 잰 328배라는 숫자가 그것입니다. 스킬을 텍스트로 들고 있는 한, 동시에 켤 수 있는 스킬 수는 컨텍스트 예산이 결정합니다. 그래서 라우터가 필요하고, 라우터가 틀리면 그 턴은 그냥 실패합니다.</p>

<p>파라메트릭 스킬은 이 제약을 다른 축으로 옮깁니다. 32개 위치짜리 prefix라면 여덟 개를 동시에 얹어도 256 위치, 5 MiB입니다. 다만 정직하게 말씀드리면 이것은 아직 교체가 아니라 보완입니다. 논문 자신의 절단 실험이 텍스트만 쓴 쪽이 가중치만 쓴 쪽보다 낫다고 말하고 있으니, 텍스트 스킬을 버리고 가중치로 옮기는 것은 논문이 지지하지 않는 방향입니다. 이 논문이 지지하는 것은 라우터가 고른 텍스트 스킬 옆에 그 스킬로 학습된 파라메트릭 대응물을 함께 놓는 구조입니다. Paxis의 자가진화 스킬 파이프라인이 이미 스킬을 만들고 고치고 평가하고 있으므로, 그 파이프라인의 산출물에 prefix 캐시를 하나 더 붙이는 것은 새 아키텍처가 아니라 기존 산출물 목록의 확장입니다.</p>

<p><strong>ai-platform 관점</strong>은 서빙 쪽입니다. prefix 캐시는 요청마다 바뀌는 어댑터이지 모델이 아닙니다. 640 KiB짜리 객체를 테넌트별로 수천 개 보관하다가 요청이 오면 붙이는 일은 LoRA 멀티 어댑터 서빙과 형태가 같습니다. 다키클라우드의 ai-platform은 K8s와 Kueue 위에서 vLLM 서빙을 멀티테넌트로 운영하고 있으므로, 테넌트마다 다른 스킬 세트를 붙여 같은 베이스 모델을 공유하는 구성은 지금 구조에서 크게 벗어나지 않습니다. 온프렘이나 소버린 환경에서는 이 점이 특히 중요합니다. 고객사마다 별도 모델을 파인튜닝해 각각 GPU를 잡아먹는 대신, 베이스 모델 하나를 공유하고 고객사별 스킬만 KiB 단위 객체로 관리하면 GPU 점유가 고객사 수에 비례해 늘지 않습니다.</p>

<p>두 렌즈는 이어집니다. 스킬을 싸게 들고 다닐 수 있으면(ai-platform) 에이전트가 들고 다닐 수 있는 스킬 수가 늘고(Paxis), 스킬 수가 늘면 라우터가 틀렸을 때의 손해가 줄어듭니다.</p>

<h2 id="한계-및-반론">한계 및 반론</h2>

<p>가장 큰 제약은 재현 가능성입니다. 코드도 체크포인트도 공개되지 않았고, SkillSmith는 베이스 모델을 거쳐 역전파하는 종단간 메타학습을 요구합니다. 태스크별 prefix 라이브러리를 먼저 만들어야 하고, 조합 텍스트도 Gemini 2.5로 생성해야 합니다. 초기 합성 태스크가 35만 개 규모라는 서술을 보면 이 준비 과정 자체가 상당한 투자입니다. 그러니 이 결과를 자체 도메인에서 그대로 얻을 수 있으리라 기대하기는 이릅니다.</p>

<p>둘째로 실험 규모가 작습니다. 코프로세서와 다운스트림 모두 4B 모델 하나이고, 메타평가 태스크는 각각 15개, 10개, 6개입니다. Elo는 상대 비교 지표이므로 절대 성능을 말해 주지 않으며, 후보 집합이 바뀌면 값도 바뀝니다. 소스 태스크 개수도 두 개로 고정돼 있어 실제 에이전트가 마주하는 다중 스킬 조합으로 바로 확장되는지는 이 논문만으로 알 수 없습니다.</p>

<p>셋째로 SNI 결과가 보여준 수렴 현상은 적용 범위를 좁힙니다. 데이터가 넉넉하고 태스크가 쉬우면 그냥 직접 학습해도 같은 곳에 도달합니다. SkillSmith가 값어치를 하는 구간은 태스크가 어렵고 인스턴스가 적은 데이터 희소 환경입니다. 사내 도메인 작업 상당수가 여기 해당하기는 하지만, 모든 작업을 이 방식으로 처리할 이유는 없습니다.</p>

<p>마지막으로 위에서 계산한 328배는 저장과 상주 비용의 차이이지 성능의 차이가 아닙니다. 640 KiB짜리 prefix가 205 MiB짜리 텍스트 컨텍스트와 같은 일을 한다는 뜻이 절대 아닙니다. 논문의 절단 실험이 말하는 바가 정확히 그 반대이고, 이 글의 계산은 두 방식을 같은 자로 재보기 위한 것이지 교체를 정당화하기 위한 것이 아닙니다.</p>

<h2 id="정리">정리</h2>

<p>에이전트에 스킬을 붙이는 문제에서 우리는 오랫동안 문서냐 가중치냐를 골라 왔습니다. 이 논문은 그 질문에 세 번째 답을 놓습니다. 가중치를 LLM이 읽을 수 있는 입력으로 만들어 두면, 문서로 적어둔 관계 서술이 실제 가중치를 만드는 지시문이 됩니다. 절단 실험의 92점은 그 지시문이 헛말이 아니었다는 증거입니다.</p>

<p>지금 스킬 라이브러리를 운영하고 계신다면, 다음 한 가지를 확인해 보시기를 권합니다. 여러분의 스킬 문서에 이 스킬이 다른 스킬과 어떻게 이어지는지를 서술한 문장이 있습니까. SkillSmith가 실제로 소비한 것은 태스크 설명이 아니라 태스크 사이의 관계 서술이었습니다. 그 문장이 없다면 나중에 파라메트릭 합성을 붙이려 할 때 가장 중요한 재료가 비어 있는 셈입니다. 반대로 그 문장이 이미 있다면, 스킬 파이프라인에 prefix 캐시를 하나 더 굽는 일은 생각보다 가까운 다음 단계입니다.</p>

<h2 id="출처">출처</h2>

<ul>
  <li>논문: <a href="https://arxiv.org/abs/2607.27497">SkillSmith: Learning to Compose Parametric Skills and Textual Knowledge (arXiv:2607.27497)</a></li>
  <li>저자: Lucio M. Dery, Benedict Aaron Tjandra, Siavash Samiei, Adhiguna Kuncoro, Zohar Yahav, Jiajun Shen, Arthur Szlam (Google DeepMind), 2026년 7월 29일 제출</li>
  <li>베이스 모델 설정: <a href="https://huggingface.co/unsloth/gemma-3-4b-it/raw/main/config.json">Gemma 3 4B config.json</a></li>
  <li>이 글의 계산 스크립트: <code class="language-plaintext highlighter-rouge">scripts/experiments/skillsmith_prefix_budget.py</code> (다키클라우드 내부 저장소)</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="research" /><category term="SkillSmith" /><category term="prefix-tuning" /><category term="파라메트릭-스킬" /><category term="스킬-합성" /><category term="PEFT" /><category term="KV-캐시" /><category term="Gemma-3" /><category term="Paxis" /><category term="ai-platform" /><summary type="html"><![CDATA[에이전트의 스킬을 문서로 쌓을지 어댑터 가중치로 구울지 고민해 보셨다면, 이 논문은 둘 중 하나를 고르라는 전제 자체를 건드립니다.]]></summary></entry><entry xml:lang="en"><title type="html">We Took Apart a Google Antigravity Skill and Found Our Own Format</title><link href="https://thakicloud.com/tech-blog/en/agentops/antigravity-skill-format-gemini-flash/" rel="alternate" type="text/html" title="We Took Apart a Google Antigravity Skill and Found Our Own Format" /><published>2026-08-03T00:00:00+09:00</published><updated>2026-08-03T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/en/agentops/antigravity-skill-format-gemini-flash</id><content type="html" xml:base="https://thakicloud.com/tech-blog/en/agentops/antigravity-skill-format-gemini-flash/"><![CDATA[<p><img src="/tech-blog/assets/images/antigravity-skill-format-gemini-flash-hero.png" alt="Identically shaped modules slotting into three different machine housings" />
<em>The same skill module dropping into different agent harnesses unchanged.</em></p>

<h2 id="why-read-this">Why read this</h2>

<p>This is for platform engineers who run skills or plugins on coding agents, and for anyone who has to choose a skill format for an in-house agent. Two conclusions up front. First, the skill file format has already converged past vendor boundaries. Second, a skill file that actually works is a body of code and numeric constraints, not prose. The Google Antigravity skill we measured is more than half code across its 314 lines.</p>

<p>The occasion was ordinary. A content creator published a tutorial on building animated websites with Gemini 3.6 Flash and Antigravity, and a repository encapsulating that workflow as a skill appeared on GitHub. The file structure turned out to be far more interesting than the tutorial, because it is effectively the same specification we use every day.</p>

<h2 id="overview">Overview</h2>

<p>The subject is <a href="https://github.com/WilkoMarketing/antigravity-video-websites-skill">WilkoMarketing/antigravity-video-websites-skill</a>, an Antigravity skill that turns a video file into a scroll-driven animated website. The repository describes itself as a “Google Antigravity skill to turn videos into premium animated scroll-driven websites.”</p>

<p>We do three things here. We take the skill file apart, we measure our own 1911 production skills with exactly the same yardstick, and we work out what the token economics of Gemini 3.6 Flash mean for agent unit cost. We did not install the Antigravity IDE and run it, so this is a structural analysis of a published artifact rather than an execution benchmark.</p>

<h2 id="what-an-antigravity-skill-is-made-of">What an Antigravity skill is made of</h2>

<p>The install instructions give the structure away. The global skills directory is <code class="language-plaintext highlighter-rouge">~/.gemini/antigravity/skills/</code> on macOS and Linux, with one folder per skill.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.gemini/antigravity/skills/creating-video-websites/
├── SKILL.md
└── scripts/
    └── extract_frames.py
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">SKILL.md</code> opens like this.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">creating-video-websites</span>
<span class="na">description</span><span class="pi">:</span> <span class="s">Turn a video into a premium scroll-driven animated website with GSAP, canvas frame rendering, and layered animation choreography. Use when the user wants to convert a video into an animated web experience.</span>
<span class="nn">---</span>
</code></pre></div></div>

<p>Two keys, <code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">description</code>, where the description states the capability in one sentence and then attaches a trigger condition with “Use when.” That matches, sentence for sentence, the skill description contract we hold as an internal rule. Different vendor, different IDE, same specification.</p>

<p>The body has four top-level sections: <code class="language-plaintext highlighter-rouge">When to use this skill</code>, <code class="language-plaintext highlighter-rouge">Input</code>, <code class="language-plaintext highlighter-rouge">Premium Checklist (Non-Negotiable)</code>, and <code class="language-plaintext highlighter-rouge">Workflow</code>. The checklist is the part worth studying. It converts design quality, which looks like a matter of taste, into sixteen numeric constraints. Hero typography at 12rem or more, marquee text at 10vw or more, total scroll height of 800vh or more for six sections, stats overlay opacity between 0.88 and 0.92, frame advance speed between 1.8 and 2.2, canvas image scale with a sweet spot of 0.82 to 0.90. Another clause requires at least four animation types and forbids repeating the same entrance effect consecutively.</p>

<p>The workflow has seven steps, and step zero stands out. <code class="language-plaintext highlighter-rouge">Step 0: The Interview (MANDATORY)</code> forces the agent to ask the user six questions covering brand name, logo, accent color, background color, overall vibe, and content source before extracting a single frame or writing a line of code. It is a structural guard against an agent inventing requirements and proceeding.</p>

<p>The remaining steps specify the entire skeleton of the output. Step one slices the video into 150 to 300 WebP frames using the bundled script, optionally running <code class="language-plaintext highlighter-rouge">rembg</code> for background removal via the <code class="language-plaintext highlighter-rouge">--remove-bg</code> flag. Step two scaffolds into <code class="language-plaintext highlighter-rouge">index.html</code>, <code class="language-plaintext highlighter-rouge">css/style.css</code>, <code class="language-plaintext highlighter-rouge">js/app.js</code>, and <code class="language-plaintext highlighter-rouge">frames/</code>, with no bundler, just vanilla HTML, CSS, and JS plus CDN libraries. From step three onward it pins the order of loader, navigation, fixed canvas, and marquee text, how Lenis drives smooth scroll and connects to the GSAP ticker, and how the canvas renderer samples its background color from frame edge pixels every twenty frames. Dependencies are explicit too: <code class="language-plaintext highlighter-rouge">opencv-python</code> and <code class="language-plaintext highlighter-rouge">numpy</code>, plus <code class="language-plaintext highlighter-rouge">rembg[cpu]</code> if background removal is used.</p>

<pre><code class="language-mermaid">flowchart TB
    A["User request&lt;br/&gt;build me a site from this video"] --&gt; B["Skill selected via&lt;br/&gt;the Use when clause in description"]
    B --&gt; C["Step 0 mandatory interview&lt;br/&gt;brand · colors · vibe · content source"]
    C --&gt; D["Run bundled script&lt;br/&gt;scripts/extract_frames.py"]
    D --&gt; E["Apply Non-Negotiable checklist&lt;br/&gt;16 numeric constraints"]
    E --&gt; F["Generate from code templates&lt;br/&gt;index.html · style.css · app.js"]
    F --&gt; G["Scroll-driven animated site"]
</code></pre>

<p><em>The execution path. Model freedom is squeezed between the checklist and the code templates.</em></p>

<h2 id="how-we-measured-it">How we measured it</h2>

<p>Instead of impressions we counted. A script downloads the file and counts lines, code fence lines, frontmatter keys, and checklist items.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">l</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">```</span><span class="sh">"</span><span class="p">):</span>
        <span class="n">in_fence</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">in_fence</span>
        <span class="n">code</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">continue</span>
    <span class="k">if</span> <span class="n">in_fence</span><span class="p">:</span>
        <span class="n">code</span> <span class="o">+=</span> <span class="mi">1</span>
</code></pre></div></div>

<p>The same function then ran across every <code class="language-plaintext highlighter-rouge">.claude/skills/*/SKILL.md</code> in our repository to build a comparison set. One trap surfaced. Extracting the description with <code class="language-plaintext highlighter-rouge">^description:\s*(.*)$</code> captures only <code class="language-plaintext highlighter-rouge">&gt;-</code> for the YAML folded style most of our skills use, dropping the body entirely. The first run reported only 71 of 1911 skills carrying a “Use when” trigger, which was a parser bug rather than a property of the corpus. After teaching it to join the indented continuation lines of a folded block, the number landed where it should.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">inline</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">partition</span><span class="p">(</span><span class="sh">"</span><span class="s">:</span><span class="sh">"</span><span class="p">)[</span><span class="mi">2</span><span class="p">].</span><span class="nf">strip</span><span class="p">()</span>
<span class="k">if</span> <span class="n">inline</span> <span class="ow">and</span> <span class="n">inline</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="sh">"</span><span class="s">&gt;</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">&gt;-</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">|</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">|-</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">&gt;+</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">|+</span><span class="sh">"</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">inline</span>
<span class="c1"># folded block: join the indented continuation lines
</span></code></pre></div></div>

<p>The scripts are at <code class="language-plaintext highlighter-rouge">scripts/blog/_skillmd_anatomy_20260803.py</code> and <code class="language-plaintext highlighter-rouge">_skillmd_corpus_20260803.py</code>, with raw output under <code class="language-plaintext highlighter-rouge">outputs/blog-impl/antigravity-skill-format-gemini-flash/</code>.</p>

<h2 id="results">Results</h2>

<p>For the Antigravity skill: <code class="language-plaintext highlighter-rouge">SKILL.md</code> is 13,735 bytes across 314 lines, of which 171 lines sit inside code fences, or 54.5 percent. The frontmatter carries exactly two keys, <code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">description</code>, the description is 205 characters and contains “Use when.” There are four top-level sections, seven workflow steps, sixteen checklist items, and the bundled <code class="language-plaintext highlighter-rouge">scripts/extract_frames.py</code> is 84 lines.</p>

<p>Our own corpus, across 1911 skills, has a median of 154 lines, a mean of 189.3, and a maximum of 2063. Code share runs to a median of 18.5 percent and a mean of 19.5 percent. 1379 skills (72.2 percent) carry a “Use when” trigger in their description and 1396 (73.1 percent) keep frontmatter to just <code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">description</code>. Only 154 (8.1 percent) ship a bundled <code class="language-plaintext highlighter-rouge">scripts/</code> directory.</p>

<p><img src="/tech-blog/assets/images/antigravity-skill-format-gemini-flash-results.png" alt="Bar chart comparing code share of skill files" />
<em>Code fence line share of SKILL.md under identical counting rules. The Antigravity skill’s 54.5 percent sits well above our corpus median of 18.5 percent but below our toolkit-class skills, which prescribe execution steps most tightly.</em></p>

<p>The comparison sharpens the picture. The Antigravity skill is twice as long as our median and close to three times as code-dense. Yet within our own corpus the skills that prescribe execution most tightly run denser still: pillow-toolkit at 679 lines and 87.3 percent, exiftool-toolkit at 519 lines and 85.9 percent, vips-toolkit at 483 lines and 77.0 percent. The median sits at 18.5 percent because the corpus also holds many prose-shaped skills that describe routing rules or judgment criteria.</p>

<p>The lesson is not about vendors. It is that the more a skill’s output quality matters, the less free prose it contains and the more code and numeric constraint it carries. Antigravity turning design taste into sixteen numbers and our toolkits embedding commands verbatim are the same prescription.</p>

<p>Separately, Gemini 3.6 Flash, the model behind this workflow, shipped on 21 July 2026 and was available in Antigravity from day one. Per Google’s announcement it uses 17 percent fewer output tokens than 3.5 Flash while output pricing dropped from $9.00 to $7.50 per million tokens. Multiplying the two effects, output cost for equivalent work lands at 0.83 times 7.50 divided by 9.00, roughly 69 percent, a saving of about 31 percent. Coding scores rose as well: DeepSWE from 37 to 49 percent, MLE Bench from 49.7 to 63.9 percent, OSWorld-Verified from 78.4 to 83.0 percent.</p>

<h2 id="what-this-means-for-thakicloud">What this means for ThakiCloud</h2>

<p>From the <strong>Paxis</strong> side this observation is immediately useful. Paxis is ThakiCloud’s Agent-Native Cloud control plane, treating skills, tools, policies, and audit logs as first-class resources. Its skill harness selects candidates from a large skill corpus with BM25 and executes them in an isolated sandbox. What this measurement confirms is that the selection signal is vendor-agnostic. An Antigravity skill also exposes <code class="language-plaintext highlighter-rouge">name</code>, <code class="language-plaintext highlighter-rouge">description</code>, and a “Use when” trigger clause, so to a router it is the same shape of input. Skills built in outside ecosystems can be indexed and added to the candidate pool as they are.</p>

<p>The boundary is equally clear. Format compatibility is not execution compatibility. This skill requires <code class="language-plaintext highlighter-rouge">pip install opencv-python</code> and <code class="language-plaintext highlighter-rouge">rembg[cpu]</code> in the user’s environment and handles local file paths directly. Pulling in arbitrary external skills uncritically drags along dependency installation and file access. That is exactly why Paxis runs skills in a sandbox and pushes every action through policy gates and audit logs. Format convergence lowers adoption cost; execution isolation remains the platform’s job.</p>

<p>The measurement also surfaced a task for our own corpus. Only 154 of 1911 skills, 8.1 percent, ship a bundled <code class="language-plaintext highlighter-rouge">scripts/</code> directory. That signals how many procedures are still expressed as prose when deterministic code could own them, and against our internal principle of letting code own the format, there is room left.</p>

<p>From the <strong>ai-platform</strong> side this becomes a cost question. Agent workload cost scales with output tokens, and the 31 percent saving computed above applies when using a commercial API. An organization that cannot send code or assets outside its perimeter has to produce that same saving on premise, and the means are Kubernetes with Kueue-based GPU scheduling and vLLM serving optimization. That is the target ThakiCloud’s ai-platform aims at.</p>

<h2 id="limits-and-counterarguments">Limits and counterarguments</h2>

<p>The largest limitation is that we did not execute anything. We never installed the Antigravity IDE and ran the skill, so whether those sixteen constraints actually produce good output is unverified. We established what the file demands, not the quality of what it yields.</p>

<p>The metric itself is coarse. Code fence line share is a proxy for a skill’s character, not a quality score. Skills that describe judgment criteria or routing rules should have low code share, and forcing code into them would make them worse. Reading our 18.5 percent median as a defect to fix would be a mistake.</p>

<p>The sample is also a single repository. One skill published by an individual developer cannot support a claim about the Antigravity skill ecosystem generally. How Google documents its official skill specification is a separate question, and we did not verify whether this repository follows it faithfully.</p>

<p>The Gemini 3.6 Flash figures rest on press coverage of Google’s announcement. The 17 percent token reduction is measured against the Artificial Analysis Index and varies by workload, so the 31 percent saving holds only under that premise. We did not measure it in a live application.</p>

<p>Finally, observing that the format has converged is not the same as standardization. Sharing <code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">description</code> is a long way from compatible tool permission models, sandbox policies, and bundled asset conventions.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>Taking apart one published Antigravity skill, we found a <code class="language-plaintext highlighter-rouge">SKILL.md</code> carrying <code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">description</code> with a “Use when” trigger clause exactly as ours do, 54.5 percent of its 314 lines given over to code, and design taste reduced to sixteen numeric constraints. Both claims from the opening hold. The format has converged past vendor lines, and a working skill is code and constraint rather than prompt.</p>

<p>There is one thing to take into practice. Every time you are tempted to write “do this well” into a new skill, ask first whether it can become a number or a line of code. If it can come down to something checkable, like 12rem or more, or between 0.88 and 0.92, write it that way. The freedom you remove from the model comes back as average output quality.</p>

<h2 id="sources">Sources</h2>

<ul>
  <li>Skill repository: <a href="https://github.com/WilkoMarketing/antigravity-video-websites-skill">WilkoMarketing/antigravity-video-websites-skill</a> (<code class="language-plaintext highlighter-rouge">SKILL.md</code>, <code class="language-plaintext highlighter-rouge">scripts/extract_frames.py</code>)</li>
  <li>Gemini 3.6 Flash launch coverage: <a href="https://9to5google.com/2026/07/21/gemini-3-6-flash-launch/">Google launches Gemini 3.6 Flash and 3.5 Flash-Lite, teases Gemini 4</a> (9to5Google, 21 July 2026)</li>
  <li>Measurement scripts and raw logs: <code class="language-plaintext highlighter-rouge">scripts/blog/_skillmd_anatomy_20260803.py</code>, <code class="language-plaintext highlighter-rouge">scripts/blog/_skillmd_corpus_20260803.py</code>, <code class="language-plaintext highlighter-rouge">outputs/blog-impl/antigravity-skill-format-gemini-flash/run-1.log</code>, <code class="language-plaintext highlighter-rouge">run-2.log</code></li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="agentops" /><category term="agentops" /><category term="antigravity" /><category term="gemini" /><category term="agent-skills" /><category term="skill-harness" /><category term="paxis" /><category term="thakicloud" /><summary type="html"><![CDATA[For platform engineers who attach skills to coding agents. The short version: the skill file format has already converged across vendors, and a skill that actually works is mostly code and numeric constraints rather than prose.]]></summary></entry><entry xml:lang="en"><title type="html">Demonstrate Once, Get a Skill: Reading Microsoft’s Skill Recorder at the Source Level</title><link href="https://thakicloud.com/tech-blog/en/agentops/microsoft-skill-recorder/" rel="alternate" type="text/html" title="Demonstrate Once, Get a Skill: Reading Microsoft’s Skill Recorder at the Source Level" /><published>2026-08-03T00:00:00+09:00</published><updated>2026-08-03T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/en/agentops/microsoft-skill-recorder</id><content type="html" xml:base="https://thakicloud.com/tech-blog/en/agentops/microsoft-skill-recorder/"><![CDATA[<p><img src="/tech-blog/assets/images/microsoft-skill-recorder-hero.png" alt="A loose hand-drawn luminous trail on the left crystallising into an ordered lattice of blocks on the right" /></p>

<p><em>One demonstration hardening into a reusable procedure. That is exactly what this tool is trying to do.</em></p>

<h2 id="why-this-matters">Why this matters</h2>

<p>This post is for platform owners who hit the “who is going to write the skills” wall while rolling out agents, and for agent engineers designing a skill authoring pipeline. The conclusion first: <strong>Skill Recorder’s real contribution is not screen recording but the design decision to generalise a recording into the agent’s native tool calls rather than replaying clicks, plus opening that generalisation up so a human can review and correct it in natural language. That middle step is what separates a usable tool from a demo.</strong> Below is what we found by cloning the repository, reading the code and running its tests.</p>

<h2 id="overview">Overview</h2>

<p>Skills as a format converged on something close to a standard over the past year. Write the procedure in a markdown file, describe in frontmatter when it applies, and the agent picks it up on its own. The problem is who writes that markdown. The person who knows a procedure best usually has no time to document it, and the person with time does not know the procedure. The bottleneck in the skill ecosystem was never consumption; it was production.</p>

<p><a href="https://github.com/microsoft/skill-recorder">microsoft/skill-recorder</a> aims at that bottleneck. It records a screen session while you do your normal work once, reconstructs it through the GitHub Copilot CLI as a single intent plus an ordered list of steps, and then builds a reusable skill or automation. The licence is MIT and the copyright line reads Microsoft Corporation. At the time we checked, the latest commit was <code class="language-plaintext highlighter-rouge">32fd0b5</code> dated 30 July 2026, merging release 0.3.1.</p>

<p>It is larger than you might guess. TypeScript-family sources including tests and docs run a little over twenty thousand lines. This is not a demo prototype; it is built to ship.</p>

<h2 id="what-the-tool-actually-does">What the tool actually does</h2>

<p>The behaviour breaks into four blocks.</p>

<p>First, <strong>collection</strong>. The event types defined in <code class="language-plaintext highlighter-rouge">common/events.ts</code> tell you immediately what it captures: session start and stop, active application switches, window title changes, clipboard changes, terminal commands, browser URLs, plus video start, stop and frame capture. Right from here you can see the posture: do not try to comprehend raw pixels, capture meaningful structured signals first.</p>

<p>Second, <strong>reconstruction</strong>. <code class="language-plaintext highlighter-rouge">electron/describer/</code> spins up a Copilot CLI agent and has it interpret the session. The agent gets three kinds of tool. <code class="language-plaintext highlighter-rouge">get_timeline</code> reads the segmented timeline, <code class="language-plaintext highlighter-rouge">get_narration</code> reads whatever the user said out loud, and a set of frame tools pulls the screen image at a specific moment. It submits its result through <code class="language-plaintext highlighter-rouge">submit_analysis</code>, whose arguments are telling: a title, the intent, <strong>a confidence score for that intent</strong>, the rationale behind that confidence, and the list of steps.</p>

<p>Third, <strong>plan and approval</strong>. The comment block in <code class="language-plaintext highlighter-rouge">common/skill.ts</code> describes this precisely. Starting from an approved analysis, a multi-turn Copilot agent first proposes a plan: how it intends to generalise the recorded task, which fixed values it needs, and which native tools of the target architecture it will use. The user refines that plan in natural language, and on confirmation the final artefact is produced.</p>

<p>Fourth, <strong>output</strong>. There are two artefact kinds: an on-demand <code class="language-plaintext highlighter-rouge">SKILL.md</code> the agent invokes when its description matches the request, and an automation, meaning a multi-step procedure run on a schedule or trigger. Microsoft Scout and Microsoft 365 Copilot (Cowork) are the currently enabled targets; Copilot Studio is marked “coming soon” and greyed out in the code.</p>

<pre><code class="language-mermaid">flowchart TB
    A["User performs the task once"] --&gt; B["Collectors&lt;br/&gt;app switches, window titles, clipboard&lt;br/&gt;terminal commands, browser URLs"]
    A --&gt; C["Optional&lt;br/&gt;screen video + spoken narration"]
    B --&gt; D["events.jsonl&lt;br/&gt;structured events are the primary signal"]
    C --&gt; E["Frame extraction + correlation&lt;br/&gt;only where events fall short"]
    D --&gt; F["Describer agent&lt;br/&gt;Copilot CLI"]
    E --&gt; F
    F --&gt; G["submit_analysis&lt;br/&gt;intent, confidence, rationale, steps"]
    G --&gt; H{"Human review"}
    H --&gt;|"correct in natural language"| G
    H --&gt;|"approve"| I["Builder agent proposes a plan&lt;br/&gt;generalisation, fixed values, native tools"]
    I --&gt; J{"Human confirms"}
    J --&gt; K["SKILL.md&lt;br/&gt;on-demand skill"]
    J --&gt; L["Automation&lt;br/&gt;scheduled or triggered"]
</code></pre>

<p>The most impressive judgement call is how video is handled. The comments in <code class="language-plaintext highlighter-rouge">electron/pipeline.ts</code> state deliberately that the whole video is never scanned. Events are the primary signal; anything they fail to explain is surfaced as a probe suggestion and only harvested where confidence is low. The describer instructions repeat the same principle, telling the agent that most steps are fully explained by events alone and to budget roughly five frames. Compared with shoving an entire video into a multimodal model, this is far more realistic on both cost and accuracy.</p>

<p>The second standout is the rule to <strong>use intent as a filter</strong>. The instructions explicitly say not to produce a literal transcript of everything on screen. Once the intent is clear, drop the activity that does not serve it. It even singles out the first step, where the user focused the Skill Recorder window to press Start, and the last step, where they returned to press Stop, as recorder bracketing rather than user actions, and tells the agent not to emit them. If the person detoured to another site mid-demonstration, that drops out too.</p>

<h2 id="setup-and-integration">Setup and integration</h2>

<p>We pulled the repository into an isolated worktree and ran it.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone <span class="nt">--depth</span> 1 https://github.com/microsoft/skill-recorder.git
<span class="nb">cd </span>skill-recorder
node <span class="nt">--version</span>   <span class="c"># v24.1.0</span>
npm <span class="nt">--version</span>    <span class="c"># 11.3.0</span>
npm <span class="nb">install</span> <span class="nt">--no-audit</span> <span class="nt">--no-fund</span>
</code></pre></div></div>

<p>Dependency installation took 25 seconds for 609 packages, which is light for something pulling in Electron 43 and Vite 8. The runtime dependency list characterises the tool well.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"@github/copilot-sdk"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^1.0.6"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"@huggingface/transformers"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^4.2.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"koffi"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^3.1.1"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"sharp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^0.34.5"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"zod"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^4.3.6"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">@github/copilot-sdk</code> is the agent that performs analysis and building, <code class="language-plaintext highlighter-rouge">koffi</code> is the FFI used to read native window information, and <code class="language-plaintext highlighter-rouge">sharp</code> handles frame images. The presence of <code class="language-plaintext highlighter-rouge">@huggingface/transformers</code> matters: it means spoken narration is transcribed <strong>locally</strong>, through that library and the ONNX runtime. The macOS microphone usage string in the build config says the same thing, stating that the microphone is used only while narration is on and that transcription happens on this computer.</p>

<p>We ran the tests.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">test</span>
<span class="c"># ℹ tests 58</span>
<span class="c"># ℹ pass 58</span>
<span class="c"># ℹ fail 0</span>
<span class="c"># ℹ duration_ms 686.846708</span>
</code></pre></div></div>

<p>All 58 tests passed in 0.69 seconds. More interesting than the pass is <strong>what is being tested</strong>. The list includes items like: warn before every start until the detailed disclosure has been reviewed, the acknowledgement does not survive a new app process, session size accounts for every artefact and deleting removes the whole directory. A tool that records your screen, clipboard and terminal ought to have that minimum courtesy, and here it is nailed down in tests.</p>

<p>The compliance tests are equally notable. Unreviewed ONNX versions fail closed, unreviewed GitHub Copilot CLI versions fail closed, and a bundle containing only licence files cannot pass release verification. The exact items an organisation would want to check during due diligence are already automated.</p>

<p>One honest note. <strong>We never ran an actual recording session.</strong> It is a desktop GUI application requiring screen capture permission and a Copilot CLI login, so it cannot be reproduced end to end in an automated environment. The pipeline behaviour described above was verified by reading the code and instruction files; the numbers come from an installation and test run that genuinely happened.</p>

<h2 id="what-this-means-for-thakicloud">What this means for ThakiCloud</h2>

<p>Paxis, ThakiCloud’s Agent-Native Cloud, is a control plane that treats skills, tools, policies and audit logs as first-class resources. It selects candidates from more than 960 skills, executes them in isolated sandboxes, and puts every action through a policy gate and an audit log. So our interest in this tool is not “how do you choose a skill” but “where do skills come from”.</p>

<p>First, <strong>it names the gap on the input side precisely.</strong> In our harness, skills are either written by a person or refined from existing skills by the nightly self-evolution loop. Both start from an already documented procedure. What never enters through either path is the procedure that lives only in someone’s fingertips, such as checking a value on a particular dashboard and transferring it into a ticket template. A path from one demonstration to a draft fills that gap.</p>

<p>Second, <strong>the principle of generalising to native tools rather than replaying UI is worth adopting directly.</strong> Automation that retraces click coordinates breaks the moment the screen changes. Translate an observed click into the API call or CLI command that produces the same result and it lives much longer. Our own rules already say deterministic code owns format and adjudication while the model produces only content, which is the same idea in a different domain.</p>

<p>Third, <strong>we are borrowing the decision to ship confidence with the artefact.</strong> Attaching an intent confidence and its rationale to the analysis tells a human where to look first. During review of an auto-generated skill, that signal cuts the review cost substantially. It is a good field to add to our skill intake gate.</p>

<p>Fourth, <strong>the boundary is clear too.</strong> Because the artefacts target Microsoft Scout and Cowork, they do not drop straight into our harness. The intermediate artefact, however, meaning the intent and the step list, is target-neutral. Splitting there and rendering into our own format through an adapter is the realistic integration path. Conveniently, the repository keeps separate evaluation harnesses for the builder and the skill builder under <code class="language-plaintext highlighter-rouge">evals/</code>, so the code itself points at which boundary is replaceable.</p>

<h2 id="limits-and-counterarguments">Limits and counterarguments</h2>

<p>The largest constraint is that this is a desktop application. A human has to sit down and demonstrate, and grant access to the screen, clipboard and terminal. It is not something you run in batch on a server. Adopting it across an organisation requires an internal policy on which screens may be recorded in the first place. It is good that the team defended this surface with tests, but tests do not substitute for the policy.</p>

<p>The quality of generalisation also needs verification. Deciding what is a fixed value and what varies each time, from a single demonstration, is inherently an inference. To extract “the procedure for submitting all forms” from one record of submitting one form, you have to know whether the value entered this time was an example or a constant. Get that wrong and the skill fails quietly. That is why the tool opens the plan step to the user and accepts natural language corrections, which also means <strong>the human review cannot be removed from the loop.</strong></p>

<p>Finally, one demonstration does not guarantee a good skill. The way a person habitually works may not be optimal, and that inefficiency gets frozen in along with everything else. Transcribing a procedure and redesigning a procedure are different jobs. This tool is good at the former; the latter remains ours.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>What is worth taking from Skill Recorder is not the recording feature but three design judgements: treat structured events as the primary signal and reach for video only where confidence is low, translate observed clicks into native tool calls instead of replaying them, and open the middle of the pipeline so a human can correct intent and plan in natural language. All three transfer directly to how we design a skill authoring path.</p>

<p>If your team is rolling out agents, try one question. Which procedure in your organisation is repeated most often and written down by nobody? That answer is your first recording target, and the slot currently empty in your skill library.</p>

<h2 id="sources">Sources</h2>

<ul>
  <li><a href="https://github.com/microsoft/skill-recorder">microsoft/skill-recorder repository (MIT)</a>: commit <code class="language-plaintext highlighter-rouge">32fd0b5</code> at time of check, release 0.3.1 (2026-07-30)</li>
  <li><a href="https://devblogs.microsoft.com/visualstudio/agent-skills-in-visual-studio/">Visual Studio Blog: Agent Skills in Visual Studio</a></li>
  <li><a href="https://learn.microsoft.com/en-us/visualstudio/ide/copilot-agent-skills?view=visualstudio">Microsoft Learn: Use Agent Skills with GitHub Copilot</a></li>
  <li>Original discussion: <a href="https://x.com/hjguyhan/status/2084036443769643295">timeline post</a></li>
  <li>Execution log: installation and test figures come from <code class="language-plaintext highlighter-rouge">outputs/blog-impl/ms-skill-recorder/run-5.log</code> and <code class="language-plaintext highlighter-rouge">run-6.log</code>.</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="agentops" /><category term="Skill-Recorder" /><category term="Microsoft" /><category term="GitHub-Copilot" /><category term="agent-skills" /><category term="SKILL-md" /><category term="workflow-automation" /><category term="AgentOps" /><category term="Paxis" /><summary type="html"><![CDATA[Consuming skills got easy. Producing them still meant a human writing the document. This tool goes after the input side.]]></summary></entry><entry xml:lang="en"><title type="html">From $1.2M a Month to $100K: Redesigning the Inference Cost of an Agentic Product</title><link href="https://thakicloud.com/tech-blog/en/llmops/agentic-inference-cost-tiered-routing/" rel="alternate" type="text/html" title="From $1.2M a Month to $100K: Redesigning the Inference Cost of an Agentic Product" /><published>2026-08-03T00:00:00+09:00</published><updated>2026-08-03T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/en/llmops/agentic-inference-cost-tiered-routing</id><content type="html" xml:base="https://thakicloud.com/tech-blog/en/llmops/agentic-inference-cost-tiered-routing/"><![CDATA[<p><img src="/tech-blog/assets/images/agentic-inference-cost-tiered-routing-hero.png" alt="Abstract image of many token streams being sorted by a branching gate into one wide low-cost lane and one thin premium lane, with a cost curve dropping sharply" /></p>

<p><em>Most traffic goes to the cheap lane, a few to the premium lane. The cost structure of an agentic product is decided at that sorting step.</em></p>

<h2 id="why-read-this">Why read this</h2>

<p>This post is for engineers running an agentic product who watch the inference bill grow faster than revenue, and for infrastructure owners deciding whether to keep paying a commercial API or move workloads onto their own cluster. The short version is that the cost of an agentic product is decided not by which model you use, but by <strong>which request you send to which model</strong>. A recent founder interview shows this with unusual clarity. A product that routed every agent action to a frontier model pushed its bill to $1.2M a month, then brought it down to about $100K after two months of redesign. We look at what changed to produce a twelvefold difference, and where the same judgment lands inside our own platform.</p>

<h2 id="a-product-whose-bill-broke-first">A product whose bill broke first</h2>

<p>The subject is a product called Polsia. Its founder, Ben Broca, runs it solo, and the goal is that a user supplies only an idea while a fleet of AI agents runs an entire company on their behalf: coding, marketing, customer acquisition, support. It drew attention when it raised $30M at a $250M valuation with zero employees, with agents even handling the data room and investor briefings. The interesting part is not the success story but the fact that the cost structure broke first.</p>

<p>The numbers the founder gives in the <a href="https://www.youtube.com/watch?v=bzf2YZa0Vkg">interview</a> run like this. In March, paying users grew from 500 to 5,000, and the inference bill hit $500K in a single month, then crossed a million, peaking at $1.2M a month. The cause is simple. Every time something happened inside the product, the thing doing it was an agent, and that agent mostly called a frontier model like Opus or Sonnet. The more automation was added, and the more complex the tasks users asked for, the faster the call volume grew, more than linearly.</p>

<p>Here is the first lesson. Adding money does not fix the product. The founder bought himself room to absorb the cost by raising a round, but he says it plainly: cash gave breathing space, it did not solve the product. The math showed that going from 5,000 to 50,000 users would burn the entire raise within a few months, so the structure had to change before scaling. Recognizing this as an architecture problem while the bill was still affordable is the most important call in the whole story.</p>

<h2 id="what-changed-split-the-requests-build-a-cheap-lane">What changed: split the requests, build a cheap lane</h2>

<p>The redesign started from an honest look at the traffic. In the founder’s own words, most users “ask rather simple things and have rather simple codebases, so you can standardize all of that.” The lever was the fact that requests are not uniform. A small number of hard requests and a large number of standardizable simple ones were mixed together, and processing both through the same frontier model was the real source of waste.</p>

<p>So the new structure is this. The standardizable long tail is pushed down to open-weight models running on rented GPUs, and only the genuinely hard reasoning stays on frontier models. When a request comes in, its complexity is classified first, and the destination model is chosen from that classification. Configuring this took about two months, and in June the bill dropped from $1.2M to roughly $100K. The twelvefold gap did not come from picking one cheaper model. It came from pulling most of the traffic out of the expensive lane in the first place.</p>

<p>I want to stress the second lesson here. This is not free. Those two months were real engineering: building the classifier, serving the open models, and above all verifying that the quality of requests sent to the cheaper model did not hurt the user experience. Routing without a quality-parity gate reduces cost by increasing churn. For the cheap lane to hold, you need a mechanism that decides deterministically whether “this request produces the same result on the cheaper model.”</p>

<p>What the classifier keys on is the hardest part of the design. Request text alone is not enough. Whether the task is generation, edit, or lookup; how large and structured the target codebase is; how deep the expected tool-call chain runs; how similar past requests were handled; these signals together decide whether a request is standardizable. And this judgment cannot be left to the model’s self-report. Not the model’s claim that “this is an easy request,” but a gate where code deterministically measures whether the output passes the same bar as the frontier model, is what earns the router trust. A classifier that quietly passes failures on the cheap lane does not reduce cost; it defers a quality incident.</p>

<pre><code class="language-mermaid">flowchart TB
    A["User request&lt;br/&gt;(agent action)"] --&gt; B{"Complexity&lt;br/&gt;classifier"}
    B --&gt;|"Standardizable simple task&lt;br/&gt;(the long tail)"| C["Open-weight model&lt;br/&gt;owned / rented GPU"]
    B --&gt;|"Hard reasoning&lt;br/&gt;(the few)"| D["Frontier model&lt;br/&gt;Opus / Sonnet"]
    C --&gt; E["Quality-parity gate"]
    D --&gt; E
    E --&gt; F["Return response"]
    G["Agent spend &amp; identity layer&lt;br/&gt;wallet · limit · metering"] -. "runaway-loop guard" .-&gt; A
</code></pre>

<p><em>Complexity-based routing sends the standardizable many to the cheap lane and only the hard few to the premium lane. Without a quality-parity gate, the cost saving turns into churn.</em></p>

<h2 id="not-one-case-but-a-whole-layer-forming">Not one case, but a whole layer forming</h2>

<p>Reading this only as one product’s war story is reading half of it. Three companies appear in the interview, and overlaying them shows how the layer that handles cost in the agent era is splitting apart.</p>

<p>At the top is the product layer. This is where a fleet of agents runs a 24-hour loop doing real work, as in Polsia. The defining trait of this layer is that every action is a model call, so cost explodes along with usage.</p>

<p>Below it is the spend and identity layer. Sapiom, which Polsia named as its agent infrastructure in the interview, sits here. Sapiom aims to give an agent a unique identity and wallet the way a person has KYC, to let it pay for external tools, APIs, and compute on a usage basis through a single API, and to stop runaway loops with spend limits and risk detection. Founded by former Shopify engineering director Ilan Zerbib, it raised a $15.75M seed according to <a href="https://siliconangle.com/2026/02/06/sapiom-reels-15-75m-equip-ai-agents-payment-features/">SiliconANGLE</a>. The moment autonomous agents buy their own resources, a mechanism that meters who spent how much at the identity level and applies limits becomes one axis of the infrastructure.</p>

<p>At the bottom is the serving layer. Sciforium, backed by AMD and SignalFire with roughly $12M raised, is a clear example. While most AI clouds depend on Nvidia, Sciforium puts its own high-efficiency serving stack on AMD hardware and offers multimodal inference at lower cost than the norm. With AMD engineers directly helping tune the runtime, it shows that a heterogeneous-accelerator strategy for routing around single-vendor premium is becoming real, with capital and talent behind it.</p>

<p>The logic running through all three companies is one thing. The moment you make a frontier model the default, the unit cost of an agentic product becomes unsustainable, and so the layers that split requests, meter spend, and push serving onto cheaper hardware are each growing large enough to be a standalone business.</p>

<h2 id="what-this-means-for-thakiclouds-products">What this means for ThakiCloud’s products</h2>

<p>Overlaying this case on our own stack, we do not find an unfamiliar new demand so much as confirmation of where we already stand. This section is not about building something new; it maps where the pattern lands in our platform.</p>

<p>The serving and platform layer overlaps precisely with the Metis value proposition. If Polsia spent two months arriving at the conclusion “rent GPUs and serve open models behind OpenAI-compatible endpoints,” then Metis Serve and ML Studio, together with Kueue-based GPU scheduling and the model registry, are that conclusion turned into product. In particular, the heterogeneous-accelerator serving that Sciforium is trying to prove, running the same models behind the same API not only on Nvidia but on AMD or domestic NPUs, looks at the same place Metis was designed to look, as an accelerator-agnostic platform. This case is external confirmation of the market demand for the serving layer we are building.</p>

<p>The agent runtime layer touches the problem space of Praxis. When a fleet of agents runs continuously and every action is a model call, there are two ways to control cost: per-node complexity-based model routing, and budget caps with spend guardrails. The spend governance that Sapiom wants to hold with identity, wallet, and limits is the same kind of control layer that Praxis agents need in production. This case proves, with a bill, that a mechanism stopping an autonomous loop from calling expensive models without limit is not optional but a precondition.</p>

<p>To add one more note, starting on a cheaper tier by complexity and escalating to a higher model only when needed is a discipline we already follow in our own automation. Exploration and simple lookups go to lighter models, only steps that need real reasoning go to higher ones, and retro-driven escalation raises the tier only when repeated failures are observed. The conclusion Polsia reached from the outside and the routing discipline we run on the inside say the same sentence: cost is decided by which request goes where, not by model price.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>The most expensive mistake in building an agentic product is leaving the default that routes every action to a frontier model unexamined. Polsia’s case shows, with a bill, how that default becomes a $1.2M-a-month invoice, and how a redesign that splits requests by complexity and pushes the standardizable many onto open-weight models produces a twelvefold difference. That saving came with the price of two months of engineering and a quality-parity gate, not from picking one cheaper model. And the three layers this redesign needs, the agent runtime, spend governance, and heterogeneous serving, are splitting into a market large enough for each to be its own business, with the serving and routing layers being exactly where we already stand with Metis and Praxis.</p>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="llmops" /><category term="inference-cost" /><category term="model-routing" /><category term="open-weight" /><category term="agent-infrastructure" /><category term="LLMOps" /><category term="heterogeneous-accelerators" /><category term="AMD" /><category term="ai-platform" /><summary type="html"><![CDATA[The cost of an agentic product is set by routing design, not model price. Here is how $1.2M a month became $100K.]]></summary></entry><entry xml:lang="en"><title type="html">Slow Decode Is Not a Weak GPU: Measuring the Real LLM Inference Bottleneck With a Roofline</title><link href="https://thakicloud.com/tech-blog/en/llmops/llm-decode-roofline/" rel="alternate" type="text/html" title="Slow Decode Is Not a Weak GPU: Measuring the Real LLM Inference Bottleneck With a Roofline" /><published>2026-08-03T00:00:00+09:00</published><updated>2026-08-03T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/en/llmops/llm-decode-roofline</id><content type="html" xml:base="https://thakicloud.com/tech-blog/en/llmops/llm-decode-roofline/"><![CDATA[<p><img src="/tech-blog/assets/images/llm-decode-roofline-hero.png" alt="A single narrow glowing conduit connecting a small cluster to a vast lattice of idle compute cubes" /></p>

<p><em>Wide compute, one narrow pipe. That is the exact shape of an LLM decode step.</em></p>

<h2 id="why-this-matters">Why this matters</h2>

<p>This post is for infrastructure owners who have to buy GPUs and put models on them, and for ML engineers deciding how hard to push their serving parameters. The conclusion first: <strong>a decode step serving a single request uses roughly 2% of the accelerator’s compute and spends the rest of its time reading weights out of memory. What you need to increase in inference serving is not GPU compute but the number of requests handled concurrently, and the threshold where that stops paying off can be computed for any device.</strong> Below we measure that threshold directly.</p>

<p>The topic came out of a timeline post listing <a href="https://x.com/hjguyhan/status/2084039209313350116">what an AI engineer should learn</a>. Near the top of that list sat two items side by side: learn the roofline model and why decode is memory-bound, and go deploy vLLM and SGLang. Those are really one story. Understand the first and the design of the second explains itself.</p>

<h2 id="overview">Overview</h2>

<p>LLM inference splits into two phases with completely different characters. Prefill pushes the whole prompt through at once; decode produces one token at a time. Most of what a user perceives as response speed comes from decode. A 500-token answer means 500 decode steps, and the sum of those 500 is the response time.</p>

<p>This is where intuition breaks. A single decode step needs very little arithmetic. You push one token through each matrix multiply and you are done. Yet it is slow. The reason is not the arithmetic but <strong>the volume of weights you must read to perform it</strong>. For an 8B model in fp16 that is 16 GB pulled out of memory for every single token. The arithmetic units spend most of their time idle, waiting for data to arrive.</p>

<p>The tool that draws this relationship on one chart is the roofline model. Formalised by Williams, Waterman and Patterson in CACM 2009, it explains a kernel’s performance ceiling using two hardware constants: peak compute and peak memory bandwidth. Compute a kernel’s <strong>arithmetic intensity</strong>, meaning the number of floating point operations performed per byte read from memory, and you know whether it is compute-bound or bandwidth-bound.</p>

<p>The key result is what that number becomes for LLM decode. Take one weight matrix and push B tokens through it: the arithmetic scales with the batch, but the weight bytes you must read stay the same. In other words, <strong>the arithmetic intensity of decode is simply the batch size</strong>. At batch 1 the intensity is 1, which is dismally low on any accelerator you can buy.</p>

<h2 id="what-the-model-actually-says">What the model actually says</h2>

<p>The roofline coordinate system is simple. Arithmetic intensity on the x-axis, achieved performance on the y-axis. The ceiling is a roof formed by two lines: a slope on the left whose gradient is the memory bandwidth, and a flat line on the right at peak compute. Where they meet is the ridge point, and its x-coordinate is peak compute divided by peak bandwidth.</p>

<p>Anything to the left of the ridge is bandwidth-bound. In that region, swapping in a faster arithmetic unit changes nothing. To the right of the ridge the kernel is compute-bound and widening memory does nothing. Few compasses point at the right optimisation this clearly.</p>

<pre><code class="language-mermaid">flowchart TB
    A["Request arrives&lt;br/&gt;prompt of N tokens"] --&gt; B["Prefill phase&lt;br/&gt;all N tokens in one matmul"]
    B --&gt; C{"Arithmetic intensity&lt;br/&gt;= batch x sequence length"}
    C --&gt;|"large → right of ridge"| D["Compute-bound region&lt;br/&gt;near the measured 5.96 TFLOP/s"]
    B --&gt; E["KV cache populated"]
    E --&gt; F["Decode loop&lt;br/&gt;one token at a time"]
    F --&gt; G{"Arithmetic intensity&lt;br/&gt;= concurrent requests"}
    G --&gt;|"small → left of ridge"| H["112 MiB of weights re-read&lt;br/&gt;for every token"]
    H --&gt; I["Bandwidth-bound region&lt;br/&gt;saturated at the measured 124 GB/s"]
    I -.-&gt;|"continuous batching grows the batch&lt;br/&gt;and pushes toward the ridge"| G
    D --&gt; J["Token streaming output"]
    I --&gt; J
</code></pre>

<p>The dashed feedback edge is the essence of what serving engines like vLLM and SGLang do. The arithmetic intensity of an individual request is not set by the user, it is set by the scheduler. Group the requests in flight at the same moment into one batch and a single weight read advances all of them, raising intensity by the number you grouped. vLLM makes this practical by splitting the KV cache into fixed-size blocks that need not be physically contiguous (<a href="https://docs.vllm.ai/en/v0.4.2/models/performance.html">PagedAttention</a>), and its chunked prefill slices long prompts so they can be interleaved between decode steps instead of monopolising an iteration. <a href="https://github.com/sgl-project/sglang">SGLang</a> adds <a href="https://arxiv.org/pdf/2312.07104">RadixAttention</a>, sharing common prefixes across requests through a radix tree to cut the prefill work itself. The three techniques are not mutually exclusive and are <a href="https://github.com/vllm-project/vllm/issues/2560">used together</a>.</p>

<h2 id="setup-and-integration">Setup and integration</h2>

<p>Let us measure it. All you need is PyTorch, and we used the version already present in the repository’s shared virtual environment.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># repository shared .venv (Python 3.12.8)</span>
<span class="nv">VIRTUAL_ENV</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PWD</span><span class="s2">/.venv"</span> uv pip <span class="nb">install </span>torch matplotlib
.venv/bin/python <span class="nt">-c</span> <span class="s2">"import torch; print(torch.__version__, torch.backends.mps.is_available())"</span>
<span class="c"># 2.13.0 True</span>
</code></pre></div></div>

<p>The benchmark has three parts. First, copy a 256 MiB fp16 buffer device-to-device and derive effective bandwidth from the combined read and write traffic. Second, run a 4096 square fp16 matmul to find the compute ceiling. Third, take a <code class="language-plaintext highlighter-rouge">4096 x 14336</code> weight, the same shape as the Llama-3-8B FFN up projection, and sweep batch size from 1 to 512.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">K</span><span class="p">,</span> <span class="n">N</span> <span class="o">=</span> <span class="mi">4096</span><span class="p">,</span> <span class="mi">14336</span>          <span class="c1"># Llama-3-8B hidden / intermediate
</span><span class="n">w</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">empty</span><span class="p">(</span><span class="n">K</span><span class="p">,</span> <span class="n">N</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="n">float16</span><span class="p">,</span> <span class="n">device</span><span class="o">=</span><span class="sh">"</span><span class="s">mps</span><span class="sh">"</span><span class="p">).</span><span class="nf">uniform_</span><span class="p">(</span><span class="o">-</span><span class="mf">0.02</span><span class="p">,</span> <span class="mf">0.02</span><span class="p">)</span>

<span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">32</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="mi">512</span><span class="p">]:</span>
    <span class="n">x</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">empty</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">K</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="n">float16</span><span class="p">,</span> <span class="n">device</span><span class="o">=</span><span class="sh">"</span><span class="s">mps</span><span class="sh">"</span><span class="p">).</span><span class="nf">uniform_</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">secs</span> <span class="o">=</span> <span class="nf">timed</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">torch</span><span class="p">.</span><span class="nf">matmul</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">w</span><span class="p">),</span> <span class="sh">"</span><span class="s">mps</span><span class="sh">"</span><span class="p">)</span>   <span class="c1"># 3 warmup + mean of 20
</span>    <span class="n">flops</span> <span class="o">=</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="n">b</span> <span class="o">*</span> <span class="n">K</span> <span class="o">*</span> <span class="n">N</span>
    <span class="n">total_bytes</span> <span class="o">=</span> <span class="n">w</span><span class="p">.</span><span class="nf">numel</span><span class="p">()</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="nf">numel</span><span class="p">()</span> <span class="o">+</span> <span class="n">b</span> <span class="o">*</span> <span class="n">N</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span>
    <span class="c1"># arithmetic intensity = flops / total_bytes ≈ b
</span></code></pre></div></div>

<p>It is worth pausing on why the third part is shaped like this. The arithmetic is <code class="language-plaintext highlighter-rouge">2 x B x K x N</code> and the bytes read are dominated by the weight at <code class="language-plaintext highlighter-rouge">K x N x 2</code>. Divide one by the other and exactly <code class="language-plaintext highlighter-rouge">B</code> remains. This sweep is therefore <strong>a walk along the x-axis of the roofline, driven by batch size</strong>. No model checkpoint to download, no serving engine to bring up.</p>

<p>Everything ran inside an isolated worktree.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bash scripts/blog/impl_sandbox.sh setup llm-decode-roofline
bash scripts/blog/impl_sandbox.sh run  llm-decode-roofline <span class="nt">--</span> .venv/bin/python roofline_bench.py
bash scripts/blog/impl_sandbox.sh teardown llm-decode-roofline
</code></pre></div></div>

<h2 id="measured-results">Measured results</h2>

<p>The environment was macOS 26.5.2 arm64, PyTorch 2.13.0, MPS backend. Every number below is taken verbatim from <code class="language-plaintext highlighter-rouge">run-1.log</code>. None of it is estimated.</p>

<p>The two hardware constants first.</p>

<table>
  <thead>
    <tr>
      <th>Metric</th>
      <th>Measured</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Effective memory bandwidth (copy)</td>
      <td>124.4 GB/s</td>
    </tr>
    <tr>
      <td>fp16 4096³ matmul</td>
      <td>5.96 TFLOP/s</td>
    </tr>
    <tr>
      <td>Roofline ridge point</td>
      <td>48.0 FLOP/byte</td>
    </tr>
  </tbody>
</table>

<p>A ridge at 48 means that unless you perform at least 48 operations per byte read from memory, you cannot fill this device’s arithmetic units. And as established, the arithmetic intensity of decode equals the batch size. So <strong>until the batch reaches 48, decode is bandwidth-bound without exception.</strong></p>

<p>The sweep shows exactly that.</p>

<table>
  <thead>
    <tr>
      <th>Batch</th>
      <th>Step time</th>
      <th>Achieved compute</th>
      <th>Time per token</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>0.834 ms</td>
      <td>140.8 GFLOP/s</td>
      <td>834.2 µs</td>
    </tr>
    <tr>
      <td>8</td>
      <td>0.904 ms</td>
      <td>1,038.8 GFLOP/s</td>
      <td>113.1 µs</td>
    </tr>
    <tr>
      <td>16</td>
      <td>0.920 ms</td>
      <td>2,042.9 GFLOP/s</td>
      <td>57.5 µs</td>
    </tr>
    <tr>
      <td>32</td>
      <td>1.033 ms</td>
      <td>3,637.6 GFLOP/s</td>
      <td>32.3 µs</td>
    </tr>
    <tr>
      <td>64</td>
      <td>1.413 ms</td>
      <td>5,317.9 GFLOP/s</td>
      <td>22.1 µs</td>
    </tr>
    <tr>
      <td>512</td>
      <td>10.015 ms</td>
      <td>6,003.9 GFLOP/s</td>
      <td>19.6 µs</td>
    </tr>
  </tbody>
</table>

<p>The interval worth staring at is batch 1 through 16. Sixteen times as many tokens, and step time moved from 0.834 ms to 0.920 ms, a rise of about 10%. Fifteen of those tokens were effectively free. Reading the 112 MiB of weights dominates everything, so it barely matters how many tokens ride along on that one read.</p>

<p>Past batch 64 the character changes. Step time starts growing in proportion to the batch while time per token barely moves, from 22.1 µs to 19.6 µs. We have crossed the ridge into the compute-bound region. The calculated ridge of 48 sitting between the observed inflection at 32 and 64 is a clean agreement.</p>

<p><img src="/tech-blog/assets/images/llm-decode-roofline-results.png" alt="Left: a log-log roofline plot of arithmetic intensity against achieved compute. Right: time per token falling from 834 µs to 19.6 µs as batch size grows" /></p>

<p><em>The measured points sit precisely on the slope. Growing the batch is not an optimisation, it is a move to the right along the axis.</em></p>

<p>Batch 1 summarised in two ratios: compute utilisation is <strong>2.36%</strong> of peak, while effective bandwidth reads as <strong>113.2%</strong> of the copy-based ceiling. Exceeding 100% is not an error, it is a property of the baseline. The copy benchmark includes writes, and writes carry extra traffic to fill the cache line first. A read-only matmul avoids that overhead and therefore achieves higher effective bandwidth. The conclusion is unambiguous: batch-1 decode saturates the memory bus completely while the arithmetic units idle.</p>

<p>The gap in per-token time between batch 1 and batch 512 is <strong>42.6x</strong>. Same hardware, same kernel, same precision. Scheduling alone.</p>

<h2 id="what-this-means-for-thakicloud">What this means for ThakiCloud</h2>

<p>ThakiCloud’s ai-platform runs tenant-isolated inference workloads on Kubernetes. The measurement above compresses the reasoning we apply when sizing GPU capacity.</p>

<p>First, <strong>it changes how you pick a GPU.</strong> For decode-heavy workloads, memory bandwidth and capacity determine throughput far more directly than the TFLOPS figure in the catalogue. At a fixed budget there is a real region where a device with lower peak compute but wider bandwidth wins on cost per token. Which side you are on is not a matter of taste; a fifteen-line sweep answers it per device.</p>

<p>Second, <strong>how you partition tenants drives cost.</strong> Handing each customer a dedicated GPU is operationally simple, but it leaves every GPU running near batch 1, using a couple of percent of its arithmetic units. That is why we shard queues with Kueue and funnel requests that share a model into a single serving instance. Filling the batch toward the ridge is the same thing as reducing GPU count.</p>

<p>Third, <strong>it moves the break-even point for on-premise deployments.</strong> Cost per token on your own cluster is GPU hourly cost divided by actual achieved throughput. If the batch never fills, that denominator is forty times smaller and no self-hosted setup will beat a commercial API. Once traffic is dense enough for batches to fill, the cost curve of the same hardware drops sharply. Traffic density is the first thing we check with customers evaluating on-premise for regulatory or data sovereignty reasons.</p>

<p>Agent workloads have a convenient property here. Paxis, ThakiCloud’s Agent-Native Cloud, runs a skill harness that selects from more than 960 skills and executes them in isolated sandboxes, and that process generates many short concurrent inference requests. Unlike traffic from humans sitting one at a time in front of a chat box, agent traffic is naturally concurrent. As the curve above shows, high concurrency means operating in the low cost-per-token region. That is the advantage of running the agent platform and the inference infrastructure under one roof.</p>

<h2 id="limits-and-counterarguments">Limits and counterarguments</h2>

<p>This was measured on a laptop-class accelerator with unified memory. Datacentre GPUs such as the H100 or H200 use HBM, with bandwidth in the single-digit TB/s range and correspondingly larger compute. The absolute numbers differ, obviously. What holds regardless of hardware is that a ridge exists and that decode’s arithmetic intensity equals batch size. In practice, compute on high-bandwidth datacentre parts has grown faster than bandwidth, so their ridge often sits further right, meaning you need an even larger batch to fill the arithmetic units.</p>

<p>Narrowing the measurement to a single FFN matmul is also a limitation. A real decode step includes attention kernels, and attention reads the KV cache rather than weights, so the bytes grow with sequence length. Growing the batch grows the KV cache with it, and memory capacity frequently binds before the ridge does. In production, the batch ceiling is usually set by free memory for the KV cache rather than by the roofline. That is precisely why PagedAttention concentrates on reducing fragmentation waste.</p>

<p>It should also be clear that growing the batch is not free. Step time at batch 512 is 10 ms against 0.834 ms at batch 1, twelve times longer. Throughput is at its best but inter-token latency for an individual user gets worse. For a conversational product, decide where you stand between throughput and latency as a service level objective first, then pick the maximum batch inside that constraint. The ridge tells you the ceiling; it does not tell you the target.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>Batch-1 decode uses 2.36% of the arithmetic units while completely saturating the memory bus, and growing the batch from there cuts time per token by 42.6x. Those two measured facts are the whole story. Once you know them, it becomes obvious why vLLM’s continuous batching and chunked prefill and SGLang’s prefix sharing all converged on filling the batch. They are not different optimisations; they are different ways of pushing requests rightward along the same axis.</p>

<p>So the next time you need to improve inference performance, add one step before you go shopping for a faster GPU. Measure bandwidth and compute on the device you intend to use, derive the ridge, and check whether your current batch sits to its left or its right. If it sits to the left, the answer is in your scheduler configuration, not in new hardware. The sweep in this post needs no model weights and no serving engine, and finishes in fifteen minutes with PyTorch alone.</p>

<h2 id="sources">Sources</h2>

<ul>
  <li>Original discussion: <a href="https://x.com/hjguyhan/status/2084039209313350116">what an AI engineer should learn (timeline)</a></li>
  <li>Roofline: An Insightful Visual Performance Model for Multicore Architectures, Williams, Waterman and Patterson, CACM 2009</li>
  <li><a href="https://docs.vllm.ai/en/v0.4.2/models/performance.html">vLLM performance and tuning docs (chunked prefill, PagedAttention)</a></li>
  <li><a href="https://github.com/sgl-project/sglang">SGLang repository</a> · <a href="https://arxiv.org/pdf/2312.07104">SGLang paper (RadixAttention)</a></li>
  <li><a href="https://github.com/vllm-project/vllm/issues/2560">vLLM issue #2560: compatibility of RadixAttention with existing techniques</a></li>
  <li>Measurement log: every figure comes from <code class="language-plaintext highlighter-rouge">outputs/blog-impl/llm-decode-roofline/run-1.log</code>.</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="llmops" /><category term="roofline" /><category term="llm-inference" /><category term="memory-bandwidth" /><category term="vLLM" /><category term="SGLang" /><category term="continuous-batching" /><category term="gpu-serving" /><category term="LLMOps" /><category term="ai-platform" /><summary type="html"><![CDATA[Going from batch 1 to batch 16 cost only 10% more time per step. Sixteen times the work for almost nothing, and the reason behind it is the whole economics of inference serving.]]></summary></entry><entry xml:lang="en"><title type="html">Your Agents Aren’t Stuck on Algorithms. They’re Stuck on Plumbing.</title><link href="https://thakicloud.com/tech-blog/en/research/agentic-rl-systems-self-evolving-agents/" rel="alternate" type="text/html" title="Your Agents Aren’t Stuck on Algorithms. They’re Stuck on Plumbing." /><published>2026-08-03T00:00:00+09:00</published><updated>2026-08-03T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/en/research/agentic-rl-systems-self-evolving-agents</id><content type="html" xml:base="https://thakicloud.com/tech-blog/en/research/agentic-rl-systems-self-evolving-agents/"><![CDATA[<p>Something strange starts the day after you ship an agent to production. Users hit it every day, the agent handles thousands of tasks, and yet the agent itself has not moved an inch from the day it was deployed. Weights are frozen, the system prompt is frozen, the tool list is frozen. The only thing accumulating is logs, and those logs usually pass through an observability dashboard once and then disappear. A paper posted to arXiv on 1 July 2026 points at a slightly unexpected culprit for this frozen state. Not the model, not the algorithm. The plumbing.</p>

<p><img src="/tech-blog/assets/images/agentic-rl-systems-self-evolving-agents-hero.png" alt="Abstract image of scattered execution records passing through a refining pipe and returning as a single loop" /></p>

<p><em>Records that pile up every day and flow nowhere are where this paper starts.</em></p>

<h2 id="why-this-is-worth-reading">Why this is worth reading</h2>

<p>This is for you if you have already deployed an agent, it has not improved with time, and you are waiting on the next model release. The conclusion up front: what you are waiting for is not a better model, it is a data pipeline you have not built yet. The execution records your agent produces every day are not in a form you can learn from, and converting them into a learnable form is a systems problem, not an algorithms problem.</p>

<h2 id="overview">Overview</h2>

<p>The paper is titled “Next-Generation Agentic Reinforcement Learning Systems Enable Self-Evolving Agents”, arXiv number 2607.01120. It went up on 1 July 2026 with a revision the next day. The authors are affiliated with Ant Group, HKUST and Tsinghua University. The title reads like another RL algorithm paper, but it is not one. It proposes no new algorithm and boasts no new benchmark score. Instead it stakes out a position.</p>

<p>The position is this. The foremost bottleneck for enterprise-scale self-evolving agents is not the absence of more powerful LLMs, nor the absence of more effective RL algorithms, but the absence of a system substrate that can transform deployed agent experience into governed, credit-assignable and replayable learning material.</p>

<p>What makes the claim interesting is that it stands in falsifiable form. If the bottleneck were the algorithm, deployed agents would improve every time a better algorithm arrived. Reality does not work that way. RL algorithms and models have both improved over the past two years, yet most agents deployed in enterprises still improve only through a human reading logs by eye, editing a prompt by hand and redeploying. The paper argues that this slow manual loop persists not out of laziness but because the plumbing does not exist.</p>

<h2 id="the-three-gaps-the-paper-identifies">The three gaps the paper identifies</h2>

<p>The paper argues that current agentic RL systems and the surrounding observability stack fall short in three essential respects. These three form the spine of the argument, so it is worth taking them one at a time.</p>

<p>First, there is no standardized agent trajectory data protocol capable of carrying RL learning signals at step granularity across heterogeneous agent paradigms. The operative phrase is step granularity. The logs we normally keep sit at the request and response level. But when an agent produces a wrong answer after ten tool calls, the information learning needs is not that the result was wrong, it is which of the ten steps went off the rails. The claim is that no standard container exists for that information.</p>

<p>Second, there is no enterprise-grade comprehensive data proxy that converts real workloads into governed learning substrates. This part is closer to governance than to technology. You cannot feed execution records laced with customer data straight into training. They have to be cleaned, permission-checked and replayable when needed, and almost nobody ships that layer as a product.</p>

<p>Third, there is no unified agent evolution control plane that automatically decides, based on trajectory statistics, whether to update policy weights or evolve the in-context harness. This is the most operationally real of the three. When an agent underperforms you have several levers available. You can edit the prompt, add a skill, refresh memory, or retrain the model. Each differs in cost by two orders of magnitude or more. And yet the decision about which lever to pull is today made almost entirely by human intuition.</p>

<p>The paper proposes three co-designed pillars answering these three gaps: a standardized trajectory data protocol, an enterprise-grade data proxy, and a unified evolution control plane. It stresses that the three cannot be built separately. They have to be designed together.</p>

<h2 id="how-the-three-pillars-interlock">How the three pillars interlock</h2>

<pre><code class="language-mermaid">flowchart TB
    A["Deployed agent&lt;br/&gt;coding assistant · support bot · research assistant"]
    A --&gt;|"execution records accumulating daily"| P1
    subgraph S["Three pillars that must be co-designed"]
        P1["Pillar 1 · Trajectory data protocol&lt;br/&gt;a standard format carrying learning signal per step"]
        P2["Pillar 2 · Data proxy&lt;br/&gt;turns real workloads into governed learning material"]
        P3["Pillar 3 · Evolution control plane&lt;br/&gt;decides what to update from trajectory statistics"]
        P1 --&gt; P2 --&gt; P3
    end
    P3 --&gt;|"expensive lever"| W["Retrain policy weights"]
    P3 --&gt;|"cheap lever"| H["Prompts · skills · tools · memory"]
    W --&gt; A
    H --&gt; A
    P2 -.fails governance.-&gt; X["Records unusable for learning"]
</code></pre>

<p><em>Break the chain and records pass through a dashboard and vanish. Connect it and you get a loop.</em></p>

<p>The part of this diagram worth staring at is the dotted line dropping to the lower right. If the data proxy cannot clear governance, those records cannot be used for learning. That is why the paper makes the data proxy a pillar of its own. The set of data you can technically collect and the set you may legally train on are different sets, and in an enterprise the latter is much smaller.</p>

<h2 id="areal-as-the-precursor-implementation">AReaL as the precursor implementation</h2>

<p>The argument is not pure thought experiment. There is a real system out of the same camp. AReaL, built jointly by the RL Lab at Ant Research and Tsinghua’s Institute for Interdisciplinary Information Sciences, with the version tagged boba² billing itself as a fully asynchronous RL system.</p>

<p>AReaL’s core design decision is to fully decouple generation from training. In traditional synchronous RL you collect rollouts and then run training, which leaves GPUs idling in alternation. Pull the two apart asynchronously and each side can run at its own pace. The project claims roughly 2.77 times the training speed of the synchronous approach with this structure, and reported state-of-the-art results at the time on LiveCodeBench, Codeforces and CodeContests. Read those numbers with the caveat that they are the project’s own measurements.</p>

<p>The more practically important part sits elsewhere. AReaL lets you independently customize the dataset, the rollout behavior and the training algorithm, and drew the boundaries so that doing so does not require touching heavy system-level code. That is one of the paper’s pillars in concrete form. The format of the learning material and the execution machinery of the system have to be separable before you can evolve by swapping material. Models at 8B, 14B and 32B are published, so the structure can be opened up directly.</p>

<h2 id="holding-the-same-ruler-against-our-own-policy-file">Holding the same ruler against our own policy file</h2>

<p>This paper stung because the third pillar was not somebody else’s problem. ThakiCloud’s automation skills already have something control-plane shaped. A file at <code class="language-plaintext highlighter-rouge">scripts/skills/skill_model_policy.json</code> governs the model tier of eighteen scheduled skills, and <code class="language-plaintext highlighter-rouge">skill_retro.py</code> records the outcome at the end of every run and updates the policy.</p>

<p>The rule is simple. A skill starts on a cheap tier, gets promoted automatically once two consecutive bad runs accumulate, and the consecutive-failure counter resets on a clean run. There is no automatic demotion. In the paper’s vocabulary this is a miniature control plane deciding what to update from trajectory statistics. Of the current eighteen, ten are tier-pinned and six are running on the upper tier.</p>

<p>Read that file through the paper’s lens, though, and two failures come into focus. Both actually happened and both are recorded in the file.</p>

<p>The first failure is one of trajectory statistics quality. Several entries in the policy file carry notes that failures caused by hitting the account’s weekly usage limit were miscounted as quality failures, pushing the tier up incorrectly. Two orchestration-flavored skills were promoted that way and were manually rolled back once the cause was understood. The paper’s first gap points exactly here. If the signal is not differentiated at step granularity you cannot distinguish the kind of failure, and decisions made on statistics that cannot tell kinds apart come out wrong. Quota exhaustion and quality shortfall present as the same exit code, but the remedies are opposites.</p>

<p>The second failure is having only one lever. One entry in the policy file was promoted to the upper tier on 11 July 2026, and its consecutive-failure counter has climbed to twenty-one since. Promotion did not solve the problem. Because the only lever our control plane could pull was model tier, there was nothing to do once the cause lay elsewhere. That is precisely why the paper defines the control plane with weight updates and in-context harness evolution side by side. One lever does not make a control plane. It makes an auto-promotion switch.</p>

<h2 id="what-this-means-for-thakicloud">What this means for ThakiCloud</h2>

<p><strong>Through the Paxis lens</strong> this paper reads like a roadmap. Paxis is the Agent-Native Cloud control plane running on top of ai-platform, treating Skills, Tools, Policies and Audit Logs as first-class resources. Map those four onto the paper’s three pillars and the picture lines up. Audit logs are where trajectory data is born, policies are the rules a data proxy applies when judging what may pass, and skills and tools are the cheap levers a control plane can pull instead of weights.</p>

<p>Separating what we already have from what we do not makes the next task obvious. We have audit logs, but not in a format that carries step-level learning signal. We have policy gates, but they do not connect to a path that promotes execution records into learning material. Defining skill execution logs as a replayable trajectory format is the first of the three pillars, and the other two can only be built on top of it. Knowing the order is the most practical thing this paper hands over.</p>

<p><strong>Through the ai-platform lens</strong> the second pillar is an infrastructure requirement. Turning execution records into learning material means storing, cleaning and replaying those records somewhere, and when customer data is mixed in, that work becomes a question of which physical boundary the data stays inside. For customers with on-premise and sovereignty requirements this is not a negotiating point, it is a precondition. Layering trajectory collection and replay pipelines onto K8s-based multi-tenant isolation and Kueue GPU scheduling would not be selling an observability feature. It would be selling the precondition for self-evolution.</p>

<p>The two lenses point the same way. ai-platform physically guarantees the boundary the data stays within, and inside it Paxis enforces the policy that promotes trajectories into learning material. The paper’s insistence on co-designing the three pillars shows up again in the relationship between these two layers.</p>

<h2 id="limits-and-counterarguments">Limits and counterarguments</h2>

<p>A few things are worth naming before taking this paper at face value.</p>

<p>First, this is a position paper, not a validated system report. The argument that three pillars are needed is persuasive, but there is no measurement inside the paper showing a system with all three actually achieving self-evolution at enterprise scale. There is a wide gap between an architecture proposal and a demonstration that it works, and this paper still stands on the near side.</p>

<p>Second, a standard protocol does not solve credit assignment for you. Building a container that holds signal per step and judging which of ten steps caused the failure are different problems. The latter is hard on its own terms and does not resolve itself once the container exists. The paper emphasizes the missing container but says relatively little about the difficulty that begins once it is full.</p>

<p>Third, governance costs can exceed the gains. Turning real work records into learning material means retaining customer data for a long time, and in regulated industries that is close to non-negotiable on its own. The data proxy is offered as the layer that resolves this, but in practice deciding not to use the data at all is often cheaper than building the proxy.</p>

<p>Fourth, automatic update decisions are themselves risky. As our two policy-file cases show, a decision made automatically on bad statistics quietly raises cost and obscures the cause. The smarter a control plane gets, the harder it becomes for a human to trace the basis of its judgment. The gain from automation and the loss in traceability have to be counted together.</p>

<p>Fifth, AReaL’s performance figures are self-reported by the project. That an asynchronous structure beats a synchronous one is structurally plausible, but the specific 2.77x multiple comes from the developers rather than an independent reproduction. Safer to cite it with the source attached.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>The value of this paper lies not in a new technique but in a relocation of the problem. It turns a gaze that looked for the reason agents fail to improve in models and algorithms toward the execution records that pile up daily and flow nowhere.</p>

<p>That was the conclusion stated at the top: what you are waiting for is not a better model, it is a data pipeline you have not built yet. We only confirmed, after holding the same ruler against ourselves, that the control plane we thought we had was in fact a one-lever promotion switch, and that the switch had spun twice on bad signal. Until then it looked like automation working fine.</p>

<p>If the agent you deployed has been unchanged for months, try one question before checking the next model release date. Where are the execution records that agent produced yesterday, and what can you change with them? If the answer ends at a dashboard, the bottleneck is not the model.</p>

<h2 id="sources">Sources</h2>

<ul>
  <li><a href="https://arxiv.org/abs/2607.01120">Next-Generation Agentic Reinforcement Learning Systems Enable Self-Evolving Agents (arXiv:2607.01120)</a></li>
  <li><a href="https://arxiv.org/abs/2607.01120v2">Revised version of the paper (arXiv:2607.01120v2)</a></li>
  <li><a href="https://github.com/inclusionAI/AReaL">AReaL open-source repository (inclusionAI/AReaL)</a></li>
  <li><a href="https://huggingface.co/inclusionAI/AReaL-boba-2-32B">AReaL-boba-2-32B model card (Hugging Face)</a></li>
  <li><a href="https://arxiv.org/abs/2505.24298">AReaL: A Large-Scale Asynchronous Reinforcement Learning System for Language Reasoning (arXiv:2505.24298)</a></li>
  <li><a href="https://deepnewz.com/china/ant-group-tsinghua-launch-open-source-areal-boba2-async-rl-system-2-77x-faster-24c514e7">Coverage of the Ant Group and Tsinghua AReaL-boba² release (DeepNewz)</a></li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="research" /><category term="self-evolving-agents" /><category term="agentic-RL" /><category term="reinforcement-learning-systems" /><category term="AReaL" /><category term="trajectory-data" /><category term="agent-governance" /><category term="Paxis" /><category term="ai-platform" /><summary type="html"><![CDATA[The agent you deployed is frozen on day one. Weights, prompts, tool lists, all fixed. This paper puts the blame on plumbing rather than algorithms.]]></summary></entry><entry xml:lang="en"><title type="html">KV Cache Does Not Scale With Model Size: Computing Serving Memory From the Attention Equations</title><link href="https://thakicloud.com/tech-blog/en/research/transformer-attention-kv-cache-math/" rel="alternate" type="text/html" title="KV Cache Does Not Scale With Model Size: Computing Serving Memory From the Attention Equations" /><published>2026-08-03T00:00:00+09:00</published><updated>2026-08-03T00:00:00+09:00</updated><id>https://thakicloud.com/tech-blog/en/research/transformer-attention-kv-cache-math</id><content type="html" xml:base="https://thakicloud.com/tech-blog/en/research/transformer-attention-kv-cache-math/"><![CDATA[<p><img src="/tech-blog/assets/images/transformer-attention-kv-cache-math-hero.png" alt="Layered key and value planes converging into a single latent vector" />
<em>Many per-head key/value caches compressing into one shared latent vector.</em></p>

<h2 id="why-read-this">Why read this</h2>

<p>This is for infrastructure engineers who serve open-weight LLMs with vLLM or SGLang and have to answer “how many GPUs does this model need?” The conclusion first: per-token KV cache is almost independent of parameter count. It is determined by three things only, namely layer count, KV head count, and head dimension. That is why a 27B model can genuinely consume more cache per token than a 70B one.</p>

<p>This post does that calculation by hand. The basis is <a href="https://arxiv.org/abs/2604.00965">Understanding Transformers and Attention Mechanisms: An Introduction for Applied Mathematicians</a>, posted to arXiv in April 2026. It is a 13-page introductory paper by Michel Fabrice Serret of the Paul Scherrer Institute in Switzerland, written as a presentation for the “Randomization in Transformer models” project at IPAM’s “Randomized Numerical Linear Algebra” workshop. Its arXiv classification is numerical analysis (math.NA), not machine learning.</p>

<h2 id="overview">Overview</h2>

<p>Transformer explainers are not scarce. What makes this paper different is that it addresses applied mathematicians. Instead of building intuition through analogy, it pins down in tables what dimension each matrix has and what stays resident in memory. That is exactly the useful part for practitioners. Capacity planning reduces to “how many floats stay in memory,” and Tables 1 and 2 of the paper answer that directly.</p>

<p>The paper starts from tokenization and embedding, then defines attention by analogy to a database lookup. You query a database of key-value pairs and get values back, except attention returns a similarity-weighted linear combination rather than an exact match. From that base it moves to multi-head attention, and the final section covers three techniques for cutting compute and memory: KV caching, Grouped Query Attention (GQA), and latent attention (MLA). That final section is what this post digs into.</p>

<h2 id="what-attention-actually-caches">What attention actually caches</h2>

<p>In autoregressive generation the model emits one token at a time. Every new token needs the key and value vectors of all preceding tokens, and recomputing them each step is wasteful, so they accumulate in memory. That is the KV cache. The paper notes this makes the cost of appending one token linear in token count, giving <code class="language-plaintext highlighter-rouge">O(N_tokens² · d)</code> overall. The price is memory: because key and value vectors must be held per layer and per head, the cost is <code class="language-plaintext highlighter-rouge">2 · N_L · N_h · N_KV · d</code> multiplied by the bits per float. In the paper’s words, this “can quickly become prohibitive, especially in the long-context case.”</p>

<p>The first way to shrink that bottleneck is GQA. Several query heads share one key-value head, so the number of cached vectors scales with KV heads rather than query heads. The extreme case of a single KV head is Multi-Query Attention (MQA). The important consequence is that cache size is set by KV head count, not query head count. This is precisely where parameter count and cache size start to diverge.</p>

<p>The second is latent attention, introduced by DeepSeek. Rather than storing keys and values separately, it keeps one vector per token projected into a shared low-rank latent space, <code class="language-plaintext highlighter-rouge">L = X·W^L</code>. The paper describes it as “a single cache vector per token, shared between all heads.” The latent form also permits merging weight matrices: latent-to-query and latent-to-key multiply into one, and latent-to-value merges with the output weights. Fewer matrices need to be resident at inference time.</p>

<pre><code class="language-mermaid">flowchart TB
    A["Input embeddings X&lt;br/&gt;N_KV × d_in"] --&gt; B{"Attention variant"}
    B --&gt;|"MHA"| C["Store K and V per head&lt;br/&gt;cache = N_heads × (d_QK + d_head)"]
    B --&gt;|"GQA"| D["Query heads share KV heads&lt;br/&gt;cache = N_KV heads × (d_QK + d_head)"]
    B --&gt;|"MLA"| E["Store one shared latent L = X·W^L&lt;br/&gt;cache = d_L"]
    C --&gt; F["Multiply by layer count&lt;br/&gt;to get per-token KV cache"]
    D --&gt; F
    E --&gt; F
    F --&gt; G["HBM left after weights&lt;br/&gt;sets concurrent token capacity"]
</code></pre>

<p><em>Tables 1 and 2 of the paper redrawn as a flow. All three branches end by multiplying through the layer count.</em></p>

<h2 id="porting-the-papers-formulas-to-code">Porting the paper’s formulas to code</h2>

<p>Table 1 lists the tensors multi-head attention keeps in memory; Table 2 does the same for latent attention. Extracting only the cache terms: MHA and GQA hold <code class="language-plaintext highlighter-rouge">N_KV heads × (d_QK + d_head)</code> floats per layer per token, while MLA holds <code class="language-plaintext highlighter-rouge">d_L</code>. For GQA the paper states explicitly that you replace <code class="language-plaintext highlighter-rouge">N_heads</code> with the KV head count in the cache terms and in W^K and W^V.</p>

<p>Feed in the specs from Table 3 and the numbers fall out. The calculation went into a short script.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BYTES_PER_FLOAT</span> <span class="o">=</span> <span class="mi">2</span>  <span class="c1"># fp16/bf16, the standard serving dtype
</span>
<span class="k">def</span> <span class="nf">cache_floats_per_token</span><span class="p">(</span><span class="n">m</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">Total KV-cache floats held per token across all layers.</span><span class="sh">"""</span>
    <span class="k">if</span> <span class="n">m</span><span class="p">.</span><span class="n">kind</span> <span class="o">==</span> <span class="sh">"</span><span class="s">mla</span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">m</span><span class="p">.</span><span class="n">layers</span> <span class="o">*</span> <span class="n">m</span><span class="p">.</span><span class="n">d_head</span>          <span class="c1"># one shared latent vector of dim d_L
</span>    <span class="k">return</span> <span class="n">m</span><span class="p">.</span><span class="n">layers</span> <span class="o">*</span> <span class="n">m</span><span class="p">.</span><span class="n">kv_heads</span> <span class="o">*</span> <span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">d_head</span> <span class="o">+</span> <span class="n">m</span><span class="p">.</span><span class="n">d_head</span><span class="p">)</span>   <span class="c1"># K and V, per kv head
</span></code></pre></div></div>

<p>Rather than trusting Table 3 outright, the script cross-checks against public HuggingFace <code class="language-plaintext highlighter-rouge">config.json</code> files.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">url</span> <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">https://huggingface.co/</span><span class="si">{</span><span class="n">repo</span><span class="si">}</span><span class="s">/raw/main/config.json</span><span class="sh">"</span>
<span class="c1"># compare num_hidden_layers / num_attention_heads / num_key_value_heads (or kv_lora_rank) / hidden_size
</span></code></pre></div></div>

<p>The full script lives at <code class="language-plaintext highlighter-rouge">scripts/blog/_kvcache_math_20260803.py</code> and the raw output at <code class="language-plaintext highlighter-rouge">outputs/blog-impl/transformer-attention-kv-cache-math/run-1.log</code>.</p>

<h2 id="results">Results</h2>

<p>Per-token KV cache for the three models, all fp16 and summed across every layer:</p>

<table>
  <thead>
    <tr>
      <th>Model</th>
      <th>Attention</th>
      <th>Layers</th>
      <th>KV heads</th>
      <th>Per token</th>
      <th>At 128k context</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Gemma 3 27B</td>
      <td>GQA</td>
      <td>62</td>
      <td>16</td>
      <td>496 KiB</td>
      <td>62 GiB</td>
    </tr>
    <tr>
      <td>Llama 3 70B</td>
      <td>GQA</td>
      <td>80</td>
      <td>8</td>
      <td>320 KiB</td>
      <td>40 GiB</td>
    </tr>
    <tr>
      <td>DeepSeek V2</td>
      <td>MLA</td>
      <td>60</td>
      <td>latent 512</td>
      <td>60 KiB</td>
      <td>7.5 GiB</td>
    </tr>
  </tbody>
</table>

<p><img src="/tech-blog/assets/images/transformer-attention-kv-cache-math-results.png" alt="Bar chart comparing per-token KV cache across three models" />
<em>Computed by applying the Table 1 and Table 2 formulas to the Table 3 specs. The 128k context figure is a normalization for comparison and does not reflect each model’s actual maximum context.</em></p>

<p>The striking result is that Gemma 3 27B holds 1.55x more cache per token than Llama 3 70B, even though Llama is nearly 2.6x larger in parameters. The formula explains it. Cache is governed by <code class="language-plaintext highlighter-rouge">layers × KV heads</code>: Gemma 3 27B gives 62 × 16 = 992, while Llama 3 70B gives 80 × 8 = 640. Llama has more layers but cut KV heads far more aggressively, and that decision outweighed the parameter gap.</p>

<p>The same formula quantifies what GQA buys. Had Llama 3 70B given each of its 64 query heads its own key and value, it would need 2560 KiB per token; sharing across 8 KV heads brings that to 320 KiB, exactly an 8x reduction. The saving ratio is simply query heads divided by KV heads. Gemma 3 27B, with 32 heads over 16 KV heads, gets only 2x.</p>

<p>DeepSeek V2’s latent attention operates on a different level: 60 KiB per token, 8.3x less than Gemma 3 27B, despite having the most heads of the three at 128. It keeps one shared 512-dimensional latent vector instead of per-head caches.</p>

<p>One honest note on verification. Of the three repositories, only <code class="language-plaintext highlighter-rouge">deepseek-ai/DeepSeek-V2</code> exposed its <code class="language-plaintext highlighter-rouge">config.json</code> without authentication, and there the values matched the paper’s Table 3 exactly: 60 layers, 128 heads, <code class="language-plaintext highlighter-rouge">kv_lora_rank</code> 512, <code class="language-plaintext highlighter-rouge">hidden_size</code> 5120. Llama 3 70B and Gemma 3 27B are gated, the script returned <code class="language-plaintext highlighter-rouge">unreachable</code>, and their figures come from the paper’s table as published.</p>

<h2 id="what-this-means-for-thakicloud">What this means for ThakiCloud</h2>

<p>This calculation maps directly onto capacity planning for ThakiCloud’s <strong>ai-platform</strong>, which schedules GPUs with Kueue on Kubernetes and serves models through vLLM. In a multi-tenant setting, how many concurrent sessions fit on one node drives unit cost, and the ceiling is set by exactly these KV cache figures.</p>

<p>Take a four-GPU H200 node with roughly 564GB of HBM. Llama 3 70B in fp16 occupies about 140GB of weights, leaving roughly 420GB. At 320 KiB per token that is arithmetically around 1.3 million tokens of cache, or about ten concurrent 128k-context sessions. Activation memory and page fragmentation cut into that in practice, but it fixes the order of magnitude. Put a latent-attention model on the same node and per-token cache drops by nearly an order of magnitude, raising concurrent sessions accordingly. In on-premise or sovereign deployments where you cannot simply add GPUs, that difference decides whether a deployment is viable at all.</p>

<p>Three practical consequences follow. First, do not estimate memory from parameter count when selecting a model; multiply <code class="language-plaintext highlighter-rouge">num_hidden_layers</code> by <code class="language-plaintext highlighter-rouge">num_key_value_heads</code> yourself. Second, the longer the context your service sells, the more KV head structure drives your cost base. Third, computing this theoretical ceiling before tuning vLLM’s <code class="language-plaintext highlighter-rouge">gpu_memory_utilization</code> and <code class="language-plaintext highlighter-rouge">max_model_len</code> narrows the search space considerably.</p>

<p>There is a <strong>Paxis</strong> angle too. Paxis is the Agent-Native Cloud control plane running on top of ai-platform, and its skill harness picks which model to call each turn. Agent workloads accumulate long conversation histories and tool output, so context grows quickly, which means model choice affects cache occupancy as well as token price. That a latent-attention model supports more concurrent agents on the same node is a legitimate input to routing policy.</p>

<h2 id="limits-and-counterarguments">Limits and counterarguments</h2>

<p>These numbers are computed, not measured. They come from applying the paper’s dimension tables to published specs, not from running a benchmark, so they will differ from what vLLM actually allocates. vLLM uses PagedAttention with block-level allocation, intra-block fragmentation occurs, and prefix cache sharing or quantized KV cache shift the figures again.</p>

<p>The paper has its own limits. It is a 13-page workshop presentation and the author calls it a brief introduction. It proposes no new technique and runs no experiments. The three models in Table 3 are examples rather than a survey, and Llama 3, Gemma 3, and DeepSeek V2 are all several generations old as of August 2026. What remains useful is not the specific numbers but the formula, which applies unchanged to any new model’s <code class="language-plaintext highlighter-rouge">config.json</code>.</p>

<p>Concluding that latent attention always wins would also be hasty. The paper adds an important caveat: without positional encoding, latent attention can be expressed exactly as an equivalent GQA or MHA model through the low-rank factorization, but applying RoPE breaks that equivalence. RoPE is applied after the keys are constructed, and in the latent form that ordering blocks the matrix merge and adds the overhead of recomputing the positional encoding at each evaluation. Real implementations therefore append a separate “non-latent” component carrying the positional encoding, keeping the computational benefit while giving up mathematical equivalence. Cache size alone cannot decide an architecture.</p>

<p>Finally, KV cache is only one axis of inference memory. Weights remain the largest single block, and at small batch sizes the bottleneck shifts from capacity to bandwidth.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>Per-token KV cache is set by <code class="language-plaintext highlighter-rouge">layers × KV heads × (d_QK + d_head)</code>, and parameter count never enters the equation. That is why Gemma 3 27B holds 1.55x more per token than Llama 3 70B and DeepSeek V2’s latent attention holds 8.3x less. The claim made at the top, that cache does not scale with model size, holds across all three.</p>

<p>Choosing your next model to serve is then straightforward. Open <code class="language-plaintext highlighter-rouge">config.json</code>, multiply <code class="language-plaintext highlighter-rouge">num_hidden_layers</code> by <code class="language-plaintext highlighter-rouge">num_key_value_heads</code> by <code class="language-plaintext highlighter-rouge">head_dim</code>, double it, and multiply by the dtype byte width. That is your per-token cache; multiply by target context length and concurrent sessions to get required HBM. It takes five minutes before you request a GPU quote, and this paper is the derivation of why those five minutes are sound.</p>

<h2 id="sources">Sources</h2>

<ul>
  <li>Paper: <a href="https://arxiv.org/abs/2604.00965">Understanding Transformers and Attention Mechanisms: An Introduction for Applied Mathematicians</a> (arXiv:2604.00965, math.NA, submitted 1 April 2026, 13 pages)</li>
  <li>Author: Michel Fabrice Serret, Center for Scientific Computing, Theory and Data, Paul Scherrer Institute</li>
  <li>Cross-check: <a href="https://huggingface.co/deepseek-ai/DeepSeek-V2/raw/main/config.json">deepseek-ai/DeepSeek-V2 config.json</a></li>
  <li>Script and raw log: <code class="language-plaintext highlighter-rouge">scripts/blog/_kvcache_math_20260803.py</code>, <code class="language-plaintext highlighter-rouge">outputs/blog-impl/transformer-attention-kv-cache-math/run-1.log</code></li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;info@thakicloud.co.kr&quot;, &quot;uri&quot;=&gt;nil, &quot;home&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Website&quot;, &quot;icon&quot;=&gt;&quot;fas fa-fw fa-link&quot;, &quot;url&quot;=&gt;&quot;https://thakicloud.co.kr&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;LinkedIn&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-linkedin&quot;, &quot;url&quot;=&gt;&quot;https://www.linkedin.com/company/thakicloud&quot;}, {&quot;label&quot;=&gt;&quot;X&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-x-twitter&quot;, &quot;url&quot;=&gt;&quot;https://x.com/thakicloud&quot;}]}</name><email>info@thakicloud.co.kr</email></author><category term="research" /><category term="research" /><category term="transformer" /><category term="attention" /><category term="kv-cache" /><category term="gqa" /><category term="mla" /><category term="deepseek" /><category term="vllm" /><category term="inference" /><category term="thakicloud" /><summary type="html"><![CDATA[For engineers serving LLMs with vLLM who need to size GPU capacity. The short version: per-token KV cache is almost independent of parameter count and falls out of layer count and KV head count alone. A 27B model really can consume more cache than a 70B one.]]></summary></entry></feed>