الكتابات/tutorial/2026/07
Tutorial1 يوليو 2026·26 دقيقة

دمج Kimi K2 مع TypeScript: بناء تطبيقات ذكاء اصطناعي على النموذج مفتوح الأوزان من Moonshot

دليل عملي لبناء تطبيقات الذكاء الاصطناعي باستخدام Kimi K2 من Moonshot AI، النموذج مفتوح الأوزان بتريليون معامل. تعلّم إكمال المحادثة والبث المباشر واستدعاء الأدوات والمخرجات المهيكلة، وكيفية ربط Kimi داخل Claude Code وCline كمزوّد بديل جاهز.

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

يُعدّ Kimi K2 من Moonshot AI أحد أقوى الخيارات في هذه الفئة: نموذج خليط الخبراء (MoE) بتريليون معامل، بأوزان مفتوحة، ورخصة متساهلة، وواجهة برمجية تتحدث بلهجتي OpenAI وAnthropic معاً. في هذا الدرس ستبني مجموعة أدوات TypeScript صغيرة لكن متكاملة حول Kimi K2 — إكمال المحادثة، والبث المباشر، واستدعاء الدوال/الأدوات، ومخرجات JSON المهيكلة — ثم تربط النموذج نفسه داخل Claude Code وCline كمساعد برمجي جاهز.

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

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

  • Node.js 20+ مثبّت (node --version)
  • إلمام أساسي بـ TypeScript وasync/await
  • حساب Moonshot AI ومفتاح واجهة برمجية من لوحة التحكم
  • طرفية (terminal) ومحرّر أكواد (يُفضّل VS Code)
  • اختياري: Claude Code أو Cline إن أردت تجربة قسم وكيل البرمجة

لا تحتاج إلى وحدة معالجة رسومية (GPU). كل ما هنا يعمل عبر واجهة Moonshot المستضافة. سنشير إلى الاستضافة الذاتية للأوزان المفتوحة في النهاية للاكتمال.

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

في النهاية سيكون لديك:

  1. عميل Moonshot مُنمّط (typed) مبني على حزمة OpenAI SDK الرسمية (Kimi K2 متوافق مع OpenAI، فلا حاجة لأكواد HTTP مخصّصة).
  2. دالة محادثة بالبث تطبع الرموز فور وصولها.
  3. حلقة وكيل استدعاء أدوات تتيح لـ Kimi K2 استدعاء دوال TypeScript الخاصة بك.
  4. مساعد مخرجات مهيكلة يُرجع JSON مُتحقّقاً منه.
  5. وصفة إعداد لاستخدام Kimi K2 داخل Claude Code وCline.

فهم Kimi K2

نموذج ذهني سريع قبل الأكواد. Kimi K2 نموذج خليط خبراء (MoE) بنحو تريليون معامل إجمالي، لكن نحو 32 مليار فقط نشطة لكل رمز — وهذا ما يجعل نموذجاً بهذا الحجم اقتصادياً في التشغيل. يأتي بـأوزان مفتوحة تحت رخصة متساهلة (بأسلوب MIT المُعدّل)، فيمكنك استضافته ذاتياً، وهو متاح أيضاً عبر واجهة Moonshot المُدارة.

الحقائق المهمة للدمج:

  • واجهة متوافقة مع OpenAI. عنوان الأساس هو https://api.moonshot.ai/v1 (دولياً) أو https://api.moonshot.cn/v1 (الصين). توجّه حزمة OpenAI SDK القياسية نحوه وكل شيء يعمل.
  • نقطة طرفية متوافقة مع Anthropic. يوفّر Moonshot أيضاً https://api.moonshot.ai/anthropic، وهذا ما يتيح إدماج Kimi K2 داخل Claude Code.
  • سياق طويل. يتعامل Kimi K2 مع نوافذ سياق طويلة جداً (128 ألف رمز فأكثر في اللقطات الأحدث)، وهو مفيد لأعباء الوكلاء والبرمجة.
  • قوة في الأعمال الوكيلية. ضُبط K2 بشدّة على استخدام الأدوات والبرمجة متعددة الخطوات، فيتصرّف جيّداً في حلقات الوكلاء.

معرّفات النماذج هي لقطات مؤرّخة (مثل kimi-k2-0711-preview، ونسخ أسرع مثل kimi-k2-turbo-preview). ولأنّ هذه الأسماء تتقدّم بمرور الوقت، تحقّق دائماً من المعرّفات الدقيقة عبر نقطة النماذج الحيّة بدلاً من تثبيت واحد للأبد. سنجلب هذه القائمة برمجياً في الخطوة الثانية.

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

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

mkdir kimi-k2-toolkit && cd kimi-k2-toolkit
npm init -y
npm install openai zod
npm install -D typescript tsx @types/node
npx tsc --init

نثبّت حزمة openai الرسمية (واجهة Kimi متوافقة مع OpenAI)، إضافة إلى zod للتحقق أثناء التشغيل من المخرجات المهيكلة. تتيح tsx تشغيل ملفات TypeScript مباشرة.

خزّن مفتاحك في ملف بيئة — لا تضع الأسرار في الكود أبداً.

# .env
MOONSHOT_API_KEY=sk-your-key-here

أضف سكربت تحميل صغيراً إلى package.json ليلتقط tsx ملف البيئة:

{
  "type": "module",
  "scripts": {
    "dev": "node --env-file=.env --import tsx"
  }
}

الآن أنشئ العميل المشترك في src/client.ts:

// src/client.ts
import OpenAI from "openai";
 
if (!process.env.MOONSHOT_API_KEY) {
  throw new Error("MOONSHOT_API_KEY is not set. Add it to your .env file.");
}
 
// يُقدَّم Kimi K2 عبر واجهة متوافقة مع OpenAI، لذا نعيد استخدام
// حزمة OpenAI SDK الرسمية ونستبدل baseURL والمفتاح فقط.
export const kimi = new OpenAI({
  apiKey: process.env.MOONSHOT_API_KEY,
  baseURL: "https://api.moonshot.ai/v1",
});
 
// مكان مركزي لتغيير لقطة النموذج لكامل المشروع.
export const KIMI_MODEL = "kimi-k2-0711-preview";

هذا هو المُحوّل بالكامل. ولأنّ Kimi يتحدث بروتوكول OpenAI، فكل مساعد نبنيه من هنا قابل للنقل — إن بدّلت المزوّد لاحقاً، تغيّر سطرين.

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

لنتأكّد من الاتصال بطلب أساسي بدون بث. أنشئ src/chat.ts:

// src/chat.ts
import { kimi, KIMI_MODEL } from "./client.js";
 
async function main() {
  const response = await kimi.chat.completions.create({
    model: KIMI_MODEL,
    messages: [
      {
        role: "system",
        content: "You are a concise senior TypeScript engineer.",
      },
      {
        role: "user",
        content: "Explain what a Mixture-of-Experts model is in two sentences.",
      },
    ],
    temperature: 0.6,
  });
 
  console.log(response.choices[0].message.content);
  console.log("---");
  console.log("Tokens used:", response.usage?.total_tokens);
}
 
main().catch((err) => {
  console.error("Request failed:", err);
  process.exit(1);
});

شغّله:

npm run dev src/chat.ts

ملاحظة حول temperature: يوصي Moonshot بقيمة معتدلة (نحو 0.6) لمخرجات متوازنة. اخفضها نحو 0.2 للمهام الحتمية مثل توليد الأكواد واستخراج البيانات.

لجلب قائمة معرّفات النماذج المتاحة حالياً بدل التخمين، أضف هذا المساعد:

// src/models.ts
import { kimi } from "./client.js";
 
const models = await kimi.models.list();
for (const model of models.data) {
  console.log(model.id);
}

تشغيل npm run dev src/models.ts يطبع كل معرّف نموذج يستطيع مفتاحك الوصول إليه — الطريقة الموثوقة لتأكيد لقطة K2 المستهدَفة.

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

لواجهات المحادثة وأدوات سطر الأوامر، تريد أن تظهر الرموز أثناء توليدها. البث مع OpenAI SDK هو تغيير علامة واحدة إضافة إلى مُكرِّر غير متزامن. أنشئ src/stream.ts:

// src/stream.ts
import { kimi, KIMI_MODEL } from "./client.js";
 
export async function streamChat(prompt: string) {
  const stream = await kimi.chat.completions.create({
    model: KIMI_MODEL,
    messages: [{ role: "user", content: prompt }],
    stream: true,
    temperature: 0.6,
  });
 
  let full = "";
  for await (const chunk of stream) {
    const delta = chunk.choices[0]?.delta?.content ?? "";
    process.stdout.write(delta); // اطبع تدريجياً
    full += delta;
  }
  process.stdout.write("\n");
  return full;
}
 
await streamChat("Write a haiku about serverless GPUs.");

يحمل كل chunk قيمة delta صغيرة. نكتبها مباشرة إلى stdout لإحداث تأثير كتابة حيّ، ونجمّع أيضاً النص الكامل لإرجاعه. في مسار Next.js ستوجّه هذه القيم إلى ReadableStream وترسلها إلى المتصفّح — الحلقة نفسها تماماً.

الخطوة 4: استدعاء الأدوات (حلقة وكيلية)

هنا يتألّق Kimi K2. يتيح استدعاء الأدوات للنموذج أن يقرّر استدعاء دوالك، وينتظر النتيجة، ويواصل الاستدلال. سنمنحه أداة طقس وهمية وأداة تحويل وحدات.

أولاً عرّف الأدوات ومعالِجاتها في src/tools.ts:

// src/tools.ts
import type OpenAI from "openai";
 
export const toolDefs: OpenAI.Chat.Completions.ChatCompletionTool[] = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "Get the current temperature for a city in Celsius.",
      parameters: {
        type: "object",
        properties: {
          city: { type: "string", description: "City name, e.g. Tunis" },
        },
        required: ["city"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "celsius_to_fahrenheit",
      description: "Convert a Celsius temperature to Fahrenheit.",
      parameters: {
        type: "object",
        properties: {
          celsius: { type: "number" },
        },
        required: ["celsius"],
      },
    },
  },
];
 
// تنفيذات حقيقية. في الإنتاج تتصل هذه بواجهة أو قاعدة بيانات.
export const handlers: Record<string, (args: any) => Promise<string>> = {
  async get_weather({ city }: { city: string }) {
    const fakeDb: Record<string, number> = { Tunis: 31, Riyadh: 42, Paris: 22 };
    const temp = fakeDb[city] ?? 25;
    return JSON.stringify({ city, celsius: temp });
  },
  async celsius_to_fahrenheit({ celsius }: { celsius: number }) {
    return JSON.stringify({ fahrenheit: (celsius * 9) / 5 + 32 });
  },
};

الآن حلقة الوكيل في src/agent.ts. النمط: أرسل الرسائل مع الأدوات، وإن أعاد النموذج tool_calls، شغّلها، وألحِق النتائج، واستدعِ مجدداً حتى يُنتج النموذج إجابة نهائية.

// src/agent.ts
import { kimi, KIMI_MODEL } from "./client.js";
import { toolDefs, handlers } from "./tools.js";
import type OpenAI from "openai";
 
export async function runAgent(userPrompt: string) {
  const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
    { role: "system", content: "You are a helpful assistant. Use tools when needed." },
    { role: "user", content: userPrompt },
  ];
 
  // حُدّ عدد التكرارات كي لا تعمل حلقة مضطربة إلى ما لا نهاية.
  for (let step = 0; step < 6; step++) {
    const res = await kimi.chat.completions.create({
      model: KIMI_MODEL,
      messages,
      tools: toolDefs,
      temperature: 0.3,
    });
 
    const msg = res.choices[0].message;
    messages.push(msg);
 
    // غياب استدعاءات الأدوات يعني أن النموذج أنهى الاستدلال.
    if (!msg.tool_calls || msg.tool_calls.length === 0) {
      return msg.content ?? "";
    }
 
    // نفّذ كل أداة مطلوبة وأعِد النتائج.
    for (const call of msg.tool_calls) {
      const handler = handlers[call.function.name];
      if (!handler) continue;
      const args = JSON.parse(call.function.arguments);
      const result = await handler(args);
      messages.push({
        role: "tool",
        tool_call_id: call.id,
        content: result,
      });
    }
  }
 
  throw new Error("Agent exceeded max iterations without finishing.");
}
 
const answer = await runAgent(
  "What is the weather in Riyadh right now, and what is that in Fahrenheit?",
);
console.log(answer);

شغّله بـ npm run dev src/agent.ts. سيستدعي Kimi K2 الأداة get_weather للرياض، ثم celsius_to_fahrenheit على النتيجة، ثم يصوغ إجابة بلغة طبيعية. حدّ التكرار مهمّ: قيّد دائماً حلقات الوكلاء كي لا يدور نموذج مرتبك بلا توقّف ويستهلك الرموز.

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

في خطوط معالجة البيانات، غالباً تريد JSON صارماً بدل النثر. اطلب response_format: json_object، وصِف الشكل في رسالة النظام، وتحقّق من النتيجة بـ Zod كي يفشل الرد المشوّه بصوت عالٍ بدل إفساد الكود اللاحق.

// src/structured.ts
import { z } from "zod";
import { kimi, KIMI_MODEL } from "./client.js";
 
const Invoice = z.object({
  vendor: z.string(),
  total: z.number(),
  currency: z.string(),
  dueInDays: z.number(),
});
 
export async function extractInvoice(text: string) {
  const res = await kimi.chat.completions.create({
    model: KIMI_MODEL,
    response_format: { type: "json_object" },
    messages: [
      {
        role: "system",
        content:
          "Extract invoice fields. Respond ONLY with JSON matching: " +
          "{ vendor: string, total: number, currency: string, dueInDays: number }",
      },
      { role: "user", content: text },
    ],
    temperature: 0,
  });
 
  const raw = res.choices[0].message.content ?? "{}";
  // تحقّق: لا تثق أبداً بـ JSON النموذج بشكل أعمى.
  return Invoice.parse(JSON.parse(raw));
}
 
const invoice = await extractInvoice(
  "Invoice from Sfax Cloud Services for 1,240 TND, payable within 30 days.",
);
console.log(invoice);

أمران يجعلان هذا متيناً: temperature: 0 للحتمية، وInvoice.parse() التي ترمي خطأً إن أعاد Kimi حقلاً من نوع خاطئ. عالِج ذلك الخطأ صراحةً (إعادة محاولة، أو تسجيل، أو بديل) — لا تبتلعه.

الخطوة 6: استخدام Kimi K2 داخل Claude Code وCline

لأن Moonshot يوفّر نقطة طرفية متوافقة مع Anthropic، يمكنك توجيه وكلاء البرمجة المبنيين لـ Claude نحو Kimi K2 بمتغيّري بيئة — بلا تغيير في الكود.

بالنسبة لـ Claude Code، اضبط عنوان الأساس والمفتاح على نقطة Anthropic لدى Moonshot قبل التشغيل:

export ANTHROPIC_BASE_URL="https://api.moonshot.ai/anthropic"
export ANTHROPIC_AUTH_TOKEN="sk-your-moonshot-key"
claude

سيوجّه Claude Code الآن طلباته إلى Kimi K2 مع الإبقاء على سير عمل الطرفية نفسه. هذا بديل عملي حين يكون مزوّدك الأساسي محدوداً بالمعدّل، أو مقيّداً جغرافياً، أو ببساطة أغلى لمهمة معيّنة.

بالنسبة لـ Cline (وكيل VS Code)، افتح إعداداته، اختر مزوّد OpenAI Compatible، واملأ:

  • Base URL: https://api.moonshot.ai/v1
  • API Key: مفتاح Moonshot الخاص بك
  • Model ID: لقطة K2 من الخطوة 2 (مثلاً kimi-k2-0711-preview)

يتحدث Cline لهجة OpenAI، فنقطة /v1 القياسية هي الصحيحة هنا. احفظ، وسيقود Cline نموذج Kimi K2 للتعديلات واستدعاءات الأدوات وأوامر الطرفية تماماً كأي نموذج آخر. هذا يعكس أسلوب الإدماج الجاهز نفسه الذي ربما رأيته مع نماذج مفتوحة الأوزان أخرى — تغيير إعداد، لا إعادة كتابة.

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

تحقّق من كل جزء على حدة:

  1. الاتصال: npm run dev src/models.ts يجب أن يطبع قائمة معرّفات نماذج. خطأ مصادقة هنا يعني أن المفتاح أو عنوان الأساس خاطئ.
  2. المحادثة: npm run dev src/chat.ts يُرجع شرحاً من جملتين وعدّ الرموز.
  3. البث: npm run dev src/stream.ts يطبع النص تدريجياً لا دفعة واحدة.
  4. الوكيل: npm run dev src/agent.ts يجب أن يُظهر تحويلاً إلى فهرنهايت مشتقاً من استدعاء أداة، لا رقماً مُهلوَساً.
  5. المهيكل: npm run dev src/structured.ts يطبع كائناً مُنمّطاً؛ غذِّه بنصّ مشوّه لتأكيد أن Zod ترمي خطأً.

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

  • 401 غير مصرّح: المفتاح مفقود أو استهدفت المنطقة الخاطئة. المفاتيح الدولية تستخدم api.moonshot.ai؛ وحسابات البرّ الرئيسي تستخدم api.moonshot.cn. وهما غير متبادلين.
  • 404 النموذج غير موجود: أُحيلت اللقطة للتقاعد. شغّل قائمة النماذج من الخطوة 2 وحدّث KIMI_MODEL.
  • tool_calls فارغة رغم توقّعك لاستدعاء: اجعل حقول description للأدوات أكثر تحديداً، واخفض درجة الحرارة. الأوصاف الغامضة تجعل النموذج يخمّن.
  • أخطاء تحقّق Zod: شدّد رسالة النظام بقائمة حقول صريحة، وأبقِ temperature: 0 للاستخراج. فكّر في إعادة محاولة تلقائية واحدة قبل الفشل.
  • بطء الرمز الأول: السياقات الطويلة تزيد الكمون. جرّب لقطة turbo للواجهات الحسّاسة للكمون.

الاستضافة الذاتية للأوزان المفتوحة (اختياري)

لأن Kimi K2 يأتي بأوزان مفتوحة، فأنت لست محبوساً أبداً في الواجهة المستضافة. الفرق ذات متطلبات إقامة البيانات — وهي وثيقة الصلة في ظل INPDP التونسي وPDPL السعودي — يمكنها تشغيل الأوزان داخل حدود ثقتها الخاصة باستخدام محرّك استدلال مثل vLLM أو SGLang، وكلاهما يعرض خادماً متوافقاً مع OpenAI. جمال الكود أعلاه أن الاستضافة الذاتية تغيّر شيئاً واحداً بالضبط: عنوان baseURL في src/client.ts يشير إلى نقطتك الخاصة بدل Moonshot. وكل مساعد — المحادثة، والبث، والأدوات، والمخرجات المهيكلة — يبقى يعمل دون تغيير. هذه القابلية للنقل هي السبب الكامل للبناء على نموذج مفتوح الأوزان متوافق مع OpenAI من الأساس.

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

  • غلّف streamChat داخل معالج مسار في Next.js ووجّه القيم إلى المتصفّح عبر ReadableStream.
  • أضف ذاكرة دائمة لحلقة الوكيل كي يبقى سياق الأدوار المتعدّدة بين الطلبات.
  • ادمج استدعاء الأدوات مع المخرجات المهيكلة لبناء وكيل استخراج بيانات مُنمّط.
  • قارن Kimi K2 مع نموذج آخر مفتوح الأوزان في منظومة تقييمك الخاصة قبل اعتماد أحدهما مزوّداً بديلاً.

الخاتمة

بنيت دمجاً كاملاً بلغة TypeScript لـ Kimi K2 — اتصال، ومحادثة، وبث، وحلقة أدوات وكيلية، ومخرجات مهيكلة مُتحقّقاً منها — بلا شيء سوى حزمة OpenAI SDK القياسية إضافة إلى Zod. وتعلّمت أيضاً إدماج النموذج نفسه داخل Claude Code وCline عبر نقاط Moonshot المتوافقة مع Anthropic وOpenAI، وكيف يستهدف الكود نفسه أوزاناً مفتوحة مُستضافة ذاتياً حين يفرض الامتثال ذلك.

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