بناء وكيل برمجة باستخدام Novita Agent Sandbox

بناء وكيل برمجة باستخدام Novita Agent Sandbox

لديك وكيل ذكاء اصطناعي، وتريد تشغيله في بيئة آمنة يمكنه الوصول فيها إلى الموارد المناسبة مثل نظام الملفات والقدرة على تنفيذ الأوامر (مثل أوامر الشل) دون خطر «إتلاف أي شيء». إذًا، ما هي الخيارات المتاحة أمامك؟

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

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

  • إنشاء وتعديل ملفات الكود في نظام الملفات
  • تنفيذ أوامر مثل git أو python أو node
  • التعاون مع المطورين عن طريق تنفيذ واختبار الكود مباشرة في البيئة

في هذا المقال، سنشرح خطوات بناء وكيل برمجة من هذا النوع. سنستخدم نماذج اللغات الكبيرة من Novita (LLMs) مع استدعاء الدوال، إلى جانب Novita agent sandbox كبيئة آمنة لنا. ولإضافة لمسة إضافية، سننشئ واجهة سهلة الاستخدام باستخدام Gradio، وننشرها على Hugging Face Spaces.

لنغوص في الموضوع!

Agent Sandbox

تعد Agent Sandbox من Novita بيئة تشغيل مصممة خصيصًا لوكلاء الذكاء الاصطناعي. توفر إعداد سحابي آمن ومنعزل يعمل كجهاز كمبيوتر افتراضي. داخل هذه البيئة، يمكن للوكلاء تنفيذ الكود المولد بأمان دون المخاطرة بالنظام الأساسي.

الميزات الرئيسية لـ Novita Agent Sandbox

  • آمن: بيئة الحماية الرملية معزولة بالكامل، لذا يمكن للوكيل الوصول فقط إلى موارده الخاصة.
  • بدء سريع: يتم تشغيل البيئات الجديدة في أقل من 200 مللي ثانية.
  • جهاز افتراضي: نظرًا لأن sandbox يعمل كجهاز افتراضي (VM)، يمكن للوكلاء تشغيل الكود بأي لغة برمجة.
  • إيقاف مؤقت واستئناف: يمكنك إيقاف sandbox مؤقتًا في أي وقت واستئنافها لاحقًا.
  • مهام خلفية: يمكن للوكلاء تشغيل المهام في الخلفية واسترداد النتائج بشكل غير متزامن.

تثبيت حزمة SDK

لاستخدام Novita Sandbox، ستحتاج إلى حزمة SDK، التي تدعم كل من Python و TypeScript/JavaScript. في هذا الدليل، سنستخدم حزمة SDK الخاصة بـ Python:

pip install novita-sandbox

بعد التثبيت، عيّن مفتاح API الخاص بـ Novita كمتغير بيئة:

export NOVITA_API_KEY=your_api_key_here

اختبار sandbox

مع اكتمال جميع الإعدادات، لننشئ sandbox وننفذ بعض العمليات الأساسية:

from novita_sandbox.code_interpreter import Sandbox

sandbox = Sandbox.create()

files = sandbox.files.list("/")

for file in files:
    print(file.name)

result = sandbox.commands.run('pwd')
print(result)

sandbox.kill()

يوضح هذا المثال كيفية:

  • إنشاء نسخة من sandbox
  • الوصول إلى نظام الملفات باستخدام كائن files
  • تنفيذ الأوامر باستخدام طريقة commands.run
  • تحرير الموارد باستخدام kill بمجرد الانتهاء

الآن بعد أن استكشفنا أساسيات الوصول إلى نظام الملفات وتنفيذ الأوامر، نحن جاهزون لبناء وكيل البرمجة الخاص بنا الذي يستخدم قدرات sandbox هذه كأدوات.

بناء وكيل برمجة

لبناء وكيل البرمجة الخاص بنا، نحتاج إلى نموذج لغة كبير (LLM) يدعم استدعاء الدوال. توفر Novita عدة نماذج يمكنها القيام بذلك. لكي يتصرف وكيلنا كمساعد برمجة، يجب أن يحتوي على مجموعة الدوال المناسبة.

لنفكر في ما يفعله المبرمج البشري. عادة ما يكتب الكود ويقرأه وينفذه. لذا يجب أن يكون وكيلنا قادرًا على:

  • الكتابابة في ملف
  • القراءة من ملف
  • تنفيذ الأوامر
  • الكتابابة في عدة ملفات في آن واحد

إعداد الوكيل

بما أن نماذج Novita متوافقة مع OpenAI، يمكننا استخدام OpenAI SDK للتفاعل معها. لنقم بتثبيتها:

pip install openai

بعد التثبيت، عيّن مفتاح API الخاص بـ Novita كمتغير بيئة تمامًا كما فعلنا سابقًا. بمجرد الانتهاء من ذلك، يمكننا بدء البرمجة بإضافة الاستيرادات الخاصة بنا:

from openai import OpenAI
import os
import json
from novita_sandbox.code_interpreter import Sandbox

الآن، لننشئ نسخة من عميل OpenAI:

client = OpenAI(
    base_url="https://api.novita.ai/openai",  
    api_key=os.environ["NOVITA_API_KEY"],
)

هنا، نشير العميل إلى عنوان URL الأساسي لـ Novita بدلاً من عنوان OpenAI الأساسي، ونستخدم مفتاح API الخاص بـ Novita للمصادقة.

بعد ذلك، سننشئ نسخة sandbox التي سوف يستخدمها وكيلنا:

sandbox = Sandbox.create(timeout=1200)

يحدد معامل timeout مدة بقاء sandbox نشطًا. في هذه الحالة، قمنا بتعيينه إلى 10 دقائق.

تعريفات الدوال

الآن يمكننا تعريف الدوال التي سوف يستخدمها وكيلنا.

1. قراءة الملف

تأخذ هذه الدالة مسار ملف وتقرأ محتوياته باستخدام كائن files الخاص بـ sandbox.

def read_file(path: str):

   print(f"[DEBUG] read_file called with path: {path}")

   try:

       content = sandbox.files.read(path)

       print(f"[DEBUG] read_file result: {content}")

       return content  # returns string content

   except Exception as e:

       print(f"[DEBUG] read_file error: {e}")

       return f"Error reading file: {e}"

2. كتابة الملف

هذه الدالة تكتب البيانات إلى مسار ملف محدد.

def write_file(path: str, data: str):
   print(f"[DEBUG] write_file called with path: {path}")
   try:
       sandbox.files.write(path, data)
       msg = f"File created successfully at {path}"
       print(f"[DEBUG] {msg}")
       return msg
   except Exception as e:
       print(f"[DEBUG] write_file error: {e}")
       return f"Error writing file: {e}"

3. كتابة عدة ملفات

تعمل هذه الدالة تمامًا مثل write_file ولكنها تتعامل مع عدة ملفات في آن واحد.

def write_files(files: list):
   print(f"[DEBUG] write_files called with {len(files)} files")
   try:
       sandbox.files.write_files(files)
       msg = f"{len(files)} file(s) created successfully"
       print(f"[DEBUG] {msg}")
       return msg
   except Exception as e:
       print(f"[DEBUG] write_files error: {e}")
       return f"Error writing multiple files: {e}"

4. تنفيذ الأوامر

تنفذ هذه الدالة أوامر الشل داخل sandbox وتعيد المخرجات القياسية.

def run_commands(command: str):
   print(f"[DEBUG] run_commands called with commands: {command}")
   try:
       result = sandbox.commands.run(command)
       print(f"[DEBUG] run_commands result: {result}")
       return result.stdout  # returns CommandResult object
   except Exception as e:
       print(f"[DEBUG] run_commands error: {e}")
       return f"Error running commands: {e}"

تسجيل الأدوات

الآن بعد أن أصبحت جميع دوالنا جاهزة، سنقوم بتسجيلها كأدوات يمكن لنموذج اللغة الكبير (LLM) استدعاؤها عند الحاجة. يتضمن تعريف كل أداة اسم الدالة والوصف ومخطط المعاملات.

tools = [

   {

       "type": "function",

       "function": {

           "name": "read_file",

           "description": "Read contents of a file inside the sandbox",

           "parameters": {

               "type": "object",

               "properties": {

                   "path": {"type": "string", "description": "File path in the sandbox"}

               },

               "required": ["path"],

           },

       },

   },

   {

       "type": "function",

       "function": {

           "name": "write_file",

           "description": "Write a single file inside the sandbox",

           "parameters": {

               "type": "object",

               "properties": {

                   "path": {"type": "string", "description": "File path in the sandbox"},

                   "data": {"type": "string", "description": "Content to write"},

               },

               "required": ["path", "data"],

           },

       },

   },

   {

       "type": "function",

       "function": {

           "name": "write_files",

           "description": "Write multiple files inside the sandbox",

           "parameters": {

               "type": "object",

               "properties": {

                   "files": {

                       "type": "array",

                       "items": {

                           "type": "object",

                           "properties": {

                               "path": {"type": "string"},

                               "data": {"type": "string"},

                           },

                           "required": ["path", "data"],

                       },

                   }

               },

               "required": ["files"],

           },

       },

   },

   {

       "type": "function",

       "function": {

           "name": "run_commands",

           "description": "Run a single shell command inside the sandbox working directory",

           "parameters": {

               "type": "object",

               "properties": {

                   "command": {

                       "type": "string",

                       "description": "The shell command to run, e.g. 'ls' or 'python main.py'",

                   }

               },

               "required": ["command"],

           },

       },

   }

]

بعد تسجيل الأدوات، لننتقل إلى إنشاء حلقة محادثة تستخدم وكيلنا وجميع الأدوات التي قمنا بتعريفها.

حلقة المحادثة

الآن سننشئ حلقة محادثة بسيطة تسمح للمستخدم بالتفاعل مع وكيل البرمجة. ستحتفظ الحلقة بقائمة من الرسائل وتتعامل مع استدعاءات الدوال كلما طلب الوكيل ذلك.

messages = []

print("💬 Enter your queries (type 'exit' to quit):")

while True:

   user_input = input("You: ")

   if user_input.lower() == "exit":

       break

   # Append user message

   messages.append({"role": "user", "content": user_input})

   # Send to model

   response = client.chat.completions.create(

       model=model,

       messages=messages,

       tools=tools,

   )

   assistant_msg = response.choices[0].message

   messages.append(assistant_msg)

   if assistant_msg.tool_calls:

       print(f"[DEBUG] Assistant requested {len(assistant_msg.tool_calls)} tool call(s).")

       for tool_call in assistant_msg.tool_calls:

           fn_name = tool_call.function.name

           fn_args = json.loads(tool_call.function.arguments)

           print(f"[DEBUG] Tool call detected: {fn_name} with args {fn_args}")

           if fn_name == "read_file":

               fn_result = read_file(**fn_args)

           elif fn_name == "write_file":

               fn_result = write_file(**fn_args)

           elif fn_name == "write_files":

               fn_result = write_files(**fn_args)

           elif fn_name == "run_commands":

               fn_result = run_commands(**fn_args)

           else:

               fn_result = f"Error: Unknown tool {fn_name}"

               print(f"[DEBUG] Unknown tool requested: {fn_name}")

           # Append result back

           messages.append({

               "tool_call_id": tool_call.id,

               "role": "tool",

               "content": str(fn_result),

           })

       # Get model's final answer with tool results

       follow_up = client.chat.completions.create(

           model=model,

           messages=messages,

       )

       final_answer = follow_up.choices[0].message

       messages.append(final_answer)

       print("Assistant:", final_answer.content)

   else:

       print("Assistant:", assistant_msg.content)

sandbox.kill()

print("[DEBUG] Sandbox terminated. 👋")

تحافظ حلقة المحادثة هذه على استمرار التفاعل، وتسمح للوكيل باستدعاء أي من الأدوات المسجلة عند الحاجة، وتنظف sandbox عند خروج المستخدم.

إنشاء واجهة مستخدم باستخدام Gradio

لدينا الآن وكيل برمجة يعمل بكامل طاقته ويمكنه الدردشة معنا، لكن التفاعل من خلال REPL ليس مثيرًا للاهتمام حقًا. لجعل التجربة أكثر جاذبية، سنمنح وكيلنا واجهة Gradio بسيطة.

إنشاء واجهة مستخدم Gradio بسيط. سنستخدم gr.ChatInterface لإدارة تفاعلات المحادثة الخاصة بنا وربطها بالمنطق الذي بنيناه سابقًا. إلى جانب ذلك، سنتضمن واجهة أوامر لتشغيل أوامر الشل داخل sandbox، بالإضافة إلى قائمة منسدلة تسمح لنا بتحديد النموذج المراد استخدامه.

لتحديث الكود السابق لدينا لدعم Gradio، سنقوم باستبدال حلقة المحادثة والسطرين الأخيرين بما يلي:

# --- Persistent chat messages ---

messages = []

# --- Global model setter ---

def set_model(selected_model):

   global model

   model = selected_model

   print(f"[DEBUG] Model switched to: {model}")

   return f"✅ Model switched to **{model}**"

def chat_fn(user_message, history):

   global messages, model

   messages.append({"role": "user", "content": user_message})

   # Send to model

   response = client.chat.completions.create(

       model=model,

       messages=messages,

       tools=tools,

   )

   assistant_msg = response.choices[0].message

   messages.append(assistant_msg)

   output_text = ""

   if assistant_msg.tool_calls:

       print(f"[DEBUG] Assistant requested {len(assistant_msg.tool_calls)} tool call(s).")

       for tool_call in assistant_msg.tool_calls:

           fn_name = tool_call.function.name

           fn_args = json.loads(tool_call.function.arguments)

           print(f"[DEBUG] Tool call detected: {fn_name} with args {fn_args}")

           if fn_name == "read_file":

               fn_result = read_file(**fn_args)

           elif fn_name == "write_file":

               fn_result = write_file(**fn_args)

           elif fn_name == "write_files":

               fn_result = write_files(**fn_args)

           elif fn_name == "run_commands":

               fn_result = run_commands(**fn_args)

           else:

               fn_result = f"Error: Unknown tool {fn_name}"

           messages.append({

               "tool_call_id": tool_call.id,

               "role": "tool",

               "content": str(fn_result),

           })

       follow_up = client.chat.completions.create(

           model=model,

           messages=messages,

       )

       final_answer = follow_up.choices[0].message

       messages.append(final_answer)

       output_text = final_answer.content

   else:

       output_text = assistant_msg.content

   return output_text

# --- Command Interface function ---

def execute_command(command):

   if not command.strip():

       return "⚠️ Please enter a command."

   print(f"[DEBUG] Executing command from interface: {command}")

   output = run_commands(command)

   return f"```bash\
{output}\
```" if output else "✅ Command executed (no output)."

# --- Gradio UI ---

with gr.Blocks(title="Novita Sandbox App") as demo:

   gr.Markdown("## 🧠 Novita Sandbox Agent")

   gr.Markdown(

   "This app is an AI-powered **code agent** that lets you chat with intelligent assistants backed by **Novita AI LLMs**. These agents can write, read, and execute code safely inside a **Novita sandbox**, providing a secure environment for running commands, testing scripts, and managing files, all through an intuitive chat interface with model selection and command execution built right in."

)

   with gr.Row(equal_height=True):

       # Left: Chat Interface

       with gr.Column(scale=2):

           gr.Markdown("### 💬 Chat Interface")

           gr.ChatInterface(chat_fn)

       # Right: Command Interface

       with gr.Column(scale=1):

           gr.Markdown("### 💻 Command Interface")

           # Model selector

           model_selector = gr.Dropdown(

               label="Select Model",

               choices=[

                   "meta-llama/llama-3.3-70b-instruct",

                   "deepseek/deepseek-v3.2-exp",

                   "qwen/qwen3-coder-30b-a3b-instruct",

                   "openai/gpt-oss-120b",

                   "moonshotai/kimi-k2-instruct",

               ],

               value=model,

               interactive=True,

           )

           model_status = gr.Markdown(f"✅ Current model: **{model}**")

           model_selector.change(set_model, inputs=model_selector, outputs=model_status)

           command_input = gr.Textbox(

               label="Command",

               placeholder="e.g., ls, python main.py",

               lines=1,

           )

           with gr.Row():

               run_btn = gr.Button("Run", variant="primary", scale=0)

           command_output = gr.Markdown("Command output will appear here...")

           run_btn.click(execute_command, inputs=command_input, outputs=command_output)

# --- Cleanup on exit ---

atexit.register(lambda: (sandbox.kill(), print("[DEBUG] Sandbox terminated. 👋")))

if __name__ == "__main__":

   demo.launch()

في هذا الإصدار، تتعامل دالة chat_fn مع كل تبادل للرسائل بين المستخدم والوكيل. تأخذ واجهة gr.ChatInterface هذه الدالة كمدخل وتدير تفاعلات واجهة المستخدم تلقائيًا.

عند بدء تشغيل تطبيق Gradio، فإنه يشغل وكيلك داخل المتصفح مما يمنح المستخدم واجهة محادثة نظيفة وتفاعلية. أخيرًا، نقوم بتسجيل روتين تنظيف باستخدام atexit لضمان إنهاء sandbox بشكل صحيح عند توقف التطبيق.

الآن لدينا وكيل برمجة مدعوم بالذكاء الاصطناعي يعمل بأمان داخل sandbox، ومجهز بواجهة محادثة Gradio ودية.

يمكنك العثور على [الكود الكامل على GitHub](https://github.com/novitalabs/Novita-CollabHub/tree/main/examples/sandbox-chat-agent).

## اختبار وكيل البرمجة

لاستخدام الوكيل، نحتاج إلى تشغيل كود Gradio الخاص بنا كبرنامج نصي.

python gradio_chat.py


عند القيام بذلك، سيكون لدينا تطبيق Gradio يعمل على localhost. مع هذا، يمكننا إجراء محادثات مع وكيل البرمجة الخاص بنا وسوف ينفذ الوكيل جميع إجراءاتنا داخل sandbox.

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

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

## النشر على Hugging Face Spaces

لدينا حاليًا وكيل البرمجة الخاص بنا يعمل محليًا على جهاز الكمبيوتر الخاص بنا. الآن، لجعله متاحًا لباقي العالم، سنقوم بنشره على Hugging Face Spaces. يتيح لنا Hugging Face Spaces استضافة الكود والتطبيق الخاصين بنا في مكان واحد. لنبدأ.

### إنشاء المساحة

انتقل إلى [Hugging Face](https://huggingface.co/spaces) وأنشئ مساحة جديدة لوكيل البرمجة الخاص بك عن طريق إعطائها اسمًا فريدًا.

![](https://ajldkp7ny4bysrxe.public.blob.vercel-storage.com/uploads/2025/12/Agent-Sandbox-and-HF-Spaces_Img0.webp)

بعد ذلك، حدد حزمة SDK للمساحة، والتي في حالتنا هي Gradio. اختر القالب الفارغ (Blank) لأننا نملك بالفعل كود التطبيق الخاص بنا.

![](https://ajldkp7ny4bysrxe.public.blob.vercel-storage.com/uploads/2025/12/Agent-Sandbox-and-HF-Spaces_Img1.webp)

ثم، حدد العتاد. نظرًا لأن وكيلنا و sandbox مدعومان من Novita، فلا نحتاج إلى أي عتاد متخصص. خيار Basic CPU كافٍ. بمجرد الانتهاء، انقر على إنشاء المساحة (Create Space).

![](https://ajldkp7ny4bysrxe.public.blob.vercel-storage.com/uploads/2025/12/Agent-Sandbox-and-HF-Spaces_Img6.webp)

سوف ينشئ Hugging Face المساحة مع ملف README.md وملف .gitignore.

هناك طرق عديدة لإضافة الكود الخاص بك، ولكن أبسطها هو النقر على Contribute → Add file.

![](https://ajldkp7ny4bysrxe.public.blob.vercel-storage.com/uploads/2025/12/Agent-Sandbox-and-HF-Spaces_Img3.webp)

أنشئ ملف requirements.txt وتضمين التبعيات التالية:

openai novita-sandbox


### إضافة متغيرات البيئة

قبل أن نتمكن من تشغيل التطبيق الخاص بنا، نحتاج إلى تعيين NOVITA_API_KEY كمتغير بيئة.

للقيام بذلك، انتقل إلى إعدادات المساحة الخاصة بك (Settings)، وانتقل إلى قسم المتغيرات والأسرار (Variables and secrets)، وأضف سرًا جديدًا باسم NOVITA_API_KEY مع مفتاح API الخاص بك كقيمة له.

### إعداد التطبيق

بعد تعيين متغير البيئة، حان الوقت لإنشاء تطبيقنا.

أنشئ ملفًا جديدًا باسم app.py وألصق كود وكيل Gradio الكامل الخاص بنا بداخله.

![](https://ajldkp7ny4bysrxe.public.blob.vercel-storage.com/uploads/2025/12/Agent-Sandbox-and-HF-Spaces_Img4.webp)

بمجرد حفظ الملف، سوف يبدأ Hugging Face تلقائيًا في بناء مساحتك.

![](https://ajldkp7ny4bysrxe.public.blob.vercel-storage.com/uploads/2025/12/Agent-Sandbox-and-HF-Spaces_Img5.webp)

بعد اكتمال عملية البناء، سيكون وكيل البرمجة الخاص بك مباشرًا ومتاحًا على Hugging Face Spaces.

يمكنك الآن الدردشة مع وكيلك من خلال واجهة المحادثة التفاعلية.

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

وهذا كل شيء، لديك الآن وكيل برمجة يعمل بكامل طاقته داخل sandbox آمن، ومجهز بواجهة Gradio، ومنشر بسلاسة على Hugging Face Spaces.

## الخلاصة

في هذا المقال، استكشفنا كيفية الاستفادة من Novita Sandbox لبناء وكيل برمجة يعمل بكامل طاقته ويمكنه قراءة وإنشاء الملفات، وتنفيذ الأوامر، والعمل بأمان داخل بيئة آمنة.

ما بنيناه هنا هو مجرد البداية. تفتح sandbox الباب أمام إمكانيات لا حصر لها، بدءًا من إنشاء وكلاء تصور البيانات المدعومين بالذكاء الاصطناعي إلى تطوير وكلاء استخدام الحواسيب التي يمكنها التفاعل مع الأنظمة بذكاء.

أي شيء تقريبًا ممكن عندما تجمع بين الوكيل ومجموعة أدوات مخصصة مثل sandbox.