لقد أصدرنا مؤخرًا ملحق المتصفح التجريبي Transformers.js المدعوم من Gemma 4 E2B لمساعدة المستخدمين على التنقل عبر الويب.
أثناء إنشائه، واجهنا العديد من الملاحظات العملية حول أوقات تشغيل Manifest V3، وتحميل النماذج، والرسائل التي تستحق المشاركة.
لمن هذا
هذا الدليل مخصص للمطورين الذين يرغبون في تشغيل ميزات الذكاء الاصطناعي المحلية في ملحق Chrome باستخدام Transformers.js ضمن قيود Manifest V3.
في النهاية، سيكون لديك نفس البنية المستخدمة في هذا المشروع: عامل خدمة الخلفية الذي يستضيف النماذج، وواجهة مستخدم للدردشة الجانبية، ونص برمجي للمحتوى للإجراءات على مستوى الصفحة.
ماذا سنبني
في هذا الدليل، سنقوم بإعادة إنشاء البنية الأساسية لـ Transformers.js جيما 4 مساعد المتصفحباستخدام الامتداد المنشور كمرجع وقاعدة التعليمات البرمجية مفتوحة المصدر كخريطة التنفيذ.
1) بنية ملحق Chrome (MV3)
قبل التعمق، ملاحظة سريعة: لن أتعمق في طبقة React UI أو تكوين بناء Vite. ينصب التركيز هنا على قرارات البنية عالية المستوى: ما الذي يتم تشغيله في كل وقت تشغيل لمتصفح Chrome وكيفية تنسيق هذه الأجزاء.
إذا كان Manifest V3 جديدًا بالنسبة لك، فاقرأ هذه النظرة العامة القصيرة أولاً: ما هو Manifest V3؟.
1.1 سياقات وقت التشغيل ونقاط الدخول
في MV3، تبدأ البنية الخاصة بك في public/manifest.json. يحدد هذا المشروع ثلاث نقاط دخول:
يعالج عامل الخدمة الخلفية أيضًا chrome.action.onClicked لفتح اللوحة الجانبية لعلامة التبويب النشطة. نقطة الدخول ذات الصلة التي يجب معرفتها: يمكن تعريف النافذة المنبثقة action.default_popup ويعمل بشكل جيد للإجراءات السريعة. يستخدم هذا المشروع لوحة جانبية للدردشة المستمرة، ولكن نمط التنسيق هو نفسه.
1.2 ما الذي يجري أين
قرار التصميم الرئيسي هو الحفاظ على التنسيق الثقيل في الخلفية والحفاظ على منطق واجهة المستخدم/الصفحة رقيقًا.
- خلفية (
src/background/background.ts) هو مستوى التحكم: دورة حياة الوكيل، وتهيئة النموذج، وتنفيذ الأداة، والخدمات المشتركة مثل استخراج الميزات. - لوحة جانبية (
src/sidebar/*) هي طبقة التفاعل: إدخال/إخراج الدردشة، وتحديثات البث، وعناصر التحكم في الإعداد. - نص المحتوى (
src/content/content.ts) هو جسر الصفحة: استخراج DOM وإجراءات التمييز.
إحدى النتائج العملية لهذا التقسيم هي أن تاريخ المحادثة يعيش أيضًا في الخلفية (Agent.chatMessages): ترسل واجهة المستخدم أحداثًا مثل AGENT_GENERATE_TEXT، تقوم الخلفية بإلحاق الرسالة، وتشغيل الاستدلال، ثم إرسالها MESSAGES_UPDATE العودة إلى اللوحة الجانبية.
يتجنب هذا التقسيم تحميلات النماذج المكررة، ويحافظ على استجابة واجهة المستخدم، ويحترم حدود أمان Chrome حول الوصول إلى DOM.
1.3 عقد المراسلة
بمجرد فصل أوقات التشغيل، تصبح المراسلة هي العمود الفقري. في هذا المشروع، تتم كتابة جميع الرسائل من خلال التعدادات src/shared/types.ts.
- اللوحة الجانبية -> الخلفية (
BackgroundTasks):CHECK_MODELS,INITIALIZE_MODELSAGENT_INITIALIZE,AGENT_GENERATE_TEXT,AGENT_GET_MESSAGES,AGENT_CLEAREXTRACT_FEATURES
- الخلفية -> اللوحة الجانبية (
BackgroundMessages):DOWNLOAD_PROGRESS,MESSAGES_UPDATE
- الخلفية -> المحتوى (
ContentTasks):EXTRACT_PAGE_DATA,HIGHLIGHT_ELEMENTS,CLEAR_HIGHLIGHTS
قاعدة التزامن بسيطة: الخلفية هي المنسق الوحيد؛ تعد اللوحة الجانبية والبرنامج النصي للمحتوى عاملين متخصصين يطلبون الإجراءات ويعرضون النتائج.
تدفق الطلب النموذجي:
- ترسل اللوحة الجانبية
AGENT_GENERATE_TEXT. - الخلفية تلحق ب
Agent.chatMessagesويقوم بتشغيل خطوات النموذج/الأداة. - تنبعث الخلفية
MESSAGES_UPDATE. - يتم إعادة عرض اللوحة الجانبية من قائمة الرسائل المحدثة.
2) تفاصيل التكامل Transformers.js
2.1 النماذج والمسؤوليات
في src/shared/constants.ts، يستخدم هذا الامتداد دورين نموذجيين:
التقسيم مقصود: يتعامل برنامج Gemma 4 مع قرارات الاستدلال/الأداة، بينما يقوم برنامج MiniLM بإنشاء تضمينات متجهة للبحث عن التشابه الدلالي في ask_website و find_history.
2.2 حيث يتم تشغيل الاستدلال
كل الاستدلال يعمل في الخلفية (src/background/background.ts):
- توليد النص عبر
pipeline("text-generation", ...)مع التخزين المؤقت المتسق لـ KV الذي تم تمكينه من خلال نظامنا الجديدDynamicCacheفصل - التضمين عبر
pipeline("feature-extraction", ...)بالإضافة إلى تطبيع المتجهات
يوفر هذا مضيف نموذج واحد لجميع علامات التبويب/الجلسات، ويتجنب استخدام الذاكرة المكررة، ويحافظ على استجابة واجهة مستخدم اللوحة الجانبية. نظرًا لأنه يتم تحميل النماذج من عامل الخدمة في الخلفية، يتم تخزين العناصر مؤقتًا تحت أصل الامتداد (chrome-extension://<extension-id>) بدلاً من أصول كل موقع ويب، مما يوفر ذاكرة تخزين مؤقت مشتركة واحدة لتثبيت الامتداد بالكامل.
ملاحظة حول دورة حياة MV3: يمكن تعليق عمال الخدمة وإعادة تشغيلهم، لذلك يجب التعامل مع حالة وقت تشغيل النموذج على أنها قابلة للاسترداد وإعادة تهيئتها عند الحاجة.
2.3 دورة حياة التنزيل والتخزين المؤقت
دورة حياة النموذج واضحة:
CHECK_MODELSيفحص ما تم تخزينه مؤقتًا بالفعل ويقدر حجم التنزيل المتبقي.INITIALIZE_MODELSالتنزيلات/تهيئة النماذج والإصداراتDOWNLOAD_PROGRESSإلى واجهة المستخدم.- يتم إعادة استخدام المثيلات طويلة الأمد بعد الإعداد:
تعد الأذونات والخصوصية جزءًا من البنية، وليست مربع اختيار في النهاية. في هذا المشروع، public/manifest.json يسأل عن sidePanel, storage, scripting، و tabsبالإضافة إلى host_permissions ل http(s)://*/*:
sidePanel: مطلوب لفتح اللوحة الجانبية UX والتحكم فيها.storage: مطلوب للاستمرار في حالة الأداة/الإعدادات عبر الجلسات.tabs+scripting: مطلوب للأدوات المدركة لعلامات التبويب والإجراءات على مستوى الصفحة.host_permissionsعلىhttp(s)://*/*: مطلوب لأن استخراج/تمييز المحتوى مصمم للعمل على مواقع الويب العشوائية.
لماذا تبقي هذا ضيقًا: تحدد الأذونات ثقة المستخدم ومخاطر مراجعة سوق Chrome الإلكتروني. اطلب فقط ما تحتاجه ميزاتك فعليًا، واذكر بوضوح أن الاستدلال يعمل محليًا في وقت تشغيل الامتداد حتى يفهم المستخدمون مكان معالجة بياناتهم.
3) حلقة تنفيذ الوكيل والأداة
3.1 أساسيات استدعاء الأدوات (سبب وجود هذه الطبقة)
قبل حلقة التنفيذ، من المفيد فهم كيفية عمل استدعاء أداة النموذج (الأساس لأي سير عمل وكيل). يمكنك تمرير الرسائل بالإضافة إلى مخطط الأداة (name, description، و parameters)، ويقوم Transformers.js بتنسيق الموجه الفعلي من تلك المدخلات باستخدام قالب الدردشة الخاص بالنموذج. نظرًا لأن قوالب الدردشة خاصة بنموذج معين، فإن التنسيق الدقيق لاستدعاء الأداة يعتمد على النموذج الذي تستخدمه. باستخدام قوالب نمط Gemma-4، يُصدر النموذج كتلة رمزية خاصة لاستدعاء الأداة عندما يقرر استدعاء واحدة.
import pipeline from "@huggingface/transformers";
const generator = await pipeline(
"text-generation",
"onnx-community/gemma-4-E2B-it-ONNX",
dtype: "q4f16",
device: "webgpu",
,
);
const messages = [ role: "user", content: "What's the weather in Bern?" ];
const output = await generator(messages, {
max_new_tokens: 128,
do_sample: false,
tools: [
{
type: "function",
function:
name: "getWeather",
description: "Get the weather in a location",
parameters:
type: "object",
properties:
location:
type: "string",
description: "The location to get the weather for",
,
,
required: ["location"],
,
,
},
],
});
في وقت التوليد، يمكن للنموذج أن يصدر مخرجات مثل:
<|tool_call>call:getWeather"<tool_call|>
هذا هو بالضبط سبب احتواء هذا المشروع على طبقة تطبيع (webMcp) والمحلل (extractToolCalls): يجب تحويل مخرجات النموذج إلى عمليات تنفيذ للأداة الحتمية.
3.2 واجهة الأداة في هذا المشروع
src/background/agent/webMcp.tsx تطبيع أدوات التمديد إلى شكل مناسب للنموذج:
name,description,inputSchema,execute
أدوات المثال تشمل get_open_tabs, go_to_tab, open_url, close_tab, find_history, ask_website، و highlight_website_element.
3.3 تصميم الحلقة (Agent.runAgent)
خيار التصميم الأساسي هنا هو فصل رسائل النماذج الداخلية عن رسائل الدردشة التي تواجه واجهة المستخدم:
- نسخة النموذج الداخلي (
messages): النظام/المستخدم/الأداة/المساعد يستخدم للرسائل فيgenerator(...). - نص واجهة المستخدم (
chatMessages): ما يراه المستخدم، بما في ذلك النص المساعد المتدفق بالإضافة إلى البيانات التعريفية لتنفيذ الأداة (tools) ومقاييس الأداء.
تدفق التنفيذ:
- إضافة مدخلات المستخدم إلى
chatMessages، وإنشاء رسالة مساعد نائبة، ودفق الرموز المميزة. - تحليل إخراج النموذج المتدفق/النهائي باستخدام
extractToolCalls.tsداخلmessage, toolCalls. - احتفظ برسالة المساعد المرئية للمستخدم كنص عادي، بينما يتم تنفيذ استدعاءات الأداة في الخلفية.
- إلحاق نتائج الأداة ببيانات تعريف الأداة المساعدة وتغذية النتائج مرة أخرى في دور المطالبة التالي.
- كرر ذلك حتى لا يتبقى أي استدعاءات للأدوات، ثم قم بإنهاء محتوى المساعد + المقاييس.
يؤدي هذا إلى إبقاء اتصالات المستخدم نظيفة مع الحفاظ على حلقة أداة حتمية في الخلفية.
4) حدود البيانات واستمراريتها
يعد وضع الحالة قرارًا معماريًا آخر يهم كثيرًا في MV3. في هذا التنفيذ، يتم تقسيم الحالة حسب دورة الحياة ونمط الوصول:
- حالة المحادثة: ذاكرة الخلفية (
Agent.chatMessages) للتنسيق السريع خطوة بخطوة. - تفضيلات الأداة:
chrome.storage.localلذلك تستمر الإعدادات عبر الجلسات. - ناقلات التاريخ الدلالي: IndexedDB (
VectorHistoryDB) للحصول على بيانات استرجاع محلية أكبر. - محتوى الصفحة المستخرجة: ذاكرة التخزين المؤقت للخلفية (
WebsiteContentManager) مرتبطًا بعنوان URL النشط.
كما هو موضح في القسم 1.2، فإن الاحتفاظ بسجل المحادثات في الخلفية يعطي حالة أساسية واحدة عبر تحديثات واجهة المستخدم. يؤدي هذا إلى الاحتفاظ بحالة قصيرة العمر في الذاكرة، وإعدادات دائمة في تخزين الامتدادات، وبيانات استرجاع كثيفة في قاعدة بيانات محلية.
5) ملاحظات البناء والتعبئة
لا تحتاج إلى إعداد بناء معقد، لكن MV3 يتطلب مخرجات يمكن التنبؤ بها لكل وقت تشغيل.
- بناء متعدد الدخول
vite.config.ts: - تأكد من أسماء/مسارات المخرجات المحاذاة للبيان (
sidebar.html,background.js,content.js). - احتفظ بالنص النصي للمحتوى كمخرج مستقل لتجنب مشكلات تحميل الأجزاء في وقت التشغيل.
الهدف بسيط: قطعة أثرية واحدة لكل نقطة دخول لمتصفح Chrome، في المكان المحدد public/manifest.json يتوقع.
الوجبات الجاهزة النهائية
إن اختيار البنية الذي يفتح هذا المشروع بأكمله هو الفصل الواضح بين الاهتمامات: تمتلك الخلفية التنسيق وتنفيذ النموذج، وتظل أسطح واجهة المستخدم رفيعة، وتتعامل البرامج النصية للمحتوى مع الوصول إلى الصفحة.
يستخدم هذا المشروع لوحة جانبية، ولكن نفس الأسلوب يعمل مع الإعدادات الأخرى:
- المساعد الأول المنبثق: الاستخدام
action.default_popupللتفاعلات السريعة، مع امتلاك الخلفية لحالة المحادثة وتنفيذ النموذج. - مساعد الطيار على اللوحة الجانبية: احتفظ بالمحادثات الطويلة الأمد في لوحة ثابتة بينما تتعامل الخلفية مع حلقات الأداة والتخزين المؤقت.
- وكلاء لكل علامة تبويب: احتفظ بحالة وكيل واحدة لكل علامة تبويب
tabIdفي الخلفية عندما يجب أن يكون لكل علامة تبويب سياقها الخاص. - واجهة المستخدم المختلطة (قائمة منبثقة + لوحة جانبية + صفحة خيارات): تتحدث جميع نقاط إدخال واجهة المستخدم مع نفس منسق الخلفية وتعيد استخدام نفس عقود الرسائل.
القاعدة العملية بسيطة: قرر أين تعيش الدولة (global, tabId، أو على نطاق الموقع)، احتفظ بهذه الحالة واستدلال النموذج في الخلفية (أساسًا كخدمات خلفية)، ودع أوقات تشغيل واجهة المستخدم/المحتوى تعمل كعملاء مركزين.