الكتابات/tutorial/2026/07
Tutorial19 يوليو 2026·28 دقيقة

دمج Qwen3.8-Max-Preview مع TypeScript: ابنِ تطبيقاتك بنموذج علي بابا العملاق 2.4 تريليون

تعلّم دمج Qwen3.8-Max-Preview — نموذج علي بابا الرائد بـ 2.4 تريليون معامل — في تطبيقات TypeScript وNext.js عبر واجهة DashScope المتوافقة مع OpenAI. يغطي البث المباشر واستدعاء الأدوات والمخرجات المهيكلة مع Zod ومسار SSE إنتاجي.

في 19 يوليو 2026، أعلن فريق Qwen في علي بابا عن Qwen3.8، وهو نموذج رائد بـ 2.4 تريليون معامل تضعه الشركة في المرتبة الثانية بعد Fable 5 من Anthropic — مع وعد بإطلاق الأوزان المفتوحة في إصدار قادم. ولست مضطراً لانتظار الأوزان: النسخة التجريبية Qwen3.8-Max-Preview متاحة الآن، ويمكن للمطورين استدعاؤها اليوم عبر واجهة برمجة التطبيقات المتوافقة مع OpenAI من Alibaba Cloud.

يرشدك هذا الدرس خطوة بخطوة لدمج Qwen3.8-Max-Preview في TypeScript: الإكمالات الأساسية، وبث الرموز (tokens)، واستدعاء الأدوات، والمخرجات المهيكلة بصيغة JSON مع التحقق عبر Zod، ومسار API إنتاجي في Next.js 15 يبث الاستجابات عبر Server-Sent Events.

إذا كنت قد اتبعت دليل دمج Kimi K3، فستجد بنية هذا الدرس مألوفة — وهذا مقصود. فكلا المزوّدين يوفران نقاط نهاية متوافقة مع OpenAI، وبالتالي فإن طبقة عميل جيدة التصميم تتيح لك تبديل النماذج الرائدة بتغيير سطرين فقط. وبنهاية الدرس ستملك هذه الطبقة بالضبط.

مختبران صينيان، أسبوع واحد، وعدان بنماذج مفتوحة الأوزان بتريليونات المعاملات. أعلنت Moonshot عن Kimi K3 بـ 2.8 تريليون معامل في 17 يوليو؛ وردّت علي بابا بـ Qwen3.8 بـ 2.4 تريليون معامل في 19 يوليو. الخلاصة العملية للمطورين: القدرات من الطراز الرائد تتحول إلى سلعة يمكن دمجها خلف طبقة تجريد رقيقة — وهذا الدرس يبني تلك الطبقة.

المتطلبات المسبقة

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

  • Node.js 20 أو أحدث (node --version)
  • TypeScript 5.4 أو أحدث
  • حساب Alibaba Cloud مع تفعيل خدمة Model Studio (المعروفة أيضاً باسم DashScope) ومفتاح API
  • إلمام أساسي بـ async/await واصطلاحات حزمة OpenAI

لا حاجة إلى وحدة معالجة رسوميات — جميع الأمثلة تستخدم واجهة النسخة التجريبية المستضافة. للحصول على مفتاح، افتح لوحة تحكم Alibaba Cloud Model Studio، وفعّل الخدمة (توجد حصة مجانية للحسابات الجديدة)، وأنشئ مفتاح API من صفحة إدارة المفاتيح.

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

بنهاية هذا الدرس سيكون لديك:

  1. عميل Qwen3.8 مُنمّط باستخدام حزمة OpenAI القياسية
  2. سكربت إكمال أساسي يفحص استهلاك الرموز
  3. دالة بث مباشر بمخرجات تدريجية في الطرفية
  4. حلقة وكيل لاستدعاء الأدوات يستدعي فيها Qwen3.8 دوالك المكتوبة بـ TypeScript
  5. مستخرج مخرجات مهيكلة يعيد JSON مُتحققاً منه ومُنمّطاً
  6. مسار API إنتاجي في Next.js 15 يغلّف النموذج كبثّ Server-Sent Events

فهم Qwen3.8-Max-Preview

قبل كتابة أي كود، إليك النموذج الذهني الذي يوجّه كل قرار في الدمج.

حالة النسخة التجريبية. Qwen3.8-Max-Preview هي بالضبط ما يقوله اسمها: معاينة لنموذج ما يزال "في تطور مستمر" بتعبير علي بابا. توقّع أن يتغير معرّف النموذج وحدود الاستخدام والسلوك قبل الإتاحة العامة. طبقة التجريد التي نبنيها في الخطوة 1 وُجدت تحديداً لحصر هذه التغييرات في ملف واحد.

الحجم والإنتاجية. بـ 2.4 تريليون معامل، هذا من أكبر النماذج التي شغّلتها علي بابا على الإطلاق. قاس المختبرون الأوائل إنتاجية تتأرجح بين 22 و55 رمزاً في الثانية — أبطأ بوضوح من نماذج أصغر مثل GLM-5.2. لذلك فإن البث المباشر ليس رفاهية؛ بل ضرورة لتجربة استخدام مقبولة. كل مثال موجّه للمستخدم في هذا الدرس يعتمد البث.

واجهة متوافقة مع OpenAI. تتيح علي بابا نماذج Qwen عبر الوضع المتوافق في DashScope. عنوان القاعدة الدولي هو https://dashscope-intl.aliyuncs.com/compatible-mode/v1 (استخدم النسخة بدون intl إذا كان حسابك مسجلاً في الصين القارية). حزمة openai القياسية من npm تعمل دون أي تعديل.

قنوات المستهلك مقابل قنوات المطورين. تتوفر النسخة التجريبية أيضاً ضمن اشتراك Token Plan ومنتجي Qoder وQoderWork من علي بابا. تلك قنوات استهلاكية — أما هذا الدرس فيستخدم واجهة المطورين التي تُحاسب بالرمز وتمنحك تحكماً برمجياً كاملاً.

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

الخطوة 1: إعداد المشروع

أنشئ المشروع وثبّت الاعتماديات:

mkdir qwen38-demo && cd qwen38-demo
npm init -y
npm install openai zod dotenv
npm install -D typescript @types/node tsx
npx tsc --init

عدّل tsconfig.json لاستهداف مخرجات ESM حديثة:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "strict": true,
    "outDir": "dist",
    "esModuleInterop": true
  }
}

أنشئ ملف .env:

DASHSCOPE_API_KEY=sk-your-key-here

ثم عميلاً مُنمّطاً في src/client.ts:

import OpenAI from "openai";
import "dotenv/config";
 
if (!process.env.DASHSCOPE_API_KEY) {
  throw new Error("DASHSCOPE_API_KEY is not set in the environment.");
}
 
export const qwen = new OpenAI({
  apiKey: process.env.DASHSCOPE_API_KEY,
  baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
});
 
// Preview model ID — expect this to change at GA. Keeping it here
// means the rest of the codebase never hardcodes it.
export const QWEN38 = "qwen3.8-max-preview" as const;

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

الخطوة 2: إكمال محادثة أساسي

أنشئ src/basic.ts:

import { qwen, QWEN38 } from "./client.js";
 
async function main() {
  const response = await qwen.chat.completions.create({
    model: QWEN38,
    messages: [
      {
        role: "system",
        content:
          "You are a concise technical assistant. Answer in short paragraphs.",
      },
      {
        role: "user",
        content:
          "Explain Mixture-of-Experts routing in large language models.",
      },
    ],
  });
 
  const choice = response.choices[0];
  console.log("Answer:\n", choice.message.content);
  console.log("\nFinish reason:", choice.finish_reason);
  console.log("Tokens:", response.usage);
}
 
main().catch(console.error);

شغّله:

npx tsx src/basic.ts

ستحصل على الإجابة إضافةً إلى كائن usage يحوي prompt_tokens وcompletion_tokens وtotal_tokens. سجّل هذه القيم في الإنتاج منذ اليوم الأول — فأسعار النسخة التجريبية قابلة للتغيير، وقياس الرموز لكل طلب هو وسيلتك لاكتشاف ارتفاع التكاليف مبكراً.

نصيحة: أبقِ المطالبات مقتضبة خلال فترة المعاينة. مع إنتاجية تتأرجح بين 22 و55 رمزاً في الثانية، قد تستغرق إجابة من 2,000 رمز حتى 90 ثانية. قيّد طول المخرجات في مطالبة النظام ("أجب في أقل من 150 كلمة") حتى ترفع علي بابا سعة التشغيل.

الخطوة 3: بث الاستجابات

نظراً لتذبذب إنتاجية النسخة التجريبية، البث المباشر أساسي. أنشئ src/stream.ts:

import { qwen, QWEN38 } from "./client.js";
 
export async function streamCompletion(prompt: string) {
  const stream = await qwen.chat.completions.create({
    model: QWEN38,
    messages: [{ role: "user", content: prompt }],
    stream: true,
    stream_options: { include_usage: true },
  });
 
  let fullText = "";
 
  for await (const chunk of stream) {
    const delta = chunk.choices[0]?.delta?.content;
    if (delta) {
      fullText += delta;
      process.stdout.write(delta);
    }
    // The final chunk carries usage when include_usage is set
    if (chunk.usage) {
      console.log("\n\nTokens:", chunk.usage);
    }
  }
 
  return fullText;
}
 
streamCompletion(
  "Write a haiku about a 2.4-trillion-parameter model waking up."
).catch(console.error);

خيار stream_options: { include_usage: true } يجعل DashScope يُلحق حساب الرموز بالقطعة الأخيرة، فتحافظ على قياس التكلفة حتى في وضع البث. عادةً ما تصل الرموز الأولى خلال ثوانٍ قليلة حتى عندما يكون التوليد الكلي بطيئاً — وهذا بالضبط سبب التحسّن الكبير في زمن الاستجابة المحسوس مع البث.

الخطوة 4: استدعاء الأدوات

يدعم Qwen3.8 استدعاء الدوال بأسلوب OpenAI. سنبني حلقة الوكيل الكلاسيكية: يقرر النموذج استدعاء أداة، فينفّذها كودك، ثم تعود النتيجة إليه ليقدّم إجابة نهائية مستندة إلى بيانات حقيقية.

أنشئ src/tools.ts:

import { qwen, QWEN38 } from "./client.js";
import type OpenAI from "openai";
 
// A fake exchange-rate lookup — swap for a real API in production.
function getExchangeRate(base: string, quote: string): string {
  const rates: Record<string, number> = {
    "USD/TND": 2.94,
    "EUR/TND": 3.42,
    "USD/SAR": 3.75,
  };
  const key = base + "/" + quote;
  const rate = rates[key];
  return rate
    ? JSON.stringify({ pair: key, rate })
    : JSON.stringify({ error: "Unknown pair " + key });
}
 
const tools: OpenAI.Chat.ChatCompletionTool[] = [
  {
    type: "function",
    function: {
      name: "get_exchange_rate",
      description: "Get the current exchange rate for a currency pair",
      parameters: {
        type: "object",
        properties: {
          base: { type: "string", description: "Base currency, e.g. USD" },
          quote: { type: "string", description: "Quote currency, e.g. TND" },
        },
        required: ["base", "quote"],
      },
    },
  },
];
 
async function main() {
  const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
    {
      role: "user",
      content: "How many Tunisian dinars is 250 US dollars right now?",
    },
  ];
 
  // First pass: the model decides whether to call the tool
  const first = await qwen.chat.completions.create({
    model: QWEN38,
    messages,
    tools,
  });
 
  const assistantMsg = first.choices[0].message;
  messages.push(assistantMsg);
 
  if (assistantMsg.tool_calls) {
    for (const call of assistantMsg.tool_calls) {
      const args = JSON.parse(call.function.arguments);
      const result = getExchangeRate(args.base, args.quote);
      messages.push({
        role: "tool",
        tool_call_id: call.id,
        content: result,
      });
    }
 
    // Second pass: the model answers using the tool result
    const second = await qwen.chat.completions.create({
      model: QWEN38,
      messages,
      tools,
    });
    console.log(second.choices[0].message.content);
  } else {
    console.log(assistantMsg.content);
  }
}
 
main().catch(console.error);

عند التشغيل، سيطلب Qwen3.8 أداة get_exchange_rate بالمعاملين base: "USD", quote: "TND"، ويستلم سعر الصرف، ثم يحسب التحويل في إجابته النهائية. للوكلاء متعددي الخطوات، غلّف عملية التمريرين داخل حلقة تواصل تنفيذ استدعاءات الأدوات حتى يعيد النموذج رسالة نصية عادية — وهو النمط نفسه الذي استخدمناه في درس وكيل ReAct.

الخطوة 5: مخرجات مهيكلة مع Zod

في خطوط المعالجة التي تغذي قواعد البيانات أو الواجهات، النص الحر عبء وخطر. اجمع بين وضع JSON والتحقق عبر Zod كي تفشل المخرجات المشوهة بصوت عالٍ عند الحدود. أنشئ src/extract.ts:

import { z } from "zod";
import { qwen, QWEN38 } from "./client.js";
 
const InvoiceSchema = z.object({
  vendor: z.string(),
  invoiceNumber: z.string(),
  currency: z.string().length(3),
  totalAmount: z.number(),
  lineItems: z.array(
    z.object({
      description: z.string(),
      quantity: z.number(),
      unitPrice: z.number(),
    })
  ),
});
 
type Invoice = z.infer<typeof InvoiceSchema>;
 
export async function extractInvoice(rawText: string): Promise<Invoice> {
  const response = await qwen.chat.completions.create({
    model: QWEN38,
    response_format: { type: "json_object" },
    messages: [
      {
        role: "system",
        content:
          "Extract invoice data as JSON with keys: vendor, invoiceNumber, " +
          "currency (ISO 4217), totalAmount (number), lineItems (array of " +
          "objects with description, quantity, unitPrice). Return JSON only.",
      },
      { role: "user", content: rawText },
    ],
  });
 
  const raw = response.choices[0].message.content ?? "{}";
  return InvoiceSchema.parse(JSON.parse(raw));
}
 
const sample = `
NOQTA SARL - Invoice 011-TN-2026
Consulting services: 20 hours at 45.00 USD each
Total due: 900.00 USD
`;
 
extractInvoice(sample).then((inv) =>
  console.log(JSON.stringify(inv, null, 2))
);

تعمل هنا طبقتا أمان معاً: خيار response_format بنوع json_object يقيّد النموذج بإصدار JSON صالح، بينما يضمن InvoiceSchema.parse صحة البنية أثناء التشغيل. إذا انحرف سلوك النموذج التجريبي — وهذا وارد في المعاينات — فستحصل على خطأ ZodError صريح بدلاً من بيانات فاسدة صامتة في قاعدة بياناتك.

الخطوة 6: مسار API إنتاجي في Next.js

أخيراً، لنغلّف كل شيء في مسار App Router في Next.js 15 يبث عبر Server-Sent Events. في مشروع Next.js لديك، أنشئ app/api/qwen/route.ts:

import OpenAI from "openai";
 
export const runtime = "nodejs";
export const maxDuration = 120; // preview throughput varies; allow headroom
 
const qwen = new OpenAI({
  apiKey: process.env.DASHSCOPE_API_KEY!,
  baseURL: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
});
 
export async function POST(req: Request) {
  const { messages } = await req.json();
 
  if (!Array.isArray(messages) || messages.length === 0) {
    return Response.json({ error: "messages array required" }, { status: 400 });
  }
 
  const stream = await qwen.chat.completions.create({
    model: "qwen3.8-max-preview",
    messages,
    stream: true,
  });
 
  const encoder = new TextEncoder();
 
  const readable = new ReadableStream({
    async start(controller) {
      try {
        for await (const chunk of stream) {
          const delta = chunk.choices[0]?.delta?.content;
          if (delta) {
            controller.enqueue(
              encoder.encode("data: " + JSON.stringify({ text: delta }) + "\n\n")
            );
          }
        }
        controller.enqueue(encoder.encode("data: [DONE]\n\n"));
      } catch (err) {
        controller.enqueue(
          encoder.encode(
            "data: " + JSON.stringify({ error: "stream failed" }) + "\n\n"
          )
        );
      } finally {
        controller.close();
      }
    },
  });
 
  return new Response(readable, {
    headers: {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache, no-transform",
      Connection: "keep-alive",
    },
  });
}

على جانب العميل، استهلك المسار عبر fetch مع قارئ بث، أو استخدم useChat من Vercel AI SDK موجّهاً إلى هذا المسار. قيمة maxDuration البالغة 120 ثانية مقصودة: في لحظات بطء النسخة التجريبية، تحتاج الإجابات الطويلة إلى هذا الهامش.

تحذير: لا تكشف مفتاح DashScope للمتصفح أبداً. يجب أن تمر جميع الاستدعاءات عبر مسار خادم كهذا. المفتاح المسرّب على حساب يُحاسب بالرمز فاتورة مفتوحة — أضف تحديد معدل الطلبات (راجع درس Arcjet) قبل النشر العلني.

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

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

  1. طبقة العميل: الأمر npx tsx src/basic.ts يعيد إجابة وكائن استهلاك رموز.
  2. البث: الأمر npx tsx src/stream.ts يطبع الرموز تدريجياً لا دفعة واحدة، وينتهي بإحصاء الرموز.
  3. استدعاء الأدوات: الأمر npx tsx src/tools.ts ينتج إجابة تتضمن نحو 735 ديناراً — دليل على استخدام نتيجة الأداة بدلاً من الهلوسة.
  4. المخرجات المهيكلة: الأمر npx tsx src/extract.ts يطبع كائن فاتورة مُتحققاً منه؛ شوّه النص التجريبي وتأكد من الحصول على ZodError وليس بيانات فاسدة.
  5. مسار API: الأمر curl -N -X POST http://localhost:3000/api/qwen -H "Content-Type: application/json" -d '{"messages":[{"role":"user","content":"Hello"}]}' يُظهر إطارات SSE تصل تباعاً.

استكشاف الأخطاء

خطأ 401 غير مصرح. المفتاح غير صالح أو خدمة Model Studio غير مفعّلة للحساب. أعد توليد المفتاح من لوحة التحكم وتأكد من حالة الخدمة.

النموذج غير موجود. معرّفات النماذج التجريبية قد تتغير بين الإصدارات. راجع قائمة النماذج في Model Studio للمعرّف الحالي وحدّث الثابت الوحيد في src/client.ts.

توليد بطيء أو متوقف. متوقع خلال المعاينة — يذكر المختبرون إنتاجية تتأرجح بين 22 و55 رمزاً في الثانية. ابثّ كل شيء، واضبط مهلات سخية، وقيّد طول المخرجات في المطالبات.

أخطاء حدود المعدل (429). حصص المعاينة متحفظة. أضف تراجعاً أسّياً بمكتبة مثل p-retry، وضع أحمال العمل غير التفاعلية في طابور.

عدم تطابق المنطقة. الحسابات المسجلة في الصين القارية يجب أن تستخدم عنوان القاعدة dashscope.aliyuncs.com بدلاً من نسخة -intl؛ والمفاتيح غير قابلة للتبادل بين المنطقتين.

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

  • قارن المطالبات نفسها مع Kimi K3 وGLM-5.2 — طبقة التجريد لديك تجعل اختبار A/B تغييراً من سطرين لكل مزوّد.
  • أضف المراقبة عبر Langfuse لتتبع التكلفة وزمن الاستجابة بين المزوّدين مع تطور النسخة التجريبية.
  • ترقّب إطلاق الأوزان المفتوحة: عند وصول الأوزان تتغير حسابات الاستضافة الذاتية كلياً، وقد تتبعها نسخ مكمّمة مجتمعية لمتغيرات أصغر.
  • اقرأ تغطية الإعلان لفهم السياق الاستراتيجي حول Token Plan ولعبة التوزيع لدى علي بابا.

الخلاصة

لقد دمجت أحدث نموذج رائد من علي بابا في TypeScript خلال ساعات من إطلاق نسخته التجريبية: عميل مُنمّط قابل للتبديل، وإكمالات بثّ مضبوطة لإنتاجية المعاينة المتذبذبة، وحلقة وكيل لاستدعاء الأدوات، واستخراج مهيكل متحقق منه عبر Zod، ومسار SSE إنتاجي في Next.js. والأهم أنك بنيت كل ذلك خلف طبقة تجريد تتعامل مع سؤال "أي نموذج رائد" كتفصيل إعدادات — وهي البنية العاقلة الوحيدة في شهر أطلق فيه مختبران نموذجين بتريليونات المعاملات في غضون 48 ساعة. عندما يصل Qwen3.8 إلى الإتاحة العامة بمعايير قياس منشورة وأوزان مفتوحة، سيكون دمجك جاهزاً بتغيير ثابت واحد.