ember ← ملاحظات إصدار v0.1 المصدر
ember / experiments / دليل

تجارب بحثية

لا يتيح كل generation run إلا تجربة Rust واحدة، على أن تكون statically compiled. ومن خلالها يمكن مراقبة التنفيذ أو تعديل activation قائمة في موضعها، من دون أن يتحول ember إلى منصة plugins عامة. ويظل العقد في v0.1 غير مستقر عن قصد.

دليل عقد v0.1 LLaMA · Qwen3 · Gemma 4
واجهة v0.1
// observation
tensor.values()
output remains numerically unchanged

// intervention
tensor.values_mut()
tensor.zero()
output may change
تجربة واحدة لكل run · استعر، لا تنسخ
API تجريبي

لا تزال واجهة التجارب في Rust غير مستقرة ضمن ember v0.1؛ فهي لا تمثل plugin ABI ديناميكية ولا تضمن التوافق وفق semver.

في مسار generation العادي، يضع ember hooks عند حدود دلالية معتمدة؛ فتظل الفروق العددية بين المعماريات ظاهرة، ولا يحتاج تشغيل hook إلى نسخ tensors.

لا ينشئ المسار العادي ExperimentRunner. وعند تفعيل التجارب، لا يضم الـ run سوى تنفيذ واحد لتجربة مجمّعة مع البرنامج؛ ثم يستدعي الـ methods الخاصة بها عند الحدود المعتمدة للطبقات والـ logits. ويقتصر السلوك الافتراضي على observation، ولا يحدث أي تدخل إلا بوصول mutable وصريح إلى activation قائمة. وهكذا تكفي واجهة الـ hooks لإجراء تجارب فعلية من دون تحويل ember إلى منصة plugins عامة.

01 تجربة واحدة لكل run

لا pipeline ولا registry ولا discovery وقت التشغيل، ولا قواعد تضبط تفاعل عدة تجارب.

02 استعر، لا تنسخ

تستعير الـ contexts ما تحتاج إليه من metadata، وتعرض TensorAccess الـ allocation في صورة view، مع بقاء ملكيته للـ runtime.

03 المعمارية تبقى ظاهرة

أسماء الـ hooks المشتركة لا تمحو normalization أو RoPE أو ترتيب العمليات الخاص بكل عائلة.

الملكية والاختيار بين المراقبة والتدخل
runtime-owned activation
TensorAccess<'_>
values() مراقبة؛ لا تغيّر الناتج العددي
values_mut() / zero() تدخّل؛ قد يغيّر التنفيذ
مسؤولية تنفيذ التجربة

لا يفرض نظام الأنواع في الواجهة سلوك observation-only؛ لذلك يتحمل تنفيذ التجربة مسؤولية هذا الضمان. يحافظ استخدام values() على التنفيذ، في حين قد يغيّره استخدام values_mut() أو zero().

بداية سريعة

ابنِ ember مرة واحدة، ثم استخدم خيار تجربة واحداً مع أمر generation عادي. الأمثلة تفترض أن ملفات النموذج والـ tokenizer موجودة في المجلد الحالي.

cargo build --release

تظهر تنبيهات التجربة وملخصاتها في stderr، فيما يحتفظ النص المولّد بتنسيق stdout الحالي. وبما أن الـ run لا يقبل أكثر من تجربة نشطة، فلا يمكن جمع الخيارين المدمجين.

التجارب المدمجة

activation-stats

عند كل tensor hook، يسجل L2 norm والقيمة المطلقة القصوى وfingerprint خفيفة.

observation فقط

تعرض جميع الـ tensor hooks في v0.1 activations من نوع f32. ومن دون طلب mutable access، تستخرج activation-stats من هذه القيم L2 norm والقيمة المطلقة القصوى وfingerprint خفيفة. فإذا اكتمل generation بنجاح، كتبت السجلات مرتبة في artifact بصيغة JSON. ولا تمس هذه العملية logits أو التوكنات المولدة.

target/release/ember \
  --arch qwen3 \
  --model Qwen3-0.6B-Q8_0.gguf \
  --tokenizer tokenizer-qwen3.json \
  --prompt "The capital of France is" \
  --max-tokens 4 \
  --temperature 0 \
  --activation-stats activation-stats.json

حقول الـ artifact

phase
prefill أو decode
stage
اسم الـ hook الدلالي
layer_index
ترقيمه يبدأ من الصفر؛ الحقل غير موجود عند logits
token range
البداية، وعدد توكنات الإدخال، وطول التسلسل
tensor
shape ثنائي الأبعاد وdtype بقيمة f32
values
L2 norm، والقيمة المطلقة القصوى، والـ fingerprint

تعتمد الـ fingerprint خوارزمية structured tracing نفسها المستخدمة في ember، وتأخذ عينة واحدة من كل 64 قيمة. لذا تفيد في تحديد موضع divergence لا في إثبات التطابق تشفيريًا. ولا يكتفي حساب الـ norm بالعينة، بل يفحص القيم كلها.

jq '.records[] | select(.stage == "after_layer") |
    {phase, layer_index, sequence_length, l2_norm, fingerprint}' \
  activation-stats.json
التوقيت

الـ observation يحافظ على الناتج العددي، لا على التوقيت. مسح activations وتخزين السجلات يضيفان عملاً؛ لذلك ينبغي قياس هذا الحمل في benchmark منفصل.

zero-layer-output

يصفّر مرحلة دلالية واحدة أثناء prefill، ومرة في كل تقييم decode أحادي التوكن يصل إلى الطبقة المختارة.

intervention

يتلقى الخيار رقم طبقة يبدأ من الصفر ومرحلة دلالية بالصيغة LAYER:STAGE. وبعد تحميل النموذج، يتحقق ember من الرقم، ثم يعدّل الـ tensor نفسها في موضعها ويحصي التدخلات. ويجري التدخل مرة في prefill، ثم مرة كلما بلغ تقييم decode الطبقة المختارة.

target/release/ember \
  --arch gemma4 \
  --model models/gemma-4-E2B-it.Q8_0.gguf \
  --tokenizer tokenizer-gemma4.json \
  --prompt "The capital of France is" \
  --max-tokens 8 \
  --temperature 0 \
  --zero-layer-output 4:attention
المرحلة الـ tensor التي تُستبدل بالصفر الموضع
attention مساهمة attention مباشرة قبل residual addition
mlp مساهمة MLP مباشرة قبل residual addition
layer hidden state المكتملة بعد اكتمال كل العمليات الخاصة بعائلة النموذج داخل الطبقة

يفشل الأمر أثناء تحليل CLI إذا كان اسم المرحلة غير صالح. وإذا خرج رقم الطبقة عن نطاق النموذج المحمّل، تعرض رسالة الخطأ معرّف النموذج وعائلته والرقم المطلوب والنطاق الصحيح.

research experiment active: zero-layer-output layer=4 stage=attention; execution will be modified
experiment zero-layer-output: 8 intervention(s) at layer 4 stage attention

يشمل العداد تدخل prefill وكل تقييم decode لاحق يبلغ الطبقة؛ لذلك قد يظهر في ملخص generation run واحد أكثر من تدخل. ولا يدل هذا العدد على أن التجربة عدّلت عدة طبقات.

الناتج والـ provenance

الواجهة من دون تجربة مع تجربة نشطة
stdout تنسيق النص المولّد الحالي التنسيق نفسه
stderr لا تنبيه خاص بالتجربة تنبيه تفعيل، وملخص عند الاكتمال الناجح
run manifest لا يوجد حقل للتجربة الاسم، والإعدادات، وهل عدّلت التنفيذ
artifact لا يوجد activation-stats يكتب JSON

يسجل الـ manifest عند استخدام activation-stats القيمة "modifies_execution": false. وعند إجراء intervention، يسجل الطبقة والمرحلة والقيمة "modifies_execution": true. وفي الحالتين، يبقى artifact الخاص بالتجربة ملفًا منفصلًا عن run manifest والنص المولّد.

حدود التوافق

الواجهة الحالة ملاحظات
generation عادي لـ LLaMA وQwen3 وGemma 4 مدعوم دعم النموذج لا يعني تلقائياً دعم experiment hooks
generation لـ Qwen2.5 experimental لم تكتمل validation المعمارية والـ tokenizer، ولا يشمله سجل parity المتحقق منه
experiment hooks لـ LLaMA وQwen3 وGemma 4 مدعوم prefill وsingle-token decode عبر الحدود الدلالية نفسها
experiment hooks لـ GPT-2 غير مدمج لا يوجد له مسار experiment hooks في v0.1
استخراج hidden states والـ probes مرفوض مع تجربة نشطة دلالات التمثيل غير معرّفة حتى الآن
layer/logit dumps مرفوض مع تجربة نشطة الواجهات العادية من دون تجربة لم تتغير
أوامر demo وinteractive وbenchmark الفرعية مرفوض مع تجربة نشطة التجارب تستهدف generation العادي حالياً
قيد حالي مهم

لا يمكن استخدام التجارب النشطة عند استخراج hidden states أو probes أو layer dumps أو logits dumps، إذ لم يحدد ember بعد ما إذا كانت واجهات الفحص هذه ستعرض القيم قبل intervention أم بعده.

إضافة تجربة مدمجة

تأتي التجارب في صورة كود Rust يجمّعه ember ضمن البرنامج. فاحصر الإضافة في نطاق ضيق، ولا توسّع الإطار لخدمة تنفيذ واحد.

  1. أضف تنفيذ التجربة داخل src/experiments/.
  2. طبّق Experiment، واترك الـ hooks غير المستخدمة على defaults الخاصة بها.
  3. أبقِ state والسجلات الخاصة بالتجربة داخل تنفيذها.
  4. استخدم TensorAccess فقط عند tensor hook معتمد.
  5. أضف خيار CLI واحداً وضيّقاً ومحدد النوع، مع مسار إنشاء صريح.
  6. مرّر الأخطاء عبر ExperimentError.
  7. أضف تغطية لترتيب الـ hooks، ودلالات التدخل، والعائلات، والـ parity، والـ allocations، والـ benchmarks.

اقرأ عقد الـ hooks قبل اختيار موضع الإدخال. ففيه توثيق لملكية tensors، والسلوك الخاص بكل عائلة، وحقول الـ context، وmetadata عند الفشل، والمسار المعطّل.

ما لا يستهدفه v0.1

النطاق ضيق عمداً. لا توجد plugins ديناميكية بصيغة .so أو .dll، ولا WASM hooks، ولا Python bindings، ولا discovery وقت التشغيل. كذلك لا توجد عدة تجارب متزامنة أو async hooks أو event bus عامة أو إمكانية لتعديل الأوزان اعتباطياً أو tokenizers وexecution backends مخصصة. ولا يقدّم الإصدار ادعاءً واسعاً عن ملاءمته لـ production inference.