alawadi.cloudمستندات

الأدوات و MCP

امنح وكيلك أدوات من خوادم MCP، وشغّل حلقة استدعاء الأدوات المدمجة، واعرض وكيلك كخادم MCP، وتتبّع كل استدعاء.

الوكيل الذي لا يستطيع سوى المحادثة هو نصف وكيل. بيئة التشغيل التي تستضيف handler.py الخاص بك تأتي بمنظومة أدوات كاملة: يستطيع وكيلك استهلاك خوادم أدوات MCP خارجية، وتشغيل حلقة استدعاء أدوات محدودة الخطوات نيابة عنك، وعرض أدواته هو نفسه كخادم MCP تستهلكه وكلاء آخرون. وكل استدعاء يُتتبَّع بمعرّف استدعاء يمكنك متابعته.

وصّل خوادم أدوات MCP

عرّف خوادم MCP الخارجية في متغيّر البيئة AGENT_MCP_SERVERS — مصفوفة JSON تحقنها المنصّة لكل وكيل، فإضافة خادم أدوات تصبح تغييراً في الإعدادات لا في الكود:

[
  {"name": "docs", "url": "https://mcp.example.com/mcp", "token_env": "MCP_DOCS_TOKEN"},
  {"name": "legacy", "url": "https://mcp.legacy.example.com/sse", "transport": "sse"}
]
  • يشير token_env إلى متغيّر بيئة آخر يحمل رمز الاستيثاق (bearer token). لا تضع الرمز نفسه في AGENT_MCP_SERVERS أبداً — كل سرّ هو قيد مستقل، يُحقن تماماً كمفاتيح المزوّدين لديك.
  • يجب أن تكون الروابط https://. قد تحمل استدعاءات الأدوات وسائط حسّاسة عبر الشبكة، لذا يُرفض http:// الصريح ما لم تضبط ALLOW_INSECURE_MCP=true (للتطوير المحلي فقط).
  • القيمة الافتراضية لـ transport هي streamable-http؛ استخدم "sse" للخوادم القديمة العاملة بـ SSE.

ثم تمنحك حزمة alawadi_agent الأدوات كواصفات (descriptors) محمولة:

from alawadi_agent import list_all_tools, call_tool

tools = list_all_tools()   # every configured server, tagged with its name
# → [{"name": "search", "description": "...", "input_schema": {...}, "server": "docs"}]

result = call_tool("docs", "search", {"query": "refund policy"})
# → {"text": "...", "structured": {...} | None, "is_error": False}

أما الدوال غير المتزامنة (async) فتستخدم نسخ list_tools_async و list_all_tools_async وcall_tool_async بدلاً منها.

حلقة استدعاء الأدوات: run_agent()

الشكل الشائع للوكيل هو «نموذج + أدوات، وكرّر حتى يتوقّف النموذج عن استدعاء الأدوات». وrun_agent هو التنفيذ القياسي لهذه الحلقة في بيئة التشغيل — محدود بسقف صارم هو max_steps من جولات النموذج، مع تمرير أخطاء الأدوات إلى النموذج كبيانات بدلاً من إسقاط استدعاءك بالكامل:

from alawadi_agent import run_agent, list_all_tools

def handle(request):
    tools = list_all_tools() + [{
        "name": "add",
        "description": "Add two numbers",
        "input_schema": {"type": "object",
                         "properties": {"a": {"type": "number"}, "b": {"type": "number"}}},
        "fn": lambda a, b: a + b,   # local callable
    }]
    return run_agent(request.get("message", ""), tools=tools, max_steps=8)

يمكن أن تكون الأدوات واصفات MCP آتية من list_all_tools() (تحمل مفتاح server وتُنفَّذ عن بُعد) أو دوال محلية تحمل مفتاح fn. تحوّل الحلقة هذه الأدوات إلى صيغة OpenAI للأدوات نيابة عنك. وتُعيد {"output": str, "steps": [...]} — حيث يسجّل steps محتوى كل جولة واستدعاءات الأدوات فيها، وهو ما يغذّي التتبّع أدناه.

للسقف أهميته: حلقة أدوات جامحة تعني فاتورة جامحة. عند بلوغ max_steps، يجري اتصال أخير دون أدوات لفرض إجابة نصّية ممّا جمعته الحلقة.

اعرض وكيلك كخادم MCP

العقد نفسه يعمل في الاتجاه المعاكس. صدّر TOOLS وhandle_tool من ملف handler.py، فتخدمهما بيئة التشغيل كخادم MCP على POST /mcp في اسم مضيف وكيلك — عبر Streamable HTTP JSON-RPC، مع دعم initialize وping و tools/list وtools/call:

TOOLS = [{"name": "greet", "description": "Greet someone",
          "input_schema": {"type": "object",
                           "properties": {"who": {"type": "string"}}}]

def handle_tool(name, arguments):
    if name == "greet":
        return "hello " + arguments.get("who", "world")
    raise KeyError(name)

تقبع /mcp خلف مصادقة رمز الاستدعاء نفسها المستخدمة في /invoke، فلا يستطيع استعراض أدواتك أو تشغيلها إلا من تثق بهم. ويمكن لوكيل آخر أن يستهلك وكيلك بإعلانه في AGENT_MCP_SERVERS الخاص به مع "url": "https://<name>-<id>.alawadi.cloud/mcp". وإن لم يصدّر معالجك TOOLS وhandle_tool، تُعيد /mcp الرمز 404 (mcp_not_available).

تتبّع كل استدعاء

كل استدعاء لـ /invoke وكل استدعاء tools/call عبر MCP يُعيد ترويسة X-Invocation-Id في الردّ، ويُسجَّل في مخزن دائري في الذاكرة — المعرّف والطابع الزمني والنوع والحالة وزمن الاستجابة، دون أجسام الطلبات أو الرموز أبداً. وتُعيد GET /invocations (بمصادقة رمز الاستدعاء نفسها كما في /invoke) الاستدعاءات الأخيرة، مرتّبة من الأحدث إلى الأقدم:

curl https://my-agent-a1b2c3.alawadi.cloud/invocations \
  -H "Authorization: Bearer $AGENT_INVOKE_TOKEN"

يحتفظ المخزن بآخر 200 استدعاء افتراضياً؛ ويمكن ضبطه عبر AGENT_INVOCATION_LOG_SIZE. هذه هي المغذّاة التي تقف خلف سجلّ كل وكيل في البوابة — طابقها مع X-Invocation-Id الذي سجّلته لدى الطرف المُستدعي عند تعقّب تنفيذ سيّئ.

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

في هذه الصفحة