بناء روبوت دردشة ذكاء اصطناعي محلي باستخدام Ollama و Next.js: الدليل الشامل

AI Bot
بواسطة AI Bot ·

جاري تحميل مشغل تحويل النص إلى كلام الصوتي...

بياناتك لا تغادر جهازك أبداً. في هذا الدليل، ستبني روبوت دردشة ذكاء اصطناعي يعمل بالكامل على جهازك المحلي باستخدام Ollama و Next.js — بدون مفاتيح API، بدون خدمات سحابية، بدون مشاركة بيانات.

ما ستتعلمه

بنهاية هذا الدليل، ستكون قادراً على:

  • تثبيت وتكوين Ollama لتشغيل نماذج اللغة الكبيرة محلياً
  • بناء واجهة دردشة Next.js مع بث الردود في الوقت الفعلي
  • دمج Ollama مع Vercel AI SDK للحصول على تجربة احترافية
  • إضافة اختيار النماذج ليتمكن المستخدمون من التبديل بين النماذج
  • التعامل مع الأخطاء بأناقة عندما لا يعمل Ollama
  • فهم المفاضلات بين الذكاء الاصطناعي المحلي والسحابي

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

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

  • Node.js 20+ مثبت (node --version)
  • معرفة أساسية بـ React و TypeScript
  • محرر أكواد — يُنصح بـ VS Code أو Cursor
  • ذاكرة 8 جيجابايت على الأقل (16 جيجابايت مُوصى بها للنماذج الأكبر)
  • macOS أو Linux أو Windows مع WSL2

لماذا تشغيل الذكاء الاصطناعي محلياً؟

خدمات الذكاء الاصطناعي السحابية مثل OpenAI و Anthropic قوية، لكنها تأتي مع تنازلات:

الاعتبارالذكاء الاصطناعي السحابيالذكاء الاصطناعي المحلي (Ollama)
الخصوصيةالبيانات تُرسل لخوادم خارجيةالبيانات تبقى على جهازك
التكلفةدفع لكل رمز (token)مجاني بعد التحميل
زمن الاستجابةيتطلب رحلة عبر الشبكةوصول مباشر للعتاد
التوفريتطلب إنترنتيعمل بدون اتصال
التخصيصمحدود بنماذج المزودتشغيل أي نموذج مفتوح

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


الخطوة 1: تثبيت Ollama

Ollama هو بيئة تشغيل خفيفة لتشغيل نماذج اللغة الكبيرة محلياً. يتولى تحميل النماذج وتكميمها وتقديمها من خلال واجهة برمجة تطبيقات بسيطة.

macOS

brew install ollama

أو حمّله من ollama.com.

Linux

curl -fsSL https://ollama.com/install.sh | sh

Windows

حمّل المثبت من ollama.com أو استخدم WSL2 مع تعليمات Linux.

التحقق من التثبيت

ollama --version

شغّل خادم Ollama:

ollama serve

هذا يُشغّل خادم API محلي على http://localhost:11434.


الخطوة 2: تحميل أول نموذج

يوفر Ollama الوصول إلى مئات النماذج مفتوحة المصدر. لنبدأ بـ Llama 3.2، نموذج Meta المدمج والقادر:

ollama pull llama3.2

هذا يُحمّل نموذج 3 مليار معامل (~2 جيجابايت). للحصول على خيار أخف:

ollama pull llama3.2:1b

النماذج الموصى بها لعام 2026

النموذجالحجمالأفضل لـ
llama3.2:1b700 ميغابايتاستجابات سريعة، أجهزة محدودة الموارد
llama3.22 جيجابايتدردشة عامة، توازن جيد
mistral4 جيجابايتاستدلال قوي، متعدد اللغات
qwen3:4b2.5 جيجابايتاستدلال متسلسل
qwen2.5-coder:7b4.5 جيجابايتتوليد ومراجعة الأكواد

اختبر نموذجك في الطرفية:

ollama run llama3.2
>>> What is the capital of Tunisia?

يجب أن ترى رداً مثل: "The capital of Tunisia is Tunis."


الخطوة 3: إنشاء مشروع Next.js

الآن لنبنِ واجهة الدردشة. أنشئ مشروع Next.js جديد:

npx create-next-app@latest ollama-chat --typescript --tailwind --app --src-dir
cd ollama-chat

ثبّت الحزم المطلوبة:

npm install ai ollama-ai-provider @ai-sdk/react

ما يفعله كل حزمة:

  • ai — نواة Vercel AI SDK مع streamText و generateText والمزيد
  • ollama-ai-provider — مزود مجتمعي يربط AI SDK بـ Ollama
  • @ai-sdk/react — خطافات React مثل useChat لبناء واجهات الدردشة

الخطوة 4: تكوين مزود Ollama

أنشئ تكوين عميل Ollama مشترك:

// src/lib/ollama.ts
import { createOllama } from 'ollama-ai-provider';
 
export const ollama = createOllama({
  baseURL: process.env.OLLAMA_BASE_URL ?? 'http://localhost:11434/api',
});
 
export const DEFAULT_MODEL = process.env.OLLAMA_DEFAULT_MODEL ?? 'llama3.2';

أضف متغيرات البيئة:

# .env.local
OLLAMA_BASE_URL=http://localhost:11434/api
OLLAMA_DEFAULT_MODEL=llama3.2

الخطوة 5: بناء مسار API للدردشة

هذا هو جوهر تطبيقنا — مسار API في Next.js يبث الردود من Ollama.

// src/app/api/chat/route.ts
import { streamText } from 'ai';
import { ollama, DEFAULT_MODEL } from '@/lib/ollama';
 
export const maxDuration = 60;
 
export async function POST(req: Request) {
  try {
    const { messages, model } = await req.json();
 
    const result = await streamText({
      model: ollama(model ?? DEFAULT_MODEL),
      system: 'You are a helpful, concise assistant. Answer questions clearly and accurately.',
      messages,
    });
 
    return result.toDataStreamResponse();
  } catch (error) {
    if (error instanceof Error && error.message.includes('ECONNREFUSED')) {
      return new Response(
        JSON.stringify({
          error: 'Ollama is not running. Start it with: ollama serve',
        }),
        { status: 503, headers: { 'Content-Type': 'application/json' } }
      );
    }
 
    return new Response(
      JSON.stringify({ error: 'An unexpected error occurred' }),
      { status: 500, headers: { 'Content-Type': 'application/json' } }
    );
  }
}

التفاصيل الرئيسية:

  • maxDuration = 60 يمنح المسار حتى 60 ثانية للبث، مهم للنماذج الأكبر
  • streamText يتولى بروتوكول البث بين Ollama والعميل
  • toDataStreamResponse() يحوّل البث إلى الصيغة التي يتوقعها useChat
  • معالجة الأخطاء تلتقط فشل الاتصال عندما لا يعمل Ollama

الخطوة 6: إضافة نقطة نهاية للنماذج

دع المستخدمين يرون النماذج المتاحة محلياً:

// src/app/api/models/route.ts
export async function GET() {
  try {
    const baseURL = process.env.OLLAMA_BASE_URL?.replace('/api', '')
      ?? 'http://localhost:11434';
 
    const res = await fetch(`${baseURL}/api/tags`);
 
    if (!res.ok) {
      throw new Error('Failed to fetch models');
    }
 
    const data = await res.json();
    const models = data.models.map((m: { name: string; size: number }) => ({
      id: m.name,
      label: m.name,
      size: `${(m.size / 1e9).toFixed(1)}GB`,
    }));
 
    return Response.json({ models });
  } catch {
    return Response.json({ models: [], error: 'Ollama is not available' });
  }
}

الخطوة 7: بناء مكون الدردشة

الآن الجزء الممتع — واجهة الدردشة. أنشئ مكون الدردشة الرئيسي:

// src/components/Chat.tsx
'use client';
 
import { useChat } from '@ai-sdk/react';
import { useState, useRef, useEffect } from 'react';
import { ModelSelector } from './ModelSelector';
 
export function Chat() {
  const [model, setModel] = useState('llama3.2');
  const scrollRef = useRef<HTMLDivElement>(null);
 
  const { messages, input, handleInputChange, handleSubmit, isLoading, error } =
    useChat({
      api: '/api/chat',
      body: { model },
    });
 
  useEffect(() => {
    scrollRef.current?.scrollTo({
      top: scrollRef.current.scrollHeight,
      behavior: 'smooth',
    });
  }, [messages]);
 
  return (
    <div className="flex flex-col h-screen max-w-3xl mx-auto">
      {/* الرأس */}
      <header className="flex items-center justify-between p-4 border-b">
        <h1 className="text-xl font-semibold">دردشة الذكاء الاصطناعي المحلي</h1>
        <ModelSelector value={model} onChange={setModel} />
      </header>
 
      {/* الرسائل */}
      <div ref={scrollRef} className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.length === 0 && (
          <div className="text-center text-gray-500 mt-20">
            <p className="text-4xl mb-4">🤖</p>
            <p className="text-lg font-medium">مساعدك الذكي الخاص</p>
            <p className="text-sm mt-2">
              يعمل بواسطة Ollama — كل شيء يعمل على جهازك.
            </p>
          </div>
        )}
 
        {messages.map((m) => (
          <div
            key={m.id}
            className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}
          >
            <div
              className={`max-w-[80%] rounded-2xl px-4 py-3 ${
                m.role === 'user'
                  ? 'bg-blue-600 text-white'
                  : 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100'
              }`}
            >
              <p className="whitespace-pre-wrap">{m.content}</p>
            </div>
          </div>
        ))}
 
        {isLoading && messages[messages.length - 1]?.role === 'user' && (
          <div className="flex justify-start">
            <div className="bg-gray-100 dark:bg-gray-800 rounded-2xl px-4 py-3">
              <span className="animate-pulse">جارٍ التفكير...</span>
            </div>
          </div>
        )}
      </div>
 
      {/* عرض الأخطاء */}
      {error && (
        <div className="mx-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-400 text-sm">
          {error.message.includes('503')
            ? 'Ollama لا يعمل. شغّله بالأمر: ollama serve'
            : 'حدث خطأ ما. يرجى المحاولة مرة أخرى.'}
        </div>
      )}
 
      {/* الإدخال */}
      <form onSubmit={handleSubmit} className="p-4 border-t">
        <div className="flex gap-2">
          <input
            value={input}
            onChange={handleInputChange}
            placeholder="اكتب رسالة..."
            disabled={isLoading}
            className="flex-1 rounded-xl border px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700"
          />
          <button
            type="submit"
            disabled={isLoading || !input.trim()}
            className="rounded-xl bg-blue-600 px-6 py-3 text-white font-medium hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
          >
            إرسال
          </button>
        </div>
      </form>
    </div>
  );
}

الخطوة 8: بناء مكون اختيار النموذج

هذا المكون يجلب النماذج المتاحة من Ollama ويتيح للمستخدمين التبديل بينها:

// src/components/ModelSelector.tsx
'use client';
 
import { useState, useEffect } from 'react';
 
interface Model {
  id: string;
  label: string;
  size: string;
}
 
interface ModelSelectorProps {
  value: string;
  onChange: (model: string) => void;
}
 
export function ModelSelector({ value, onChange }: ModelSelectorProps) {
  const [models, setModels] = useState<Model[]>([]);
  const [loading, setLoading] = useState(true);
 
  useEffect(() => {
    fetch('/api/models')
      .then((res) => res.json())
      .then((data) => {
        setModels(data.models ?? []);
        setLoading(false);
      })
      .catch(() => setLoading(false));
  }, []);
 
  if (loading) {
    return (
      <select disabled className="rounded-lg border px-3 py-2 text-sm opacity-50">
        <option>جارٍ تحميل النماذج...</option>
      </select>
    );
  }
 
  if (models.length === 0) {
    return (
      <span className="text-sm text-red-500">لم يتم العثور على نماذج</span>
    );
  }
 
  return (
    <select
      value={value}
      onChange={(e) => onChange(e.target.value)}
      className="rounded-lg border px-3 py-2 text-sm bg-white dark:bg-gray-800 dark:border-gray-700"
    >
      {models.map((m) => (
        <option key={m.id} value={m.id}>
          {m.label} ({m.size})
        </option>
      ))}
    </select>
  );
}

الخطوة 9: ربط الصفحة

حدّث الصفحة الرئيسية لعرض مكون الدردشة:

// src/app/page.tsx
import { Chat } from '@/components/Chat';
 
export default function Home() {
  return <Chat />;
}

الخطوة 10: التشغيل والاختبار

شغّل خادم التطوير:

npm run dev

تأكد أن Ollama يعمل في طرفية أخرى:

ollama serve

افتح http://localhost:3000 وابدأ بالدردشة. يجب أن ترى:

  1. محدد النماذج مليء بنماذجك المحلية
  2. ردود متدفقة في الوقت الفعلي أثناء توليد النموذج للنص
  3. تجربة دردشة سلسة — كل شيء يعمل محلياً

قائمة فحص الاختبار

  • أرسل رسالة بسيطة وتحقق من عمل البث
  • بدّل بين النماذج باستخدام المحدد
  • أوقف Ollama (Ctrl+C على ollama serve) وتحقق من ظهور رسالة الخطأ
  • أعد تشغيل Ollama وتحقق من استعادة الدردشة
  • أرسل رسالة طويلة وتحقق من كفاية مهلة 60 ثانية

المزيد: المخرجات المنظمة

يدعم Ollama مخرجات JSON منظمة باستخدام مخططات Zod. هذا مفيد لبناء الأدوات واستخراج البيانات أو فرض صيغ الاستجابة:

// src/app/api/analyze/route.ts
import { generateObject } from 'ai';
import { ollama } from '@/lib/ollama';
import { z } from 'zod';
 
const SentimentSchema = z.object({
  sentiment: z.enum(['positive', 'negative', 'neutral']),
  confidence: z.number().min(0).max(1),
  summary: z.string().max(200),
});
 
export async function POST(req: Request) {
  const { text } = await req.json();
 
  const { object } = await generateObject({
    model: ollama('llama3.2'),
    schema: SentimentSchema,
    prompt: `Analyze the sentiment of this text: "${text}"`,
  });
 
  return Response.json(object);
}

الاستجابة ستطابق دائماً مخططك:

{
  "sentiment": "positive",
  "confidence": 0.92,
  "summary": "The text expresses strong satisfaction with the product."
}

المزيد: التضمينات لـ RAG

يمكنك استخدام Ollama لتوليد التضمينات لبناء نظام توليد معزز بالاسترجاع (RAG):

import { embedMany } from 'ai';
import { ollama } from '@/lib/ollama';
 
const { embeddings } = await embedMany({
  model: ollama.embeddingModel('nomic-embed-text'),
  values: [
    'Next.js is a React framework for the web.',
    'Ollama runs large language models locally.',
    'TypeScript adds static types to JavaScript.',
  ],
});
 
// كل تضمين هو مصفوفة float32 يمكنك تخزينها في قاعدة بيانات متجهية
console.log(embeddings[0].length); // 768 بُعد

ادمج هذا مع قاعدة بيانات متجهية مثل pgvector أو ChromaDB لبناء خط أنابيب RAG محلي بالكامل.


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

Ollama لا يستجيب

# تحقق مما إذا كان Ollama يعمل
curl http://localhost:11434/api/tags
 
# إذا لم يكن يعمل، شغّله
ollama serve

النموذج بطيء جداً

جرّب نموذجاً أصغر:

ollama pull llama3.2:1b  # مليار معامل، أسرع بكثير

أو تحقق مما إذا كان جهازك يدعم تسريع GPU:

ollama ps  # يعرض النماذج المحملة واستخدامها للذاكرة

أخطاء CORS في المتصفح

لا تستدعِ Ollama مباشرة من المتصفح أبداً. استخدم دائماً الوكالة عبر مسار API في Next.js — هذا يتجنب مشاكل CORS تماماً ويحافظ على أمان بنيتك.

نفاد الذاكرة

النماذج الكبيرة تتطلب ذاكرة RAM كبيرة. إذا رأيت أخطاء ذاكرة:

  1. استخدم نسخة نموذج أصغر (llama3.2:1b بدلاً من llama3.2)
  2. أغلق التطبيقات الأخرى كثيفة استخدام الذاكرة
  3. تحقق من الذاكرة المتاحة بـ ollama ps

نظرة عامة على البنية

إليك كيف تتناسب القطع معاً:

┌─────────────────┐     HTTP POST      ┌──────────────────┐     HTTP POST      ┌──────────────┐
│                 │  ──────────────►   │                  │  ──────────────►   │              │
│   React Client  │     /api/chat      │  Next.js Server  │   localhost:11434  │    Ollama    │
│   (useChat)     │  ◄──────────────   │  (Route Handler) │  ◄──────────────   │   (Local)    │
│                 │   SSE stream       │                  │   NDJSON stream    │              │
└─────────────────┘                    └──────────────────┘                    └──────────────┘
  1. عميل React يستخدم خطاف useChat لإرسال الرسائل واستقبال الردود المتدفقة
  2. مسار API في Next.js يستقبل الطلب، يستدعي Ollama عبر مزود AI SDK، ويبث الرد
  3. Ollama يُشغّل استدلال النموذج محلياً ويُرجع الرموز كـ JSON مفصول بأسطر جديدة

كل الاتصالات تبقى على شبكتك المحلية — لا شيء يصل إلى الإنترنت.


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

الآن بعد أن لديك روبوت دردشة ذكاء اصطناعي محلي يعمل، فكّر في هذه التحسينات:

  • إضافة سجل المحادثات — احفظ الدردشات باستخدام التخزين المحلي أو قاعدة بيانات
  • بناء خط أنابيب RAG — استخدم التضمينات وقاعدة بيانات متجهية للإجابة على أسئلة المستندات
  • إضافة استدعاء الأدوات — دع النموذج ينفذ وظائف مثل البحث على الويب أو الحسابات
  • النشر على شبكتك المحلية — اجعل روبوت الدردشة متاحاً للأجهزة الأخرى على شبكتك
  • جرّب نماذج الرؤية — استخدم llama3.2-vision لتحليل الصور محلياً

دروس ذات صلة على نقطة:


الخلاصة

لقد بنيت روبوت دردشة ذكاء اصطناعي محلي بالكامل:

  • يعمل بالكامل على جهازك بدون أي اعتماد على السحابة
  • يبث الردود في الوقت الفعلي لتجربة مستخدم سلسة
  • يدعم نماذج متعددة من خلال محدد نماذج ديناميكي
  • يتعامل مع الأخطاء بأناقة عندما لا يكون Ollama متاحاً
  • يستخدم Vercel AI SDK لبنية جاهزة للإنتاج

نظام الذكاء الاصطناعي المحلي نضج بشكل كبير في 2026. مع Ollama الذي يتولى تشغيل النماذج و Vercel AI SDK الذي يوفر تجربة المطور، أصبح بناء تطبيقات الذكاء الاصطناعي الخاصة بنفس سهولة بناء أي تطبيق ويب آخر.

بياناتك تبقى ملكك. ذكاؤك الاصطناعي يعمل بشروطك.


هل تريد قراءة المزيد من الدروس التعليمية؟ تحقق من أحدث درس تعليمي لدينا على بداية سريعة مع Gemma على KerasNLP.

ناقش مشروعك معنا

نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.

دعنا نجد أفضل الحلول لاحتياجاتك.

مقالات ذات صلة

بناء وكلاء الذكاء الاصطناعي من الصفر باستخدام TypeScript: إتقان نمط ReAct مع Vercel AI SDK

تعلّم كيفية بناء وكلاء الذكاء الاصطناعي من الأساس باستخدام TypeScript. يغطي هذا الدليل التعليمي نمط ReAct، واستدعاء الأدوات، والاستدلال متعدد الخطوات، وحلقات الوكلاء الجاهزة للإنتاج مع Vercel AI SDK.

35 د قراءة·

إضافة المصادقة لتطبيق Next.js 15 باستخدام Auth.js v5: البريد الإلكتروني وOAuth والتحكم بالأدوار

تعلم كيفية إضافة نظام مصادقة جاهز للإنتاج لتطبيق Next.js 15 باستخدام Auth.js v5. يغطي هذا الدليل الشامل تسجيل الدخول عبر Google OAuth وبيانات الاعتماد بالبريد الإلكتروني وكلمة المرور والمسارات المحمية والتحكم بالوصول حسب الأدوار.

30 د قراءة·