الكتابات/tutorial/2026/07
Tutorial4 يوليو 2026·24 دقيقة

الذكاء الاصطناعي المدمج في Chrome: ابنِ ميزات ذكاء اصطناعي محلية باستخدام Prompt API و Gemini Nano

تعلّم كيفية استخدام Prompt API المدمجة في Chrome لتشغيل Gemini Nano مباشرة داخل المتصفح. يغطي هذا الدرس العملي اكتشاف التوفر، وإدارة الجلسات، والبث المباشر للردود، والمخرجات المنظمة بصيغة JSON، والأوامر متعددة الوسائط، واستراتيجية احتياطية هجينة مع السحابة.

لسنوات، كان إضافة الذكاء الاصطناعي إلى تطبيق ويب يعني شيئًا واحدًا: إرسال بيانات المستخدم إلى واجهة برمجة سحابية، والدفع مقابل كل رمز (token)، والأمل في أن يكون زمن الاستجابة مقبولًا. الذكاء الاصطناعي المدمج في Chrome يغيّر هذه المعادلة. مع Gemini Nano المُضمَّن داخل المتصفح نفسه، تتيح لك Prompt API تشغيل استدلال نموذج لغوي بالكامل على جهاز المستخدم — بدون مفتاح API، وبدون تكلفة لكل طلب، وبدون خروج أي بيانات من الجهاز.

في هذا الدرس، ستبني مجموعة كاملة من ميزات الذكاء الاصطناعي المحلي: اكتشاف التوفر، وإدارة الجلسات، والردود المتدفقة، والمخرجات المنظمة بصيغة JSON، ونمطًا هجينًا جاهزًا للإنتاج يتحول تلقائيًا إلى نموذج سحابي عندما لا يكون النموذج المحلي متاحًا. كل شيء يعمل بجافاسكريبت خالص، لذا يمكنك دمجه في أي إطار عمل — Next.js أو Nuxt أو SvelteKit أو HTML عادي.

المتطلبات الأساسية

قبل البدء، تأكد من توفر ما يلي:

  • Chrome 138 أو أحدث على سطح المكتب (Windows 10/11 أو macOS 13+ أو Linux أو ChromeOS). تخرّجت Prompt API من مرحلة التجربة الأصلية (origin trial) وأصبحت متاحة في نسخة Chrome المستقرة للإضافات، وتدريجيًا لمواقع الويب.
  • ما لا يقل عن 22 جيجابايت من المساحة الحرة على القرص الذي يحتوي ملف تعريف Chrome — يتم تنزيل Gemini Nano مرة واحدة ويُشارك بين جميع المواقع.
  • بطاقة رسومية بذاكرة تتجاوز 4 جيجابايت من VRAM (البطاقات المدمجة في الأجهزة الحديثة تفي بالغرض).
  • معرفة أساسية بالوعود (Promises) في جافاسكريبت و async/await.
  • Node.js 20 أو أحدث إذا أردت تشغيل مشروع العرض التوضيحي محليًا بخادم تطوير.

تعمل Prompt API حاليًا على Chrome لسطح المكتب فقط. دعم الأجهزة المحمولة موجود على خارطة الطريق، وهذا بالضبط سبب أهمية نمط الاحتياط الهجين في الخطوة 7 للتطبيقات الإنتاجية.

ما الذي ستبنيه

أداة "ملاحظات ذكية" صغيرة تعمل بالكامل داخل المتصفح:

  1. تكتشف ما إذا كان Gemini Nano متاحًا على جهاز الزائر
  2. تنزّل النموذج مع شريط تقدم عند الاستخدام الأول
  3. تلخّص الملاحظات وتعيد صياغتها مع مخرجات متدفقة
  4. تستخرج مهامًا منظمة من نص حر بصيغة JSON مُتحقق منها
  5. تتحول إلى نموذج سحابي على الخادم عندما لا يتوفر الذكاء الاصطناعي المحلي

الخطوة 1: فهم بنية الذكاء الاصطناعي المدمج

يوفر Chrome عدة واجهات برمجة متخصصة فوق Gemini Nano — Summarizer و Translator و Language Detector و Writer و Rewriter — بالإضافة إلى Prompt API ذات الأغراض العامة. الواجهات المتخصصة أبسط وأكثر تحسينًا، لكن Prompt API هي التي تمنحك التحكم الحواري الكامل، وأوامر النظام المخصصة، والمخرجات المنظمة. وهي محور تركيزنا هنا.

الواجهة العامة الأساسية هي LanguageModel. دورة الحياة هي نفسها دائمًا:

// 1. التحقق من التوفر
const availability = await LanguageModel.availability();
// "unavailable" | "downloadable" | "downloading" | "available"
 
// 2. إنشاء جلسة (يبدأ التنزيل عند الحاجة)
const session = await LanguageModel.create();
 
// 3. إرسال أمر
const result = await session.prompt("اشرح التخزين المؤقت في HTTP بجملة واحدة.");
console.log(result);
 
// 4. تحرير الموارد عند الانتهاء
session.destroy();

هذا هو النموذج الذهني بأكمله. كل ما تبقى في هذا الدرس هو تحسين لهذه الاستدعاءات الأربعة.

الخطوة 2: اكتشاف الميزة بالطريقة الصحيحة

لا تفترض أبدًا وجود الواجهة. المتصفحات الأقدم و Chrome على الجوال و Firefox و Safari ستُطلق جميعها خطأ إذا لمست LanguageModel مباشرة. غلّف الاكتشاف في دالة مساعدة:

// lib/ai-detect.js
export async function detectOnDeviceAI() {
  // قد لا تكون الواجهة موجودة أصلًا
  if (!("LanguageModel" in self)) {
    return { supported: false, reason: "api-missing" };
  }
 
  const availability = await LanguageModel.availability();
 
  switch (availability) {
    case "available":
      return { supported: true, ready: true };
    case "downloadable":
    case "downloading":
      return { supported: true, ready: false, needsDownload: true };
    default:
      // "unavailable": العتاد أو السياسة تمنع النموذج
      return { supported: false, reason: "hardware-or-policy" };
  }
}

التمييز بين الحالات الإيجابية الثلاث مهم لتجربة المستخدم:

  • available — النموذج موجود على القرص، والجلسات تُنشأ فورًا
  • downloadable — العتاد مدعوم، لكن المستخدم لم يبدأ التنزيل بعد
  • downloading — علامة تبويب أو موقع آخر بدأ بالفعل جلب النموذج

استدعاء LanguageModel.create() عندما تكون الحالة downloadable يبدأ تنزيلًا بحجم عدة جيجابايتات. احصل دائمًا على إجراء صريح من المستخدم (نقرة زر) قبل تشغيله، واعرض التقدم. التنزيل الصامت لجيجابايتات عند تحميل الصفحة هو أسرع طريقة لإغضاب المستخدمين على الاتصالات المحدودة.

الخطوة 3: إنشاء جلسة مع تقدم التنزيل

يقبل استدعاء create() دالة monitor تكشف أحداث تقدم التنزيل. اربطها بشريط تقدم:

// lib/ai-session.js
export async function createSession(onProgress) {
  const session = await LanguageModel.create({
    monitor(m) {
      m.addEventListener("downloadprogress", (e) => {
        // e.loaded كسر بين 0 و 1
        onProgress?.(Math.round(e.loaded * 100));
      });
    },
    initialPrompts: [
      {
        role: "system",
        content:
          "أنت مساعد كتابة موجز مدمج في تطبيق ملاحظات. " +
          "أجب بنص عادي بدون عناوين markdown.",
      },
    ],
  });
 
  return session;
}

تفصيلان يستحقان الانتباه:

أوامر النظام توضع في initialPrompts. المدخل الأول بدور system يحدد السلوك الدائم للجلسة بأكملها. يمكنك أيضًا إضافة أمثلة few-shot بالتناوب بين دوري user و assistant بعده — يعاملها النموذج كأدوار محادثة سابقة.

الجلسات ذات حالة. كل استدعاء لـ prompt() يُضاف إلى نافذة سياق الجلسة. بالنسبة لأداة الملاحظات هذا ما تريده؛ أما للعمليات عديمة الحالة، فاستنسخ جلسة جديدة من جلسة أساسية بدلًا من ذلك (الخطوة 6 تغطي هذا).

الخطوة 4: الأوامر مع المخرجات المتدفقة

قد يستغرق الرد الكامل عدة ثوانٍ على العتاد المتواضع. البث المباشر هو الفرق بين تطبيق يبدو معطلًا وآخر يبدو حيًا. استخدم promptStreaming() التي تعيد ReadableStream:

// lib/ai-actions.js
export async function summarizeStreaming(session, noteText, onChunk) {
  const stream = session.promptStreaming(
    `لخّص الملاحظة التالية في جملتين كحد أقصى:\n\n${noteText}`
  );
 
  let fullText = "";
  for await (const chunk of stream) {
    fullText += chunk;
    onChunk(fullText); // تحديث الواجهة تدريجيًا
  }
  return fullText;
}

ربطها بالـ DOM يتم بسطر واحد في أي إطار عمل. مثال بجافاسكريبت خالص:

const output = document.querySelector("#summary");
summarizeBtn.addEventListener("click", async () => {
  output.textContent = "";
  await summarizeStreaming(session, noteArea.value, (text) => {
    output.textContent = text;
  });
});

إذا غادر المستخدم الصفحة أو نقر على إلغاء، أوقف التدفق باستخدام AbortController — مرّر الإشارة في كائن الخيارات:

const controller = new AbortController();
const stream = session.promptStreaming(promptText, {
  signal: controller.signal,
});
// لاحقًا، عند الإلغاء:
controller.abort();

الخطوة 5: مخرجات JSON منظمة بمخطط

الميزة الحاسمة للتطبيقات الحقيقية: تقبل Prompt API مخطط JSON Schema كقيد على الرد، ومخرجات النموذج مضمونة المطابقة. هكذا تستخرج مهام العمل من ملاحظات حرة بدون ألاعيب التعبيرات النمطية:

const todoSchema = {
  type: "object",
  properties: {
    todos: {
      type: "array",
      items: {
        type: "object",
        properties: {
          task: { type: "string" },
          priority: { type: "string", enum: ["high", "medium", "low"] },
          dueMention: { type: "string" },
        },
        required: ["task", "priority"],
      },
    },
  },
  required: ["todos"],
};
 
export async function extractTodos(session, noteText) {
  const raw = await session.prompt(
    `استخرج بنود العمل من هذه الملاحظة:\n\n${noteText}`,
    { responseConstraint: todoSchema }
  );
  return JSON.parse(raw).todos;
}

لأن القيد يُفرض على مستوى أخذ العينات، لن يفشل JSON.parse على مخرجات مشوهة — تحسّن هائل في الموثوقية مقارنة بأسلوب "من فضلك أجب بصيغة JSON"، وهو أمر لا تزال حتى كثير من واجهات السحابة تخطئ فيه تحت الضغط.

حافظ على مخططات ضحلة. المخططات شديدة التداخل تبطئ أخذ العينات المقيد بشكل ملحوظ على الجهاز. مستويان من التداخل، كما في المثال أعلاه، هما النقطة المثالية.

الخطوة 6: إدارة السياق باستنساخ الجلسات والحصص

نافذة سياق Gemini Nano صغيرة مقارنة بنماذج السحابة الرائدة. يكشف كائن الجلسة حسابات الاستخدام لتتفاعل قبل الاصطدام بالحد:

console.log(session.inputUsage);  // الرموز المستهلكة حتى الآن
console.log(session.inputQuota);  // إجمالي الرموز المتاحة
 
const remaining = session.inputQuota - session.inputUsage;
if (remaining < 1000) {
  // السياق شبه ممتلئ — ابدأ من جديد
}

للعمليات عديمة الحالة (لخّص هذا، أعد صياغة ذاك)، تجنب تلويث جلسة واحدة طويلة الأمد. أنشئ جلسة أساسية مرة واحدة — دافعًا تكلفة أمر النظام مرة واحدة فقط — واستنسخها لكل عملية:

const baseSession = await createSession();
 
async function runIsolated(promptText) {
  const clone = await baseSession.clone();
  try {
    return await clone.prompt(promptText);
  } finally {
    clone.destroy();
  }
}

تنسخ clone() الأوامر الأولية لكن ليس المحادثة المتراكمة، مانحة كل عملية سياقًا نظيفًا ورخيصًا. دمّر النسخ دائمًا بـ destroy() — الجلسات المحلية تحتجز ذاكرة GPU.

الخطوة 7: نمط الاحتياط الهجين

واقع الإنتاج: شريحة معتبرة من مستخدميك ستكون على الجوال أو على Firefox أو على أجهزة لا تجتاز عتبة العتاد. البنية الصحيحة تعامل الذكاء الاصطناعي المحلي كتحسين تدريجي فوق مسار على الخادم.

// lib/smart-ai.js
import { detectOnDeviceAI } from "./ai-detect.js";
 
let session = null;
 
export async function smartPrompt(promptText) {
  const status = await detectOnDeviceAI();
 
  if (status.supported && status.ready) {
    session ??= await LanguageModel.create();
    return {
      source: "on-device",
      text: await session.prompt(promptText),
    };
  }
 
  // الاحتياط: مسار على الخادم يمرر إلى نموذج سحابي
  const res = await fetch("/api/ai", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ prompt: promptText }),
  });
  const data = await res.json();
  return { source: "cloud", text: data.text };
}

ومعالج المسار المقابل في Next.js باستخدام واجهة Claude كمستوى سحابي:

// app/api/ai/route.ts
import Anthropic from "@anthropic-ai/sdk";
 
const anthropic = new Anthropic(); // يقرأ ANTHROPIC_API_KEY من متغيرات البيئة
 
export async function POST(req: Request) {
  const { prompt } = await req.json();
 
  const message = await anthropic.messages.create({
    model: "claude-haiku-4-5-20251001",
    max_tokens: 512,
    messages: [{ role: "user", content: prompt }],
  });
 
  const text = message.content
    .filter((block) => block.type === "text")
    .map((block) => block.text)
    .join("");
 
  return Response.json({ text });
}

يمنحك هذا النمط أفضل ما في العالمين: استدلال مجاني وخاص ومنخفض الكمون للعملاء القادرين، وتغطية شاملة للبقية. اعرض حقل source في واجهتك — شارة صغيرة "تمت المعالجة على جهازك" هي إشارة ثقة حقيقية، خاصة للجمهور الحساس للخصوصية.

الخطوة 8: الأوامر متعددة الوسائط (الصور والصوت)

توسّع إصدارات Chrome الحديثة Prompt API لقبول مدخلات الصور والصوت. صرّح بأنواع المدخلات المتوقعة عند إنشاء الجلسة، ثم مرّر أجزاء المحتوى:

const session = await LanguageModel.create({
  expectedInputs: [{ type: "image" }],
});
 
const fileInput = document.querySelector("#screenshot");
const file = fileInput.files[0];
 
const description = await session.prompt([
  {
    role: "user",
    content: [
      { type: "text", value: "صِف لقطة الشاشة هذه لاستخدامها كنص بديل." },
      { type: "image", value: file },
    ],
  },
]);

فهم الصور على الجهاز يفتح ميزات كانت في السابق غير قابلة للتصور لأسباب تتعلق بالخصوصية — توليد نصوص بديلة تلقائيًا لصور المستخدمين، وتصنيف لقطات الشاشة، وقراءة الإيصالات — دون خروج بايت واحد من الجهاز.

اختبار التنفيذ

تحقق من كل طبقة على حدة:

  1. حالات التوفر. في chrome://flags يمكن تفعيل أو تعطيل النموذج المحلي؛ اختبر واجهتك مقابل unavailable و downloadable و available. اختبر أيضًا في Firefox لتتأكد من تفعّل مسار الاحتياط.
  2. تجربة التنزيل. امسح النموذج عبر chrome://on-device-internals، وأعد التحميل، وتأكد من ظهور شريط التقدم أثناء الجلب.
  3. المخرجات المنظمة. غذِّ مستخرج المهام بملاحظات فوضوية عمدًا ("ربما شراء حليب؟؟ عاجل: الاتصال بسامي الجمعة") وتحقق من مطابقة المصفوفة المحللة للمخطط.
  4. سلوك الحصص. كرر أمرًا طويلًا حتى يقترب inputUsage من inputQuota وتأكد من تفعّل منطق إعادة تعيين الجلسة لديك.

استكشاف الأخطاء وإصلاحها

تعيد LanguageModel.availability() القيمة unavailable على عتاد قادر. راجع chrome://on-device-internals لمعرفة السبب الحقيقي — غالبًا مساحة قرص غير كافية (متطلب 22 جيجابايت يُفرض بهامش أمان) أو سياسة مؤسسية تعطّل ميزات الذكاء الاصطناعي التوليدي.

أول create() يعلق إلى الأبد. لا يتقدم التنزيل إلا عندما يعتبر Chrome الشبكة غير محدودة. على نقاط الاتصال المحمولة، قد يؤجله Chrome بصمت. اعرض تلميح "في انتظار Wi-Fi" إذا بقيت availability() على downloading.

جودة المخرجات تبدو أقل من نماذج السحابة. هي كذلك فعلًا — Gemini Nano نموذج صغير. أبقِ المهام ضيقة (تلخيص، إعادة صياغة، استخراج، تصنيف) وقدّم له أمثلة few-shot عبر initialPrompts. احتفظ بالتوليد المفتوح لمستواك السحابي.

خطأ QuotaExceededError عند الأمر. أمر واحد تجاوز نافذة السياق. قسّم المدخلات الطويلة، أو وجّه الطلبات الضخمة مباشرة إلى الاحتياط السحابي.

الخطوات التالية

الخلاصة

تحوّل Prompt API المدمجة في Chrome المتصفح إلى بيئة تشغيل للذكاء الاصطناعي. تعلمت كيفية اكتشاف التوفر دون كسر المتصفحات غير المدعومة، وتنزيل Gemini Nano بتجربة مستخدم محترمة، وبث الردود، وفرض مخرجات JSON منظمة بالمخططات، وإدارة حصص السياق باستنساخ الجلسات، والتعامل مع الصور، والأهم — تغليف كل ذلك في نمط هجين يتراجع بأناقة إلى نموذج سحابي. الذكاء الاصطناعي المحلي ليس بديلًا عن الاستدلال السحابي؛ إنه مستوى جديد مجاني وخاص في بنيتك التقنية. التطبيقات التي تبدو ساحرة في 2026 هي تلك التي تستخدم كليهما.