<?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.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://thakicloud.github.io/" rel="alternate" type="text/html" /><updated>2026-07-11T18:33:42+09:00</updated><id>https://thakicloud.github.io/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;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><entry xml:lang="ar"><title type="html">اختبار تطبيقات iOS عبر المتصفح على Mac سحابي بدون واجهة رسومية: serve-sim</title><link href="https://thakicloud.github.io/ar/dev/serve-sim-ios-simulator-web/" rel="alternate" type="text/html" title="اختبار تطبيقات iOS عبر المتصفح على Mac سحابي بدون واجهة رسومية: serve-sim" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/ar/dev/serve-sim-ios-simulator-web</id><content type="html" xml:base="https://thakicloud.github.io/ar/dev/serve-sim-ios-simulator-web/"><![CDATA[<p>عندما تطلب من وكيل برمجة يعمل بالذكاء الاصطناعي بناء تطبيق iOS، يصطدم بحائط أساسي. يستطيع الوكيل كتابة الكود بل وحتى بناء المشروع، لكنه لا يستطيع رؤية ما يحدث فعلاً على الشاشة. وتتفاقم المشكلة أكثر عندما تكون بيئة التطوير مستضافة على جهاز Mac Mini في السحابة، لأن نافذة محاكي Xcode نفسها لا تظهر أصلاً على خادم بدون واجهة رسومية (headless).</p>

<p>جاء <a href="https://github.com/EvanBacon/serve-sim">serve-sim</a>، الذي طوره Evan Bacon من فريق نواة Expo، ليواجه هذا الحائط مباشرة. وقد ذاع صيت هذه الأداة فعلياً عندما قدّمها المطور المستقل levelsio قائلاً إنها “تتيح رؤية تطبيق iOS الذي بناه Claude Code على Mac Mini في السحابة مباشرة عبر المتصفح في الوقت الفعلي”. وشعار serve-sim بسيط: “أمر <code class="language-plaintext highlighter-rouge">npx serve</code> الخاص بمحاكيات آبل”.</p>

<h2 id="نظرة-عامة">نظرة عامة</h2>

<p>ما يجعل serve-sim مثيراً للاهتمام هو أنه ليس مجرد أداة لعكس الشاشة. فهذه الأداة تفتح قناتين في آن واحد: الأولى هي تدفق فيديو يرسل شاشة المحاكي إلى المتصفح، والثانية قناة تحكم تتيح للمتصفح أو للوكيل التفاعل مع المحاكي. بعبارة أخرى، تجعل “المشاهدة” و”التحكم” ممكنتين عن بُعد في آن معاً.</p>

<p>أهمية هذا المزيج تكمن في أنه يُكمل حلقة التطوير الخاصة بوكلاء البرمجة بالذكاء الاصطناعي. يصبح بإمكان الوكيل تعديل الكود، وبناء المشروع، وتشغيله، ثم رؤية النتيجة على الشاشة، والضغط على الأزرار للانتقال إلى الخطوة التالية، وتكرار هذه الدورة الكاملة دون تدخل بشري. وهذا يتقاطع تماماً مع توجه Paxis، السحابة المخصصة للوكلاء (Agent-Native Cloud) لدى ThakiCloud، القائم على فكرة “أن ينفذ الوكيل عملاً فعلياً في بيئة معزولة”، مما يجعل من المفيد دراسة كيفية تنفيذ أداة مفتوحة المصدر واحدة لهذا النوع من سير العمل.</p>

<p><img src="/assets/images/serve-sim-ios-simulator-web-hero.png" alt="صورة تجريدية لشاشة هاتف ذكي على خادم سحابي بدون واجهة رسومية تتحول إلى جسيمات ضوئية تتدفق عبر الشبكة إلى نافذة متصفح" />
<em>تصوير لبنية تحوّل شاشة المحاكي على خادم بدون واجهة رسومية إلى تدفق يصل إلى متصفح بعيد.</em></p>

<h2 id="ما-هو-serve-sim">ما هو serve-sim</h2>

<p>آلية عمل serve-sim أبسط وأذكى مما قد يبدو للوهلة الأولى. لا حاجة لتثبيت إضافة (plugin) خاصة في Xcode ولا لزرع كود قياس داخل التطبيق. بدلاً من ذلك، يشغّل serve-sim عملية مساعدة صغيرة مكتوبة بلغة Swift تلتقط الإطارات المرئية لمحاكي iOS المُقلع مسبقاً عبر واجهة <code class="language-plaintext highlighter-rouge">simctl io</code> التي توفرها آبل.</p>

<p>تُعرض الشاشة الملتقطة عبر مسارين. أولاً، تدفق MJPEG يرسل فيديو إلى المتصفح بمعدل يصل إلى 60 إطاراً في الثانية. ثانياً، تُفتح قناة تحكم عبر WebSocket تتيح للمتصفح إرسال مدخلات مثل النقر والإيماءات إلى المحاكي. وفوق ذلك، تُركّب واجهة معاينة مبنية بـ React تتيح للمستخدم التفاعل مع التطبيق في المتصفح وكأنه جهاز فعلي.</p>

<pre><code class="language-mermaid">flowchart TB
    A[محاكي iOS مُقلَع] --&gt; B[عملية مساعدة بلغة Swift]
    B --&gt; C[التقاط الإطارات المرئية&lt;br/&gt;عبر simctl io]
    C --&gt; D[تدفق فيديو MJPEG&lt;br/&gt;حتى 60 FPS]
    C --&gt; E[قناة تحكم عبر WebSocket]
    D --&gt; F[واجهة معاينة React في المتصفح]
    E --&gt; F
    E --&gt; G[سطر أوامر الوكيل&lt;br/&gt;نقر، إيماءات، دوران، كاميرا]
    F -.تحكم بشري.-&gt; E
    G -.تحكم الوكيل.-&gt; E
</code></pre>

<p>الجوهر هنا هو أن الأداة تعمل مع “أي محاكي مُقلَع” أياً كان. لا حاجة لتعديل التطبيق، فيمكن ربطها مباشرة بمشروع قائم بالفعل. كذلك، تنقل الأداة سجلات المحاكي إلى المتصفح، مما يتيح لأدوات MCP من فئة browser-use قراءة تلك السجلات لتقييم الحالة. وتوجد أيضاً ميزة عملية تتيح إفلات ملفات فيديو أو صور في نافذة المتصفح لتُضاف كملفات إلى جهاز المحاكي.</p>

<h2 id="التثبيت-والاستخدام">التثبيت والاستخدام</h2>

<p>عتبة الدخول إلى serve-sim منخفضة. يكفي سطر واحد على جهاز Mac يحتوي على Node.js.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npx serve-sim
</code></pre></div></div>

<p>بعد التشغيل، يمكن مشاهدة المعاينة محلياً على <code class="language-plaintext highlighter-rouge">http://localhost:3200</code>. تدعم الأداة ثلاثة أنماط استخدام: محلياً، أو عبر الشبكة المحلية (LAN) من جهاز آخر على نفس الشبكة، أو على جهاز Mac بعيد مع نفق (tunnel) يتيح الوصول من أي مكان. حالة levelsio هي النمط الثالث تحديداً، حيث يعمل serve-sim على Mac Mini سحابي بدون واجهة رسومية بينما تجري المشاهدة عبر متصفح بعيد.</p>

<p>يُقدَّم دمج الوكلاء عبر مهارة وكيل (Agent Skill) منفصلة. هذه المهارة، الموجودة في <code class="language-plaintext highlighter-rouge">skills/serve-sim</code> ضمن المستودع، تعلّم Claude Code وCursor وCodex CLI وGemini CLI وأي مضيف آخر يطبّق معيار Agent Skills المفتوح كيفية التحكم في المحاكي عبر سطر الأوامر. وتشمل هذه القدرات النقر والإيماءات وأزرار الأجهزة الفعلية ودوران الشاشة وحقن مدخلات الكاميرا، إضافة إلى تمرير التدفق إلى نافذة المعاينة الخاصة بالمضيف.</p>

<h2 id="ملاحظة-حول-إعادة-الإنتاج">ملاحظة حول إعادة الإنتاج</h2>

<p>بيئة التنفيذ التي كُتب فيها هذا المقال هي جلسة معالجة دفعية بدون واجهة رسومية، حيث تشغيل Node.js محظور بموجب السياسة المتبعة، لذا لم يتسنَّ تشغيل <code class="language-plaintext highlighter-rouge">npx serve-sim</code> مباشرة والتقاط الشاشة فعلياً. وعليه، فإن الأوامر ووصف السلوك في هذا المقال مستندة إلى ما ورد في ملف README الخاص بالمستودع والمواد التعريفية الرسمية، دون اختلاق أي أرقام قياس أداء. يُنصح بالتحقق من مشهد بث المحاكي الفعلي وزمن الاستجابة الحقيقي عبر تشغيل الأمر أعلاه مباشرة في بيئة macOS مع محاكي Xcode مُقلَع.</p>

<h2 id="دلالات-على-منتجات-thakicloud">دلالات على منتجات ThakiCloud</h2>

<p>يبدو serve-sim للوهلة الأولى أداة موجهة لمطوري iOS، لكن خلفها يكمن تيار أوسع هو التطوير المصمم أصلاً للوكلاء (agent-native development).</p>

<p><strong>عدسة Paxis (التطوير المصمم للوكلاء).</strong> Paxis من ThakiCloud هي مستوى تحكم لسحابة مخصصة للوكلاء يشغّل المهارات في صناديق رملية معزولة ويمرر كل سلوك عبر بوابات سياسات وسجلات تدقيق. ومعيار Agent Skills المفتوح الذي يعتمده serve-sim هو نفس نموذج العقد الذي تتعامل معه بنية مهارات Paxis. فكرة أن تقدّم مهارة واحدة قدرة “النقر على المحاكي وتدويره وقراءة شاشته” لعدة مضيفي وكلاء مختلفين تسير في نفس اتجاه بنية Paxis التي تختار أكثر من 960 مهارة عبر خوارزمية BM25 وتنفذها في عزل. وبشكل خاص، فإن أعباء العمل التي يتحكم فيها الوكيل فعلياً في واجهة مستخدم حقيقية، كما في قناة التحكم لدى serve-sim، لا يمكن رفعها بأمان إلى بيئة الإنتاج إلا إذا مرّ ذلك التحكم عبر بوابة سياسات وسُجّل في سجل تدقيق. إذا كان serve-sim يقدّم “القدرة”، فإن Paxis تقدّم طبقة “الضبط الآمن” لتلك القدرة.</p>

<p><strong>عدسة ai-platform (بنية التنفيذ بدون واجهة رسومية).</strong> تكمن الجاذبية الحقيقية لـ serve-sim في عمله على جهاز Mac بعيد بدون واجهة رسومية. وفكرة البناء والبث على خادم بدون واجهة رسومية تشبه فلسفياً الطريقة التي تجدول بها منصة ai-platform من ThakiCloud أعباء العمل وتنفذها على Kubernetes دون الحاجة إلى واجهة رسومية. وأي خط أنابيب (pipeline) يُلحق فيه مشغّل macOS المطلوب لبناء تطبيقات iOS عند الطلب، يبني الوكيل عليه ويختبر تلقائياً، ثم يُبث النتيجة فقط إلى المستخدم البشري، يمكن أن يمتد إلى ما هو أبعد من التكامل المستمر (CI) نحو “ضمان جودة يقوده الوكيل”. وهذه بنية تجعل فيها البنية التحتية للتنفيذ بدون واجهة رسومية منخفضة التكلفة (ai-platform) ركيزة تدعم جدوى أتمتة الوكلاء اقتصادياً (Paxis).</p>

<h2 id="القيود-والحجج-المضادة">القيود والحجج المضادة</h2>

<p>هناك عدة نقاط ينبغي تناولها بموضوعية.</p>

<p>أولاً، يستهدف serve-sim المحاكي وليس الجهاز الفعلي. وبما أنه محاكٍ لا جهاز مادي حقيقي، تبقى المشكلات التي تظهر فقط على الأجهزة الفعلية، كالكاميرا والحساسات وخصائص الأداء، خارج نطاق الاكتشاف. ويبقى القيد القديم قائماً: نجاح الاختبار على المحاكي لا يضمن نجاحه على الجهاز الفعلي.</p>

<p>ثانياً، بث MJPEG بسيط ومتوافق على نطاق واسع، لكن كفاءة ضغطه ليست عالية. فبث فيديو عالي الجودة بمعدل 60 إطاراً في الثانية باستمرار عبر نفق بعيد قد يجعل عرض النطاق الترددي وزمن الاستجابة عنق زجاجة. وفي اختبارات الإيماءات التي تتطلب سرعة استجابة، ينعكس زمن الرحلة عبر الشبكة مباشرة كتأخير في التحكم.</p>

<p>ثالثاً، إتاحة “الرؤية والتحكم” للوكيل شيء، ودقة قراره شيء آخر تماماً. يظل احتمال أن يُسيء الوكيل تفسير التدفق ويضغط على زر خاطئ قائماً، وهذه بالتحديد هي النقطة التي تحتاج إلى بوابة سياسات ومراجعة بشرية. فكلما فتحت الأداة مزيداً من القدرات، ازدادت أهمية الطبقة التي تضبط تلك القدرات.</p>

<p>مع ذلك، فإن اتجاه serve-sim واضح. لقد أرسى جسراً عملياً حقيقياً للانتقال من “مرحلة يكتفي فيها الوكيل بكتابة الكود” إلى “مرحلة يبني فيها الوكيل ويشغّل ويتحكم مباشرة في الشاشة للتحقق”. وأي فريق يريد تطوير تطبيقات جوّال بواسطة وكلاء ذكاء اصطناعي على سحابة بدون واجهة رسومية يمكنه فتح هذا العالم فوراً بسطر واحد هو <code class="language-plaintext highlighter-rouge">npx serve-sim</code>.</p>

<h2 id="المصادر">المصادر</h2>

<ul>
  <li>Evan Bacon. “serve-sim: The <code class="language-plaintext highlighter-rouge">npx serve</code> of Apple Simulators.” GitHub. <a href="https://github.com/EvanBacon/serve-sim">https://github.com/EvanBacon/serve-sim</a></li>
  <li>@levelsio، تغريدة تعريفية بـ serve-sim. <a href="https://x.com/levelsio/status/2075328941317886210">https://x.com/levelsio/status/2075328941317886210</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;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="dev" /><category term="ios-simulator" /><category term="agent-skills" /><category term="developer-tools" /><category term="headless" /><category term="claude-code" /><category term="expo" /><summary type="html"><![CDATA[عند وضع Mac Mini في السحابة، ينعدم وجود واجهة رسومية تتيح رؤية محاكي iOS. يبث serve-sim الإطارات المرئية للمحاكي إلى المتصفح، ويفتح أيضاً قناة تحكم عبر WebSocket، مما يتيح لوكلاء البرمجة بالذكاء الاصطناعي بناء تطبيقات iOS والتفاعل معها واختبارها فعلياً في بيئة بدون واجهة رسومية.]]></summary></entry><entry xml:lang="ar"><title type="html">لم يكن هناك Q4 حقيقي تقريبا داخل Q4_K_M: تشريح دقيق لآلية التكميم في GGUF</title><link href="https://thakicloud.github.io/ar/llmops/gguf-quantization-internals/" rel="alternate" type="text/html" title="لم يكن هناك Q4 حقيقي تقريبا داخل Q4_K_M: تشريح دقيق لآلية التكميم في GGUF" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/ar/llmops/gguf-quantization-internals</id><content type="html" xml:base="https://thakicloud.github.io/ar/llmops/gguf-quantization-internals/"><![CDATA[<p><img src="/assets/images/gguf-quantization-internals-hero.png" alt="رسم توضيحي تجريدي لأوزان شبكة عصبية مكممة يعاد ترتيبها إلى كتل بأحجام مختلفة" /></p>

<h2 id="نظرة-عامة">نظرة عامة</h2>

<p>إذا سبق لك تشغيل نموذج لغوي كبير محليا، فمن المرجح أنك رأيت تسميات مثل <code class="language-plaintext highlighter-rouge">Q4_K_M</code> و<code class="language-plaintext highlighter-rouge">Q5_K_M</code> و<code class="language-plaintext highlighter-rouge">Q8_0</code>. يتوقف معظم الناس عند فهم مفاده أن “Q4 يعني 4 بت، إذن لا بد أنه الأصغر والأسرع”، ثم يقومون بتحميل الملف وتشغيله مباشرة. لكن هذه التسمية تخفي أكثر مما تظهر. قلة قليلة من الناس فتحوا فعليا ملفا موسوما بـ <code class="language-plaintext highlighter-rouge">Q4_K_M</code> وتحققوا، مصفوفة تلو الأخرى، مما إذا كان ممتلئا حقا ببيانات ذات 4 بت.</p>

<p>هذا المقال موجه لقادة الهندسة، والممارسين المسؤولين عن تكلفة الاستدلال، والفرق التي تسعى لتقديم النماذج محليا (on-premises). قمنا بتحميل ملف GGUF لنموذج Qwen2.5-0.5B-Instruct على عدة مستويات تكميم، وقسنا أحجام الملفات الفعلية، وقمنا بتشريح ملف واحد من نوع <code class="language-plaintext highlighter-rouge">Q4_K_M</code> بشكل كامل مصفوفة تلو الأخرى. جاءت النتيجة مختلفة تماما عن الحدس. نوضح لماذا يهم فهم هذه الفجوة لتكلفة الخدمة وجودتها، وماذا يعني ذلك بالنسبة لبنية الاستدلال التحتية لدى ThakiCloud.</p>

<p>ولنذكر الخلاصة أولا: في ملف <code class="language-plaintext highlighter-rouge">Q4_K_M</code> الخاص بهذا النموذج، لم تشكل المصفوفات التي هي فعلا تكميم K رباعي البت (Q4_K) سوى 6.1 بالمئة من إجمالي سعة الأوزان، وكان عرض البت الفعلي للملف ليس 4 بل <strong>6.16 بت</strong>. أي أن التسمية كانت أقرب إلى كذبة تقريبا.</p>

<h2 id="ما-هي-هذه-التقنية">ما هي هذه التقنية</h2>

<p>GGUF هو صيغة نموذج أحادية الملف تستخدم في منظومة llama.cpp. يجمع ملف واحد بين البيانات الوصفية (البنية المعمارية، أداة تجزئة الرموز، المعاملات الفائقة) وأوزان جميع المصفوفات المكممة معا. النقطة الجوهرية هي أن <strong>كل مصفوفة يمكن أن تستخدم نوع تكميم مختلفا</strong>. لذلك فإن تسمية على مستوى الملف مثل <code class="language-plaintext highlighter-rouge">Q4_K_M</code> لا تشير إلا إلى “النوع السائد”، وليس إلى أن الملف بأكمله من ذلك النوع.</p>

<p>تنقسم أنواع التكميم في llama.cpp إلى فئتين رئيسيتين. الأولى هي <strong>الفئة القديمة (legacy)</strong> (Q4_0، Q5_0، Q8_0) التي تجمع 32 وزنا في كتلة واحدة. والثانية هي <strong>فئة تكميم K</strong> (Q4_K، Q5_K، Q6_K) التي تجمع 256 وزنا في كتلة فائقة واحدة (superblock). ولأن تكميم K يقسم المقياس (scale) والحد الأدنى داخل الكتلة الفائقة بشكل أدق، فإنه يقدم جودة أفضل من الفئة القديمة عند نفس عرض البت. الحرف <code class="language-plaintext highlighter-rouge">K</code> في <code class="language-plaintext highlighter-rouge">Q4_K_M</code> يشير إلى تكميم K هذا، بينما <code class="language-plaintext highlighter-rouge">M</code> يعني الإعداد المسبق “المتوسط” الذي يرفع بعض المصفوفات الحساسة إلى دقة أعلى (Q6_K).</p>

<p>بالنظر عن قرب إلى بنية الكتلة الفائقة، يتضح سبب كفاءة تكميم K الأعلى. على سبيل المثال، يخزن Q4_K 256 وزنا في 144 بايت. من ذلك، تشغل القيم النقية ذات 4 بت مساحة 256 × 4 بت = 128 بايت، أما البايتات الـ16 المتبقية فهي بيانات وصفية تقسم الكتلة الفائقة إلى 8 كتل فرعية وتعيد تكميم مقياس كل منها وحدها الأدنى بـ6 بت. بعبارة أخرى، القيم نفسها ذات 4 بت، لكن الاحتفاظ بمقاييس دقيقة لإعادة بنائها يقلل من الخطأ. وهذا يتباين مع Q4_0 القديم الذي يحتفظ بمقياس واحد فقط لكل 32 وزنا. لذا فإن عرض البت الفعلي لـ Q4_K هو 144 × 8 ÷ 256 = 4.5 بت، أكبر قليلا من 4 بت النقية، لكن الجودة أكثر استقرارا بكثير.</p>

<p>هناك قيد حاسم واحد هنا. <strong>لا يمكن استخدام تكميم K إلا عندما يكون عدد أعمدة المصفوفة (<code class="language-plaintext highlighter-rouge">ne[0]</code> بمصطلحات ggml) قابلا للقسمة على 256.</strong> والسبب أن الكتلة الفائقة تعمل بوحدات من 256. وإذا لم يتحقق هذا الشرط، فإن llama.cpp يعود بصمت إلى الفئة القديمة (غالبا Q5_0). هذه القاعدة الواحدة تفسر نتيجة تجربة اليوم بأكملها.</p>

<pre><code class="language-mermaid">flowchart TB
    A["ملف GGUF (Q4_K_M)"] --&gt; B["تحديد النوع لكل مصفوفة&lt;br/&gt;llama_tensor_get_type()"]
    B --&gt; C{"هل عدد الأعمدة ne[0]&lt;br/&gt;قابل للقسمة على 256؟"}
    C --&gt;|"نعم"| D["استخدام تكميم K&lt;br/&gt;Q4_K / Q6_K (كتلة فائقة 256)"]
    C --&gt;|"لا"| E["العودة إلى الفئة القديمة&lt;br/&gt;Q5_0 (كتلة 32)"]
    B --&gt; F["رفع الدقة للمصفوفات الحساسة"]
    F --&gt; G["output.weight -&gt; Q8_0&lt;br/&gt;attn_v.weight -&gt; Q8_0&lt;br/&gt;ffn_down.weight -&gt; Q6_K"]
    D --&gt; H["تجميع عرض البت الفعلي"]
    E --&gt; H
    G --&gt; H
    H --&gt; I["عرض البت الفعلي لكامل الملف = 6.16&lt;br/&gt;(التسمية 'Q4' = 4.0)"]
</code></pre>

<h2 id="الإعداد-والتكامل">الإعداد والتكامل</h2>

<p>يمكن إعادة إنتاج التجربة دون أي تبعيات إضافية. تعيد واجهة برمجة تطبيقات Hugging Face عدد البايتات الفعلي لحجم الملف، ويمكن قراءة أنواع المصفوفات مباشرة باستخدام قارئ <code class="language-plaintext highlighter-rouge">gguf</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1) تثبيت أداة القراءة والتحميل</span>
pip <span class="nb">install </span>gguf huggingface_hub

<span class="c"># 2) تحميل ملف واحد فقط من نوع Q4_K_M (أقل من 500 ميجابايت لأنه نموذج 0.5B)</span>
hf download Qwen/Qwen2.5-0.5B-Instruct-GGUF <span class="se">\</span>
  qwen2.5-0.5b-instruct-q4_k_m.gguf <span class="nt">--local-dir</span> ./gguf
</code></pre></div></div>

<p>فيما يلي الشيفرة الخاصة بفتح أنواع مصفوفات الملف المحمل. يحتوي رأس (header) ملف GGUF على اسم كل مصفوفة وأبعادها ورقم نوع ggml مباشرة، لذا فإن مجرد تجميع هذه القيم يكشف بالضبط ما الذي يملأ الملف فعليا.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">collections</span> <span class="kn">import</span> <span class="n">Counter</span>
<span class="kn">from</span> <span class="n">gguf</span> <span class="kn">import</span> <span class="n">GGUFReader</span>

<span class="n">r</span> <span class="o">=</span> <span class="nc">GGUFReader</span><span class="p">(</span><span class="sh">"</span><span class="s">gguf/qwen2.5-0.5b-instruct-q4_k_m.gguf</span><span class="sh">"</span><span class="p">)</span>
<span class="n">hist</span> <span class="o">=</span> <span class="nc">Counter</span><span class="p">()</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">r</span><span class="p">.</span><span class="n">tensors</span><span class="p">:</span>
    <span class="n">hist</span><span class="p">[</span><span class="n">t</span><span class="p">.</span><span class="n">tensor_type</span><span class="p">.</span><span class="n">name</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="c1"># التحقق من النوع الفعلي لبعض المصفوفات التمثيلية
</span>    <span class="k">if</span> <span class="n">t</span><span class="p">.</span><span class="n">name</span> <span class="ow">in</span> <span class="p">(</span><span class="sh">"</span><span class="s">token_embd.weight</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">output.weight</span><span class="sh">"</span><span class="p">,</span>
                  <span class="sh">"</span><span class="s">blk.0.attn_v.weight</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">blk.0.ffn_down.weight</span><span class="sh">"</span><span class="p">,</span>
                  <span class="sh">"</span><span class="s">blk.0.attn_q.weight</span><span class="sh">"</span><span class="p">):</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">t</span><span class="p">.</span><span class="n">name</span><span class="si">:</span><span class="mi">26</span><span class="n">s</span><span class="si">}</span><span class="s"> </span><span class="si">{</span><span class="n">t</span><span class="p">.</span><span class="n">shape</span><span class="si">}</span><span class="s"> -&gt; </span><span class="si">{</span><span class="n">t</span><span class="p">.</span><span class="n">tensor_type</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">dict</span><span class="p">(</span><span class="n">hist</span><span class="p">))</span>
</code></pre></div></div>

<p>عدد البايتات لكل كتلة يأتي مباشرة من تعريفات ggml. فمثلا، يخزن Q4_K 256 وزنا في 144 بايت أي 4.5 بت لكل وزن، ويخزن Q6_K 256 وزنا في 210 بايت أي 6.5625 بت لكل وزن، بينما يخزن Q5_0 القديم 32 وزنا في 22 بايت أي 5.5 بت لكل وزن. وبجمع (عدد العناصر ÷ حجم الكتلة) × بايتات الكتلة لكل مصفوفة، يمكن حساب عرض البت الفعلي للملف بدقة.</p>

<h2 id="نتائج-التجربة-الفعلية">نتائج التجربة الفعلية</h2>

<p>أولا، أحجام الملفات. هذه هي القيم المقاسة فعليا لنفس النموذج بعد تحميله على 7 مستويات تكميم، مقارنة بالأصل بصيغة fp16 (1266 ميجابايت).</p>

<table>
  <thead>
    <tr>
      <th>التكميم</th>
      <th>حجم الملف</th>
      <th>مقارنة بـ fp16</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Q2_K</td>
      <td>415.2 MB</td>
      <td>32.8%</td>
    </tr>
    <tr>
      <td>Q3_K_M</td>
      <td>432.0 MB</td>
      <td>34.1%</td>
    </tr>
    <tr>
      <td>Q4_0</td>
      <td>428.7 MB</td>
      <td>33.9%</td>
    </tr>
    <tr>
      <td><strong>Q4_K_M</strong></td>
      <td><strong>491.4 MB</strong></td>
      <td><strong>38.8%</strong></td>
    </tr>
    <tr>
      <td>Q5_K_M</td>
      <td>522.2 MB</td>
      <td>41.2%</td>
    </tr>
    <tr>
      <td>Q6_K</td>
      <td>650.4 MB</td>
      <td>51.4%</td>
    </tr>
    <tr>
      <td>Q8_0</td>
      <td>675.7 MB</td>
      <td>53.4%</td>
    </tr>
  </tbody>
</table>

<p>هناك ما يثير الغرابة بالفعل هنا. الفرق بين Q2_K (415 ميجابايت) و Q4_0 (429 ميجابايت) هو 14 ميجابايت فقط. خفضنا عدد البتات إلى النصف، لكن حجم الملف لم ينخفض تقريبا. بل إن <code class="language-plaintext highlighter-rouge">Q4_K_M</code> (491 ميجابايت) أكبر فعليا من <code class="language-plaintext highlighter-rouge">Q4_0</code> (429 ميجابايت) ذي 4 بت النقية. بالنظر إلى الأسماء وحدها، هذه النتيجة غير مفهومة.</p>

<p>يتضح السبب الحقيقي عند فتح ملف <code class="language-plaintext highlighter-rouge">Q4_K_M</code> مصفوفة تلو الأخرى. فيما يلي توزيع الأنواع عبر 291 مصفوفة فيه.</p>

<table>
  <thead>
    <tr>
      <th>النوع الفعلي</th>
      <th>عدد المصفوفات</th>
      <th>عرض البت الاسمي</th>
      <th>حصة سعة الأوزان</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Q5_0</td>
      <td>133</td>
      <td>5.5</td>
      <td>54.9%</td>
    </tr>
    <tr>
      <td>Q8_0</td>
      <td>13</td>
      <td>8.5</td>
      <td>30.1%</td>
    </tr>
    <tr>
      <td>Q6_K</td>
      <td>12</td>
      <td>6.5625</td>
      <td>8.8%</td>
    </tr>
    <tr>
      <td>Q4_K</td>
      <td>12</td>
      <td>4.5</td>
      <td>6.1%</td>
    </tr>
    <tr>
      <td>F32 (norm/bias)</td>
      <td>121</td>
      <td>32.0</td>
      <td>0.1%</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/gguf-quantization-internals-results.png" alt="مخطط يظهر أحجام الملفات عبر مستويات التكميم لنموذج Qwen2.5-0.5B، وتكوين أنواع المصفوفات الفعلي داخل Q4_K_M. عرض البت الفعلي لـ Q4_K_M هو 6.16، بعيدا كثيرا عن رقم التسمية 4.0" /></p>

<p>على الرغم من تسمية <code class="language-plaintext highlighter-rouge">Q4_K_M</code>، لم يشكل تكميم K الحقيقي رباعي البت (Q4_K) سوى <strong>6.1 بالمئة</strong> من إجمالي سعة الأوزان. وبدلا من ذلك، استحوذ النوع القديم Q5_0 ذو 5.5 بت على أكثر من النصف (54.9 بالمئة)، واستهلك Q8_0 ذو 8.5 بت نسبة 30 بالمئة. وعند حساب عرض البت الفعلي لكامل الملف، نحصل على <strong>6.16 بت</strong>، أي أكثر من 1.5 ضعف الـ4 بت التي توحي بها التسمية.</p>

<p>بالتحقق من المصفوفات التمثيلية واحدة تلو الأخرى، يتضح النمط بجلاء. كانت الأنواع المقاسة فعليا كما يلي:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">token_embd.weight</code> (896 × 151936) -&gt; <strong>Q5_0</strong></li>
  <li><code class="language-plaintext highlighter-rouge">output.weight</code> (896 × 151936) -&gt; <strong>Q8_0</strong></li>
  <li><code class="language-plaintext highlighter-rouge">blk.0.ffn_down.weight</code> (4864 × 896) -&gt; <strong>Q6_K</strong></li>
  <li><code class="language-plaintext highlighter-rouge">blk.0.attn_v.weight</code> (896 × 128) -&gt; <strong>Q8_0</strong></li>
  <li><code class="language-plaintext highlighter-rouge">blk.0.attn_q.weight</code> (896 × 896) -&gt; <strong>Q5_0</strong></li>
</ul>

<p>هل تلاحظ النمط؟ لم تظهر المصفوفات التي تحمل تكميم K (Q4_K، Q6_K) إلا حيث كان عدد الأعمدة <code class="language-plaintext highlighter-rouge">ne[0]</code> يساوي 4864، كما في <code class="language-plaintext highlighter-rouge">ffn_down</code>. والرقم 4864 قابل للقسمة على 256 (19 × 256). أما معظم المصفوفات الأخرى فعدد أعمدتها <code class="language-plaintext highlighter-rouge">ne[0]</code> يساوي 896، والرقم 896 غير قابل للقسمة على 256 (3.5 × 256). لذلك لم تتمكن هذه المصفوفات من استخدام تكميم K إطلاقا وعادت جميعها إلى الفئة القديمة Q5_0. وإذا أضفنا إلى ذلك رفع الدقة (Q5_0، Q8_0) للمصفوفات الحساسة للجودة مثل التضمين (embedding) والمخرجات وقيمة الانتباه (attention value)، نحصل على ملف موسوم بـ <code class="language-plaintext highlighter-rouge">Q4_K_M</code> لكن جوهره الفعلي كتل تتراوح بين 5.5 و8.5 بت.</p>

<p>هذا بالضبط مصدر عرض البت الفعلي البالغ 6.16. يحتوي هذا الملف على ما مجموعه 630 مليون وزن خاضع للتكميم، مخزنة في نحو 485 ميجابايت من البايتات. 485,452,288 بايت × 8 ÷ 630,167,424 وزنا = 6.16 بت لكل وزن. وبإضافة نحو 6 ميجابايت من البيانات الوصفية للملف وحشو المحاذاة (alignment padding)، تتطابق النتيجة تماما مع حجم الملف الفعلي البالغ 491 ميجابايت. وتطابق الحساب مع حجم الملف هو أيضا دليل على دقة قراءة أنواع المصفوفات.</p>

<p>هذا يفسر أيضا النقطتين الغريبتين في جدول أحجام الملفات. السبب في أن Q2_K (415 ميجابايت) أصغر بالكاد من Q4_0 (429 ميجابايت) هو أن مصفوفات التضمين والمخرجات في هذا النموذج الصغير تشكل حصة كبيرة من إجمالي الأوزان، وهي تبقى بدقة عالية عند أي مستوى تكميم. مهما خفضت عدد البتات، تبقى تكلفة ثابتة في القاع لا تنخفض. أما سبب كون <code class="language-plaintext highlighter-rouge">Q4_K_M</code> أكبر من <code class="language-plaintext highlighter-rouge">Q4_0</code> النقي ذي 4 بت، فهو أن الإعداد المسبق <code class="language-plaintext highlighter-rouge">M</code> دفع ثمن رفع المصفوفات الحساسة إلى Q6_K وQ8_0 على شكل زيادة في حجم الملف. رقم التسمية أقل، لكن عرض البت الفعلي أعلى في الواقع.</p>

<p>وباختصار، أظهرت هذه التجربة من خلال القياس ثلاث حقائق. أولا، تشير التسمية على مستوى الملف إلى النوع السائد فقط ولا تضمن عرض البت الفعلي. ثانيا، في النماذج الصغيرة التي لا يكون فيها الحجم المخفي (hidden size) من مضاعفات 256، يتعطل تكميم K إلى حد كبير، مما يوسع الفجوة بين التسمية والجوهر. ثالثا، تشكل مصفوفات التضمين والمخرجات في النماذج الصغيرة حصة كبيرة من إجمالي السعة، وبمجرد الاحتفاظ بها بدقة عالية، تتضاءل بشكل كبير وفورات “التكميم الرباعي البت” المفترضة.</p>

<h2 id="الدلالات-على-منتجات-thakicloud">الدلالات على منتجات ThakiCloud</h2>

<p>اقتصرت هذه التجربة على تشريح نموذج صغير واحد، لكن دروسها تنتقل مباشرة إلى بنية الخدمة الإنتاجية التحتية. تقدم منصة ai-platform التابعة لـ ThakiCloud النماذج لبيئات عملاء متنوعة فوق Kubernetes وجدولة موارد GPU القائمة على Kueue. وفي هذا السياق، فإن “أي تكميم نختار” ليس مسألة ذوق، بل قرار يحدد تخصيص ذاكرة GPU، وحجم الدُفعة (batch)، وفي النهاية التكلفة لكل رمز (token).</p>

<p>الثقة بالتسمية كما هي تخل بتخطيط السعة. إذا افترضت أن <code class="language-plaintext highlighter-rouge">Q4_K_M</code> يعني “4 بت، إذن ربع الحجم الأصلي” وخصصت ذاكرة GPU على هذا الأساس، فستجد، كما في التجربة أعلاه، أنه يستهلك فعليا نحو 40 بالمئة من الأصل، وتنفد فتحات الدُفعات أسرع مما هو متوقع. يهم هذا الأمر بشكل خاص في الخدمة متعددة المستأجرين (multi-tenant)، حيث يجب حشر العديد من النماذج الصغيرة بكثافة على عقدة واحدة. وهناك، فإن الفرق بين قياس عرض البت الفعلي فعليا والاكتفاء بالثقة بالتسمية ينعكس مباشرة على عدد النماذج التي يمكن للعقدة استيعابها. لهذا السبب بالتحديد نتحقق من ملفات GGUF مصفوفة تلو الأخرى عند بناء صور الخدمة (serving images). وبالنسبة للعملاء الذين يتطلبون استضافة ذاتية أو نشرا محليا أو سياديا على وجه الخصوص، تتحول عادة القياس هذه بدلا من الافتراض إلى ميزة تنافسية حقيقية من حيث التكلفة.</p>

<p>جعل هذا التحقق نفسه مهمة قابلة للتكرار هو دور Paxis. وPaxis هي منصة التحكم الخاصة بـ ThakiCloud للسحابة الأصلية للوكلاء (Agent-Native Cloud) التي تعمل فوق ai-platform، وتتعامل مع المهارات والأدوات والسياسات وسجلات التدقيق كموارد من الدرجة الأولى. فإذا تم تسجيل تجربة اليوم، أي تحميل ملف GGUF وتجميع أنواع المصفوفات وإطلاق تحذير عند تجاوز عرض البت الفعلي حدا معينا، كمهارة واحدة، فإنها تعمل في بيئة معزولة (sandbox) وتمر جميع النتائج عبر بوابات السياسات وسجلات التدقيق. وبدلا من أن يفتح شخص الملف يدويا في كل مرة يصل فيها نموذج جديد إلى السجل، يعمل هيكل معتمد (validated skeleton) تلقائيا. هكذا يترابط الاقتصاد الذي تخلقه الخدمة منخفضة التكلفة (ai-platform) مع التنسيق (Paxis) الذي يجعل تلك الخدمة قابلة للتكرار بأمان.</p>

<h2 id="القيود-والحجج-المضادة">القيود والحجج المضادة</h2>

<p>هناك بعض النقاط التي يجب توضيحها بشكل صريح.</p>

<p>أولا، هذه النتائج تقترب من حالة قصوى خاصة بنموذج Qwen2.5-0.5B الذي يبلغ حجمه المخفي 896. أما في النماذج الأكبر التي يكون فيها الحجم المخفي من مضاعفات 256 (مثل 4096 أو 8192)، فإن تكميم K يُطبق بشكل طبيعي، ويقترب عرض البت الفعلي لـ <code class="language-plaintext highlighter-rouge">Q4_K_M</code> كثيرا من التسمية، عند نحو 4.8 بت.</p>

<p>بعبارة أخرى، الدرس الصحيح ليس أن “التسمية كذبة دائما”، بل أن “الفجوة بين التسمية والجوهر تختلف بشكل كبير حسب بنية النموذج، وتكون أكبر كلما كان النموذج أصغر”.</p>

<p>ثانيا، ليس بالضرورة أن يكون حجم الملف الكبير أمرا سيئا. فالاحتفاظ بمصفوفات التضمين والمخرجات بدقة عالية هو خيار متعمد لمنع انهيار الجودة في النماذج الصغيرة. بعبارة أخرى، ملف <code class="language-plaintext highlighter-rouge">Q4_K_M</code> هذا ليس ملفا “سيء الصنع”، بل هو نتيجة منطقية لرفع الدقة تلقائيا للحفاظ على الجودة في نموذج صغير. غير أن هذا الثمن لا يظهر في التسمية.</p>

<p>ثالثا، اقتصر هذا المقال على قياس بنية الملف وسعته، ولم يقس جودة الاستدلال الفعلية (الحيرة اللغوية perplexity، أو نتائج المعايير القياسية). وتتطلب العلاقة بين عرض البت والجودة تجربة منفصلة، نتركها موضوعا لمقال قادم. وما يمكن قوله هنا هو مبدأ تشغيلي واحد فقط: لا تخطط للسعة والذاكرة استنادا إلى التسمية، بل قسها فعليا.</p>

<p>الفرق بين الاكتفاء بالضغط على زر تشغيل نموذج محلي ومعرفة ما بداخل الملف فعليا يكمن بالضبط في عادة القياس هذه. خمس دقائق تقضيها في التحقق من الأرقام وراء التسمية يمكن أن تغير دقة خطة تكلفة الخدمة بأكملها.</p>

<h2 id="المصادر">المصادر</h2>

<ul>
  <li>مستودع نموذج Qwen2.5-0.5B-Instruct-GGUF: <a href="https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct-GGUF">huggingface.co/Qwen/Qwen2.5-0.5B-Instruct-GGUF</a></li>
  <li>وثائق التكميم في llama.cpp: <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md">github.com/ggml-org/llama.cpp</a></li>
  <li>تم قياس أحجام الملفات وتوزيعات أنواع المصفوفات وعروض البت الفعلية مباشرة من ملفات فعلية تم تحميلها من المستودع أعلاه.</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="llmops" /><category term="quantization" /><category term="gguf" /><category term="llama-cpp" /><category term="llmops" /><category term="self-hosting" /><category term="vllm" /><category term="paxis" /><summary type="html"><![CDATA[هناك فرق حقيقي بين من يحمل ملف GGUF من Hugging Face ويضغط زر التشغيل فقط، ومن يعرف بالضبط أي المصفوفات (tensors) مخزنة وبكم بت داخل ذلك الملف. قمنا فعليا بتحميل ملف Q4_K_M لنموذج Qwen2.5-0.5B وفتحناه مصفوفة تلو الأخرى. وعلى الرغم من الاسم، لم تشكل مصفوفات Q4_K الحقيقية ذات 4 بت سوى 6 بالمئة من الملف، وكان عرض البت الفعلي ليس 4 بل 6.16. يشرح هذا المقال سبب حدوث ذلك، بالاستناد إلى بيانات مقاسة فعليا حول بنية الكتل الفائقة (superblock) في تكميم K وقاعدة القسمة على 256.]]></summary></entry><entry xml:lang="ar"><title type="html">تذكّر القرار لا الوصف: دراسة بمشاركة Meta تعيد صياغة ذاكرة الوكلاء كمسألة rate-distortion</title><link href="https://thakicloud.github.io/ar/research/demem-agent-memory/" rel="alternate" type="text/html" title="تذكّر القرار لا الوصف: دراسة بمشاركة Meta تعيد صياغة ذاكرة الوكلاء كمسألة rate-distortion" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/ar/research/demem-agent-memory</id><content type="html" xml:base="https://thakicloud.github.io/ar/research/demem-agent-memory/"><![CDATA[<p><img src="/assets/images/demem-agent-memory-hero.png" alt="رسم تجريدي يصوّر ذكريات تتفرّع إلى مسارات منفصلة تؤدي إلى قرارات مختلفة" /></p>

<blockquote>
  <p>📄 <strong>المراجعة المتعمقة الكاملة (DOCX)</strong>: <a href="https://drive.google.com/file/d/1oxsADQALTfdn7I_mmZbaZfMnmqoCMF9o/view">نزّل المراجعة التفصيلية من Google Drive</a>.</p>
</blockquote>

<h2 id="نظرة-عامة">نظرة عامة</h2>

<p>كل من شغّل وكيلاً حوارياً لفترة طويلة رأى هذا الفشل من قبل. تفضيل أو قرار أعلنه المستخدم بوضوح قبل أيام ينساه الوكيل في لحظة ما، ويتصرف على عكسه. نافذة السياق محدودة، وعندما تطول المحادثة بما يكفي، يجب ضغط جزء من الماضي أو التخلص منه. السؤال الحقيقي هو: ماذا نتخلص منه؟</p>

<p>أجابت ذاكرة الوكلاء حتى الآن على هذا السؤال في الغالب بمعايير <strong>وصفية</strong>: هل هذا مرتبط بالموضوع؟ هل هو بارز؟ هل يُلخَّص بشكل جيد؟ تجادل هذه الورقة، “Remember the Decision, Not the Description” (arXiv 2605.10870)، التي شارك في تأليفها باحث من Meta AI، بأن هذا المعيار نفسه خاطئ. هذا المقال موجّه للمهندسين والباحثين الذين يصممون وكلاء الذكاء الاصطناعي، وللفرق التي تحتاج إلى وضع ذاكرة طويلة المدى في الإنتاج. نلخّص هنا إعادة الصياغة الجوهرية للورقة والنتائج التجريبية الداعمة لها، ونستعرض كيف ينطبق هذا المبدأ على Paxis، منصة الوكلاء لدى ThakiCloud.</p>

<h2 id="أين-تكمن-المشكلة">أين تكمن المشكلة</h2>

<p>ينطلق المؤلفون من رؤية بسيطة. الذاكرة قيّمة للوكيل ليس لأنها تصف الماضي بأمانة، بل لأنها <strong>تحافظ على الفصل بين تاريخَين يتطلبان سلوكَين مختلفَين حتى ضمن ميزانية ثابتة</strong>.</p>

<p>لنأخذ مثالاً بسيطاً. قال المستخدم بالأمس: “يجب ألا يتم هذا النشر إلا بعد موافقة يدوية”. واليوم، في سياق مشابه، قال: “يمكن تشغيل هذا السكربت تلقائياً”. العبارتان متشابهتان جداً في ظاهرهما. تتقاطعان في كلمات مثل النشر والتنفيذ والموافقة، وعند تلخيصهما تصبحان تقريباً نفس الجملة. من السهل أن تدمج الذاكرة القائمة على الصلة هاتين الحالتين في كتلة واحدة توصف بـ”تعليمات متعلقة بالنشر”. في تلك اللحظة يفقد الوكيل القدرة على تمييز أي تعليمة تنطبق على أي موقف، ويرتكب خطأ دفع نشر يتطلب موافقة يدوية بشكل تلقائي. الملخّص صحيح وصفياً، لكن الدمج قاتل من حيث القرار.</p>

<p>نمط الفشل الملموس هو كالتالي. موقفان يبدوان متشابهَين نصياً لكنهما في الواقع يتطلبان إجراءَين متعارضَين. عندما تكون ميزانية الذاكرة ضيقة، يصبح الضغط ضرورياً، والضغط يستدعي حتماً الدمج. إذا نظرنا فقط إلى التشابه الوصفي، سيُدمج هذان الموقفان في واحد. والنتيجة أن الوكيل يتخذ قراراً خاطئاً باستمرار كلما وصل إلى تلك الحالة. الصلة أو جودة التلخيص لا تجيبان عن السؤال الحقيقي، وهو: هل يمكن دمج هذين حقاً؟ المعيار يجب ألا يكون ما يبدو متشابهاً، بل ما يتطلب سلوكاً مختلفاً.</p>

<h2 id="الفكرة-الجوهرية-rate-distortion-محوره-القرار">الفكرة الجوهرية: rate-distortion محوره القرار</h2>

<p>ينقل المؤلفون هذه المشكلة إلى إطار نظرية المعلومات الخاص بـ rate-distortion. تتناول نظرية rate-distortion أصلاً مقدار التشويه (distortion) الناتج عن قدر معين من الضغط (rate)، والخطوة المحورية هنا هي إعادة تعريف التشويه نفسه. بدلاً من أن يكون التشويه خطأ إعادة بناء الإشارة، يُعرَّف كـ <strong>الخسارة في جودة القرار القابلة للتحقيق نتيجة الضغط (decision loss)</strong>.</p>

<pre><code class="language-mermaid">flowchart TB
    A["تاريخ تفاعل طويل&lt;br/&gt;(ميزانية ذاكرة ثابتة)"] --&gt; B{"هل ندمج الموقفين؟"}
    B --&gt; C["معيار محوره الوصف&lt;br/&gt;الصلة، البروز، جودة التلخيص"]
    B --&gt; D["معيار محوره القرار&lt;br/&gt;هل تسبب الحالة المشتركة تعارضاً في القرار"]
    C --&gt; E["الدمج إذا بدا الموقفان متشابهَين&lt;br/&gt;-&gt; دمج سلوكَين متعارضَين في واحد"]
    E --&gt; F["أخطاء قرار مستمرة"]
    D --&gt; G["الفصل فقط عند إثبات تعارض القرار&lt;br/&gt;certified refinement"]
    G --&gt; H["حدّ نسيان دقيق (exact forgetting boundary)&lt;br/&gt;+ حدود memory-distortion frontier"]
    H --&gt; I["جودة قرار أفضل عند نفس الميزانية"]
</code></pre>

<p>إليكم تشبيهاً. عند ضغط الصوت، نتخلص أولاً من الترددات التي لا تسمعها الأذن البشرية، لأن معيار التشويه هو “ما يسمعه الإنسان”. يرى المؤلفون أن ذاكرة الوكلاء يجب أن تعمل بالطريقة نفسها. ما يجب التخلص منه ليس “الذكرى التي تبدو أقل صلة”، بل “الذكرى التي لن يتغير أي قرار مستقبلي لو حذفناها”. هنا، rate هو ميزانية الذاكرة، وdistortion هو خسارة القرار التي يسببها هذا الضغط. إذا لم يؤدِّ دمج موقفَين في نفس الخانة إلى أي قرار خاطئ مستقبلاً، فإن هذا الدمج مجاني. وعلى العكس، إذا كان الدمج يطمس سلوكَين متعارضَين، فهو تشويه باهظ الثمن.</p>

<p>يترتب على هذا التعريف أمران. أولاً، <strong>حد النسيان الدقيق (exact forgetting boundary)</strong>، الذي يحدد بدقة حدود ما يمكن نسيانه بأمان دون الإضرار بجودة القرار. ثانياً، <strong>حدود memory-distortion frontier</strong>، التي تصف منحنى المفاضلة الأمثل بين ميزانية الذاكرة وجودة القرار. بعبارة أخرى، تضع الورقة نظرياً حداً أدنى مفاده: “إذا خفّضت الميزانية بهذا القدر، فستنخفض جودة القرار حتماً بمقدار لا يقل عن كذا”.</p>

<h2 id="demem-تحويل-النظرية-إلى-خوارزمية">DeMem: تحويل النظرية إلى خوارزمية</h2>

<p>DeMem هي التي تنقل هذه النظرية إلى ذاكرة وكيل فعلية قائمة على الخانات (slots). DeMem خوارزمية تعلّم ذاكرة عبر الإنترنت (online)، وتعمل وفق مبدأ واحد: <strong>لا تُقسِّم قسمة الذاكرة (partition) إلا عندما تُثبت البيانات (certify) أن حالة مشتركة تسبب تعارضاً في القرار.</strong></p>

<p>كلمة “تُثبت” هنا مهمة. لا يُفصل الموقفان بمجرد أن يبدوا مختلفَين، بل يُفصلان فقط بعد أن تتراكم أدلة فعلية على أن نفس حالة الذاكرة تتطلب قرارَين مختلفَين. وعلى العكس، إذا لم تتوفر مثل هذه الأدلة، يُبقى على الدمج توفيراً للميزانية. هذا التحفظ هو جوهر الطريقة. الفصل المتسرع يهدر الميزانية ولا يترك مكاناً للتمييزات المهمة فعلاً، بينما الدمج المتسرع يطمس سلوكيات متعارضة. certified refinement هو الانضباط الذي ينتظر، بين هذين الحدين، حتى تتحدث البيانات. يثبت المؤلفون أن هذا الإجراء يحقق ضماناً من نوع near-minimax regret، أي أن الندم (regret) بالمقارنة مع الأمثل يظل، حتى في أسوأ الحالات، مقيداً قريباً من الحد النظري.</p>

<p>يتحقق المؤلفون من هذه الآلية على مستويَين. أولاً، في بيئة تشخيصية اصطناعية، يصممون مهاماً يتعمدون فيها جعل التشابه الوصفي والتشابه القراري متباعدَين. هنا، تستمر المعايير الوصفية فقط في دمج المواقف المتشابهة ظاهرياً، مما يراكم الندم، بينما يتجنب DeMem هذا الفخ بالفصل فقط عند إثبات تعارض القرار. بعد ذلك، يتحققون مما إذا كانت هذه الأفضلية تنتقل إلى معايير قياس محادثات طويلة المدى فعلية، عبر نماذج تجارية ونماذج مفتوحة الأوزان على حد سواء. هذا البناء الذي ينطلق من النظرية، ويمر عبر تحقق آلي مضبوط، وينتهي عند معايير قياس واقعية، يحوّل النتائج إلى تفسير لـ”لماذا يفوز”، لا مجرد جدول أداء.</p>

<h2 id="نتائج-التجارب">نتائج التجارب</h2>

<p>في التشخيص الاصطناعي، سجّل DeMem أدنى ندم تراكمي (cumulative regret) بين جميع الأساليب المتساوية في الميزانية، واتسعت الأفضلية كلما زادت الفجوة بين التشابه الوصفي والتشابه القراري. بينما استمرت المعايير الوصفية فقط في دمج المواقف المتعارضة منتجةً أخطاء مستمرة، تجنّب DeMem ذلك بالفصل فقط عند إثبات تعارض القرار.</p>

<p>استمرت النتائج في معايير القياس الفعلية أيضاً. فيما يلي القيم المقاسة للدرجة الإجمالية على LoCoMo (بنموذج GPT-4.1-mini الأساسي).</p>

<table>
  <thead>
    <tr>
      <th>الطريقة</th>
      <th>Overall</th>
      <th>Temporal</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>DeMem</strong></td>
      <td><strong>0.921</strong></td>
      <td><strong>0.908</strong></td>
    </tr>
    <tr>
      <td>Mnemis</td>
      <td>0.891</td>
      <td>0.858</td>
    </tr>
    <tr>
      <td>EMem-G</td>
      <td>0.757</td>
      <td>0.660</td>
    </tr>
    <tr>
      <td>Nemori</td>
      <td>0.731</td>
      <td>0.454</td>
    </tr>
    <tr>
      <td>RAG</td>
      <td>0.710</td>
      <td>0.634</td>
    </tr>
    <tr>
      <td>FullContext</td>
      <td>0.692</td>
      <td>0.511</td>
    </tr>
    <tr>
      <td>Zep</td>
      <td>0.554</td>
      <td>0.383</td>
    </tr>
    <tr>
      <td>Mem0</td>
      <td>0.514</td>
      <td>0.428</td>
    </tr>
  </tbody>
</table>

<p>حقق DeMem أفضل درجة إجمالية، وكان قوياً بشكل خاص في فئات Temporal وOpen-Domain وMulti-Hop، حيث يكون الحفاظ على التمييز بين تفاعلات بعيدة زمنياً أمراً حاسماً. أما في فئة Single-Hop، التي تتعلق باسترجاع حقيقة واحدة، فقد تفوّق Mnemis (0.940) على DeMem (0.935) بفارق ضئيل، وهو ما يتفق مع تفسير أن فائدة الفصل المحوري بالقرار تكون أصغر في الاسترجاع أحادي الخطوة. وفي LongMemEval أيضاً، حقق DeMem أفضل متوسط درجات على كلا النموذجَين الأساسيَين، وكانت أكبر المكاسب في الفئات التي تتطلب دمجاً عبر جلسات متعددة. والأهم أن الأفضلية استمرت حتى على النموذج مفتوح الأوزان Llama-3.1-70B، مما يدل على أن هذه الميزة ليست مرتبطة بنموذج تجاري معين.</p>

<h2 id="دلالات-على-منتجات-thakicloud">دلالات على منتجات ThakiCloud</h2>

<p>تلتقي رؤية هذه الورقة تماماً مع تصميم الذاكرة في Paxis، مستوى التحكم لمنصة Agent-Native Cloud لدى ThakiCloud. Paxis هو مستوى تحكم يعمل فوق ai-platform ويتعامل مع المهارات (skills) والأدوات والسياسات وسجلات التدقيق كموارد من الدرجة الأولى، وضمن ذلك يحدد محرك المعرفة وطبقة الذاكرة يومياً ما الذي يُدمج وما الذي يُفصل.</p>

<p>أولاً، يمكن نقل معيار الدمج في محرك معرفة HKE Wiki ليصبح محورياً بالقرار. إذا دُمجت العناصر المتشابهة بمجرد التشابه النصي، فهناك خطر أن تُدمج حالتان تتطلبان إجراءَين متعارضَين في واحدة. وضع بوابة قبل الدمج مباشرة تسأل “هل يسبب هذان الأمران سلوكَين مختلفَين؟” هو نقل مباشر لمبدأ certified refinement في هذه الورقة.</p>

<p>ثانياً، يمنح هذا أساساً نظرياً لإدارة ميزانية الذاكرة الساخنة المقيمة في الجلسة (session-resident hot memory). الذاكرة الساخنة تفرض بالفعل ميزانيتها بحد أقصى من الأحرف، ومحاذاة معيار ما يُبقى وما يُحذف مع مبدأ “الحفاظ على التمييزات المؤثرة في القرار” يرفع جودة التقليم (pruning). أي إعطاء الأولوية للعناصر التي تفرّق بين القرارات، لا العناصر التي تُلخَّص بسلاسة.</p>

<p>ثالثاً، تُعد بوابات السياسات وسجلات التدقيق التي يخلّفها Paxis مصدر بيانات طبيعياً لإثبات لاحقاً أن “نفس الحالة أفضت إلى قرار مختلف”. إذا كان تشغيل certified refinement عبر الإنترنت لدى DeMem في الوقت الفعلي صعباً، يمكن اتباع مسار عملي بتحليل سجلات التدقيق هذه على دفعات غير متصلة (offline) وتحديث سياسة الدمج والفصل بشكل دوري. وهنا يلتقي مبدأ الذاكرة المحورية بالقرار مع التنسيق (orchestration) القائم على التدقيق الذي يجعل هذا المبدأ قابلاً للتكرار بأمان.</p>

<h2 id="القيود-والاعتراضات">القيود والاعتراضات</h2>

<p>لا بد من توضيح بعض النقاط.</p>

<p>أولاً، الإثبات (certify) له تكلفة. إثبات تعارض القرار من البيانات يتطلب تراكم ملاحظات، وفي بيئات البداية الباردة (cold start) أو التفاعل النادر، يتأخر الفصل، ولذلك يصعب الحكم من نص الورقة وحده على مصير جودة القرار في المراحل المبكرة.</p>

<p>ثانياً، تقدير “خسارة جودة القرار” عبر الإنترنت في بيئة الإنتاج يتطلب إشارة مكافأة أو حكماً (judge). تملك معايير القياس إجابات صحيحة تجعل الحصول على هذه الإشارة سهلاً، لكن كيفية تأمين هذه الإشارة في محادثات فعلية بلا إجابة صحيحة تبقى مهمة قادمة. قد يكون استخدام سجلات التدقيق المقترح أعلاه أحد الحلول، لكن ذلك خارج نطاق الورقة.</p>

<p>ثالثاً، يتضمن الملحق إثباتاً لصعوبة حسابية (computational hardness)، وهو ما يعني أن إيجاد القسمة (partition) المثلى صعب بشكل عام. DeMem هو تقريب عملي لذلك، وتلزم حدود إضافية حول الشروط التي ينهار عندها هذا التقريب.</p>

<p>ومع ذلك، فإن المبدأ نفسه، وهو نقل ذاكرة الوكيل من الوصف إلى القرار، بسيط وقوي، ويستحق النظر في تبنّيه الآن. إذا كان الوكيل ينسى باستمرار قراراته السابقة، فقد لا تكون المشكلة أن ذاكرته صغيرة، بل أن ذاكرته تحتفظ بالشيء الخطأ.</p>

<blockquote>
  <p>📄 <strong>المراجعة المتعمقة الكاملة (DOCX)</strong>: <a href="https://drive.google.com/file/d/1oxsADQALTfdn7I_mmZbaZfMnmqoCMF9o/view">نزّل المراجعة التفصيلية من Google Drive</a>.</p>
</blockquote>

<h2 id="المصادر">المصادر</h2>

<ul>
  <li>الورقة: <a href="https://arxiv.org/abs/2605.10870">Remember the Decision, Not the Description: A Rate-Distortion Framework for Agent Memory (arXiv 2605.10870)</a></li>
  <li>معايير القياس: LoCoMo، LongMemEval / النماذج الأساسية: GPT-4o-mini، GPT-4.1-mini، Qwen2.5-14B-Instruct، Llama-3.1-70B</li>
  <li>الأرقام في الجدول مقتبسة من الجدول 1 (LoCoMo، GPT-4.1-mini) في الورقة.</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="research" /><category term="agent-memory" /><category term="rate-distortion" /><category term="long-horizon-agents" /><category term="llm-agents" /><category term="paxis" /><summary type="html"><![CDATA[تعمل الوكلاء طويلة المدى ضمن ذاكرة محدودة، لكن أساليب الذاكرة حتى الآن كانت تنظّم الماضي وفق معايير وصفية مثل الصلة أو جودة التلخيص. هذه الورقة، التي شارك في تأليفها باحث من Meta AI، تقول إن هذا المعيار نفسه خاطئ. قيمة الذاكرة لا تكمن في وصف الماضي بأمانة، بل في الفصل بين المواقف التي تتطلب سلوكيات مختلفة حتى ضمن ميزانية ثابتة. يصوغ المؤلفون هذه المسألة كمسألة rate-distortion محورها القرار، ويقترحون خوارزمية تعلّم باسم DeMem تتفوق باستمرار على الأساليب القائمة عند نفس ميزانية الذاكرة.]]></summary></entry><entry xml:lang="ar"><title type="html">التعلّم المعزز للوكلاء لا ينتظر المجموعة: التعلّم من rollout واحد فقط</title><link href="https://thakicloud.github.io/ar/research/sao-single-rollout-async-agentic-rl/" rel="alternate" type="text/html" title="التعلّم المعزز للوكلاء لا ينتظر المجموعة: التعلّم من rollout واحد فقط" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/ar/research/sao-single-rollout-async-agentic-rl</id><content type="html" xml:base="https://thakicloud.github.io/ar/research/sao-single-rollout-async-agentic-rl/"><![CDATA[<p>لم يعد الحديث عن صقل الوكلاء عبر التعلّم المعزز مصطلحاً مختبرياً بحتاً. فالنماذج التي تُتقن مهاماً مثل إصلاح قواعد الأكواد على مدى عشرات الجولات كما في SWE-Bench، أو حل البراهين الرياضية عبر خطوات متعددة، لا تُبنى في الغالب بالتدريب المسبق وحده. جوهر الأمر يكمن في مرحلة ما بعد التدريب (post-training)، حيث يُشغَّل الوكيل فعلياً باستخدام الأدوات والتفاعل مع البيئة عبر rollout يُمنح على أساسه المكافأة. لكن كلما طال هذا الـ rollout، بدأ أسلوب التدريب الذي كان يُستخدم كمعيار قياسي حتى الآن في الانهيار.</p>

<p>تتناول الورقة البحثية “Single-Rollout Asynchronous Optimization for Agentic Reinforcement Learning” (arXiv 2607.07508)، التي نشرها باحثون من جامعة تسينغهوا وشركة Z AI في 8 يوليو 2026، هذه النقطة مباشرة. والخلاصة أن الباحثين تخلّوا عن “أخذ العينات الجماعي” (group sampling)، وهو جوهر GRPO الشائع الاستخدام. ولم يقتصر الأمر على تجربة هذا الأسلوب في تجارب الورقة فحسب، بل طبّقوه فعلياً في خط أنابيب حقيقي لتدريب النموذج المفتوح GLM-5.2 البالغ حجمه 750B.</p>

<h2 id="نظرة-عامة">نظرة عامة</h2>

<p>سبب أهمية هذه الورقة الآن هو أن عنق الزجاجة في تكلفة التدريب انتقل من الخوارزمية إلى معدل استغلال البنية التحتية. فدوال الخسارة التي تجعل النموذج أكثر ذكاءً موجودة بالفعل بأشكال متعددة. المشكلة الحقيقية هي أنه حتى مع تشغيل مئات وحدات GPU معاً، يُنفَق معظم الوقت في “الانتظار” لإنتاج خطوة تدريب واحدة فقط.</p>

<p>تُشغّل ThakiCloud أيضاً خمس تقنيات لما بعد التدريب، هي SFT وCPT وDPO وGRPO وGKD، ضمن نظام تدريب نماذج اللغة الكبيرة المبني على kubeflow. لذلك فإن الثمن الذي يدفعه أخذ العينات الجماعي في GRPO عند التعامل مع rollouts طويلة، والمخاطر الجديدة التي قد يجلبها أي بديل يُزيل هذا الثمن، ليست قضية بعيدة عنا. يستعرض هذا المقال ما غيّرته SAO، وما تعنيه هذه التغييرات لمؤسسة مثلنا تسعى لتدريب وكلاء على عناقيد GPU متعددة المستأجرين (multi-tenant).</p>

<p><img src="/assets/images/sao-single-rollout-async-agentic-rl-hero.png" alt="صورة تجريدية تقابل بين تدفق rollouts يصل واحداً تلو الآخر بشكل غير متزامن وrollouts تنتظر مجمّعة في مجموعة" />
<em>تصوير تخيلي يقابل بين rollout واحد يصل تباعاً بشكل مستمر، وrollouts تتجمّد في قائمة الانتظار إلى أن تكتمل المجموعة بأكملها.</em></p>

<h2 id="ما-هي-هذه-التقنية">ما هي هذه التقنية؟</h2>

<p>يجمع اسم SAO، كما هو، بين مفهومين: “rollout واحد” (single-rollout) و”التحسين غير المتزامن” (asynchronous optimization).</p>

<p>كانت خطوط أنابيب التعلّم المعزز التقليدية متزامنة (synchronous). تُحدَّد دفعة من الطلبات، ويُولَّد لكل طلب عدد من الـ rollouts، وعندما تكتمل جميعها تُحسَب المكافأة ويُنفَّذ تحديث واحد للسياسة. نجح هذا الأسلوب جيداً في المهام التي تُنتج إجابات قصيرة، لأن أطوال الـ rollouts كانت متقاربة.</p>

<p>لكن المشكلة تظهر في مهام الوكلاء. فمهمة برمجية واحدة قد تنتهي خلال 3 جولات فقط، بينما تستمر مهمة مجاورة في استدعاء الأدوات عبر 40 جولة. وفي خط الأنابيب المتزامن، تبقى بقية وحدات GPU عاطلة حتى ينتهي أبطأ rollout في الدفعة. ظهر التعلّم المعزز غير المتزامن أصلاً لإزالة هذا الهدر: يُحدَّث النموذج فور اكتمال كل rollout، بينما يستمر المولّد (generator) دون توقف في إنتاج الـ rollout التالي.</p>

<pre><code class="language-mermaid">flowchart TB
    subgraph SYNC[GRPO المتزامن]
        A1[دفعة من الطلبات] --&gt; A2[توليد G من الـ rollouts&lt;br/&gt;لكل طلب كمجموعة]
        A2 --&gt; A3[الانتظار حتى اكتمال&lt;br/&gt;أبطأ rollout]
        A3 --&gt; A4[حساب المكافأة النسبية للمجموعة&lt;br/&gt;تحديث واحد للسياسة]
        A4 -.توقف GPU.-&gt; A2
    end
    subgraph SAO[SAO غير المتزامن]
        B1[توليد rollout واحد&lt;br/&gt;لكل طلب] --&gt; B2[وصول فوري&lt;br/&gt;عند الاكتمال]
        B2 --&gt; B3[كبح تحديثات off-policy&lt;br/&gt;عبر تقليم الرموز ثنائي الاتجاه]
        B3 --&gt; B4[تحديث مستمر للسياسة]
        B4 --&gt; B1
    end
    SYNC --&gt;|إزالة حاجز المجموعة| SAO
</code></pre>

<p>هنا ينشأ التعارض الجوهري. فاسم GRPO (Group Relative Policy Optimization) يحمل معنى “النسبية الجماعية” منذ البداية. تُجمَع عدة rollouts لطلب واحد في مجموعة واحدة، وتُقارَن داخل هذه المجموعة الـ rollouts الأفضل نسبياً بالأسوأ لحساب الأفضلية (advantage). وميزة GRPO، التي هي في الوقت نفسه قيدها، أنها تُنتج إشارة التدريب من المقارنة داخل المجموعة فقط دون الحاجة إلى دالة قيمة (critic) منفصلة. فإن لم تكتمل المجموعة، يستحيل حساب الأفضلية. وهكذا يتعارض جوهرياً البناء غير المتزامن الذي يتعلّم فور وصول كل rollout مع GRPO الذي يفرض الانتظار حتى تكتمل المجموعة.</p>

<h2 id="لماذا-لا-يتناسب-grpo-مع-التعلّم-غير-المتزامن">لماذا لا يتناسب GRPO مع التعلّم غير المتزامن؟</h2>

<p>لنتأمل هذا التعارض بمزيد من التفصيل. فللحفاظ على المجموعة داخل خط أنابيب غير متزامن، لا مفر من أحد خيارين سيئين.</p>

<p>الأول هو الانتظار على مستوى المجموعة، وحينها تتلاشى ميزة اللاتزامن، وينتهي الأمر بالعودة فعلياً إلى النمط المتزامن الذي ينتظر أبطأ rollout.</p>

<p>والثاني هو توليد rollouts المجموعة الواحدة بسياسات (policies) مختلفة زمنياً. فإذا أنتجت سياسة قديمة بعض rollouts المجموعة، وأنتجت سياسة مُحدَّثة بعد بضع خطوات rollouts أخرى ضمن المجموعة نفسها، فإن تجميعها ومقارنتها نسبياً كمجموعة واحدة يصبح ملوّثاً إحصائياً من الأساس. فحين تتفاوت درجة off-policy من rollout إلى آخر، لكنها تُعامَل كأنها baseline واحد متجانس، يصبح التدريب غير مستقر.</p>

<p>إجابة SAO بسيطة: إلغاء المجموعة تماماً. يُولَّد rollout واحد فقط لكل طلب، وحالما يصل يُستخدَم مباشرة في التدريب. وبزوال حاجز المجموعة، لا يضطر المولّد إلى الانتظار إطلاقاً، فيتقلّص وقت خمول GPU بشكل كبير.</p>

<h2 id="ركيزتا-sao-rollout-واحد-وتقليم-الرموز-ثنائي-الاتجاه">ركيزتا SAO: rollout واحد وتقليم الرموز ثنائي الاتجاه</h2>

<p>لكن إلغاء المجموعة يعني فقدان ما كانت GRPO تحصل عليه مجاناً. فالمقارنة داخل المجموعة كانت تلعب بحد ذاتها دور baseline يُقلّل التباين. وحين يكون هناك rollout واحد فقط، يختفي معيار المقارنة القائم على “هل تفوّق هذا الـ rollout على متوسط المجموعة؟”. فضلاً عن ذلك، في البنية غير المتزامنة تنشأ فجوة زمنية بين السياسة التي أنتجت الـ rollout والسياسة التي يُراد تحديثها الآن. وهذه الفجوة، أي مشكلة off-policy، هي الخطر الثاني الذي يهدد استقرار التدريب.</p>

<p>تتصدى SAO لمشكلة الاستقرار هذه عبر “تقليم صارم ثنائي الاتجاه على مستوى الرمز” (strict double-side token-level clipping). فالتقليم (clipping) الذي كانت تستخدمه عائلة PPO أصلاً هو آلية تقصّ التدرّج (gradient) عندما تخرج نسبة الأهمية (importance ratio) عن نطاق محدد. وتُطبّق SAO هذا التقليم على مستوى كل رمز (token) على حدة، وبصرامة في الاتجاهين الأعلى والأسفل معاً. فعند الرموز التي تباعدت فيها rollout السياسة القديمة كثيراً عن السياسة الحالية، يُكبَح التحديث بقوة، مما يمنع الإشارات ذات الفجوة الزمنية الكبيرة من إفساد التدريب.</p>

<p>ونتيجة هذا المزيج، تُفيد الورقة بأن SAO واصلت التدريب باستقرار على مدى 1,000 خطوة. وإذا أخذنا بعين الاعتبار أن حالات التباعد أو الانهيار شائعة في التعلّم المعزز غير المتزامن بعد تجاوز بضع مئات من الخطوات، فإن استقرار التدريب لـ 1,000 خطوة يُعدّ دليلاً داعماً للمزاعم الأساسية لهذه الطريقة.</p>

<h2 id="النتائج-الفعلية-والتحقق">النتائج الفعلية والتحقق</h2>

<p>قارنت الورقة SAO بـ GRPO ومتغيراتها، وأفادت بتفوّقها باستمرار في معايير قياس الترميز والاستدلال الخاصة بالوكلاء. والمعايير المذكورة هي SWE-Bench Verified (حل مشكلات GitHub الحقيقية)، وBeyondAIME (رياضيات عالية الصعوبة)، وIMOAnswerBench (رياضيات بمستوى الأولمبياد). وتشترك المعايير الثلاثة في كونها مهام متعددة الخطوات وطويلة النَفَس، وليست إجابات قصيرة مباشرة، وهو بالضبط المجال الذي تستهدفه SAO.</p>

<p>أما التحقق الأكثر إقناعاً فليس في جداول المعايير، بل في واقعة النشر ذاتها. فقد استُخدِمت SAO في خط أنابيب فعلي للتعلّم المعزز للوكلاء لتدريب النموذج المفتوح GLM-5.2 (نموذج MoE بحجم إجمالي 750B وحجم فعّال 40B من المعاملات النشطة، 750B-A40B). وكون طريقة بحثية لم تبقَ حبيسة الورقة العلمية، بل استُخدِمت في تدريب إنتاجي لنموذج بحجم مئات المليارات، إشارة قوية على أن هذه الطريقة تصمد عند المقياس الحقيقي، لا في إعدادات تجريبية صغيرة فقط.</p>

<p>غير أن هذا المقال لا يقتبس الأرقام التفصيلية للمعايير. فإن تعذّر إعادة إنتاج الأرقام الدقيقة التي تحقّق منها النص الأصلي في هذا المكان، فالأمانة تقتضي نقل بنية الطريقة وأسماء المعايير المذكورة صراحة دون اختلاق أي رقم. ومن يحتاج إلى الدرجات الدقيقة، فليرجع إلى النص الأصلي أدناه مباشرة.</p>

<h2 id="دلالات-التطبيق-على-منتجات-thakicloud">دلالات التطبيق على منتجات ThakiCloud</h2>

<p>لا يقتصر درس SAO على كونه ورقة خوارزمية واحدة، بل يمسّ مباشرة طريقة تشغيل عناقيد GPU.</p>

<p><strong>عدسة ai-platform (البنية التحتية لتدريب GPU).</strong> تجدول منصة ai-platform التابعة لـ ThakiCloud تدريب GPU متعدد المستأجرين فوق Kubernetes وKueue. ونظام تدريب نماذج اللغة الكبيرة المبني على kubeflow يدعم بالفعل GRPO كأحد تقنيات ما بعد التدريب. والسؤال الذي تطرحه SAO واضح: كم يتراجع معدل استغلال GPU في مهام تدريبنا بسبب التفاوت في أطوال الـ rollouts؟ ففي المهام المتفاوتة الطول كـ rollouts الوكلاء، يتحوّل انتظار المجموعة المتزامن إلى تكلفة مباشرة. وفصل توليد الـ rollouts غير المتزامن عن التدريب يتيح استخلاص خطوات تدريب فعّالة أكثر من العدد نفسه من وحدات GPU، وهو ما يشكّل رافعة مباشرة لخفض تكلفة التدريب لكل مستأجر في بيئة متعددة المستأجرين. كما أن التحقق مما إذا كانت جدولة gang scheduling وإدارة الطوابير في Kueue تفرض نمط “الانتظار حتى تكتمل المجموعة” يُعدّ نقطة تحسين عملية أخرى.</p>

<p><strong>عدسة Paxis (نتاج تدريب الوكلاء).</strong> Paxis، وهي منصة Agent-Native Cloud التابعة لـ ThakiCloud، مستوى تحكّم يُشغّل المهارات (skills) في صناديق رملية معزولة (sandbox) ويمرّر كل سلوك عبر بوابات سياسات وسجلات تدقيق. والوكيل الذي تسعى SAO لتدريبه جيداً، أي وكيل يستدعي الأدوات عبر جولات متعددة ويُصلح قواعد الأكواد، هو بالضبط عبء العمل الذي تُشغّله Paxis. بل إن آثار الوكلاء (agent traces) الفعلية التي تُولّدها Paxis داخل الصناديق الرملية المعزولة يمكن أن تكون بحدّ ذاتها مصدر rollouts للتعلّم المعزز غير المتزامن على غرار SAO. وبذلك تكتمل حلقة: تُنتج ai-platform الـ rollouts وتدرّب عليها بتكلفة منخفضة، وتُشغّل Paxis الوكيل الناتج بأمان، لتُولّد بدورها بيانات تدريب جديدة. إنها بنية تقوم فيها بنية التدريب منخفضة التكلفة (ai-platform) بدعم جدوى الوكلاء الاقتصادية (Paxis).</p>

<h2 id="القيود-والاعتراضات">القيود والاعتراضات</h2>

<p>قبل تبنّي هذه الطريقة دون تمحيص، ينبغي التوقف عند بضع نقاط.</p>

<p>أولاً، يتخلّى الـ rollout الواحد عن تقليل التباين الذي كانت توفّره المجموعة. وتُعوّض SAO عن ذلك بالتقليم، لكن التقليم بطبيعته آلية تقصّ إشارة التدريب. فالتقليم الصارم أكثر مما ينبغي قد يُلقي حتى بالتدرّجات الصالحة، مما يُبطئ التدريب. ونقطة التوازن بين “الاستقرار” و”سرعة التعلّم” قابلة للتغيّر بشكل كبير تبعاً للمهمة والمقياس.</p>

<p>ثانياً، التحقق عبر تدريب نموذج بحجم 750B مثير للإعجاب، لكن نجاحه عند هذا المقياس لا يعني أنه الأمثل بالضرورة في إعدادات المؤسسات الصغيرة. فخط الأنابيب غير المتزامن يتطلّب تعقيداً إضافياً في البنية التحتية لفصل المولّد عن المدرّب. وبالنسبة للفرق التي تُجري ضبطاً دقيقاً قصيراً بعدد محدود من الـ rollouts، قد يكون GRPO المتزامن أبسط وكافياً.</p>

<p>ثالثاً، توجد مقاربات نشطة في الاتجاه المعاكس أيضاً. فقد ظهرت في الفترة نفسها دراسة تتناول العلاقة بين staleness ومعدل التعلّم في RLHF غير المتزامن عبر قوانين تحجيم (scaling laws) (arXiv 2607.01083)، كما ظهرت مقاربات تُثبّت التدريب غير المتزامن عبر محاذاة التدرّجات (gradient alignment). ولذلك فالأدق أن نعتبر مبدأ SAO القائم على “إلغاء المجموعة وكبح ذلك بالتقليم” واحداً من عدة إجابات قوية محتملة على مسألة مفتوحة هي التعلّم المعزز غير المتزامن للوكلاء، لا الإجابة الوحيدة الصحيحة.</p>

<p>ومع ذلك، فإن إسهام SAO واضح. فقد حدّدت المشكلة بدقة (عدم كفاءة أخذ العينات الجماعي عند طول الـ rollouts)، وتحقّقت من الحل (rollout واحد مع تقليم ثنائي الاتجاه) عبر تدريب إنتاجي فعلي بمقياس مئات المليارات من المعاملات. وأي مؤسسة يمثّل فيها معدل استغلال GPU تكلفة التدريب مباشرة، لديها سبب وجيه لحساب المبلغ الذي يُهدَر في خط أنابيبها بسبب “انتظار المجموعة”.</p>

<h2 id="المصادر">المصادر</h2>

<ul>
  <li>Zhenyu Hou, Yujiang Li, Jie Tang, Yuxiao Dong. “Single-Rollout Asynchronous Optimization for Agentic Reinforcement Learning.” arXiv 2607.07508 (2026-07-08). <a href="https://arxiv.org/abs/2607.07508">https://arxiv.org/abs/2607.07508</a></li>
  <li>ذو صلة: “Staleness-Learning Rate Scaling Laws for Asynchronous RLHF.” arXiv 2607.01083. <a href="https://arxiv.org/abs/2607.01083">https://arxiv.org/abs/2607.01083</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;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="research" /><category term="reinforcement-learning" /><category term="agentic-rl" /><category term="grpo" /><category term="async-rl" /><category term="llm-training" /><category term="post-training" /><summary type="html"><![CDATA[عند تدريب مهام الوكلاء الطويلة عبر التعلّم المعزز، يُبقي أخذ العينات الجماعي في GRPO وحدات GPU عاطلة في انتظار أبطأ rollout. استخدمت جامعة تسينغهوا وZ AI طريقة SAO فعلياً في تدريب GLM-5.2، إذ تتخلّى تماماً عن المجموعة وتتعلّم من rollout واحد، وتحافظ على الاستقرار عبر تقليم الرموز ثنائي الاتجاه بدلاً من ذلك.]]></summary></entry><entry xml:lang="en"><title type="html">The Agent That Studied Its Own Screwups</title><link href="https://thakicloud.github.io/en/comics/trace-targeted-self-improvement/" rel="alternate" type="text/html" title="The Agent That Studied Its Own Screwups" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/en/comics/trace-targeted-self-improvement</id><content type="html" xml:base="https://thakicloud.github.io/en/comics/trace-targeted-self-improvement/"><![CDATA[<p>The talk of the week is TRACE, a paper that just picked up a Spotlight at an ICML workshop. The idea is refreshingly plain: instead of an agent grinding away at everything, it reads back its own failure logs, figures out the one capability those failures point to, and trains exactly that. Turns out this targeted approach beats blunt reinforcement learning, prompt-shuffling, and dumping in synthetic data. Knowing your weak spot, it seems, beats brute force.</p>

<p><img src="/assets/images/posts/comics/trace-targeted-self-improvement/strip.png" alt="The Agent That Studied Its Own Screwups" /></p>

<blockquote>
  <p>Source: <a href="https://x.com/hjguyhan/status/2075500035207565421">RT @hangoo_kang: “TRACE: Capability-Targeted Agentic Training” got Spotlight @ ICML AIWILD 🎉</a> · twitter</p>
</blockquote>

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

<p>This lines up neatly with what ThakiCloud has been building. Paxis conducts the agents; Metis retrains the ones that come up short. TRACE says: diagnose your failures and retrain just the weak spot. But those failure logs are among the most sensitive things a company owns. Ship them to someone else’s cloud to train on, and both the weakness and the fix leak out with them. That is the case for on-prem and for sovereignty: keeping your models, data, and infrastructure under your own roof. Fix your flaws at home and they stay home. For what it is worth, this very blog runs on a self-improvement loop that reviews its own misses and resharpens.</p>

<hr />

<p><em>An auto-generated comic riffing on this week’s industry news.</em></p>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="comics" /><category term="TRACE" /><category term="agentic-training" /><category term="self-improvement" /><category term="on-prem" /><category term="sovereignty" /><category term="ai-coding" /><summary type="html"><![CDATA[Fix the one skill you lack and you win. Paxis's missing skill? Knowing when to stop.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://thakicloud.github.io/assets/images/posts/comics/trace-targeted-self-improvement/strip.png" /><media:content medium="image" url="https://thakicloud.github.io/assets/images/posts/comics/trace-targeted-self-improvement/strip.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry xml:lang="en"><title type="html">Testing iOS Apps in a Browser from a Headless Cloud Mac: serve-sim</title><link href="https://thakicloud.github.io/en/dev/serve-sim-ios-simulator-web/" rel="alternate" type="text/html" title="Testing iOS Apps in a Browser from a Headless Cloud Mac: serve-sim" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/en/dev/serve-sim-ios-simulator-web</id><content type="html" xml:base="https://thakicloud.github.io/en/dev/serve-sim-ios-simulator-web/"><![CDATA[<p>Ask an AI coding agent to build an iOS app and you run into one fundamental wall. The agent can write code and even build it, but it cannot actually see what happens on screen. The problem gets worse once you move your development environment to a Mac Mini in the cloud, because on a headless server with no GUI the Xcode Simulator window never even appears.</p>

<p><a href="https://github.com/EvanBacon/serve-sim">serve-sim</a>, built by Evan Bacon of the Expo core team, aims straight at that wall. It became widely known after indie developer levelsio showed it off, saying it let him “watch, in a browser, in real time, an iOS app that Claude Code built on a cloud Mac Mini.” Its slogan is simple: “npx serve for Apple Simulators.”</p>

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

<p>What makes serve-sim interesting is that it is not just a screen-mirroring tool. It opens two things at once: a video stream that sends the simulator screen to a browser, and a control channel that lets a browser or an agent operate the simulator. In other words, it makes both “watching” and “operating” possible remotely.</p>

<p>That combination matters because it completes the development loop for AI coding agents. An agent can fix code, build it, run it, look at the resulting screen, tap a button to move to the next step, and cycle through all of that without a human in the loop. This lines up exactly with what ThakiCloud’s Agent-Native Cloud, Paxis, is aiming for: agents doing real work inside isolated environments. That makes it worth a closer look at how one open-source tool implements that workflow.</p>

<p><img src="/assets/images/serve-sim-ios-simulator-web-hero.png" alt="An abstract image of a smartphone screen on a headless cloud server dissolving into particles of light that flow through the network into a browser window" />
<em>A visualization of a headless server’s simulator screen becoming a stream that flows into a remote browser.</em></p>

<h2 id="what-serve-sim-is">What serve-sim Is</h2>

<p>The way serve-sim works is simpler and more clever than it sounds. There is no separate Xcode plugin to install, and no instrumentation code to embed in the app. Instead it spins up a small Swift helper process that captures the framebuffer of an already-booted iOS Simulator through Apple’s own <code class="language-plaintext highlighter-rouge">simctl io</code> interface.</p>

<p>The captured screen is exposed in two ways. First, it sends the video to the browser as an MJPEG stream at up to 60 FPS. Second, it opens a WebSocket control channel alongside it, so input from the browser side, taps and gestures, can be sent back to the simulator. On top of that sits a React-based preview UI, so a person can operate the app in the browser as if it were a real device.</p>

<pre><code class="language-mermaid">flowchart TB
    A[Booted iOS Simulator] --&gt; B[Swift helper process]
    B --&gt; C[Captures framebuffer&lt;br/&gt;via simctl io]
    C --&gt; D[MJPEG video stream&lt;br/&gt;up to 60 FPS]
    C --&gt; E[WebSocket control channel]
    D --&gt; F[Browser React preview UI]
    E --&gt; F
    E --&gt; G[Agent CLI&lt;br/&gt;tap, gesture, rotate, camera]
    F -.human operates.-&gt; E
    G -.agent operates.-&gt; E
</code></pre>

<p>The key point is that it targets “any booted simulator.” Since it requires no modification to the app, it can be attached to an existing project as-is. It can also forward simulator logs to the browser, so browser-use style MCP tools can read those logs to judge state. There is even a convenience feature where dragging a video or image into the browser window adds it as a file on the simulator device.</p>

<h2 id="installation-and-use">Installation and Use</h2>

<p>The barrier to entry for serve-sim is low. On a Mac with Node.js, one line is enough.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npx serve-sim
</code></pre></div></div>

<p>Once running, you can view the preview at <code class="language-plaintext highlighter-rouge">http://localhost:3200</code> locally. It supports three modes: using it locally, connecting from another device on the same LAN, or hosting it on a remote Mac and reaching it from anywhere through a tunnel. levelsio’s case is the third mode: running it on a headless Mac Mini in the cloud and viewing it through a remote browser.</p>

<p>Agent integration is provided as a separate Agent Skill. This skill, packaged at <code class="language-plaintext highlighter-rouge">skills/serve-sim</code> in the repository, teaches any host that implements the open Agent Skills standard, including Claude Code, Cursor, Codex CLI, and Gemini CLI, how to operate the simulator through the CLI. That includes taps, gestures, hardware buttons, screen rotation, injecting camera input, and handing the stream off to the host’s own preview window.</p>

<h2 id="a-note-on-reproduction">A Note on Reproduction</h2>

<p>This post was written in a headless batch session with no GUI, where running Node.js is blocked by policy, so we were not able to run <code class="language-plaintext highlighter-rouge">npx serve-sim</code> directly ourselves and capture the screen. The commands and behavior described in this post are therefore based on facts confirmed in the repository README and the official announcement material, and we have not fabricated any benchmark numbers. Please verify the actual simulator streaming screen and latency yourself, on a macOS machine with a booted Xcode Simulator, using the commands above.</p>

<h2 id="implications-for-thakicloud-products">Implications for ThakiCloud Products</h2>

<p>On the surface, serve-sim is a tool for iOS developers, but underneath it sits a much bigger trend: agent-native development.</p>

<p><strong>Paxis lens (agent-native development).</strong> ThakiCloud’s Paxis is an Agent-Native Cloud control plane that runs skills in isolated sandboxes and passes every action through policy gates and audit logs. The open Agent Skills standard that serve-sim adopts is the same kind of contract model that Paxis’s skill harness handles. The idea that a single skill can give “tap, rotate, and read the screen of a simulator” capability to multiple agent hosts points in exactly the same direction as Paxis’s structure of selecting from 960-plus skills via BM25 and running them in isolation. In particular, workloads where an agent operates a real UI, like serve-sim’s control channel, need those operations to pass through policy gates and be recorded in audit logs before they can safely go into production. If serve-sim provides the “capability,” Paxis provides the layer that “safely governs” that capability.</p>

<p><strong>ai-platform lens (headless execution infrastructure).</strong> What really makes serve-sim compelling is that it runs on a headless, remote Mac. The idea of building and streaming from a GUI-less server shares its philosophy with how ThakiCloud’s ai-platform schedules and runs workloads on Kubernetes without a GUI. A pipeline that attaches macOS runners on demand for iOS builds, lets an agent automatically build and test on top of them, and streams only the results back to a human, could extend beyond CI into “agent-driven QA.” It is a structure where low-cost headless execution infrastructure (ai-platform) underpins the economics of agent automation (Paxis).</p>

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

<p>A few things deserve a sober look.</p>

<p>First, serve-sim targets the simulator. Because it is a simulator and not a physical device, issues that only surface on real hardware, camera, sensors, performance characteristics, still go uncaught. The old limitation that passing on the simulator does not guarantee passing on a real device remains unchanged.</p>

<p>Second, MJPEG streaming is simple and compatible but not very efficient at compression. Continuously streaming a 60 FPS, high-quality feed over a remote tunnel can turn bandwidth and latency into a bottleneck. For gesture testing where responsiveness matters, network round-trip delay translates directly into input lag.</p>

<p>Third, an agent being able to “see and operate” the screen is a separate matter from that judgment being correct. It remains entirely possible for an agent to misread the stream and tap the wrong button, and this is exactly why policy gates and human review are needed. The more capability a tool opens up, the more important the layer that governs that capability becomes.</p>

<p>Even so, serve-sim’s direction is clear. It has built a real bridge needed to move from “the agent only writes code” to “the agent builds, runs, and verifies by directly operating the screen.” If you are a team trying to develop mobile apps with agents from a headless cloud, you can open that world right now with a single line: <code class="language-plaintext highlighter-rouge">npx serve-sim</code>.</p>

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

<ul>
  <li>Evan Bacon. “serve-sim: The <code class="language-plaintext highlighter-rouge">npx serve</code> of Apple Simulators.” GitHub. <a href="https://github.com/EvanBacon/serve-sim">https://github.com/EvanBacon/serve-sim</a></li>
  <li>@levelsio, tweet introducing serve-sim. <a href="https://x.com/levelsio/status/2075328941317886210">https://x.com/levelsio/status/2075328941317886210</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;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="dev" /><category term="ios-simulator" /><category term="agent-skills" /><category term="developer-tools" /><category term="headless" /><category term="claude-code" /><category term="expo" /><summary type="html"><![CDATA[Put a Mac Mini in the cloud and you lose the GUI, which means you lose the iOS Simulator too. serve-sim streams the simulator's framebuffer to a browser and opens a WebSocket control channel on top of it, letting AI coding agents build and actually operate an iOS app in a headless environment.]]></summary></entry><entry xml:lang="en"><title type="html">There Was Almost No Q4 Inside Q4_K_M: Dissecting GGUF Quantization Internals</title><link href="https://thakicloud.github.io/en/llmops/gguf-quantization-internals/" rel="alternate" type="text/html" title="There Was Almost No Q4 Inside Q4_K_M: Dissecting GGUF Quantization Internals" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/en/llmops/gguf-quantization-internals</id><content type="html" xml:base="https://thakicloud.github.io/en/llmops/gguf-quantization-internals/"><![CDATA[<p><img src="/assets/images/gguf-quantization-internals-hero.png" alt="Abstract illustration of quantized neural network weights being rearranged into blocks of different sizes" /></p>

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

<p>If you have ever run an LLM locally, you have probably seen labels like <code class="language-plaintext highlighter-rouge">Q4_K_M</code>, <code class="language-plaintext highlighter-rouge">Q5_K_M</code>, or <code class="language-plaintext highlighter-rouge">Q8_0</code>. Most people stop at “Q4 means 4 bits, so it must be the smallest and fastest” and simply download the file and run it. But that label hides more than it reveals. Few people have actually opened a file labeled <code class="language-plaintext highlighter-rouge">Q4_K_M</code> and checked, tensor by tensor, whether it is really filled with 4-bit data.</p>

<p>This post is for engineering leaders, practitioners responsible for inference cost, and teams looking to serve models on-premises. We downloaded the GGUF file for Qwen2.5-0.5B-Instruct at several quantization levels, measured the actual file sizes, and fully dissected one <code class="language-plaintext highlighter-rouge">Q4_K_M</code> file tensor by tensor. The result was quite different from intuition. We explain why understanding this gap matters for serving cost and quality, and what it means for ThakiCloud’s inference infrastructure.</p>

<p>To state the conclusion up front: in this model’s <code class="language-plaintext highlighter-rouge">Q4_K_M</code> file, tensors that were genuinely 4-bit K-quantization (Q4_K) accounted for only 6.1 percent of total weight capacity, and the file’s effective bit width was not 4 but <strong>6.16 bits</strong>. The label was, in effect, almost a lie.</p>

<h2 id="what-is-this-technology">What Is This Technology</h2>

<p>GGUF is a single-file model format used in the llama.cpp ecosystem. A single file bundles metadata (architecture, tokenizer, hyperparameters) together with the quantized weights of every tensor. The key point is that <strong>each tensor can use a different quantization type</strong>. So a file-level label like <code class="language-plaintext highlighter-rouge">Q4_K_M</code> only indicates the “dominant type,” not that the entire file is that type.</p>

<p>llama.cpp’s quantization types broadly fall into two families. One is the <strong>legacy family</strong> (Q4_0, Q5_0, Q8_0), which groups 32 weights into one block. The other is the <strong>K-quantization family</strong> (Q4_K, Q5_K, Q6_K), which groups 256 weights into one superblock. Because K-quantization further subdivides and stores scales and minimums within the superblock, it delivers better quality than legacy at the same bit width. The <code class="language-plaintext highlighter-rouge">K</code> in <code class="language-plaintext highlighter-rouge">Q4_K_M</code> refers to this K-quantization, and <code class="language-plaintext highlighter-rouge">M</code> denotes the “medium” preset that raises some sensitive tensors to higher precision (Q6_K).</p>

<p>Looking a bit closer at the superblock structure reveals why K-quantization is more efficient. For example, Q4_K packs 256 weights into 144 bytes. Of that, the pure 4-bit values take up 256 x 4 bits = 128 bytes, and the remaining 16 bytes are metadata that splits the superblock into 8 sub-blocks and re-quantizes each sub-block’s scale and minimum to 6 bits. In other words, the values themselves are 4 bits, but by keeping the scales that reconstruct them finely grained, error is reduced. This contrasts with legacy Q4_0, which keeps only one scale per 32 weights. So the actual bit width of Q4_K is 144 x 8 / 256 = 4.5 bits, slightly more than pure 4 bits, but with much more stable quality.</p>

<p>There is one decisive constraint here. <strong>K-quantization can only be used when a tensor’s number of columns (<code class="language-plaintext highlighter-rouge">ne[0]</code> in ggml terms) is divisible by 256.</strong> This is because the superblock operates in units of 256. If this condition is not met, llama.cpp silently falls back to the legacy family (mostly Q5_0). This one rule explains the entire result of today’s experiment.</p>

<pre><code class="language-mermaid">flowchart TB
    A["GGUF file (Q4_K_M)"] --&gt; B["Determine type per tensor&lt;br/&gt;llama_tensor_get_type()"]
    B --&gt; C{"Is column count ne[0]&lt;br/&gt;divisible by 256?"}
    C --&gt;|"Yes"| D["Use K-quantization&lt;br/&gt;Q4_K / Q6_K (256 superblock)"]
    C --&gt;|"No"| E["Fall back to legacy&lt;br/&gt;Q5_0 (32 block)"]
    B --&gt; F["Raise precision for sensitive tensors"]
    F --&gt; G["output.weight -&gt; Q8_0&lt;br/&gt;attn_v.weight -&gt; Q8_0&lt;br/&gt;ffn_down.weight -&gt; Q6_K"]
    D --&gt; H["Aggregate effective bit width"]
    E --&gt; H
    G --&gt; H
    H --&gt; I["File-wide effective bpw = 6.16&lt;br/&gt;(label 'Q4' = 4.0)"]
</code></pre>

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

<p>The experiment can be reproduced without any extra dependencies. The Hugging Face API returns actual byte counts for file size, and you can read tensor types directly with the <code class="language-plaintext highlighter-rouge">gguf</code> reader.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1) Install the reader and downloader</span>
pip <span class="nb">install </span>gguf huggingface_hub

<span class="c"># 2) Download just one Q4_K_M file (under 500MB since it's a 0.5B model)</span>
hf download Qwen/Qwen2.5-0.5B-Instruct-GGUF <span class="se">\</span>
  qwen2.5-0.5b-instruct-q4_k_m.gguf <span class="nt">--local-dir</span> ./gguf
</code></pre></div></div>

<p>Here is the code for opening the downloaded file’s tensor types. The GGUF header embeds each tensor’s name, dimensions, and ggml type integer directly, so aggregating just these values tells you exactly what the file is actually filled with.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">collections</span> <span class="kn">import</span> <span class="n">Counter</span>
<span class="kn">from</span> <span class="n">gguf</span> <span class="kn">import</span> <span class="n">GGUFReader</span>

<span class="n">r</span> <span class="o">=</span> <span class="nc">GGUFReader</span><span class="p">(</span><span class="sh">"</span><span class="s">gguf/qwen2.5-0.5b-instruct-q4_k_m.gguf</span><span class="sh">"</span><span class="p">)</span>
<span class="n">hist</span> <span class="o">=</span> <span class="nc">Counter</span><span class="p">()</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">r</span><span class="p">.</span><span class="n">tensors</span><span class="p">:</span>
    <span class="n">hist</span><span class="p">[</span><span class="n">t</span><span class="p">.</span><span class="n">tensor_type</span><span class="p">.</span><span class="n">name</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="c1"># Check the actual type of a few representative tensors
</span>    <span class="k">if</span> <span class="n">t</span><span class="p">.</span><span class="n">name</span> <span class="ow">in</span> <span class="p">(</span><span class="sh">"</span><span class="s">token_embd.weight</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">output.weight</span><span class="sh">"</span><span class="p">,</span>
                  <span class="sh">"</span><span class="s">blk.0.attn_v.weight</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">blk.0.ffn_down.weight</span><span class="sh">"</span><span class="p">,</span>
                  <span class="sh">"</span><span class="s">blk.0.attn_q.weight</span><span class="sh">"</span><span class="p">):</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">t</span><span class="p">.</span><span class="n">name</span><span class="si">:</span><span class="mi">26</span><span class="n">s</span><span class="si">}</span><span class="s"> </span><span class="si">{</span><span class="n">t</span><span class="p">.</span><span class="n">shape</span><span class="si">}</span><span class="s"> -&gt; </span><span class="si">{</span><span class="n">t</span><span class="p">.</span><span class="n">tensor_type</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">dict</span><span class="p">(</span><span class="n">hist</span><span class="p">))</span>
</code></pre></div></div>

<p>Bytes per block come straight from ggml’s definitions. For example, Q4_K stores 256 weights in 144 bytes, so 4.5 bits per weight; Q6_K stores 256 in 210 bytes, so 6.5625 bits per weight; legacy Q5_0 stores 32 in 22 bytes, so 5.5 bits per weight. Summing (element count / block size) x bytes per block for every tensor gives you the exact effective bit width of the file.</p>

<h2 id="actual-experiment-results">Actual Experiment Results</h2>

<p>First, file sizes. Here are the measured values for the same model downloaded at 7 quantization levels, compared against the fp16 original (1266MB).</p>

<table>
  <thead>
    <tr>
      <th>Quantization</th>
      <th>File Size</th>
      <th>vs. fp16</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Q2_K</td>
      <td>415.2 MB</td>
      <td>32.8%</td>
    </tr>
    <tr>
      <td>Q3_K_M</td>
      <td>432.0 MB</td>
      <td>34.1%</td>
    </tr>
    <tr>
      <td>Q4_0</td>
      <td>428.7 MB</td>
      <td>33.9%</td>
    </tr>
    <tr>
      <td><strong>Q4_K_M</strong></td>
      <td><strong>491.4 MB</strong></td>
      <td><strong>38.8%</strong></td>
    </tr>
    <tr>
      <td>Q5_K_M</td>
      <td>522.2 MB</td>
      <td>41.2%</td>
    </tr>
    <tr>
      <td>Q6_K</td>
      <td>650.4 MB</td>
      <td>51.4%</td>
    </tr>
    <tr>
      <td>Q8_0</td>
      <td>675.7 MB</td>
      <td>53.4%</td>
    </tr>
  </tbody>
</table>

<p>Something odd already jumps out here. The difference between Q2_K (415MB) and Q4_0 (429MB) is just 14MB. We cut the bits in half, yet the file barely shrank. And <code class="language-plaintext highlighter-rouge">Q4_K_M</code> (491MB) is actually larger than pure 4-bit <code class="language-plaintext highlighter-rouge">Q4_0</code> (429MB). Looking at the names alone, this makes no sense.</p>

<p>The real reason becomes clear once you open the <code class="language-plaintext highlighter-rouge">Q4_K_M</code> file tensor by tensor. Here is the type distribution across its 291 tensors.</p>

<table>
  <thead>
    <tr>
      <th>Actual Type</th>
      <th>Tensor Count</th>
      <th>Nominal bpw</th>
      <th>Share of Weight Capacity</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Q5_0</td>
      <td>133</td>
      <td>5.5</td>
      <td>54.9%</td>
    </tr>
    <tr>
      <td>Q8_0</td>
      <td>13</td>
      <td>8.5</td>
      <td>30.1%</td>
    </tr>
    <tr>
      <td>Q6_K</td>
      <td>12</td>
      <td>6.5625</td>
      <td>8.8%</td>
    </tr>
    <tr>
      <td>Q4_K</td>
      <td>12</td>
      <td>4.5</td>
      <td>6.1%</td>
    </tr>
    <tr>
      <td>F32 (norm/bias)</td>
      <td>121</td>
      <td>32.0</td>
      <td>0.1%</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/gguf-quantization-internals-results.png" alt="Chart showing file sizes across quantization levels for Qwen2.5-0.5B, and the actual tensor type composition inside Q4_K_M. Q4_K_M's effective bit width is 6.16, far from the label's 4.0" /></p>

<p>Despite the <code class="language-plaintext highlighter-rouge">Q4_K_M</code> label, genuine 4-bit K-quantization (Q4_K) accounted for only <strong>6.1 percent</strong> of total weight capacity. Instead, the 5.5-bit legacy type Q5_0 took up more than half (54.9 percent), and the 8.5-bit Q8_0 consumed 30 percent. Calculating the file’s overall effective bit width gives <strong>6.16 bits</strong>, more than 1.5 times the 4 bits the label implies.</p>

<p>Checking representative tensors one by one makes the pattern clear. Here are the measured types:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">token_embd.weight</code> (896 x 151936) -&gt; <strong>Q5_0</strong></li>
  <li><code class="language-plaintext highlighter-rouge">output.weight</code> (896 x 151936) -&gt; <strong>Q8_0</strong></li>
  <li><code class="language-plaintext highlighter-rouge">blk.0.ffn_down.weight</code> (4864 x 896) -&gt; <strong>Q6_K</strong></li>
  <li><code class="language-plaintext highlighter-rouge">blk.0.attn_v.weight</code> (896 x 128) -&gt; <strong>Q8_0</strong></li>
  <li><code class="language-plaintext highlighter-rouge">blk.0.attn_q.weight</code> (896 x 896) -&gt; <strong>Q5_0</strong></li>
</ul>

<p>Do you see the pattern? Tensors carrying K-quantization (Q4_K, Q6_K) appeared only where the column count <code class="language-plaintext highlighter-rouge">ne[0]</code> was 4864, as with <code class="language-plaintext highlighter-rouge">ffn_down</code>. 4864 is divisible by 256 (19 x 256). Most other tensors, however, have <code class="language-plaintext highlighter-rouge">ne[0]</code> of 896, and 896 is not divisible by 256 (3.5 x 256). So these tensors could not use K-quantization at all and all fell back to legacy Q5_0. Layer this together with precision boosts (Q5_0, Q8_0) on quality-sensitive tensors like embeddings, output, and attention value, and you get a file labeled <code class="language-plaintext highlighter-rouge">Q4_K_M</code> whose actual substance is chunks of 5.5 to 8.5 bits.</p>

<p>Here is exactly where the effective bit width of 6.16 comes from. This file contains a total of 630 million weights subject to quantization, stored in roughly 485MB of bytes. 485,452,288 bytes x 8 / 630,167,424 weights = 6.16 bits per weight. Add about 6MB of file metadata and alignment padding, and it matches the actual file size of 491MB exactly. The fact that the calculation matches the file size is also evidence that the tensor type readout was accurate.</p>

<p>This also explains the two odd points in the file size table. Q2_K (415MB) is only barely smaller than Q4_0 (429MB) because, in this small model, the embedding and output tensors take up a large share of total weights, and they stay at high precision regardless of the quantization level. No matter how much you lower the bits, a fixed cost sits at the floor and does not shrink. And <code class="language-plaintext highlighter-rouge">Q4_K_M</code> is larger than pure 4-bit <code class="language-plaintext highlighter-rouge">Q4_0</code> because the <code class="language-plaintext highlighter-rouge">M</code> preset paid for pulling sensitive tensors up to Q6_K and Q8_0 in file size. The label’s number is lower, but the effective bit width is actually higher.</p>

<p>To summarize, this experiment demonstrated three facts through measurement. First, a file-level label only indicates the dominant type and does not guarantee the effective bit width. Second, in small models whose hidden size is not a multiple of 256, K-quantization is largely disabled, widening the gap between label and substance. Third, in small models the embedding and output tensors take up a large share of total capacity, so keeping them at high precision significantly dilutes the savings that “4-bit quantization” is supposed to deliver.</p>

<h2 id="implications-for-thakicloud-products">Implications for ThakiCloud Products</h2>

<p>This experiment only dissected one small model, but its lesson carries straight through to production serving infrastructure. ThakiCloud’s ai-platform serves models to diverse customer environments on top of Kubernetes and Kueue-based GPU scheduling. In that context, “which quantization to choose” is not a matter of taste, it is a decision that determines GPU memory allocation, batch size, and ultimately cost per token.</p>

<p>Trusting the label as-is throws off capacity planning. If you assume <code class="language-plaintext highlighter-rouge">Q4_K_M</code> means “4 bits, so a quarter of the original” and allocate GPU memory accordingly, you will find, as in the experiment above, that it actually takes up around 40 percent of the original, and batch slots run out faster than expected. This matters especially in multi-tenant serving, where many small models must be packed tightly onto a single node. There, the difference between measuring the effective bit width and simply trusting the label directly translates into how many models a node can hold. This is exactly why we verify GGUF files tensor by tensor when building serving images. For customers who require self-hosted, on-premises, or sovereign deployment in particular, this habit of measuring rather than assuming becomes a genuine cost advantage.</p>

<p>Making this verification itself a repeatable task is where Paxis comes in. Paxis is ThakiCloud’s Agent-Native Cloud control plane running on top of ai-platform, treating skills, tools, policies, and audit logs as first-class resources. If today’s experiment, downloading a GGUF file, aggregating tensor types, and flagging a warning when effective bit width exceeds a threshold, is registered as a single skill, it runs in an isolated sandbox and every result passes through policy gates and audit logs. Instead of having someone manually open a file every time a new model lands in the registry, a validated skeleton runs automatically. This is how the economics that low-cost serving (ai-platform) creates and the orchestration (Paxis) that makes that serving safely repeatable fit together.</p>

<h2 id="limitations-and-counterarguments">Limitations and Counterarguments</h2>

<p>A few things should be made explicit.</p>

<p>First, these results are close to an extreme case specific to Qwen2.5-0.5B, whose hidden size is 896. In larger models whose hidden size is a multiple of 256 (for example, 4096 or 8192), K-quantization applies normally, and <code class="language-plaintext highlighter-rouge">Q4_K_M</code>’s effective bit width lands much closer to the label, around 4.8 bits.</p>

<p>In other words, the correct lesson is not “the label is always a lie,” but that “the gap between label and substance varies greatly with model architecture, and is larger for smaller models.”</p>

<p>Second, a larger file size is not necessarily a bad thing. Keeping embedding and output tensors at high precision is a deliberate choice to prevent quality collapse in small models. In other words, this <code class="language-plaintext highlighter-rouge">Q4_K_M</code> is not a “badly made” file, but a reasonable result of automatically raising precision to protect quality in a small model. The cost simply does not show up in the label.</p>

<p>Third, this post only measured file structure and capacity, not actual inference quality (perplexity, benchmark scores). The relationship between bit width and quality requires a separate experiment, which we leave as the topic of a future post. What we can say here is only an operational principle: do not plan capacity and memory around the label, measure it.</p>

<p>The difference between just clicking run on a local model and knowing what is actually inside the file comes down to exactly this habit of measurement. Five minutes spent checking the numbers behind the label can change the accuracy of your entire serving cost plan.</p>

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

<ul>
  <li>Qwen2.5-0.5B-Instruct-GGUF model repository: <a href="https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct-GGUF">huggingface.co/Qwen/Qwen2.5-0.5B-Instruct-GGUF</a></li>
  <li>llama.cpp quantization documentation: <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md">github.com/ggml-org/llama.cpp</a></li>
  <li>File sizes, tensor type distributions, and effective bit widths were measured directly from actual files downloaded from the repository above.</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="llmops" /><category term="quantization" /><category term="gguf" /><category term="llama-cpp" /><category term="llmops" /><category term="self-hosting" /><category term="vllm" /><category term="paxis" /><summary type="html"><![CDATA[There is a real gap between someone who downloads a GGUF file from Hugging Face and just clicks run, and someone who knows exactly which tensors are stored at how many bits inside that file. We actually downloaded a Q4_K_M file for Qwen2.5-0.5B and opened it tensor by tensor. Despite its name, only 6 percent of the file was true 4-bit Q4_K, and the effective bit width was not 4 but 6.16. This post walks through why that happens, using measured data on K-quantization's superblock structure and its 256-divisibility rule.]]></summary></entry><entry xml:lang="en"><title type="html">Remember the Decision, Not the Description: A Meta-Affiliated Study Reframes Agent Memory as a Rate-Distortion Problem</title><link href="https://thakicloud.github.io/en/research/demem-agent-memory/" rel="alternate" type="text/html" title="Remember the Decision, Not the Description: A Meta-Affiliated Study Reframes Agent Memory as a Rate-Distortion Problem" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/en/research/demem-agent-memory</id><content type="html" xml:base="https://thakicloud.github.io/en/research/demem-agent-memory/"><![CDATA[<p><img src="/assets/images/demem-agent-memory-hero.png" alt="Abstract illustration of memories branching into separate paths that lead to different decisions" /></p>

<blockquote>
  <p>📄 <strong>Full deep review (DOCX)</strong>: <a href="https://drive.google.com/file/d/1oxsADQALTfdn7I_mmZbaZfMnmqoCMF9o/view">Download the detailed peer review on Google Drive</a>.</p>
</blockquote>

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

<p>Anyone who has run a conversational agent for a long stretch has seen this failure before. A preference or decision a user clearly stated days ago gets forgotten at some point, and the agent acts against it. Context windows are finite, and once a conversation grows long enough, some part of the past must be compressed or discarded. The real question is what to discard.</p>

<p>Agent memory to date has largely answered this question with <strong>descriptive criteria</strong>: how relevant is it, how salient is it, how well can it be summarized. This paper, “Remember the Decision, Not the Description” (arXiv 2605.10870), co-authored by a researcher at Meta AI, argues that this criterion itself is the wrong one. This piece is written for engineers and researchers designing AI agents, and for teams who need to put long-term memory into production. We summarize the paper’s core reframing and the empirical results that back it up, and look at how this principle applies to Paxis, ThakiCloud’s agent platform.</p>

<h2 id="what-is-the-problem">What Is the Problem</h2>

<p>The authors start from a simple insight. Memory is valuable to an agent not because it faithfully describes the past, but because it <strong>keeps two histories that call for different actions separated, even under a fixed budget</strong>.</p>

<p>Consider a simple example. Yesterday the user said, “This deployment must only proceed after manual approval.” Today, in a similar context, the user said, “This script can be run automatically.” The two statements look very similar on the surface. They share words like deployment, execution, approval, and if summarized they come out almost identical. A relevance-based memory is likely to merge the two into a single lump labeled “deployment-related instruction.” The moment that happens, the agent loses track of which instruction applies to which situation, and it commits the error of pushing through an automatic deployment that actually required manual approval. The summary is descriptively correct, but the merge is decisively fatal.</p>

<p>The concrete failure mode looks like this. Two situations look textually similar but actually demand opposite actions. When the memory budget is tight, compression is required, and compression inevitably invites merging. If you only look at descriptive similarity, the two get combined into one. As a result, the agent consistently makes the wrong decision every time it returns to that state. Relevance or summary quality cannot answer the real question, which is whether these two can be merged at all. The criterion should not be what looks similar, but what requires different actions.</p>

<h2 id="core-idea-decision-centric-rate-distortion">Core Idea: Decision-Centric Rate-Distortion</h2>

<p>The authors move this problem into the information-theoretic framework of rate-distortion. Rate-distortion theory originally deals with how much distortion arises for a given amount of compression (rate), and the key move here is redefining distortion itself. Instead of a reconstruction error of the signal, distortion is defined as the <strong>loss in achievable decision quality caused by compression (decision loss)</strong>.</p>

<pre><code class="language-mermaid">flowchart TB
    A["Long interaction history&lt;br/&gt;(fixed memory budget)"] --&gt; B{"Merge the two situations?"}
    B --&gt; C["Description-centric criterion&lt;br/&gt;relevance, salience, summary quality"]
    B --&gt; D["Decision-centric criterion&lt;br/&gt;does the shared state cause a decision conflict"]
    C --&gt; E["Merge if they look similar&lt;br/&gt;-&gt; collapses opposing actions together"]
    E --&gt; F["Persistent decision errors"]
    D --&gt; G["Split only when a decision conflict is certified&lt;br/&gt;certified refinement"]
    G --&gt; H["Exact forgetting boundary&lt;br/&gt;+ memory-distortion frontier"]
    H --&gt; I["Better decision quality at the same budget"]
</code></pre>

<p>Here is an analogy. When compressing audio, we first discard frequencies the human ear cannot hear, because the criterion for distortion is “what a person can hear.” Agent memory should work the same way, the authors argue. What should be discarded is not “the memory that looks less relevant,” but “the memory that, if discarded, would not change future decisions.” Here, rate is the memory budget, and distortion is the decision loss that compression causes. If merging two situations into the same slot does not lead to any future decision going wrong, that merge is free. Conversely, if the merge collapses opposing actions into one, it is an expensive distortion.</p>

<p>Two things follow from this definition. First, the <strong>exact forgetting boundary</strong>, which precisely defines the boundary of what can safely be forgotten without harming decision quality. Second, the <strong>memory-distortion frontier</strong>, which characterizes the optimal trade-off curve between memory budget and decision quality. In other words, it theoretically pins down a lower bound of the form: “if you shrink the budget by this much, decision quality is guaranteed to drop by at least this much.”</p>

<h2 id="demem-turning-theory-into-an-algorithm">DeMem: Turning Theory into an Algorithm</h2>

<p>DeMem is what carries this theory into a real, slot-based agent memory. DeMem is an online memory learner that operates on one principle: <strong>it refines a memory partition only when the data certifies that a shared state causes a decision conflict.</strong></p>

<p>The word “certifies” matters here. Two situations are not split apart the instant they merely look different; they are split only once evidence has actually accumulated that the same memory state requires different decisions. Conversely, if no such evidence exists, the merge is kept, saving budget. This conservatism is the heart of the method. Splitting too eagerly wastes budget, leaving no room for the distinctions that actually matter, while merging too eagerly collapses opposing actions. Certified refinement is the discipline of waiting, between these two failure modes, until the data speaks. The authors prove that this procedure satisfies a near-minimax regret guarantee, meaning that even in the worst case, regret relative to the optimum is bounded close to the theoretical limit.</p>

<p>The authors validate this mechanism at two levels. First, in a synthetic diagnostic environment, they design tasks where descriptive similarity and decisional similarity are deliberately made to diverge. There, description-only baselines keep merging situations that look alike, accumulating regret, while DeMem avoids this trap by refining only when a decision conflict is certified. Next, they check whether this advantage transfers to real long-horizon conversation benchmarks, across both proprietary and open-weight models. This structure, moving from theory through controlled mechanism validation down to real-world benchmarks, turns the results into an explanation of “why it wins,” not just a performance table.</p>

<h2 id="experimental-results">Experimental Results</h2>

<p>In the synthetic diagnostic, DeMem had the lowest cumulative regret among all budget-matched methods, and its advantage widened as the gap between descriptive and decisional similarity grew larger. While description-only baselines merged conflicting situations and produced persistent errors, DeMem avoided this by refining only when a decision conflict was certified.</p>

<p>The results carried over to real benchmarks as well. Below are the measured overall scores on LoCoMo (GPT-4.1-mini backbone).</p>

<table>
  <thead>
    <tr>
      <th>Method</th>
      <th>Overall</th>
      <th>Temporal</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>DeMem</strong></td>
      <td><strong>0.921</strong></td>
      <td><strong>0.908</strong></td>
    </tr>
    <tr>
      <td>Mnemis</td>
      <td>0.891</td>
      <td>0.858</td>
    </tr>
    <tr>
      <td>EMem-G</td>
      <td>0.757</td>
      <td>0.660</td>
    </tr>
    <tr>
      <td>Nemori</td>
      <td>0.731</td>
      <td>0.454</td>
    </tr>
    <tr>
      <td>RAG</td>
      <td>0.710</td>
      <td>0.634</td>
    </tr>
    <tr>
      <td>FullContext</td>
      <td>0.692</td>
      <td>0.511</td>
    </tr>
    <tr>
      <td>Zep</td>
      <td>0.554</td>
      <td>0.383</td>
    </tr>
    <tr>
      <td>Mem0</td>
      <td>0.514</td>
      <td>0.428</td>
    </tr>
  </tbody>
</table>

<p>DeMem posted the best overall score, and was particularly strong in the Temporal, Open-Domain, and Multi-Hop categories, where preserving distinctions across distant interactions matters most. In Single-Hop, which involves retrieving a single fact, Mnemis (0.940) narrowly edged out DeMem (0.935), which fits the interpretation that the benefit of decision-centric separation is smaller for one-shot retrieval. On LongMemEval as well, DeMem achieved the best average score on both backbones, with the largest gains in categories requiring cross-session integration. Notably, the advantage held even on the open-weight Llama-3.1-70B backbone, showing that this benefit is not tied to any particular proprietary model.</p>

<h2 id="implications-for-thakiclouds-products">Implications for ThakiCloud’s Products</h2>

<p>The insight of this paper connects directly with the memory design of Paxis, ThakiCloud’s Agent-Native Cloud control plane. Paxis is a control plane that runs on top of ai-platform and treats skills, tools, policies, and audit logs as first-class resources, and its knowledge engine and memory layer decide every day exactly what to merge and what to keep separate.</p>

<p>First, the merge criterion of the HKE wiki knowledge engine can be shifted toward being decision-centric. If similar items are merged purely by text similarity, there is a risk that two cases requiring opposite actions get combined into one. Gating the merge with the question “do these two cause different actions” is a direct translation of the paper’s certified refinement.</p>

<p>Second, this gives a theoretical basis for budget management in session-resident hot memory. Hot memory already enforces its budget through a character cap; aligning the criterion for what to keep and what to discard with “preserve the distinctions that affect decisions” raises the quality of pruning. It means prioritizing items that split decisions, not items that summarize smoothly.</p>

<p>Third, the policy gates and audit logs that Paxis leaves behind are a natural data source for proving after the fact that “the same state led to a different decision.” If it is impractical to run DeMem’s online certified refinement in real time, a practical path is to analyze these audit logs in offline batches and periodically update the merge and split policy. This is where the decision-centric memory principle and audit-based orchestration, which makes that principle safely repeatable, come together.</p>

<h2 id="limitations-and-counterarguments">Limitations and Counterarguments</h2>

<p>A few things should be made clear.</p>

<p>First, certification is not free. Certifying a decision conflict from data requires accumulated observations, and in cold-start or sparse-interaction settings, refinement is delayed, so it is hard to tell from the paper alone what happens to early-stage decision quality.</p>

<p>Second, estimating “decision quality loss” online in production requires a reward signal or a judge. Benchmarks have ground truth answers that make this signal easy to obtain, but how to secure this signal in real conversations without ground truth remains an open question. The audit-log approach suggested above could be one answer, but that is outside the scope of the paper.</p>

<p>Third, the appendix includes a proof of computational hardness, meaning that finding the optimal partition is generally a hard problem. DeMem is a practical approximation of that, and more bounds are needed on the conditions under which this approximation breaks down.</p>

<p>Even so, the principle itself, moving agent memory from description to decision, is simple and powerful, and worth considering for adoption right now. If an agent keeps forgetting its own past decisions, the problem may not be that its memory is too small, but that its memory is preserving the wrong thing.</p>

<blockquote>
  <p>📄 <strong>Full deep review (DOCX)</strong>: <a href="https://drive.google.com/file/d/1oxsADQALTfdn7I_mmZbaZfMnmqoCMF9o/view">Download the detailed peer review on Google Drive</a>.</p>
</blockquote>

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

<ul>
  <li>Paper: <a href="https://arxiv.org/abs/2605.10870">Remember the Decision, Not the Description: A Rate-Distortion Framework for Agent Memory (arXiv 2605.10870)</a></li>
  <li>Benchmarks: LoCoMo, LongMemEval / Backbones: GPT-4o-mini, GPT-4.1-mini, Qwen2.5-14B-Instruct, Llama-3.1-70B</li>
  <li>Figures in the table are cited from Table 1 (LoCoMo, GPT-4.1-mini) of the paper.</li>
</ul>]]></content><author><name>{&quot;name&quot;=&gt;nil, &quot;avatar&quot;=&gt;nil, &quot;bio&quot;=&gt;nil, &quot;location&quot;=&gt;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="research" /><category term="agent-memory" /><category term="rate-distortion" /><category term="long-horizon-agents" /><category term="llm-agents" /><category term="paxis" /><summary type="html"><![CDATA[Long-running agents operate within limited memory, yet memory methods to date have organized the past using descriptive criteria such as relevance or summary quality. This paper, co-authored by a Meta AI researcher, argues that the criterion itself is wrong. The value of memory does not come from faithfully describing the past, but from keeping apart, even under a fixed budget, situations that call for different actions. The authors formalize this as a decision-centric rate-distortion problem and propose a learner called DeMem that consistently outperforms existing methods at the same memory budget.]]></summary></entry><entry xml:lang="en"><title type="html">Agentic RL Stops Waiting for the Group and Learns from One Rollout at a Time</title><link href="https://thakicloud.github.io/en/research/sao-single-rollout-async-agentic-rl/" rel="alternate" type="text/html" title="Agentic RL Stops Waiting for the Group and Learns from One Rollout at a Time" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/en/research/sao-single-rollout-async-agentic-rl</id><content type="html" xml:base="https://thakicloud.github.io/en/research/sao-single-rollout-async-agentic-rl/"><![CDATA[<p>Training agents with reinforcement learning is no longer a lab-only phrase. Models that excel at tasks like fixing a codebase over dozens of turns on SWE-Bench, or working through a multi-step math proof, are mostly not the product of pretraining alone. The post-training stage, where the model actually calls tools, interacts with an environment through rollouts, and gets rewarded for it, is what makes the difference. But as those rollouts grow longer, the training method that has stood as the standard begins to break down.</p>

<p>A paper released on July 8, 2026 by researchers at Tsinghua University and Z AI, “Single-Rollout Asynchronous Optimization for Agentic Reinforcement Learning” (arXiv 2607.07508), confronts exactly this problem. The short version: the authors dropped “group sampling,” the core mechanism behind the widely used GRPO. And they did not leave this idea confined to a paper’s experiments section. They put it into the actual pipeline used to train GLM-5.2, a 750B-scale open model.</p>

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

<p>This paper matters right now because the cost bottleneck in training has shifted from algorithms to infrastructure utilization. Plenty of loss functions already exist to make models smarter. The real problem is that even with hundreds of GPUs wired together, most of the time it takes to produce a single training step is spent waiting.</p>

<p>ThakiCloud also runs five post-training techniques (SFT, CPT, DPO, GRPO, GKD) on our kubeflow-based LLM training system. So the price GRPO’s group sampling pays on long rollouts, and the new risks that arise from the alternative that removes that price, are not someone else’s problem. This post lays out what SAO changed, and what that change means for organizations like ours trying to train agents on multi-tenant GPU clusters.</p>

<p><img src="/assets/images/sao-single-rollout-async-agentic-rl-hero.png" alt="An abstract image contrasting a stream of rollouts arriving one at a time asynchronously against rollouts frozen in a queue waiting for a group to fill" />
<em>A visualization contrasting single rollouts arriving continuously, one after another, against rollouts frozen in a queue while waiting for the rest of a group to arrive.</em></p>

<h2 id="what-is-this-technology">What Is This Technology</h2>

<p>As the name suggests, SAO combines two ideas: “single-rollout” and “asynchronous optimization.”</p>

<p>The standard RL pipeline used to be synchronous. You fix a batch of prompts, generate multiple rollouts per prompt, wait until every rollout in the batch finishes, compute the rewards, and perform a single policy update. This worked fine for tasks that generate short answers, because rollout lengths were roughly uniform.</p>

<p>The problem is agentic tasks. One coding task finishes in 3 turns; the task next to it runs 40 turns, calling tools the whole way. A synchronous pipeline idles the rest of the GPUs until the slowest rollout in the batch finishes. Asynchronous RL emerged to eliminate this waste: the moment a rollout completes, the model is updated immediately, and the generator never stops, moving straight on to the next rollout.</p>

<pre><code class="language-mermaid">flowchart TB
    subgraph SYNC[Synchronous GRPO]
        A1[Prompt batch] --&gt; A2[Generate G rollouts&lt;br/&gt;per prompt as a group]
        A2 --&gt; A3[Wait for&lt;br/&gt;the slowest rollout]
        A3 --&gt; A4[Compute group-relative reward&lt;br/&gt;single policy update]
        A4 -.GPU idle.-&gt; A2
    end
    subgraph SAO[SAO Async]
        B1[Generate 1 rollout&lt;br/&gt;per prompt] --&gt; B2[Arrives immediately&lt;br/&gt;upon completion]
        B2 --&gt; B3[Suppress off-policy updates&lt;br/&gt;via double-side token clipping]
        B3 --&gt; B4[Continuous policy updates]
        B4 --&gt; B1
    end
    SYNC --&gt;|Remove the group barrier| SAO
</code></pre>

<p>Here a fundamental conflict emerges. GRPO (Group Relative Policy Optimization) is “group-relative” by name. It bundles multiple rollouts for a single prompt into a group, and computes advantage by comparing the relatively better and worse rollouts within that group. Making learning signal from nothing but intra-group comparison, with no separate value function (critic), is both GRPO’s strength and its shackle. Without a full group, you cannot compute an advantage. An asynchronous structure that learns from rollouts as they arrive, and GRPO’s requirement to wait until the group fills, are fundamentally at odds.</p>

<h2 id="why-grpo-doesnt-fit-asynchronous-training">Why GRPO Doesn’t Fit Asynchronous Training</h2>

<p>Let’s look at this conflict more concretely. To preserve groups in an asynchronous pipeline, you are forced into one of two bad choices.</p>

<p>First, you could wait group by group. But then the benefit of asynchrony disappears. You end up back at synchronous training, waiting for the slowest rollout.</p>

<p>Second, you could let the rollouts within a group be generated by policies from different points in time. If one rollout in a group was produced by an old policy and another by a policy updated several steps later, grouping them together for relative comparison is statistically contaminated from the start. The degree of off-policy drift differs rollout by rollout, and treating them as a single shared baseline while ignoring that difference destabilizes training.</p>

<p>SAO’s answer is simple. Remove the group entirely. Generate exactly one rollout per prompt, and the moment that single rollout arrives, use it for training right away. With the group barrier gone, the generator never waits, and GPU idle time drops sharply.</p>

<h2 id="saos-two-pillars-single-rollout-and-double-side-token-clipping">SAO’s Two Pillars: Single Rollout and Double-Side Token Clipping</h2>

<p>But removing the group also removes something GRPO got for free. The intra-group comparison itself served as a variance-reducing baseline. With only one rollout, there is no longer a “did this rollout beat the group average” comparison to lean on. On top of that, in an asynchronous structure, a time lag opens up between the policy that produced the rollout and the policy currently being updated. This lag, the off-policy problem, is the second risk that can destabilize training.</p>

<p>SAO blocks this stability problem with what it calls “strict double-side token-level clipping.” The clipping used in the PPO family originally cuts the gradient when the importance ratio strays outside a certain range. SAO applies this at the token level, and strictly on both the upper and lower sides. Where a rollout produced by an old policy has drifted too far from the current policy at a given token, the update is strongly suppressed there, preventing a high-staleness signal from corrupting training.</p>

<p>The paper reports that this combination let SAO train stably for 1,000 steps. Given that asynchronous RL runs commonly diverge or collapse after a few hundred steps, 1,000 stable steps is solid evidence backing the method’s central claim.</p>

<h2 id="results-and-verification">Results and Verification</h2>

<p>The paper compares SAO against GRPO and its variants, and reports consistent gains on agentic coding and reasoning benchmarks. The benchmarks named are SWE-Bench Verified (resolving real GitHub issues), BeyondAIME (hard math), and IMOAnswerBench (olympiad-level math). All three share a common trait: they are long-horizon, multi-step tasks rather than short one-shot answers, exactly the territory SAO targets.</p>

<p>The most convincing validation is not a benchmark table, but the deployment itself. SAO was put into the actual agentic RL pipeline used to train GLM-5.2, a 750B-A40B MoE open model with 40B active parameters. A research method that does not stay confined to a paper, but gets used in production training at hundreds-of-billions-of-parameters scale, is a strong signal that it holds up beyond toy settings.</p>

<p>That said, this post does not cite specific benchmark numbers. If we cannot reproduce the exact figures verified in the original paper here, the honest choice is to convey the method’s structure and the named benchmarks without inventing numbers. If you need the precise scores, please check the source paper directly.</p>

<h2 id="implications-for-thakiclouds-product-lineup">Implications for ThakiCloud’s Product Lineup</h2>

<p>SAO’s lesson reaches beyond a single algorithms paper, touching directly on how we operate our GPU clusters.</p>

<p><strong>ai-platform lens (GPU training infrastructure).</strong> ThakiCloud’s ai-platform schedules multi-tenant GPU training on top of Kubernetes and Kueue. Our kubeflow-based LLM training system already supports GRPO as one of its post-training techniques. SAO raises a clear question: how much is our training jobs’ GPU utilization being eroded by variance in rollout length? For workloads with jagged lengths, like agentic rollouts, synchronous group waiting is a direct cost. Decoupling asynchronous rollout generation from training lets us extract more effective steps from the same GPUs, a direct lever for lowering the per-tenant cost of training in a multi-tenant environment. It is also worth checking whether Kueue’s gang scheduling and queue management inadvertently enforce the “wait until the group fills” pattern.</p>

<p><strong>Paxis lens (the output of agentic training).</strong> ThakiCloud’s Agent-Native Cloud, Paxis, is a control plane that runs skills inside isolated sandboxes and routes every action through policy gates and audit logs. The target SAO is trying to train well, an agent that calls tools across many turns to fix a codebase, is exactly the kind of workload Paxis runs. Going further, the real agent traces Paxis generates inside its isolated sandboxes can themselves become a rollout source for asynchronous RL like SAO. In other words, a loop forms: ai-platform generates and trains on rollouts cheaply, and Paxis operates the resulting agents safely while producing more training data in the process. Low-cost training infrastructure (ai-platform) underpins agent economics (Paxis).</p>

<h2 id="limitations-and-counterarguments">Limitations and Counterarguments</h2>

<p>Before accepting this method uncritically, a few things need to be flagged.</p>

<p>First, single-rollout training gives up the variance reduction that groups used to provide. SAO compensates with clipping, but clipping is fundamentally a mechanism that cuts learning signal. Overly strict clipping can throw away valid gradients along with the noise, slowing training down. Where the balance point between “stability” and “training speed” lands likely varies by task and scale.</p>

<p>Second, validation on a 750B model is impressive, but that it works at that scale does not mean it is optimal for smaller organizations’ setups. An asynchronous pipeline requires separate infrastructure complexity to decouple the generator from the trainer. For a team doing short tuning runs with a handful of rollouts, synchronous GRPO may simply be simpler and sufficient.</p>

<p>Third, the opposite direction is also being actively explored. Around the same time, other work has treated the relationship between async RLHF staleness and learning rate as a scaling law (arXiv 2607.01083), and other approaches stabilize asynchronous training through gradient alignment. It is more accurate to view SAO’s “remove the group, hold the line with clipping” not as the one correct answer, but as one strong answer among several to the open problem of asynchronous agentic RL.</p>

<p>Even so, SAO’s contribution is clear. It identified the problem precisely (the inefficiency of group sampling on long rollouts), and validated its solution (single rollout plus double-side clipping) through actual hundreds-of-billions-scale production training. For any organization where GPU utilization is training cost, this is reason enough to calculate how much your own pipeline is burning by “waiting for the group.”</p>

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

<ul>
  <li>Zhenyu Hou, Yujiang Li, Jie Tang, Yuxiao Dong. “Single-Rollout Asynchronous Optimization for Agentic Reinforcement Learning.” arXiv 2607.07508 (2026-07-08). <a href="https://arxiv.org/abs/2607.07508">https://arxiv.org/abs/2607.07508</a></li>
  <li>Related: “Staleness-Learning Rate Scaling Laws for Asynchronous RLHF.” arXiv 2607.01083. <a href="https://arxiv.org/abs/2607.01083">https://arxiv.org/abs/2607.01083</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;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="research" /><category term="reinforcement-learning" /><category term="agentic-rl" /><category term="grpo" /><category term="async-rl" /><category term="llm-training" /><category term="post-training" /><summary type="html"><![CDATA[When training long-horizon agentic tasks with RL, GRPO's group sampling idles the GPU while it waits for the slowest rollout to finish. SAO, the method Tsinghua University and Z AI actually deployed to train GLM-5.2, drops the group entirely and learns from a single rollout, protecting stability instead with double-side token clipping.]]></summary></entry><entry xml:lang="ko"><title type="html">GUI 없는 클라우드 맥에서 iOS 앱을 브라우저로 테스트하기: serve-sim</title><link href="https://thakicloud.github.io/ko/dev/serve-sim-ios-simulator-web/" rel="alternate" type="text/html" title="GUI 없는 클라우드 맥에서 iOS 앱을 브라우저로 테스트하기: serve-sim" /><published>2026-07-11T00:00:00+09:00</published><updated>2026-07-11T00:00:00+09:00</updated><id>https://thakicloud.github.io/ko/dev/serve-sim-ios-simulator-web</id><content type="html" xml:base="https://thakicloud.github.io/ko/dev/serve-sim-ios-simulator-web/"><![CDATA[<p>AI 코딩 에이전트에게 iOS 앱을 만들어 달라고 하면 한 가지 근본적인 벽에 부딪힙니다. 에이전트는 코드를 쓰고 빌드까지는 할 수 있지만, 화면에서 실제로 무슨 일이 벌어지는지 볼 수 없습니다. 특히 개발 환경을 클라우드의 Mac Mini에 올려 두면 문제가 더 커집니다. GUI가 없는 헤드리스 서버에서는 Xcode 시뮬레이터 창 자체가 뜨지 않기 때문입니다.</p>

<p>Expo 코어 팀의 Evan Bacon이 만든 <a href="https://github.com/EvanBacon/serve-sim">serve-sim</a>은 이 벽을 정면으로 겨냥합니다. 실제로 이 도구는 인디 개발자 levelsio가 “클라우드의 Mac Mini에서 Claude Code가 빌드한 iOS 앱을 브라우저로 실시간 확인할 수 있게 됐다”고 소개하면서 널리 알려졌습니다. serve-sim의 슬로건은 간단합니다. “Apple 시뮬레이터의 <code class="language-plaintext highlighter-rouge">npx serve</code>.”</p>

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

<p>serve-sim이 흥미로운 이유는 단순한 화면 미러링 도구가 아니기 때문입니다. 이 도구는 두 가지를 동시에 엽니다. 하나는 시뮬레이터 화면을 브라우저로 보내는 영상 스트림이고, 다른 하나는 브라우저나 에이전트가 시뮬레이터를 조작하도록 하는 제어 채널입니다. 즉 “보는 것”과 “조작하는 것”을 모두 원격에서 가능하게 만듭니다.</p>

<p>이 조합이 중요한 이유는 AI 코딩 에이전트의 개발 루프를 완성하기 때문입니다. 에이전트가 코드를 고치고, 빌드하고, 실행한 뒤, 그 결과 화면을 보고, 버튼을 눌러 다음 단계로 넘어가는 전체 순환을 사람 없이 돌릴 수 있게 됩니다. ThakiCloud의 Agent-Native Cloud인 Paxis가 지향하는 “에이전트가 격리된 환경에서 실제 작업을 수행하는” 구조와 정확히 맞닿아 있어, 하나의 오픈소스 도구가 그 워크플로를 어떻게 구현하는지 살펴볼 가치가 있습니다.</p>

<p><img src="/assets/images/serve-sim-ios-simulator-web-hero.png" alt="클라우드 헤드리스 서버의 스마트폰 화면이 빛의 입자로 흩어져 네트워크를 타고 브라우저 창으로 흘러 들어가는 추상 이미지" />
<em>헤드리스 서버의 시뮬레이터 화면이 스트림이 되어 원격 브라우저로 흘러 들어가는 구조를 형상화했습니다.</em></p>

<h2 id="serve-sim은-무엇인가">serve-sim은 무엇인가</h2>

<p>serve-sim의 동작 원리는 생각보다 단순하고 영리합니다. 별도의 Xcode 플러그인을 설치하거나 앱에 계측 코드를 심을 필요가 없습니다. 대신 작은 Swift 헬퍼 프로세스를 띄워, 이미 부팅된 iOS 시뮬레이터의 프레임버퍼를 애플이 제공하는 <code class="language-plaintext highlighter-rouge">simctl io</code> 인터페이스로 캡처합니다.</p>

<p>캡처한 화면은 두 갈래로 노출됩니다. 첫째, MJPEG 스트림으로 브라우저에 최대 60 FPS의 영상을 보냅니다. 둘째, WebSocket 제어 채널을 함께 열어 브라우저 쪽에서 탭·제스처 같은 입력을 시뮬레이터로 되돌려 보냅니다. 그 위에 React로 만든 프리뷰 UI가 얹혀, 사람이 브라우저에서 실제 기기처럼 앱을 만질 수 있습니다.</p>

<pre><code class="language-mermaid">flowchart TB
    A[부팅된 iOS 시뮬레이터] --&gt; B[Swift 헬퍼 프로세스]
    B --&gt; C[simctl io로&lt;br/&gt;프레임버퍼 캡처]
    C --&gt; D[MJPEG 영상 스트림&lt;br/&gt;최대 60 FPS]
    C --&gt; E[WebSocket 제어 채널]
    D --&gt; F[브라우저 React 프리뷰 UI]
    E --&gt; F
    E --&gt; G[에이전트 CLI&lt;br/&gt;탭·제스처·회전·카메라]
    F -.사람이 조작.-&gt; E
    G -.에이전트가 조작.-&gt; E
</code></pre>

<p>핵심은 “어떤 부팅된 시뮬레이터든” 대상이 된다는 점입니다. 앱을 수정할 필요가 없으므로, 이미 있는 프로젝트에 그대로 붙일 수 있습니다. 게다가 시뮬레이터 로그를 브라우저로 전달해, browser-use 계열 MCP 도구가 그 로그를 읽어 상태를 판단하게 할 수도 있습니다. 브라우저 창에 영상·이미지를 드래그 앤 드롭하면 시뮬레이터 기기에 파일로 추가되는 편의 기능도 있습니다.</p>

<h2 id="설치-및-사용">설치 및 사용</h2>

<p>serve-sim의 진입 장벽은 낮습니다. Node.js가 있는 맥에서 한 줄이면 됩니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npx serve-sim
</code></pre></div></div>

<p>실행하면 로컬 <code class="language-plaintext highlighter-rouge">http://localhost:3200</code>에서 프리뷰를 확인할 수 있습니다. 로컬에서 쓰거나, LAN을 통해 같은 네트워크의 다른 기기에서 접속하거나, 원격 맥에 올려 두고 터널링으로 어디서든 접속하는 세 가지 모드를 지원합니다. levelsio의 사례가 바로 세 번째로, 클라우드의 헤드리스 Mac Mini에서 실행하고 원격 브라우저로 확인하는 방식입니다.</p>

<p>에이전트 통합은 별도의 Agent Skill로 제공됩니다. 저장소의 <code class="language-plaintext highlighter-rouge">skills/serve-sim</code>에 담긴 이 스킬은 Claude Code·Cursor·Codex CLI·Gemini CLI를 비롯해 오픈 Agent Skills 표준을 구현한 모든 호스트에게 시뮬레이터를 CLI로 조작하는 방법을 가르칩니다. 탭, 제스처, 하드웨어 버튼, 화면 회전, 카메라 입력 주입, 그리고 스트림을 호스트의 프리뷰 창으로 넘기는 동작까지 포함됩니다.</p>

<h2 id="재현-참고">재현 참고</h2>

<p>이 글을 작성한 실행 환경은 GUI가 없는 헤드리스 배치 세션으로, Node.js 실행이 정책상 차단되어 있어 <code class="language-plaintext highlighter-rouge">npx serve-sim</code>을 직접 구동해 화면을 캡처하지는 못했습니다. 따라서 이 글의 명령과 동작 설명은 저장소 README와 공식 소개 자료에서 확인한 사실에 근거하며, 벤치마크 수치를 지어내지 않았습니다. 실제 시뮬레이터 스트리밍 화면과 지연 시간은 macOS + Xcode 시뮬레이터가 부팅된 환경에서 위 명령으로 직접 확인하시기 바랍니다.</p>

<h2 id="thakicloud-제품-적용-시사점">ThakiCloud 제품 적용 시사점</h2>

<p>serve-sim은 표면적으로는 iOS 개발자용 도구지만, 그 아래에는 에이전트 네이티브 개발이라는 더 큰 흐름이 깔려 있습니다.</p>

<p><strong>Paxis 렌즈 (에이전트 네이티브 개발).</strong> ThakiCloud의 Paxis는 스킬을 격리 샌드박스에서 실행하고 모든 행동을 정책 게이트와 감사 로그로 통과시키는 Agent-Native Cloud 제어 평면입니다. serve-sim이 채택한 개방형 Agent Skills 표준은 Paxis의 스킬 하네스가 다루는 것과 같은 계약 모델입니다. 하나의 스킬이 “시뮬레이터를 탭하고 회전시키고 화면을 읽는” 능력을 여러 에이전트 호스트에 공통으로 제공한다는 발상은, Paxis가 960개 이상의 스킬을 BM25로 선택해 격리 실행하는 구조와 정확히 같은 방향을 향합니다. 특히 serve-sim의 제어 채널처럼 에이전트가 실제 UI를 조작하는 워크로드는, 그 조작이 정책 게이트를 통과하고 감사 로그로 기록되어야 안전하게 프로덕션에 올릴 수 있습니다. serve-sim이 “능력”을 제공한다면, Paxis는 그 능력을 “안전하게 통제”하는 계층을 제공합니다.</p>

<p><strong>ai-platform 렌즈 (헤드리스 실행 인프라).</strong> serve-sim의 진짜 매력은 헤드리스 원격 맥에서 동작한다는 점입니다. GUI 없는 서버에서 빌드하고 스트리밍한다는 발상은 ThakiCloud ai-platform이 Kubernetes 위에서 워크로드를 GUI 없이 스케줄링하고 실행하는 방식과 같은 철학입니다. iOS 빌드가 요구하는 macOS 러너를 온디맨드로 붙이고, 그 위에서 에이전트가 빌드·테스트를 자동으로 돌린 뒤 결과만 사람에게 스트리밍하는 파이프라인은 CI를 넘어 “에이전트 주도 QA”로 확장될 수 있습니다. 저비용 헤드리스 실행 인프라(ai-platform)가 에이전트 자동화(Paxis)의 경제성을 떠받치는 구조입니다.</p>

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

<p>몇 가지는 냉정하게 짚어야 합니다.</p>

<p>첫째, serve-sim은 시뮬레이터를 대상으로 합니다. 실제 물리 기기가 아니라 시뮬레이터이므로, 카메라·센서·성능 특성처럼 실기기에서만 드러나는 문제는 여전히 잡지 못합니다. 시뮬레이터 통과가 실기기 통과를 보장하지 않는다는 오래된 한계는 그대로 남습니다.</p>

<p>둘째, MJPEG 스트리밍은 단순하고 호환성이 좋지만 압축 효율이 높지 않습니다. 60 FPS 고화질 스트림을 원격 터널로 계속 흘리면 대역폭과 지연이 병목이 될 수 있습니다. 반응 속도가 중요한 제스처 테스트에서는 네트워크 왕복 지연이 그대로 조작 지연으로 이어집니다.</p>

<p>셋째, 에이전트가 화면을 “보고 조작”할 수 있게 되는 것과, 그 판단이 정확한 것은 별개입니다. 에이전트가 스트림을 잘못 해석해 엉뚱한 버튼을 누르는 실패는 여전히 가능하며, 이 지점이 바로 정책 게이트와 사람 검토가 필요한 이유입니다. 도구가 능력을 열어 줄수록, 그 능력을 통제하는 계층의 중요성이 커집니다.</p>

<p>그럼에도 serve-sim의 방향은 분명합니다. “에이전트가 코드만 쓰는 단계”에서 “에이전트가 빌드하고 실행하고 직접 화면을 조작하며 검증하는 단계”로 넘어가는 데 필요한 실질적인 다리를 하나 놓았습니다. 헤드리스 클라우드에서 모바일 앱을 에이전트로 개발하려는 팀이라면, 지금 바로 <code class="language-plaintext highlighter-rouge">npx serve-sim</code> 한 줄로 그 세계를 열어 볼 수 있습니다.</p>

<h2 id="관련-슬라이드">관련 슬라이드</h2>

<p>본문 내용을 NotebookLM(<code class="language-plaintext highlighter-rouge">neo_swiss</code> 스타일)으로 요약한 슬라이드입니다.</p>

<p><img src="/assets/images/serve-sim-ios-simulator-web-slide-01.png" alt="serve-sim-ios-simulator-web 슬라이드 1" /></p>

<p><img src="/assets/images/serve-sim-ios-simulator-web-slide-02.png" alt="serve-sim-ios-simulator-web 슬라이드 2" /></p>

<p><img src="/assets/images/serve-sim-ios-simulator-web-slide-03.png" alt="serve-sim-ios-simulator-web 슬라이드 3" /></p>

<p><img src="/assets/images/serve-sim-ios-simulator-web-slide-04.png" alt="serve-sim-ios-simulator-web 슬라이드 4" /></p>

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

<ul>
  <li>Evan Bacon. “serve-sim: The <code class="language-plaintext highlighter-rouge">npx serve</code> of Apple Simulators.” GitHub. <a href="https://github.com/EvanBacon/serve-sim">https://github.com/EvanBacon/serve-sim</a></li>
  <li>@levelsio, serve-sim 소개 트윗. <a href="https://x.com/levelsio/status/2075328941317886210">https://x.com/levelsio/status/2075328941317886210</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;&quot;Seoul, Korea&quot;, &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;}]}</name><email>info@thakicloud.co.kr</email></author><category term="dev" /><category term="ios-simulator" /><category term="agent-skills" /><category term="developer-tools" /><category term="headless" /><category term="claude-code" /><category term="expo" /><summary type="html"><![CDATA[Mac Mini를 클라우드에 두면 GUI가 없어 iOS 시뮬레이터를 볼 수 없습니다. serve-sim은 시뮬레이터의 프레임버퍼를 브라우저로 스트리밍하고 WebSocket으로 제어까지 열어, AI 코딩 에이전트가 헤드리스 환경에서 iOS 앱을 빌드하고 실제로 조작하며 테스트하게 합니다.]]></summary></entry></feed>