بناء وكيل ذكاء اصطناعي يبدأ بفكرة بسيطة: إعطاء النموذج القدرة على اتخاذ إجراءات، ومراقبة النتائج، والتكرار. النموذج يفكر في الهدف، ويقرر ما سيفعله بعد ذلك، ويستدعي أداة، ويحصل على نتيجة، ويستمر حتى تكتمل المهمة. هذه الحلقة المكونة من قراءة-تقييم-تنفيذ هي جوهر كل وكيل ستبنيه، سواء كان أداة استدعاء بخطوة واحدة أو نظامًا متعدد الخطوات يكتب الكود، ويشغله، ويصلح أخطاءه بنفسه.
هذا الدليل يشرح المكونات الأربعة التي تجعل الوكيل يعمل: التخطيط، استخدام الأدوات، تنفيذ الكود، والتقييم. ستخرج بنموذج عقلي عملي وتنفيذ أدنى يمكنك تشغيله اليوم.
ما الذي يجعل الشيء وكيل ذكاء اصطناعي
يختلف وكيل الذكاء الاصطناعي عن استدعاء LLM الأساسي بطريقة واحدة حاسمة: فهو يتخذ إجراءات ويراقب نتائجها.
إنشاء محادثة نصية عادية ينتج نصًا. الوكيل يستخدم هذا النص لاستدعاء دالة، أو الاستعلام عن API، أو تشغيل أمر، أو قراءة ملف — ويُغذي النتيجة مرة أخرى كمدخل للخطوة التالية. حلقة التغذية الراجعة هذه هي ما يجعل الوكلاء مفيدين للمهام التي لا يمكن الإجابة عليها في رد واحد.
ثلاث قدرات تحدد الوكيل:
- استدعاء الأدوات — استدعاء دوال معرفة بالاسم مع وسائط منظمة
- تراكم السياق — حمل نتائج الأدوات إلى الأمام مع نمو المحادثة
- التكرار الموجه نحو الهدف — الاستمرار حتى يتم استيفاء شرط التوقف، وليس فقط بعد رد واحد
إذا كان النظام يحتوي على الثلاثة، فأنت تبني وكيلًا. إذا كان تراكم السياق أو التكرار مفقودًا، فهو روبوت محادثة معزز بالأدوات.
نفس البنية تدير مجموعة واسعة من المهام: وكلاء بحث يبحثون ويُركبون، وكلاء برمجة يكتبون ويشغلون الكود، وكلاء بيانات يستعلمون عن قواعد البيانات ويولدون تقارير. نمط التنفيذ متطابق تقريبًا في كل حالة.
الخطوة 1: التخطيط — طبقة التفكير
طبقة التخطيط هي حيث يقرر النموذج ما سيفعله بعد ذلك. لا تحتاج إلى نموذج مخطط منفصل — نفس النموذج الذي يولد النص يمكنه التفكير في هدف وإنتاج إجراء منظم (استدعاء أداة) في كل خطوة.
موجه النظام يقوم بمعظم العمل. موجه تخطيط جيد يخبر النموذج:
- ما الهدف الذي يعمل من أجله
- ما الأدوات المتاحة وماذا تفعل كل واحدة
- ما التنسيق الذي يجب استخدامه عند استدعاء أداة
- متى يتوقف (شرط الإنهاء)
مع Novita LLM API — المتوافق مع OpenAI — يبدو هذا كالتالي:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.novita.ai/v3/openai",
api_key=os.environ["NOVITA_API_KEY"],
)
tools = [
{
"type": "function",
"function": {
"name": "run_code",
"description": "Execute Python code and return stdout/stderr.",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Python code to execute"
}
},
"required": ["code"]
}
}
}
]
messages = [
{
"role": "system",
"content": "You are a coding assistant. Use the run_code tool to execute Python and solve tasks. Stop when you have a final answer."
},
{
"role": "user",
"content": "Find all prime numbers under 50."
}
]
response = client.chat.completions.create(
model="meta-llama/llama-3.1-8b-instruct",
messages=messages,
tools=tools,
tool_choice="auto",
)
النموذج يعيد استدعاء أداة — كائن JSON باسم الدالة والوسائط — بدلاً من رد نصي عادي. الكود الخاص بك يستخرج هذا، وينفذ الدالة، ويعيد التكرار مع إلحاق النتيجة بـ messages.
الخطوة 2: استخدام الأدوات — ربط النموذج بالعالم
الأدوات هي ما يفعله الوكلاء بالفعل. بدونها، يمكن للنموذج فقط إنتاج النص. معها، يمكنه قراءة الملفات، واستدعاء APIs، وتشغيل الكود، والبحث في قواعد البيانات، أو تشغيل أي دالة تعرفها.
تعريف الأدوات
كل أداة هي مخطط JSON يصف دالة. النموذج يقرأ هذا المخطط لتنسيق استدعائه بشكل صحيح. بعض القواعد العملية:
- احتفظ بالأوصاف قصيرة ودقيقة. النموذج يقرأها في كل خطوة. الأوصاف المطولة تهدر الرموز.
- طابق أسماء المعاملات مع ما تقبله الدالة فعليًا. عدم التطابق بين المخطط والتنفيذ يسبب فشلاً صامتًا.
- أعد ناتجًا منظمًا. إذا كانت أداتك تعيد HTML خامًا أو نثرًا غير منسق، فإن النموذج يهدر الرموز في تحليله. أعد سلاسل نظيفة أو JSON مع تسميات واضحة.
حلقة استدعاء الأداة
بعد أن يقوم النموذج باستدعاء الأداة، الكود الخاص بك:
- يحلل اسم الأداة والوسائط من الرد
- يبحث عن دالة بايثون المطابقة
- يستدعيها مع الوسائط المحللة
- يلحق النتيجة بـ
messagesمعrole: "tool" - يقوم بطلب إنشاء آخر
تستمر الحلقة حتى يعيد النموذج رسالة عادية بدون استدعاء أداة (شرط الإنهاء).
import json
def run_agent_loop(client, model, messages, tools, tool_fns, max_steps=10):
for _ in range(max_steps):
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice="auto",
)
choice = response.choices[0]
# تم — لا يوجد استدعاء أداة
if choice.finish_reason == "stop":
return choice.message.content
# تنفيذ استدعاء الأداة
tool_call = choice.message.tool_calls[0]
fn_name = tool_call.function.name
fn_args = json.loads(tool_call.function.arguments)
result = tool_fns[fn_name](**fn_args)
# إلحاق كل من استدعاء الأداة من النموذج والنتيجة
messages.append(choice.message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result),
})
return "تم الوصول إلى الحد الأقصى للخطوات دون إجابة نهائية."
هذه هي حلقة الوكيل الكاملة. الحارس max_steps يمنع الحلقات الجامحة — إذا علق النموذج في استدعاء نفس الأداة مرارًا، تنتهي الحلقة بدلاً من الاستمرار إلى أجل غير مسمى. كل شيء آخر — تنسيق الأدوات المتعددة، الذاكرة، البث — يبني فوق هذا النمط.
أنماط الأدوات الشائعة
الوصول إلى الملفات: قراءة وكتابة الملفات داخل دليل عمل الوكيل. إعادة محتويات الملف كسلاسل نصية.
البحث على الويب: إعادة نتائج البحث كقائمة من كائنات {title, url, snippet}. اجعل المقتطفات قصيرة.
تنفيذ الكود: تنفيذ الكود في بيئة معزولة وإعادة stdout/stderr (مغطى في القسم التالي).
البحث عن بيانات منظمة: الاستعلام عن قاعدة بيانات أو API وإعادة السجلات كـ JSON. النموذج يتولى التلخيص.
كل أداة يجب أن تفعل شيئًا واحدًا وتعيد ناتجًا حتميًا لنفس المدخلات. الوكلاء الذين يحاولون حشر منطق كثير في أداة واحدة يصبحون أصعب في التصحيح والتقييم.
الخطوة 3: تنفيذ الكود — تشغيل الكود بأمان
إذا كان وكيلك يولد ويشغل الكود، فأنت بحاجة إلى بيئة معزولة. تشغيل الكود المولد بواسطة النموذج مباشرة على جهازك هو خطر أمني: قد ينتج النموذج أوامر شل مدمرة، أو حلقات لا نهائية، أو كودًا يقرأ ملفات حساسة لا ينبغي له الوصول إليها.
النهج القياسي هو بيئة عازلة (sandbox) — بيئة Linux معزولة وقصيرة العمر حيث يمكن للوكيل تنفيذ الكود بحرية دون التأثير على النظام المضيف. Agent Sandbox من Novita مصمم خصيصًا لحالة الاستخدام هذه: بيئة تنفيذ قائمة على السحابة تبدأ في أقل من 200ms، وتشغل أي كود ينتجه الوكيل، وتتجاهل البيئة عند انتهاء الجلسة.
إليك كيف سيتم تنفيذ أداة run_code من قسم التخطيط باستخدام البيئة العازلة:
from novita_sandbox.code_interpreter import Sandbox
sandbox = Sandbox.create()
def run_code(code: str) -> str:
result = sandbox.run_code(code)
output = ""
if result.logs.stdout:
output += "\n".join(result.logs.stdout)
if result.logs.stderr:
output += "\nSTDERR: " + "\n".join(result.logs.stderr)
return output or "(no output)"
عندما يستدعي الوكيل run_code، هذه الدالة تنفذ الكود في البيئة المعزولة وتعيد stdout/stderr كسلسلة نصية. الوكيل يرى النتيجة ويقرر ما سيفعله بعد ذلك — سواء كان إصلاح خطأ، أو الاستمرار بالمخرجات، أو إعلان اكتمال المهمة.
هذه البنية تفصل بشكل نظيف طبقة التفكير عن طبقة التنفيذ. LLM يخطط ويستدعي الأدوات؛ البيئة العازلة تشغل الكود دون كشف أي شيء خارج الحاوية.
ما تتعامل معه البيئة العازلة
- أوامر الشل (
bash،git،python،node) - عمليات نظام الملفات داخل البيئة العازلة
- طلبات الشبكة من داخل الحاوية
- المهام الخلفية طويلة الأمد مع استرجاع غير متزامن للنتائج
- الإيقاف المؤقت والاستئناف لسير العمل متعدد الجلسات
ما لا تسمح به: الوصول إلى النظام المضيف، أو البيئات العازلة الأخرى، أو بيانات الاعتماد التي لم تمررها صراحةً. هذا الاحتواء هو ما يجعل من الآمن ترك النموذج يكتب ويشغل كودًا عشوائيًا.
الخطوة 4: التقييم — معرفة متى يكون الوكيل صحيحًا
التقييم هو أكثر خطوة يتم تخطيها في تطوير الوكيل. الوكيل الذي ينتج مخرجات ليس هو نفس الوكيل الذي ينتج مخرجات صحيحة.
ثلاثة مستويات من التقييم
1. الفحوصات الحتمية
للمهام ذات الإجابات الصحيحة المعروفة — تحليل تاريخ، حساب قيمة، استخراج حقل — اكتب اختبارات وحدة ضد مخرجات الوكيل. هذه رخيصة في التشغيل وتلتقط الانحدارات بسرعة.
2. الفحوصات القائمة على التنفيذ
لوكلاء البرمجة، الإشارة الأكثر موثوقية هي: هل يعمل الكود فعليًا وينتج النتيجة الصحيحة؟ قم بتنفيذ مخرجات الوكيل في البيئة العازلة وقارن stdout بالقيم المتوقعة. هذا أقوى من مقارنة نص الكود لأنه يختبر السلوك، وليس بناء الجملة.
def evaluate_code_task(agent_code: str, expected_stdout: str) -> bool:
result = run_code(agent_code)
return result.strip() == expected_stdout.strip()
3. LLM كحكم
للمهام مفتوحة النهاية — كتابة ملخص، الإجابة على سؤال بحثي — استخدم استدعاء LLM ثانيًا لتصنيف المخرجات وفقًا لمعايير. اجعل المعايير محددة: “هل تضمنت الإجابة مثال كود عملي؟” أفضل من “هل الإجابة جيدة؟” المعايير الغامضة تنتج درجات غير متسقة.
تتبع أنماط الفشل
الوكلاء يفشلون في مجموعة متكررة من الأنماط:
| الفشل | السبب | الإصلاح |
|---|---|---|
| حلقة استدعاء الأداة | النموذج يستمر في استدعاء نفس الأداة | تعيين max_steps؛ تسجيل الخطوات الوسيطة |
| عدم تطابق الوسائط | المخطط لا يتطابق مع التنفيذ | التحقق من صحة وسائط الأداة قبل الاستدعاء |
| تجاوز السياق | الجلسة تنمو خارج حد سياق النموذج | تلخيص أو اقتطاع نتائج الأدوات الأقدم |
| اسم أداة مختلق | النموذج يخترع أداة غير موجودة | استخدام قائمة أدوات صارمة مع tool_choice: "auto" |
التقييم ليس مرحلة منفصلة في النهاية — إنه جزء من التطوير. كل مرة توسع فيها قدرات الوكيل، اكتب اختبارًا واحدًا على الأقل من شأنه اكتشاف انحدار في السلوك الجديد.
اختيار نموذج لتفكير الوكيل
معظم الدروس التعليمية افتراضيًا تستخدم نماذج مغلقة المصدر لتفكير الوكيل. هذا يعمل بشكل جيد للنمذجة الأولية، لكن في الإنتاج — حيث قد يقوم الوكيل بعمل 20 استدعاء LLM لكل مهمة — تصبح تكلفة النموذج وحدود المعدل قيودًا حقيقية.
النماذج مفتوحة المصدر المستضافة عبر Novita LLM API تطابق بشكل متزايد أداء النماذج مغلقة المصدر في المهام التي يحتاجها الوكلاء فعليًا: استدعاء الدوال، المخرجات المنظمة، والتفكير متعدد الخطوات. نماذج مثل Llama 3.3 70B و DeepSeek-V3 تعطي معايير على مستوى GPT-4 في البرمجة واتباع التعليمات بينما تعمل بجزء بسيط من التكلفة. لأحمال العمل الوكيلة عالية الإنتاجية، هذا الفرق يتراكم بسرعة.
Novita API متوافق تمامًا مع OpenAI، لذا التبديل من نموذج مغلق المصدر إلى نموذج مفتوح المصدر هو تغيير في سطر واحد:
# قبل
model = "gpt-4o"
# بعد — نفس عقد API، تكلفة أقل
model = "meta-llama/llama-3.3-70b-instruct"
لحلقات الوكيل الحساسة لزمن الاستجابة، النماذج الأصغر مثل meta-llama/llama-3.1-8b-instruct تستجيب بشكل أسرع وتكلف أقل لكل خطوة، وهو ما يهم عندما تتضمن كل مهمة مستخدم العديد من الاستدعاءات المتسلسلة. نمط عملي: استخدم نموذجًا أسرع وأصغر لخطوات التوجيه والتصنيف، ونموذجًا أكبر لخطوات التفكير المعقدة التي تحتاج سعة أكبر.
مكتبة نماذج Novita تتضمن أيضًا خيارات قوية لمهام فرعية وكيلة محددة — نماذج رؤية لنتائج الأدوات القائمة على الصور، نماذج تضمين للاسترجاع، ونماذج متخصصة في البرمجة لوكلاء البرمجة — وكلها يمكن الوصول إليها من نفس نقطة نهاية API.
تجميع الأجزاء — وكيل عامل أدنى
إليك وكيل كامل يأخذ مسألة رياضية، ويولد كود بايثون لحلها، ويشغله في بيئة عازلة، ويعيد النتيجة المؤكدة:
import os
import json
from openai import OpenAI
from novita_sandbox.code_interpreter import Sandbox
client = OpenAI(
base_url="https://api.novita.ai/v3/openai",
api_key=os.environ["NOVITA_API_KEY"],
)
sandbox = Sandbox.create()
def run_code(code: str) -> str:
result = sandbox.run_code(code)
stdout = "\n".join(result.logs.stdout)
stderr = "\n".join(result.logs.stderr)
return stdout or stderr or "(no output)"
tool_fns = {"run_code": run_code}
tools = [
{
"type": "function",
"function": {
"name": "run_code",
"description": "Execute Python code. Returns stdout or stderr.",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string"}
},
"required": ["code"]
}
}
}
]
messages = [
{
"role": "system",
"content": "Solve the task by writing and running Python code. Use run_code to execute it. Stop when you have a verified answer."
},
{
"role": "user",
"content": "What is the sum of all even Fibonacci numbers under 1,000,000?"
}
]
for _ in range(10):
response = client.chat.completions.create(
model="meta-llama/llama-3.1-8b-instruct",
messages=messages,
tools=tools,
tool_choice="auto",
)
choice = response.choices[0]
if choice.finish_reason == "stop":
print(choice.message.content)
break
tool_call = choice.message.tool_calls[0]
fn_args = json.loads(tool_call.function.arguments)
result = tool_fns[tool_call.function.name](**fn_args)
messages.append(choice.message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
sandbox.kill()
هذا يغطي حلقة الوكيل الكاملة في حوالي 50 سطرًا: التخطيط، استدعاء الأداة، التنفيذ، تراكم السياق، حارس الحد الأقصى للخطوات، والتفكيك النظيف للبيئة العازلة. الوكيل يكتب الكود، يشغله، يرى المخرجات، وإما يستمر أو يعطي إجابة نهائية.
لتوسيعه: أضف المزيد من الأدوات إلى tool_fns و tools، واستبدل بنموذج أكبر للمهام الأصعب، أو استبدل run_code للبيئة العازلة بأي دالة أخرى — بحث ويب، استعلام قاعدة بيانات، استدعاء API. الحلقة تبقى كما هي.
الأسئلة الشائعة
ما الفرق بين وكيل الذكاء الاصطناعي وروبوت المحادثة؟
روبوت المحادثة يولد ردودًا نصية. الوكيل يتخذ إجراءات — استدعاء أدوات، تشغيل كود، الاستعلام عن APIs — ويستخدم النتائج لمواصلة التفكير نحو هدف. وجود استدعاءات الأدوات وحلقة التغذية الراجعة هو ما يجعل الشيء وكيلًا.
هل أحتاج إلى إطار عمل مثل LangChain لبناء وكيل ذكاء اصطناعي؟
لا. حلقة الوكيل الأساسية بسيطة بما يكفي لتنفيذها مباشرة ضد أي API متوافق مع OpenAI. الأطر تضيف قيمة للتنسيق المعقد (أنظمة متعددة الوكلاء، ذاكرة دائمة، مخرجات متدفقة)، لكنها تضيف طبقات تجريد قد تجعل التصحيح أكثر صعوبة. ابدأ بـ API الخام، ثم أضف إطار عمل إذا كنت بحاجة حقًا إلى ما يوفره.
كيف أمنع الوكيل من تشغيل كود خطير؟
استخدم بيئة تنفيذ معزولة. كود الوكيل يعمل داخل حاوية معزولة ليس لها وصول إلى نظام المضيف أو العمليات الأخرى. البيئة العازلة يتم التخلص منها بعد انتهاء الجلسة، لذا أي تغييرات يقوم بها الوكيل تكون محصورة في تلك الحاوية.
هل يمكن للنماذج مفتوحة المصدر التعامل مع استدعاء الدوال بشكل موثوق؟
نعم — مع تحفظات على حجم النموذج. Llama 3.3 70B و DeepSeek-V3 و Qwen 2.5 72B جميعها تتعامل بشكل جيد مع استدعاء الأدوات المنظم. النماذج الأصغر (7B–8B) تعمل مع مخططات أدوات بسيطة ومحددة جيدًا ولكنها قد تواجه صعوبة مع التعليمات الغامضة أو سلاسل التفكير المعقدة متعددة الخطوات. لوكلاء الإنتاج، اختبر على توزيع مهامك الفعلي قبل الالتزام بنموذج.
كم عدد الخطوات التي يجب أن يتخذها الوكيل لكل مهمة؟
ضع حدًا أقصى صارمًا — 10 إلى 20 خطوة هو النموذجي لمعظم المهام. سجل الخطوات الوسيطة حتى تتمكن من رؤية ما فعله الوكيل بالضبط وتصحيح الأخطاء. المهام التي تحتاج باستمرار إلى خطوات أكثر تستفيد عادة من تقسيمها إلى وكلاء فرعيين أو مهام فرعية بدلاً من تمديد حد الحلقة.
