Mohamed Ashraf Elsaed

تصميم Loupe: تعليقات تبقى بعد إعادة النشر

بقلم Mohamed Ashraf Elsaedنُشر 6 دقيقة قراءة

هذه ترجمة آلية لم تُراجَع بعد. الأصل الإنجليزي هو المرجع. اقرأ الأصل الإنجليزي

Loupe أداة ملاحظات مرئية: يثبّت شخص تعليقًا على عنصر في صفحة حية، ويقرؤه وكيل ذكاء اصطناعي عبر MCP. الجزء الصعب في تصميمه أمران. الأول أن يبقى التعليق مثبّتًا بعد إعادة نشر تغيّر الشيفرة. والثاني أن يستطيع الوكيل العمل على التعليق دون أن يقرر أبدًا أن العمل انتهى.

شعار Loupe: عدسة مكبّرة بيضاء بمقبض برتقالي على مربع بنفسجي بزوايا مدورة

شكل النظام

مخطط نظام Loupe: الأداة وإضافة المتصفح ترسلان التعليقات إلى API يخزنها في قاعدة بيانات وتخزين ملفات؛ وخادم MCP عبر stdio يتيح للوكيل قراءتها وتحديثها؛ والإنسان هو من يحسمها
الأداة تتصل بـ API، وخادم MCP يعطي الوكيل التعليقات نفسها، والإنسان هو من يحسمها.
المكوّن بيئة التشغيل الدور
@loupekit/sdk المتصفح الأداة، داخل Shadow DOM، على أي صفحة
@loupekit/server Node 24 واجهة HTTP API، وPostgres مضمّنة (PGlite) أو حقيقية، والملفات على القرص
@loupekit/dashboard المتصفح اللوحة ذات المراحل الخمس
@loupekit/mcp Node (الإصدار 24 موثّق) خادم MCP عبر stdio بـ 19 أداة، مع جسر محلي
@loupekit/extension المتصفح، Manifest V3 نواة SDK نفسها، للمواقع التي لا تستطيع تغييرها
loupekit/laravel PHP 8.2+ الـ API واللوحة وخادم MCP بأربع أدوات داخل تطبيق Laravel

محليًا تشغّل عملية Node واحدة الـ API وقاعدة البيانات وتخزين الملفات والاستضافة الثابتة للوحة والعرض التجريبي وحزمة SDK. يمكن لتطبيق Laravel أن يحل محل هذه العملية. أما Loupe Hub فهو خادم منفصل تستضيفه بنفسك، وهو اختياري، ويوجّه التذاكر بين التطبيقات.

رحلة التعليق

  1. الالتقاط. تسجّل SDK مرساة، وهي HTML الخارجي للعنصر (حتى 6,000 حرف)، وبعض الأنماط المحسوبة، ولقطة شاشة.
  2. محوّل التخزين. تستدعي الأداة StorageAdapter. محوّل HTTP يرفع لقطة الشاشة أولًا إلى POST /v1/blobs، ثم يرسل التعليق مع الرابط المُعاد. إذا فشل الرفع يحتفظ بالبيانات المضمّنة. أما المحوّل دون اتصال فيكتب في localStorage بدلًا من ذلك.
  3. الـ API. يتحقق الخادم من الهوية، ويوحّد صيغة الرابط، ثم يُدرج التعليق أو يحدّثه بمعرّفه الذي ولّده العميل.
  4. اللوحة وMCP. يفرز الناس التعليقات على اللوحة. ويقرأ الوكيل التعليقات نفسها عبر خادم MCP.
  5. اقتراح أو طلب دمج. يكتب الوكيل اقتراحًا (HTML وCSS وملاحظات تظهر كمعاينة قبل وبعد) أو سجل طلب دمج.
  6. المراجعة. ينقل الوكيل المحادثة إلى In Review، فتشير الأداة واللوحة إلى أنها تحتاج إلى شخص.
  7. الحسم. يحسمها شخص.

ثلاث بيانات اعتماد تُبقي المسارات منفصلة. ترسل الأداة هوية مستخدم موقّعة بـ HMAC (X-Loupe-User وX-Loupe-Hmac). ويستخدم تطبيق Laravel جلسته الخاصة ورمز CSRF. ويستخدم خادم MCP واللوحة سر المشروع في X-Loupe-Admin.

المرساة: بصمة لا محدِّد

يتعطل محدِّد CSS عند أول تغيير في اسم صنف. يخزن Loupe عدة إشارات عن العنصر، تسمى المرساة، ويحلّها من جديد عند كل تحميل وكل تغيير في DOM.

الحقل المحتوى
tag اسم الوسم بأحرف صغيرة
cssPath مسار CSS ينطلق من أقرب معرّف ثابت أو معرّف اختبار
xpath مسار XPath إلى العنصر
testid data-testid أو data-test أو id ثابت
text النص بعد توحيده، حتى 120 حرفًا
attrs role وaria-label وname وtype وalt وhref وplaceholder وtitle، كلٌّ حتى 200 حرف
nthOfType الترتيب بين الإخوة من الوسم نفسه
rect, viewport موضع المستند وحجمه، ومنفذ العرض وقت الالتقاط

حلّ المرساة من جديد

مخطط قرار حلّ المرساة: معرّف اختبار فريد يفوز بدرجة 0.98، ومسار CSS من معرّف ثابت بدرجة 0.90، وإلا تُقيَّم المرشحات وينفصل التعليق عند أقل من 0.5
ترتيب الحلّ. الثوابت في packages/sdk/src/fingerprint.ts.

الترتيب مقصود: الإشارات الأقوى أولًا.

  1. معرّف اختبار أو id فريد يفوز بدرجة 0.98.
  2. مسار CSS ينطلق من معرّف ثابت، بتطابق واحد ونفس الوسم، يفوز بدرجة 0.9. وهذا يصمد أمام تغيّر النص.
  3. وإلا فالعناصر التي يحددها مسار CSS ومسار XPath، وكل عنصر بالوسم نفسه، تصبح مرشحة، وتُضاف العناصر ذات النص المطابق حتى يزيد العدد على 4,000. يُقيَّم كلٌّ منها بتشابه موزون: النص 0.34، والخصائص 0.22، ومعرّف الاختبار 0.22، ومسار CSS 0.2، والوسم 0.12، والموضع 0.1.
  4. إذا بلغت أفضل درجة 0.5 على الأقل، يلتصق التعليق بذلك العنصر. وتحت 0.5 ينفصل التعليق وتعرض القائمة شارة “moved”.

العتبة تعبّر عن قرار في المنتج: تقول الوثائق إن التعليق المنفصل أأمن من تعليق واثق على عنصر خاطئ. التعليق الواثق الخاطئ يرسل المطوّر لإصلاح الشيء الخطأ، أما المنفصل فيطلب من شخص أن ينظر. العتبة تقلل احتمال التثبيت الخاطئ؛ لكن إصابة بدرجة 0.9 أو 0.98، أو درجة فوق 0.5، قد تظل خاطئة.

مرشّحان يُبقيان المرشحين نزيهين. يُتجاهل المعرّف الذي يبدو مولَّدًا من إطار عمل: أطول من 40 حرفًا، أو يبدأ بـ ember أو react أو radix أو headlessui أو :r، أو يحتوي على :. وتُتخطى العناصر المخفية إذا كان المتصفح يدعم checkVisibility.

تعليق مثبّت على صفحة Loupe التجريبية قبل إعادة نشر محاكاة
تعليق مثبّت على الصفحة التجريبية قبل إعادة نشر محاكاة. هاتان الصورتان من وثائق Loupe وتعرضان نسخة أقدم من الأداة بشريط أدوات سفلي؛ شارة العدد هنا 1 وفي الصورة التالية 2.
التعليق نفسه ملتصق بالعنصر نفسه بعد تغيّر شيفرة الصفحة التجريبية
التعليق نفسه بعد تغيّر الشيفرة. في الصفحة التجريبية زر Simulate redeploy لهذا الغرض.

يرتبط تعليق المنطقة بأصغر عنصر أب يغطي 60% على الأقل من المنطقة، صعودًا من العنصر الواقع تحت مركز المنطقة. ولا يصلح body ولا html أبدًا. وتخزّن المنطقة مستطيلها ككسور من تلك المرساة، فيمكن وضعها من جديد بعد إعادة تدفق التخطيط المتجاوب.

دورة الحياة، ومن يحق له تحريكها

خمس مراحل للتعليق: Queue وTo Do وIn Progress وIn Review وResolved. يجوز للوكيل نقل التعليق إلى In Review؛ والإنسان وحده يحسمه
خمس مراحل، ليُفرَّق بين "لم يُفرز" و"ينتظرني" و"ينتظر معاينة".

القاعدة سهلة الصياغة: يجوز للوكيل نقل التعليق إلى In Review، والإنسان وحده يحسمه. وأدوات MCP في Node تحملها. الأداة mark_thread_addressed، المصممة للتسليم، ليس لها معامل للحالة وتضبط In Review دائمًا. أما update_status فتذكر القاعدة نفسها في وصفها، ومنذ 0.14.1 ترفض resolved دون تغيير التعليق.

تنبيهان. درس Loupe نفسه يقول إن هذا تعليمات للوكيل، لا فحص صلاحية. وأداة update-status في حزمة Laravel لا ترفض resolved: تقبله وتحفظه. وفي معالج PATCH في خادم Node لم أجد أي قاعدة على الحالة (قرأت الشيفرة ولم أشغّلها).

وتقرر الأداة أيضًا متى يلزم أن يتصرف شخص (needsYou())، بهذا الترتيب:

  1. المحادثة المحسومة لا تحتاج أحدًا أبدًا.
  2. فشل تشغيل الوكيل: “The agent’s run failed”.
  3. آخر رسالة من الوكيل تنتهي بعلامة استفهام: “The agent asked you something”.
  4. المحادثة في In Review: “Waiting on your review”.

الفشل يتقدم على السؤال، وكلاهما يتقدم على المراجعة الروتينية.

ما الذي يغطيه الحجب

العناصر الموسومة بـ data-loupe-redact تُستبعد من لقطات الشاشة التي يلتقطها الالتقاط المدمج في Loupe. هذا للصور فقط: نص المرساة وHTML الخارجي للعنصر (حتى 6,000 حرف) قد يظلان يحتويان المحتوى، ومن يستبدل الالتقاط عليه أن يجري الحجب بنفسه. وتسجيلات الشاشة تلتقط البكسلات الحقيقية، بما فيها العناصر المحجوبة، وتذكر الوثائق ذلك. أما خادم Node واللوحة فيعملان على جهازك أو على مضيف تختاره، والتعليقات في قاعدة بيانات تتحكم بها.

المقايضات

ما الذي فحصته وما الذي لم أفحصه

قرأت ثوابت المرساة في packages/sdk/src/fingerprint.ts على نسخة مستنسخة من Loupe 0.14.2 في 2026-10-31: درجتي 0.98 و0.9، وعتبة القبول 0.5، وتجربة معرّف الاختبار الفريد ومسار CSS أولًا. ووثيقة البنية تصف 0.14.1، فأسماء الأدوات وقواعد المراحل هنا مأخوذة من تلك الوثيقة. شغّلت الخادم المحلي من تلك النسخة وحمّلت الصفحة التجريبية واللوحة.

قرأت معالج الحالة وأداة Laravel في الشيفرة ولم أشغّلهما. ولم أشغّل المزامنة ثنائية الاتجاه في Hub ولا حزمة Laravel ولا إضافة المتصفح من أجل هذه المقالة، ولم أختبر إعادة المحاذاة على إعادة نشر حقيقية لموقعي. صور قبل وبعد من وثائق Loupe نفسها.

ما الذي كنت سأفعله بشكل مختلف

  1. فرض قاعدة المراجعة على الخادم أيضًا. اليوم تحملها أدوات Node، ويصفها الدرس بأنها تعليمات للوكيل لا فحص صلاحية. وأداة Laravel تقبل resolved اليوم.
  2. إخبار الفرق عن data-testid قبل أول تعليق. التقييم مصمم ليصمد أمام التغيير، لكن المقبض الثابت أقوى إشارة، وهو يكلّف الفريق خاصية واحدة.

المصادر

كُتبت مسودة هذه المقالة بمساعدة الذكاء الاصطناعي. راجعت كل ادعاء ونفذت الأوامر المعروضة.

عن المشروع: Loupe.

Mohamed Ashraf Elsaed
Mohamed Ashraf Elsaed
قائد تقني في Convertedin، القاهرة. أبني أدوات لوكلاء البرمجة بالذكاء الاصطناعي وأكتب عن لارافيل وNode والهندسة.

ما يقدمه aletheia-mem وما لا يقدمه

فوائد Aletheia بأرقامها ومصادرها: مخزن محلي واحد لعدة أدوات، وقواعد تُكتب مرة واحدة، وخصوصية افتراضية، واسترجاع مقيس، مع الحدود.