معظم دروس الذكاء الاصطناعي في 2026 تبني وكلاء في الخلفية — Claude و LangGraph و Mastra و Pydantic AI. مفيد، لكنها كلها تتوقف عند نقطة دردشة. المشكلة التالية أصعب: كيف تُدمج مساعد ذكاء اصطناعي داخل منتجك بحيث يقرأ نفس الحالة التي يراها المستخدم ويستدعي نفس الإجراءات التي تستدعيها أزرارك؟
هذا بالضبط ما تفعله CopilotKit. هي إطار React مفتوح المصدر يضع مساعداً بجوار واجهتك، ويمنحه رؤية مكتوبة (typed) لحالة تطبيقك، ويسمح له باستدعاء دوال كتبتها بنفسك. بنهاية هذا الدرس سيكون لديك تطبيق Next.js 15 يعمل، حيث يستطيع مساعد جانبي قراءة قائمة مهام، وإضافة مهام، وتمييزها كمنجزة، وعرض بطاقات واجهة غنية داخل الدردشة — كل ذلك دون مغادرة قاعدتك البرمجية.
لماذا CopilotKit بدلاً من واجهة دردشة عادية؟ الدردشة العادية تجيب على الأسئلة. أما المساعد فـيشغّل التطبيق. يمنحك CopilotKit خطافات React مكتوبة (useCopilotReadable و useCopilotAction) ليحصل النموذج اللغوي على سياق منظّم وسطح أدوات منظّم — وهذا هو الفرق بين "يبدو ذكاءً اصطناعياً" و"يتصرّف كزميل عمل".
ما ستتعلّمه
بنهاية هذا الدرس ستكون قادراً على:
- تثبيت حزم CopilotKit وربط مزوّد
<CopilotKit>في مشروع Next.js 15 App Router - بناء مسار
/api/copilotkitللتشغيل باستخدام محوّل OpenAI - مشاركة حالة React مع النموذج اللغوي عبر
useCopilotReadable - تعريف إجراءات أمامية مكتوبة عبر
useCopilotActionليستطيع المساعد تعديل حالة الواجهة - عرض واجهة توليدية داخل الدردشة (بطاقات مخصّصة، لا مجرّد نص)
- إطلاق
CopilotSidebarوتنسيقه ليطابق هويّتك البصرية
المتطلبات الأساسية
قبل البدء تأكّد من توفّر:
- Node.js 20 أو أحدث (Node 18 لن يعمل — انظر قسم استكشاف الأخطاء)
- إلمام بـ Next.js 15 (App Router والفرق بين Server Components و Client Components)
- مفتاح OpenAI API (أو Anthropic أو Groq أو Azure — يدعم CopilotKit عدّة محوّلات)
- معرفة أساسية بـ خطافات React و TypeScript
ما ستبنيه
سنبني مدير مهام ذكي — صفحة Next.js 15 فيها قائمة مهام وشريط CopilotKit جانبي. يستطيع المستخدم محادثة المساعد لإضافة مهام، أو تمييز مهام كمنجزة، أو التصفية حسب الحالة، أو طلب ملخّص لما تبقّى من اليوم. سيعرض المساعد بطاقات المهام مباشرة داخل الدردشة بدلاً من نص خام.
الخطوة 1: إعداد المشروع
أنشئ مشروع Next.js 15 جديد بـ TypeScript و Tailwind:
npx create-next-app@latest copilot-tasks \
--typescript --tailwind --app --eslint --src-dir
cd copilot-tasksثم ثبّت حزم CopilotKit الثلاث المطلوبة. الواجهة الأمامية تحتاج react-core و react-ui، أما مسار API فيحتاج runtime:
npm install @copilotkit/react-core @copilotkit/react-ui @copilotkit/runtime
npm install openaiأضف مفتاح OpenAI إلى .env.local:
OPENAI_API_KEY=sk-proj-...نصيحة: لا تُودِع ملف .env.local أبداً في Git. تأكّد أنّ .gitignore يستثنيه (يضيفه Next.js افتراضياً).
الخطوة 2: بناء مسار API للتشغيل
يأتي CopilotKit مع وقت تشغيل (runtime) يربط تطبيق React بمزوّد النموذج اللغوي. في App Router يكون ذلك بمعالج طلبات واحد في app/api/copilotkit/route.ts:
// src/app/api/copilotkit/route.ts
import {
CopilotRuntime,
OpenAIAdapter,
copilotRuntimeNextJSAppRouterEndpoint,
} from "@copilotkit/runtime";
import OpenAI from "openai";
import { NextRequest } from "next/server";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const serviceAdapter = new OpenAIAdapter({ openai });
const runtime = new CopilotRuntime();
export const POST = async (req: NextRequest) => {
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
runtime,
serviceAdapter,
endpoint: "/api/copilotkit",
});
return handleRequest(req);
};ثلاث ملاحظات مهمّة:
OpenAIAdapterليس الوحيد — يمكنك استبداله بـAnthropicAdapterأوGroqAdapterأوLangChainAdapterدون تعديل أي ملف آخر.CopilotRuntimeهو العقل المركزي. سنمرّر له لاحقاً إجراءات تعمل من جهة الخادم.- تُرجع
copilotRuntimeNextJSAppRouterEndpointدالةhandleRequestتتحدّث بروتوكول App Router مباشرة — دون أيّ ربط بثّ يدوي.
الخطوة 3: تركيب مزوّد CopilotKit
يجب أن يلفّ مزوّد <CopilotKit> أيّ شجرة تستخدم خطافات المساعد. ضعه داخل التخطيط الجذري، لكن علّم الغلاف كمكوّن عميل:
// src/components/CopilotProvider.tsx
"use client";
import { CopilotKit } from "@copilotkit/react-core";
import "@copilotkit/react-ui/styles.css";
export function CopilotProvider({ children }: { children: React.ReactNode }) {
return (
<CopilotKit runtimeUrl="/api/copilotkit">
{children}
</CopilotKit>
);
}ثم ضمّنه في التخطيط الجذري (الذي يبقى Server Component):
// src/app/layout.tsx
import { CopilotProvider } from "@/components/CopilotProvider";
import "./globals.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ar" dir="rtl">
<body>
<CopilotProvider>{children}</CopilotProvider>
</body>
</html>
);
}runtimeUrl هو المسار العام للمسار الذي بنيناه في الخطوة 2. أيّ شيء داخل <CopilotProvider> يصبح قادراً على استخدام useCopilotReadable و useCopilotAction.
الخطوة 4: بناء واجهة قائمة المهام
قبل إضافة أيّ ذكاء اصطناعي، نبني قائمة مهام عادية تعتمد على حالة React. سيتصل المساعد بهذه الحالة بالضبط — دون إعادة كتابة.
// src/app/page.tsx
"use client";
import { useState } from "react";
import { CopilotSidebar } from "@copilotkit/react-ui";
type Task = {
id: string;
title: string;
status: "todo" | "done";
};
export default function HomePage() {
const [tasks, setTasks] = useState<Task[]>([
{ id: "1", title: "كتابة مسوّدة الدرس", status: "todo" },
{ id: "2", title: "مراجعة طلب الدمج رقم 42", status: "todo" },
]);
return (
<main className="mx-auto max-w-3xl p-8">
<h1 className="mb-6 text-3xl font-semibold">المهام الذكية</h1>
<ul className="space-y-2">
{tasks.map((task) => (
<li
key={task.id}
className="flex items-center justify-between rounded border p-3"
>
<span
className={
task.status === "done"
? "text-gray-400 line-through"
: "text-gray-900"
}
>
{task.title}
</span>
<span className="text-xs uppercase text-gray-500">
{task.status}
</span>
</li>
))}
</ul>
<CopilotSidebar
labels={{
title: "مساعد المهام",
initial: "مرحباً! أستطيع إضافة مهام، أو إكمالها، أو تلخيصها.",
}}
/>
</main>
);
}شغّل npm run dev، وافتح http://localhost:3000، ستظهر القائمة وشريط دردشة على الجانب. سيردّ المساعد — لكنه لا يعرف بعد بمهامك. سنُصلح ذلك في الخطوة التالية.
الخطوة 5: مشاركة الحالة عبر useCopilotReadable
يحقن useCopilotReadable أيّ قيمة React في نافذة سياق المساعد عند كلّ دور. أضفه بجوار useState:
import { useCopilotReadable } from "@copilotkit/react-core";
// داخل HomePage()
useCopilotReadable({
description: "قائمة المهام الحالية للمستخدم، تشمل العنوان والحالة",
value: tasks,
});هذا هو كلّ ما يلزم لمنح المساعد صلاحية القراءة. أعد تحميل الصفحة واسأل: "كم مهمّة تبقّت؟" — سيجيب بدقّة لأن tasks صار جزءاً من كل تلميح (prompt).
لماذا نصف القيمة؟ يظهر حقل description للنموذج اللغوي ضمن تلميح النظام. أوصاف غامضة مثل "بيانات" تعطي إجابات غامضة. أوصاف ملموسة مثل "قائمة المهام الحالية للمستخدم، تشمل العنوان والحالة" تنتج استدعاءات أدوات دقيقة.
الخطوة 6: تعريف إجراءات أمامية عبر useCopilotAction
قراءة الحالة نصف العمل. الآن نسمح للمساعد بـتعديلها. useCopilotAction دالة مكتوبة يستطيع النموذج اللغوي استدعاؤها مباشرة من الدردشة. يتولّى الإطار توليد JSON Schema وتوجيه الأداة وبثّ الاستجابة.
أضف إجراء addTask:
import { useCopilotAction } from "@copilotkit/react-core";
useCopilotAction({
name: "addTask",
description: "إضافة مهمّة جديدة إلى قائمة مهام المستخدم",
parameters: [
{
name: "title",
type: "string",
description: "عنوان المهمّة الجديدة",
required: true,
},
],
handler: async ({ title }) => {
const newTask: Task = {
id: crypto.randomUUID(),
title,
status: "todo",
};
setTasks((prev) => [...prev, newTask]);
return `تمّت إضافة المهمّة: ${title}`;
},
});اطلب من المساعد: "أضف مهمّة لنشر فرع الـ staging يوم الجمعة." سترى الدردشة تؤكّد الإجراء، وتظهر المهمّة الجديدة في القائمة فوراً. دون إعادة تحميل ودون رحلة API — يعمل الإجراء داخل شجرة React.
أضف إجراءً ثانياً لتمييز المهام كمنجزة:
useCopilotAction({
name: "markTaskDone",
description: "تمييز مهمّة كمنجزة بناءً على عنوانها",
parameters: [
{
name: "title",
type: "string",
description: "العنوان أو جزء من العنوان للمهمّة المراد إنجازها",
required: true,
},
],
handler: async ({ title }) => {
let matched: Task | undefined;
setTasks((prev) =>
prev.map((t) => {
if (
t.status === "todo" &&
t.title.toLowerCase().includes(title.toLowerCase())
) {
matched = t;
return { ...t, status: "done" };
}
return t;
}),
);
return matched
? `تمّ الإنجاز: ${matched.title}`
: `لم أعثر على مهمّة تطابق "${title}"`;
},
});جرّب: "ميّز مراجعة طلب الدمج كمنجزة." سيختار المساعد المهمّة الصحيحة حتى دون عنوان دقيق — useCopilotReadable من الخطوة 5 هو ما يجعل هذه المطابقة الذكية ممكنة.
الخطوة 7: الواجهة التوليدية — عرض البطاقات داخل الدردشة
ردود النصّ الخام جيّدة للتأكيد، لكن المساعد قادر على عرض React عشوائي داخل الدردشة. مرّر دالة render لإجرائك فتصبح فقاعة الدردشة هي ما تُرجعه أنت.
useCopilotAction({
name: "showTaskSummary",
description: "عرض ملخّص بصري للمهام المعلّقة والمنجزة",
parameters: [],
handler: async () => "تمّ عرض الملخّص",
render: () => {
const todo = tasks.filter((t) => t.status === "todo").length;
const done = tasks.filter((t) => t.status === "done").length;
return (
<div className="rounded-lg border bg-white p-4 shadow-sm">
<div className="mb-2 text-sm font-semibold text-gray-700">
ملخّص المهام
</div>
<div className="flex gap-4 text-sm">
<span className="text-blue-600">معلّقة: {todo}</span>
<span className="text-green-600">منجزة: {done}</span>
</div>
</div>
);
},
});اطلب: "اعرض لي ملخّصاً." بدلاً من فقرة نصّية، تحصل على بطاقة منسّقة مدمجة في الدردشة. هذا النمط قابل للتوسيع — يمكنك عرض مخطّطات Recharts أو خرائط أو فروقات ملفات أو حتى نماذج تفاعلية.
الخطوة 8: تنسيق الشريط الجانبي
تنسيقات CopilotSidebar متغيّرات CSS، فيمكنك مطابقة هويّتك دون نسخ المكوّن. أضف ما يلي إلى globals.css:
:root {
--copilot-kit-primary-color: #0ea5e9;
--copilot-kit-contrast-color: #ffffff;
--copilot-kit-background-color: #ffffff;
--copilot-kit-secondary-color: #f4f4f5;
--copilot-kit-secondary-contrast-color: #18181b;
}يمكنك أيضاً تمرير instructions لإعطاء المساعد شخصية على مستوى النظام:
<CopilotSidebar
instructions={`أنت مساعد إنتاجية. اجعل الردود تحت ثلاث جمل. أكّد الإجراءات الحسّاسة قبل تنفيذها دائماً.`}
labels={{ title: "مساعد المهام" }}
/>اختبار التنفيذ
جرّب هذه التلميحات على خادم التطوير. الأربعة جميعها يجب أن تعمل من البداية إلى النهاية:
- "ما الموجود في قائمتي؟" — إجابة من
useCopilotReadable - "أضف مهمّة لتجديد شهادة SSL." — يستدعي
addTaskوتنمو القائمة - "ميّز مهمّة الـ SSL كمنجزة." — يستدعي
markTaskDoneبمطابقة ذكية - "اعرض لي ملخّصاً." — يعرض بطاقة الواجهة التوليدية
إن فشل أيّ منها افتح تبويب Network. كلّ دور للمساعد يُصدر POST /api/copilotkit. الاستجابة جسم JSON مبثوث — إذا أعاد 500 فمفتاح OPENAI_API_KEY ناقص أو اسم النموذج خاطئ.
استكشاف الأخطاء
يظهر الشريط الجانبي لكن الرسائل لا تفعل شيئاً. لا يطابق runtimeUrl المسار. تأكّد أنّ runtimeUrl="/api/copilotkit" يطابق مسار الملف app/api/copilotkit/route.ts.
Module not found: @copilotkit/react-ui/styles.css. نسيت استيراد ورقة الأنماط في CopilotProvider.tsx. بدونها يُعرض الشريط بلا تنسيق.
يُطلق الإجراء لكن الحالة لا تتحدّث. على الأرجح عرّفت الإجراء خارج المكوّن المالك للحالة. يجب أن يعمل useCopilotAction داخل نفس شجرة المكوّن التي تحتوي useState المراد تعديلها.
خطأ بناء: Unexpected identifier 'assert'. أنت على Node 18. انتقل إلى Node 20 بـ nvm use 20.
الخطوات التالية
أصبح لديك مساعد حقيقي يقرأ ويكتب حالة التطبيق. توسّعات طبيعية:
- إجراءات من جهة الخادم: مرّر
actions: [...]لـCopilotRuntimeلكشف عمليات قاعدة بيانات يستدعيها النموذج دون المرور بالمتصفّح. - تدفّقات متعدّدة الوكلاء: ادمج CopilotKit مع LangGraph لوكلاء ذوي حالة وفروع يحتفظون بالشريط الجانبي عبر أدوار متعدّدة.
- بثّ Markdown: استبدل المعالجات النصّية بردود مبثوثة لتظهر الإجابات الطويلة كلمة بكلمة.
- مصادقة وسياق لكل مستخدم: اقرأ الجلسة في معالج المسار وأحقن قيماً قابلة للقراءة محدودة بالمستخدم (معرّف مساحة العمل، الدور) في وقت التشغيل.
لأنماط ذكاء اصطناعي أوسع، يغطّي درس Claude Agent SDK جانب الوكلاء الخلفي، ويغطّي درس Vercel AI SDK للاسترجاع الوكيل المساعدات المعزّزة بالاسترجاع.
الخلاصة
تردم CopilotKit الفجوة بين "روبوت دردشة مدبّس على موقع" و*"ذكاء اصطناعي يشغّل المنتج"*. ثلاث وحدات أساسية تحمل التجربة كلها: المزوّد، و useCopilotReadable لمشاركة الحالة، و useCopilotAction للأدوات المكتوبة. ما تبقّى — الشريط الجانبي والواجهة التوليدية ومحوّلات النماذج — مجرّد صقل.
أطلق مساعداً واحداً لكل سطح من منتجك، وأبقِ القيم القابلة للقراءة محدّدة، وأبقِ الإجراءات ضيّقة، وسيبدأ مستخدموك بمعاملة الدردشة كلوحة مفاتيح أسرع لا كمكتب مساعدة.