tools إلى جانب أدواتك؛ يشغّلها المزوّد، ويُبلغ التشغيل عن كل استدعاء في أحداثه، ويحتفظ به في السجل، ويحتسبه ضمن الاستهلاك.
الدوال المساعدة
تفرّق
isHostedTool(value) بين الأداة المستضافة والأداة المحلية. اسم كل دالة مساعدة هو أيضًا الاسم الذي يستخدمه النموذج والأحداث وusage.hostedToolCalls. في صيغة السجل (record) لـ tools يجب أن يكون مفتاح الأداة المستضافة هو اسمها (tools: { web_search: webSearch(), lookup }). أداة مستضافة تحمل اسم أداة أخرى تُنتج الخطأ LOUSHO_CONFIG_INVALID عند إنشاء الوكيل.
يستخدم المزوّد الخيارات التي يدعمها ويُسقط الباقي مع console.warn واحد. يقبل OpenAI الخيارات searchContextSize وuserLocation وallowedDomains (من دون maxUses ومن دون blockedDomains). ويقبل Anthropic الخيارات maxUses وallowedDomains وblockedDomains وuserLocation (من دون searchContextSize؛ وتنفيذ الشيفرة لديه بلا container). ويقبل OpenRouter الخيارات maxUses وallowedDomains وblockedDomains (تُرسل بصيغة excluded_domains الخاصة به) وsearchContextSize (من دون userLocation).
أي مزوّد يشغّل أي أداة
تحتاج الأدوات المستضافة إلى
ai 6 أو 7، باستثناء البحث على الويب في OpenRouter فهو يعمل مع ai 4 أيضًا. ومع ai 4 (و@ai-sdk/openai بإصدار 0.0.x أو 1.x) تُرفض كل أداة مستضافة أخرى. في OpenAI النموذج المستخدم هو نموذج Responses API الذي توفّره @ai-sdk/openai من الإصدار 2 فصاعدًا.
في Anthropic تستخدم الدوال المساعدة أحدث أداة خادم مؤرَّخة تصدّرها حزمة @ai-sdk/anthropic المثبّتة: تستخدم webSearch() أحدث tools.webSearch_*()، وتستخدم codeInterpreter() أحدث tools.codeExecution_*() (يسمّيها Anthropic code_execution؛ ويُبقي SDK الاسم code_interpreter في الأحداث وفي usage.hostedToolCalls على كل المزوّدات). الحزمة التي لا تحتوي أيًّا منهما تُرفض مع ذكر التوليفة التي ستعمل. لأدوات الخادم الأخرى في Anthropic (جلب الويب مثلًا) استخدم hostedTool('web_fetch', anthropic.tools.webFetch_20260318()).
في OpenRouter تضيف webSearch() أداة الخادم openrouter:web_search الخاصة بـ OpenRouter إلى الطلب، فيجري البحث من جهة OpenRouter لأي نموذج. يختار OpenRouter محرك البحث (بحث النموذج نفسه إن كان له بحث، وإلا Exa) ويحاسب على كل عملية بحث فوق الرموز، بالأسعار المنشورة في صفحة أسعار OpenRouter؛ وانظر دليل البحث على الويب لمعرفة المحركات. ولأن الخادم ينفّذ البحث داخل الطلب، لا يُبلغ النموذج عن أي استدعاء أداة. ويُبلغ SDK عن استدعاء مستضاف واحد لكل خطوة بحثت فيها، يقرؤه من استجابة OpenRouter: name: 'web_search'، وargs فارغة، والروابط المستشهد بها من تعليقات url_citation على هيئة sources، وresult: { requests } حين يُبلغ OpenRouter عن usage.server_tool_use.web_search_requests (وإلا تكون result هي {}؛ فالاختبار الحي لم يستلم عددًا). أما الاستعلام الذي بحث عنه النموذج فلا يُبلغ عنه.
التوليفة غير المدعومة ترفض التشغيل قبل أول استدعاء للنموذج مع الخطأ LOUSHO_HOSTED_TOOL_UNSUPPORTED، ويذكر المزوّد والأداة وما الذي سيعمل.
يعلن LLMProvider المخصّص ما يستطيع إرساله عبر supportsHostedTool(type) (قيمة type هي 'web_search' أو 'code_interpreter' أو 'file_search' أو 'custom' لـ hostedTool()) ويستلم الأدوات في GenerateOptions.hostedTools. المزوّد الذي لا يملك هذه الدالة لا يدعم أيًّا منها. وتسأل withRetry() وwithFallback() المزوّد المغلَّف (الأول).
أي أداة مزوّد من AI SDK: hostedTool()
ترسلhostedTool(name, tool) كائن أداة مزوّد بنيته بحزمة المزوّد نفسها، من دون تغيير، على ai 6 أو 7. وتعمل مع المزوّدات المدمجة ومع أي نموذج AI SDK مغلَّف بـ fromAiSdk():
الأحداث
يُبلغ عن الاستدعاء المستضاف كما يُبلغ عن استدعاء الأداة، معexecutedBy: 'provider' في tool.start وtool.done وtool.error:
tool.done.result محدودة بـ 20,000 حرف من JSON (النتيجة الأطول تصبح نص JSON مقطوعًا ينتهي بـ ... [truncated N characters])؛ وفي tool.error تكون error.name هي 'HostedToolError' مع رسالة المزوّد. ويضع بثّ واجهة AI SDK (toUIMessageStream()) العلامة providerExecuted: true على أجزاء الأدوات هذه.
في التتبّع (traces) يحمل المقطع chat لاستدعاء النموذج السمة lousho.hosted_tool_calls (أسماء الأدوات التي شغّلها المزوّد فيه)؛ ولا يوجد مقطع execute_tool لها، لأن SDK لم ينفّذ شيئًا.
السجل وإعادة التشغيل
تحصل رسالة المساعد في الخطوة علىmetadata.hostedToolCalls: لكل استدعاء id وname وargs وresult (محدودة كما سبق) وisError وsources من الروابط التي استشهد بها المزوّد بعده. ستجدها في result.messages.
لا يُعاد إلى النموذج في الاستدعاءات اللاحقة إلا نص المساعد، ولا تُعاد كتل أدوات المزوّد أبدًا. وهذا يعمل مع كل مزوّد ونموذج؛ والثمن أن الاستشهادات من الدورات السابقة لا تُعاد إلى النموذج.
الاستهلاك والتكلفة
تحصيresult.usage.hostedToolCalls (وكذلك usage.hostedToolCalls في run.done) الاستدعاءات لكل اسم أداة، مثل { web_search: 2 }؛ وتُضاف استدعاءات الوكيل الفرعي إلى استدعاءات الوكيل الرئيسي. أما الرموز التي تضيفها الأداة المستضافة (نتائج البحث التي يقرؤها النموذج، ومخرجات الشيفرة) فهي ضمن أعداد الرموز التي يُبلغ عنها المزوّد.
تغطي costUsd الرموز فقط. أما رسوم الاستدعاء للأدوات المستضافة (عملية بحث على الويب أو جلسة مفسّر شيفرة) فيحاسب عليها المزوّد ولا تُضمَّن؛ راجع أسعار المزوّد واستخدم الأعداد أعلاه.
الموافقات والصلاحيات
لا يستطيع SDK أن يضبط ما لا ينفّذه. تعمل الأداة المستضافة داخل طلب المزوّد، فلا ترى أي قاعدة صلاحيات ولا حاجز حماية للأدوات ولاneedsApproval ولا خطّاف preToolCall / postToolCall ولا onToolCall استدعاءها، ولا يمكن إيقاف أداة مستضافة مؤقتًا لطلب موافقة. إذا كان يجب ألا يبحث التشغيل أو ينفّذ شيفرة من دون إنسان، فاترك الأداة خارج tools، أو اختر الأدوات لكل تشغيل بدالة:
يشغّل مفسّر الشيفرة شيفرة، و
hostedTool() أداة لا يعرف SDK عنها شيئًا، لذا يعامل وضع plan كليهما كأداتين لهما آثار جانبية. يُقرأ الوضع قبل كل استدعاء للنموذج، فأي تبديل (session.setPermissionMode() أو وضع على هيئة دالة) يسري من الاستدعاء التالي. ولا يُكتب إدخال permission.decision لأداة مستضافة تُركت خارج الطلب: فلم يحدث أي استدعاء.
لا ترث الوكلاء الفرعيون الأدوات المستضافة للوكيل الرئيسي؛ أعطِ كل وكيل فرعي أدواته الخاصة في tools. والتشغيل المستأنف من نقطة حفظ أو موافقة يرسل الأدوات المستضافة نفسها؛ أما الوكيل المستأنِف بمجموعة مختلفة فيُبلغ عن انحراف الوكيل (agent drift).
الاختبار
يقبلmockModel استدعاءات مستضافة في الدورة ويسجّل hostedTools في كل طلب، فيعمل وكيل ذو أدوات مستضافة من دون اتصال:
غير مغطّى بعد
لا توجد دوال مساعدة لتوليد الصور واستخدام الحاسوب وMCP المستضاف والصدفة (shell) المستضافة؛ مرّر أداة حزمة المزوّد عبرhostedTool() حيث تعمل. وإعادة عرض كتل أدوات المزوّد للإبقاء على الاستشهادات عبر الدورات غير مدعومة.