الدرس 6 من 6

أنشئ أول وكيل ذكاء اصطناعي

نكتب أول ملف Python يرسل قائمة رسائل إلى النموذج المحلي ويعرض ردّه، ونفهم كل سطر فيه.

8 دقائق للقراءة

في الدرسين السابقين اخترنا نموذجًا محليًّا، وثبّتنا Ollama، وقمنا بتحميل النموذج qwen2.5:0.5b.

في هذا الدرس نكتب أول ملف Python يرسل رسالة إلى النموذج ويعرض ردّه. هذه أول لبنة في الوكيل، ونتوقف عند أول رد كي نفهم كل سطر.

ما الذي نبنيه؟

ملف واحد اسمه agent.py. يفعل ثلاثة أشياء:

  1. يجهّز قائمة رسائل: قواعد، ثم رسالتك.
  2. يرسلها إلى Ollama، فيمررها إلى النموذج.
  3. يستلم الرد ويعرضه.
رحلة الرسالةagent.pyملفناقائمة الرسائلOllamaيعمل على جهازكqwen2.5:0.5bالرد يعود إلى الملفالملف يرسل، والنموذج يرد، وprint يعرض الرد
رحلة الرسالة: ملف agent.py يرسل قائمة الرسائل إلى Ollama، فيمررها إلى النموذج، ويعود الرد إلى الملف.

وسنبني وكيل ملاحظات: هو مساعد يحفظ ما تقوله له. وفي هذا الدرس نرسل إليه أول ملاحظة فقط. وسنرى في الدروس القادمة لماذا لا يكفي هذا الملف، وكيف نجعله يتذكر.

ما تحتاج إليه قبل أن تبدأ

  • Python 3 على جهازك. تأكد منه بالأمر python3 --version، فيجب أن يظهر رقم الإصدار.
  • Ollama يعمل، والنموذج qwen2.5:0.5b منزَّل. تأكد منه بالأمر ollama list. إن لم تفعل ذلك بعد، فراجع درس تثبيت Ollama.
  • محرر نصوص تكتب فيه الملف. في الطرفية يكفي nano. يمكنك استخدام أي محرر من اختيارك.

ولا نحتاج إلى تثبيت أي حزمة. نستخدم أدوات Python المدمجة فقط.

ثم أنشئ مجلدًا للمشروع، لاحظ يمكنك استخدام أدواتك لإنشاء المجلدات، أنا سأقوم بالتطبيق عبر طرفية لينكس:

mkdir ~/my-first-agent
cd ~/my-first-agent
الأمر معناه
mkdir ~/my-first-agent mkdir اختصار لـ make directory، أي «أنشئ مجلدًا». يُنشئ مجلدًا اسمه my-first-agent داخل مجلدك الشخصي، ويعني الرمز ~ مجلدك الشخصي.
cd ~/my-first-agent cd اختصار لـ change directory، أي «انتقل إلى مجلد». وبعده تعمل الأوامر التي تكتبها داخل هذا المجلد.

وافتح الملف الجديد:

nano agent.py

nano محرر نصوص بسيط يعمل داخل الطرفية. وagent.py اسم الملف، فإن لم يكن موجودًا أنشأه nano لك.

إن كنتَ تستخدم Windows أو VS Code

لا تحتاج إلى الأوامر السابقة، ولكل أمر منها ما يقابله:

  1. أنشئ المجلد بالطريقة المعتادة: في مستكشف الملفات اضغط بزر الفأرة الأيمن، ثم «جديد»، ثم «مجلد»، وسمّه my-first-agent.
  2. افتحه في VS Code: من القائمة File اختر Open Folder، ثم اختر المجلد.
  3. أنشئ الملف: في لوحة Explorer على اليسار اضغط أيقونة New File، واكتب اسمه agent.py.
  4. اكتب الشيفرة في الملف، واحفظه بالضغط على Ctrl+S.
  5. افتح الطرفية: من القائمة Terminal اختر New Terminal. وتفتح الطرفية داخل المجلد نفسه، فلا تحتاج إلى cd.

ولم أجرّب هذه الخطوات على Windows، وهي مأخوذة من الاستخدام المعتاد لـ VS Code.

الملف سطرًا سطرًا

نبني الملف على خمس قطع. اكتب كل قطعة تحت التي قبلها.

تذكّر: ما JSON؟

JSON طريقة لكتابة البيانات في نص: أقواس معقوفة، وأسماء، وقيم، مثل {"role": "user"}. وستراها في أكثر من مكان في هذا الدرس. فإن لم تكن تعرفها، شاهد الفيديو.

القطعة الأولى: الأدوات الجاهزة

أدوات جاهزة في Pythonjsonيحوّل البياناتإلى نص JSON والعكسurllib.requestيرسل الطلباتعبر الشبكةimport jsonimport urllib.requestنستورد الأداة قبل أن نستخدمها
أدوات جاهزة في Python: json يحوّل البيانات إلى JSON، وurllib.request يرسل الطلبات عبر الشبكة.
import json
import urllib.request
السطر ماذا يفعل؟
import json يحمّل أداة جاهزة في Python تحوّل البيانات إلى نص بصيغة JSON والعكس. وJSON هي اللغة التي يفهمها Ollama.
import urllib.request يحمّل أداة جاهزة ترسل الطلبات عبر الشبكة، وتستلم الردود.

القطعة الثانية: الإعدادات

إعدادان في أول الملفURLعنوان Ollamalocalhost:11434MODELاسم النموذجqwen2.5:0.5bنكتبهما مرة واحدة، ونغيّرهما في مكان واحد
إعدادان في أول الملف: URL عنوان Ollama، وMODEL اسم النموذج.
URL = "http://localhost:11434/v1/chat/completions"
MODEL = "qwen2.5:0.5b"
السطر ماذا يفعل؟
URL = ... عنوان Ollama على جهازك. وهو نفسه الذي رأيناه في درس ما النموذج المحلي؟. ونكتبه في أول الملف ليسهل تغييره.
MODEL = ... اسم النموذج الذي نسأله. كتبناه حروفًا كبيرة لأنه ثابت لا يتغير أثناء التشغيل.

القطعة الثالثة: قائمة الرسائل

قائمة الرسائلmessages = [ … ]role: systemcontent:القواعد التي نكتبهاrole: usercontent:رسالتكالدور ثابت تقريبًا، والمحتوى يختلف
قائمة الرسائل: رسالة system فيها القواعد، ورسالة user فيها كلام المستخدم. الدور ثابت تقريبًا والمحتوى يختلف.
messages = [
    {"role": "system", "content": "You are a note keeper, Answer briefly"},
    {"role": "user", "content": "Remember: my meeting is on sunday at 10"},
]

هذه هي القائمة التي سيقرؤها النموذج.

الجزء ماذا يعني؟
messages = [ ... ] قائمة، وتُكتب بين أقواس مربعة، وتحوي عناصر مفصولة بفواصل.
{ ... } كل رسالة قاموس: بين أقواس معقوفة، وفيه اسم ثم قيمة.
"role": "system" دور الرسالة، وقيمته ثابتة تقريبًا. system للقواعد التي نكتبها نحن.
"role": "user" دور رسالتك أنت، وهو أيضًا من القيم الثابتة.
"content" نص الرسالة نفسه، وهو يختلف من رسالة إلى أخرى، ونكتب فيه ما نشاء.

الأدوار ثابتة تقريبًا: الأدوار الأساسية ثلاثة: system وuser وassistant، وتضيف بعض الواجهات أدوارًا أخرى مثل tool. ولا نخترع دورًا جديدًا من عقلنا، لأن النموذج دُرّب على هذه الأسماء.

المحتوى يختلف: في content نكتب ما نشاء. فنغيّر القواعد في رسالة system، ونغيّر السؤال في رسالة user، متى أردنا، ويبقى الدور كما هو.

وهنا قاعدتان: «You are a note keeper» أي أنت تحفظ الملاحظات، و«Answer briefly» أي أجب باختصار. ورسالتك: «تذكّر أن اجتماعي يوم الأحد في العاشرة».

القطعة الرابعة: الدالة التي تسأل النموذج

الدالة آلة صغيرةقائمة الرسائلask_model(messages)١. يجهّز الطلب (JSON)٢. يرسله إلى Ollama٣. يقرأ الرد ويختار النصنص الرد
الدالة ask_model: تستقبل قائمة الرسائل، فتجهز الطلب وترسله إلى Ollama وتقرأ الرد، ثم تعيد نص الرد.
def ask_model(messages):
    body = json.dumps({"model": MODEL, "messages": messages}).encode()
    request = urllib.request.Request(URL, body, {"Content-Type": "application/json"})
    with urllib.request.urlopen(request) as response:
        data = json.load(response)
        return data["choices"][0]["message"]["content"]

نضع كود السؤال في دالة، لنستطيع أن نستدعيها مرات كثيرة لاحقًا. والأسطر التي تحت def مزاحة بأربع مسافات، وهذا يعني أنها داخل الدالة.

السطر ماذا يفعل؟
def ask_model(messages): يعرّف دالة اسمها ask_model، تستقبل قائمة الرسائل. وتعريف الدالة لا يشغّلها، كما أن كتابة الوصفة لا تطبخ الطعام.
body = json.dumps({...}).encode() يجمع اسم النموذج وقائمة الرسائل في قاموس، ويحوّله إلى نص JSON (dumps)، ثم إلى بايتات جاهزة للإرسال (encode).
request = urllib.request.Request(URL, body, {...}) يجهّز الطلب: العنوان، ثم المحتوى، ثم ترويسة تقول إن المحتوى JSON. وهو مثل الرسالة البريدية قبل أن تُرسل.
with urllib.request.urlopen(request) as response: يرسل الطلب وينتظر الرد، ويسمي الرد response. وكلمة with تفتح الاتصال ثم تغلقه وحدها بعد أن تنتهي الأسطر التي تحتها.
data = json.load(response) يقرأ الرد ويحوّله من JSON إلى قاموس نستطيع أن نبحث فيه.
return data["choices"][0]["message"]["content"] يعيد من الرد الكبير نص الرد فقط.

والسطر الأخير يسير في الرد هكذا:

data
 └─ "choices"
     └─ [0]
         └─ "message"
             └─ "content"
  • "choices": قائمة الإجابات الممكنة.
  • [0]: أول إجابة فيها.
  • "message": رسالة الرد، ودورها assistant.
  • "content": النص الذي نريده.

وهو المسار الذي رأيناه في ناتج curl حين جرّبنا النموذج يدويًّا.

القطعة الخامسة: التشغيل

التشغيل والعرض$ python3 agent.pythe meeting on Sundayat 10:00 am?reply = ask_model(messages) ← يشغّل ويحفظ الردprint(reply) ← يعرضه على الشاشة
التشغيل: python3 agent.py يعرض في الطرفية الرد الذي أعاده النموذج.
reply = ask_model(messages)
print(reply)
السطر ماذا يفعل؟
reply = ask_model(messages) يشغّل الدالة بقائمة رسائلنا، ويحفظ الرد في reply.
print(reply) يعرض الرد على الشاشة.

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

الملف كاملًا

import json
import urllib.request

URL = "http://localhost:11434/v1/chat/completions"
MODEL = "qwen2.5:0.5b"

messages = [
    {"role": "system", "content": "You are a note keeper, Answer briefly"},
    {"role": "user", "content": "Remember: my meeting is on sunday at 10"},
]


def ask_model(messages):
    body = json.dumps({"model": MODEL, "messages": messages}).encode()
    request = urllib.request.Request(URL, body, {"Content-Type": "application/json"})
    with urllib.request.urlopen(request) as response:
        data = json.load(response)
        return data["choices"][0]["message"]["content"]


reply = ask_model(messages)
print(reply)

احفظ الملف.

شغّله واقرأ الرد

في الطرفية، داخل مجلد المشروع:

python3 agent.py

وفي Windows اكتب python agent.py، فإن لم يعمل فاكتب py agent.py.

قد يتأخر أول رد بضع ثوانٍ، لأن النموذج يُحمَّل في الذاكرة. فإن صار الجهاز بطيئًا، فاضغط Ctrl+C لإيقاف الملف.

ما ظهر لي حين جرّبتُ

$ python3 agent.py
the meeting on Sunday at 10:00 am?

هذا هو أول رد: النموذج أعاد جملتي على هيئة سؤال. وهذا ليس رد جيد من وكيل ملاحظات، فالمفروض أن يقول «حسنًا، سجّلتُ». السبب أن qwen2.5:0.5b أصغر أحجام Qwen2.5، فهو يخطئ أحيانًا في فهم التعليمات. وهو أخف على جهازك، وهذا ما اخترناه.

وهناك شيء أهم: لا شيء حُفظ فعلًا. النموذج كتب نصًّا فقط، ولو سألته بعد قليل عن اجتماعك فلن يعرف. وسنرى هذا في الدرس القادم.

ماذا تلاحظ؟

  • هل نفّذ الملف دون خطأ؟
  • هل ظهر رد؟ وهل هو جواب جيد لوكيل ملاحظات؟
  • ما الذي تغيّر في الرد حين غيّرتَ رسالتك أنت؟ جرّب جملة أخرى مكان Remember: my meeting is on sunday at 10.

هل Python هي اللغة الوحيدة؟

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

  • JavaScript وPHP وغيرهما: كلٌّ منها يرسل طلب ويب.
  • الأمر curl في الطرفية: استخدمناه في بداية المسار دون أي لغة برمجة.

اخترنا Python لأنها سهلة القراءة، وكل سطر فيها قصير. وإن كنتَ تعرف لغة أخرى، فأعد كتابة الملف بها بالخطوات نفسها.

اختبر نفسك

في أي سطر نغيّر اسم النموذج لو أردنا أن نجرّب نموذجًا آخر؟

اعرض الجواب

في السطر MODEL = "qwen2.5:0.5b". وضعنا الاسم في أول الملف لنغيّره في مكان واحد. وبعد تغييره يجب أن يكون النموذج الجديد منزَّلًا في Ollama.

ما الذي تعلّمناه؟

  • ملف agent.py يرسل قائمة رسائل إلى النموذج عبر Ollama، ويعرض الرد.
  • كل رسالة قاموس فيه role وcontent، والقائمة بين أقواس مربعة.
  • الدالة ask_model تجمع الطلب وترسله وتعيد نص الرد فقط.
  • لا شيء يُحفظ: النموذج يكتب نصًّا فقط. ولهذا نحتاج إلى الذاكرة والأدوات.
  • Python ليست اللغة الوحيدة، فالمطلوب أي لغة ترسل طلب ويب.

أنهيت آخر درس في المسار

سُجّل تقدمك تلقائيًا. عد إلى المسار لترى ما أنهيته وما سيأتي قريبًا.

هذا الدرس جزء من مساروكلاء الذكاء الاصطناعي من الصفر ←