# مرجع AGENTO لتجهيز Config

هذه نسخة قابلة للإرفاق مع `api-context.md` وPrompt إنشاء Config. توثق الحزمة العامة `agento-runtime@1.1.0`. تبقى صيغة الـ Config على `version: "2"`، وهي مستقلة عن إصدار حزمة npm. الروابط النسبية تشير إلى الموقع، لكن محتوى الفصول الأساسية مرفق هنا.

---

# اربط أول Endpoint من API الموجودة لديك

إذا كانت API لديك موثقة بصيغة OpenAPI، يمكنك استخدام [مولّد OpenAPI](/ar/docs/openapi) مسارًا بديلًا لتجهيز Config عندما يحتويه الإصدار المثبّت. راجع الناتج قبل متابعة تكامل Runtime.

لديك API تعمل بالفعل، وتريد أن يستطيع مساعد استخدام إحدى عملياتها. هذه البداية تأخذك من طلب تعرف أنه يعمل إلى Tool ينفّذها AGENTO، مع فهم دور كل جزء في `config.yml`. لن تحتاج إلى إنشاء خادم تجريبي أو تغيير backend.

**ما الذي ستخرج به؟** ملف `api-context.md` يصف API ويمكن نقله إلى أي AI Model، وملف `config.yml` لـ Tool واحدة، وطلب حقيقي يمكنك مقارنة نتيجته باستدعاء API المباشر.

ستستخدم الـ AI Model اختياريًا لتحليل التوثيق وتجهيز مسودة. لا تحتاج إلى AI Model أثناء اختبار التنفيذ المباشر؛ نضيف المحادثة بعد التأكد من أن الإعداد يرسل الطلب الصحيح.

## ١. اختر عملية تعرف كيف تستدعيها

اختر Endpoint للقراءة لا تغيّر البيانات، مثل البحث عن خدمات أو عرض منتجات. جرّبها بعميل HTTP الذي تستخدمه عادةً، مثل curl أو Postman، واحتفظ بالطلب الناجح وعينة استجابته بعد إزالة البيانات الحساسة.

لا تبدأ بعملية حجز أو دفع أو حذف. لهذه العمليات خطوة Confirmation سنشرحها بعد أن تتضح العلاقة بين الـ Input وطلب HTTP.

دوّن أربع معلومات من API **لديك**: method، والعنوان الكامل، والحقول المطلوبة ومكانها، وطريقة Auth.

>لا تغيّر أسماء حقول API لتطابق المثال في هذا الدليل.

سنشرح Mapping بمثال Endpoint بحث يقبل `GET /services?q=hair`. المسار واسم `q` مجرد مثال للتفسير؛ العنوان `YOUR_API_HOST` يجب استبداله بعنوانك، وقد تكون عمليتك `/products?search=phone` أو Endpoint مختلفًا تمامًا

## ٢. حوّل توثيق API إلى ملف يمكن إعادة استخدامه

إذا كنت تعرف متطلبات الـ API، اكتبها بنفسك وانتقل إلى الخطوة التالية. وللاستعانة بـ AI Model، زوّده بملف OpenAPI أو توثيق الـ Endpoints أو تعريفات الـ Routes، مع أمثلة Request وResponse بعد إزالة البيانات الحساسة.

[افتح prompt تحليل API](/examples/api-onboarding/analyze-api.ar.md)، وأرسله مع المصادر إلى الـ AI Model الذي تستخدمه. سيطلب منه إنتاج `api-context.md`؛ لن يطلب منه تخمين `config.yml` من اسم المشروع.

الملف الناتج يجب أن يجيب عن هذه الأسئلة:

- ما العملية التي سنربطها أولًا، وما فائدتها للمستخدم؟
- ما الحقول التي يستقبلها الطلب، وأيها مطلوب؟ هل تأتي في `path` أم `query` أم `body`؟
- من يملك كل قيمة؟ هل يقولها المستخدم، أم يوفرها خادمك، أم تعيدها Endpoint أخرى؟
- هل تحتاج العملية إلى Bearer Token للمستخدم أم API key للخدمة؟
- ما شكل الـ Response والأخطاء الموثقة؟ وما المعلومات التي ما زالت ناقصة؟

**راجع الملف قبل استخدامه.** إذا وصف الـ AI Model حقلًا بأنه مطلوب، ابحث عن دليل ذلك في توثيق الـ API. وإذا لم يعرف اسم حقل، اتركه `UNKNOWN` بدل استخدام تخمينات. رؤية `id` في التقرير لا تكفي للقول إنه دائمًا موجود أو إنه الـ **ID** المناسب لعملية أخرى.

يمكنك البدء أيضًا من [قالب `api-context.md`](/examples/api-onboarding/api-context.ar.md) إذا أردت كتابته يدويًا.



احفظ الملف مع مشروعك بعد إزالة البيانات الحساسة. لاحقًا تستطيع إعطاءه لنموذج مختلف بدل إعادة شرح API من البداية. AGENTO نفسه لا يقرأ هذا الملف؛ إنه وثيقة للمطوّر ولمساعدته في إعداد YAML.

## ٣. أنشئ Tool واحدة، ثم افهم ما كتبته

يمكنك كتابة الإعداد بنفسك باستخدام الشرح أدناه، أو إعطاء الـ AI Model `api-context.md` مع [prompt إنشاء الإعداد](/examples/api-onboarding/generate-config.ar.md) و[مرجع AGENTO القابل للإرفاق](/examples/api-onboarding/agento-reference.ar.md). يجمع المرجع شرح الـ Input والـ Mapping وAuth وبقية الإعدادات الأساسية؛ لا يحتاج الـ AI Model إلى معرفة محتوى محادثة سابقة.

اطلب **ملفًا كاملًا لـ Tool واحدة**. لا تبدأ بتحويل جميع Endpoints دفعة واحدة؛ إذا فشل الطلب، نريد أن يكون مصدر الخطأ محددًا.

لنفترض أن Endpoint فعلًا يطابق مثال البحث العام دون مصادقة. يصبح الملف التالي نقطة البداية. غيّر العنوان، والمسار، والحقول، وAuth لتطابق Endpoint قبل تشغيله:

```yaml
version: "2"

assistant:
  name: Agento #Your agent's name
  language: auto
  timezone: UTC
  system_prompt: |
     Help users find and book appointments using the available tools.
      Do not invent availability or claim a booking before it completes.


models:
  routing:
    provider: openai
    model: gpt-5.6-luna
    api_key: $ENV:OPENAI_API_KEY
    temperature: 0

tools:
  - id: search-services
    tool:
      description: Search available services by the user's search phrase.
      input_schema:
        type: object
        properties:
          query:
            type: string
            minLength: 1
        required: [query]
    request:
      method: GET
      url: https://YOUR_API_HOST/services
      auth:
        type: none
      map:
        query:
          q: $.query
```

اقرأ الملف من الخارج إلى الداخل. `version: "2"` يحدد صيغة إعداد AGENTO، وليس إصدار API لديك. تحت `tools` قائمة؛ كل عنصر فيها يصف عملية متاحة للمساعد. أضفنا عنصرًا واحدًا وسمّيناه `search-services`.

قيمة `id` هي الاسم الداخلي الذي تختاره للـ Tool. سنستخدمه عند استدعاء `invoke()`، ويجب أن يكون فريدًا. لا يشترط أن يكون نفس اسم route في backend.

داخل `tool` نضع ما تحتاج المكتبة إلى معرفته عن العملية ومدخلاتها. يشرح `description` متى تصلح الـ Tool؛ يستفيد منه الـ AI Model عندما نضيف المحادثة. لكنه لا ينشئ طلب HTTP ولا يحدد اسم `query` parameter.

تحدد `input_schema` أن الـ Tool تقبل كائنًا يحتوي الحقل `query` كنص مطلوب وغير فارغ. اخترنا `query` اسمًا للـ Input داخل AGENTO، بينما تستخدم الـ API الاسم `q`. يربط `request.map` بين الاسمين.

داخل `request` نحدد التنفيذ الفعلي. `method` و`url` يأتيان من Endpoint API. و`auth.type: none` صحيح فقط إذا كانت العملية لا تحتاج لمصادقة. تغيير الوصف في `tool` لا يغيّر هذا العنوان أو طريقة الطلب.

### ربط Input الـ Tool بالـ API

يستخدم `request.map.query` لربط Input الـ Tool بالـ `query` parameters التي يستقبلها الـ API.

```yaml
q: $.query
```

تعني:

- `q` هو اسم الـ parameter في الـ API.
- `$.query` هي القيمة المأخوذة من Input الـ Tool.

فإذا كانت الـ Input:

```json
{ "query": "hair" }
```

سيتم إرسال:

```http
GET /services?q=hair
```

ببساطة: **اليسار هو اسم الحقل في الـ API، واليمين هو مصدر قيمته من Input الـ Tool.**

لم نضف `output_schema` أو سياسة Retry. يتضمن ملف المثال إعدادًا اختياريًا للـ AI Model من أجل `chat()`؛ لا يحتاج `invoke()` مع `presentation: "raw"` إلى AI Model. إذا أبقيت قسم `models` في الملف، فوفّر مفتاحه المطلوب عند إنشاء الـ Runtime. هدف التجربة الأولى هو التأكد من طلب HTTP الفعلي. بعد نجاحه، سنضبط [التحقق من الـ Response](/ar/docs/responses) وفق شكل الـ Response الذي تحققت منه.

## ٤. اضبط Auth

يتم تحديد نوع Auth لكل Tool داخل `request.auth`.

إذا كانت الـ Endpoint عامة:

```yaml
auth:
  type: none
```

فلا تحتاج إلى Access Token للمستخدم.

إذا كانت الـ Endpoint تعمل بصلاحيات المستخدم الحالي:

```yaml
auth:
  type: session
```

في هذه الحالة، لا يتم تخزين الـ Access Token داخل `config.yml`. تطبيقك يمرّر Token المستخدم الحالي وقت تنفيذ الطلب:

```js
{
  sessionId: `user:${user.id}`,
  auth: {
    token: user.accessToken,
  }
}
```

هذان الحقلان لهما وظيفتان مختلفتان:

```text
sessionId   → يحدد جلسة المستخدم داخل AGENTO
auth.token  → يصادق المستخدم مع الـ API لديك
```

القيم `user.id` و`user.accessToken` تأتي من تطبيقك ونظام Auth لديك. AGENTO لا يسجل دخول المستخدم ولا ينشئ Access Token.

مثلًا، في تطبيق ويب قد تحصل على المستخدم الحالي من الطلب:

```js
const user = req.user;
```

ثم تمرر بياناته إلى AGENTO:

```js
const request = {
  sessionId: `user:${user.id}`,
  auth: {
    token: user.accessToken,
  },
};
```

استخدم `sessionId` ثابتًا للمحادثة نفسها عندما تريد الحفاظ على سياق الـ Session.

صيغة `user:${user.id}` في المقاطع التوضيحية أدناه وأعلاه تحتاج إلى التكييف مع التطبيق: لا يقبل التنفيذ المرجعي المحرف `:` في Session ID. استخدم ID يديره الخادم ويتوافق مع [الصيغة المقبولة](/ar/docs/runtime-api)، واربطه بالمستخدم الذي جرى التحقق من هويته.

إذا كانت الـ Tool المختارة تستخدم:

```yaml
auth:
  type: none
```

فلا تحتاج إلى تمرير `auth.token`.

### مفاتيح الخدمة الثابتة

بعض الـ APIs تستخدم مفتاحًا ثابتًا يخص التطبيق نفسه، وليس مستخدمًا محددًا.

في هذه الحالة استخدم:

```yaml
auth:
  type: api-key
  env: $ENV:SERVICE_KEY
  header: X-API-Key
```

في مثال مفتاح الخدمة أعلاه، استخدم اسم Environment Variable وحده في `env`؛ بادئة `$ENV:` الظاهرة فيه تخص إعداد مفتاح الـ AI Model، ولا تطابق إعداد مفتاح الخدمة في التنفيذ المرجعي.

`SERVICE_KEY` هو اسم Environment Variable الذي يحتوي مفتاح الخدمة؛ يقرأه `request.auth.env` دون بادئة `$ENV:`. أما مفتاح الـ AI Model فيستخدم صيغة مثل `$ENV:OPENAI_API_KEY`. يقرأ AGENTO القيم من بيئة العملية، ولا يحمّل ملف `.env` تلقائيًا؛ حمّله بآلية تطبيقك كما يشرح [دليل التثبيت](/ar/docs/installation).

الفرق باختصار:

```text
session   → بيانات مصادقة ديناميكية للمستخدم الحالي
api-key   → بيانات مصادقة ثابتة للتطبيق أو الخدمة
none      → بدون مصادقة
```

---

## ٥. أرسل رسالة إلى AGENTO

أنشئ الـ Runtime مرة واحدة عند تشغيل تطبيقك:

```js
import { AgentRuntime } from "agento-runtime";

const runtime = await AgentRuntime.create({
  configPath: "./config.yml",
  presentation: "raw",
});
```

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

### دع AGENTO يختار الـ Tool

عند إعداد AI Provider، استخدم `chat()` في الاستخدام الطبيعي للـ Agent:

```js
const result = await runtime.chat({
  sessionId: `user:${user.id}`,
  auth: {
    token: user.accessToken,
  },
  message: "ابحث عن خدمات الشعر",
});
```

ترسل رسالة المستخدم بدل تحديد الـ Tool و`arguments` بنفسك. وتستمر في تمرير `sessionId` وبيانات Auth المطلوبة كما في المثال.

بعدها يتولى AGENTO الباقي:

```text
رسالة المستخدم
      ↓
فهم الطلب
      ↓
اختيار الأداة
      ↓
جمع المدخلات
      ↓
تنفيذ طلب الـ API
```

لا تحتاج إلى تحديد `search-services` بنفسك.

إذا كانت الـ Tool التي اختارها AGENTO تستخدم:

```yaml
auth:
  type: session
```

فسيستخدم `auth.token` الممرر مع الطلب عند الاتصال بالـ API.

أما إذا كانت الـ Tool عامة، فيمكن أن يكون الاستدعاء أبسط:

```js
const result = await runtime.chat({
  sessionId: `user:${user.id}`,
  message: "ابحث عن خدمات الشعر",
});
```

### تشغيل Tool مباشرة

أحيانًا يكون تطبيقك يعرف مسبقًا أي Tool يريد تشغيلها. في هذه الحالة استخدم `invoke()`:

```js
const result = await runtime.invoke({
  sessionId: `user:${user.id}`,
  auth: {
    token: user.accessToken,
  },
  tool: "search-services",
  arguments: {
    query: "hair",
  },
});
```

`invoke()` يتجاوز مرحلة اختيار الـ Tool بالـ AI ويشغل الـ Tool التي حددتها مباشرة.



## ٦. اقرأ النتيجة بدل افتراض النجاح

إذا كانت الحالة `completed`، افحص `result.data` وقارنها بالنتيجة التي حصلت عليها عند استدعاء API مباشرةً بالقيم نفسها. قد تكون قائمة فارغة نتيجة صحيحة؛ النجاح لا يعني وجود عناصر.

إذا كانت `needs_input`، فهناك مدخل مطلوب لم تقدمه. افحص `missing`، ثم قدّم قيمة الـ Input المطلوبة في الاستدعاء التالي. جرّب حذف `query` لترى هذا السلوك؛ لا ينبغي إرسال طلب بحث ناقص.

إذا كانت `error`، اقرأ `error.code` و`error.message`. غياب مدخل مطلوب يختلف عن وجود مدخل من النوع الخطأ. مثلًا، `{ "query": 15 }` يخالف Schema النصية. أما `AUTH_REJECTED` فيعني مراجعة Auth، وليس إعادة تسمية الـ Input.

إذا لم تصل إلى النتيجة أصلًا وفشل `create()`، فالمشكلة في تحميل الإعداد أو التحقق منه، مثل عنوان غير صحيح أو Environment Variable مفقود. راجع أول رسالة خطأ قبل تعديل عدة خيارات معًا.

تحتاج الحالة `needs_selection` أو `needs_confirmation` إلى تفاعل من المستخدم. Tool القراءة البسيطة هنا لا تضيف هاتين الخطوتين؛ إذا أضفتهما لاحقًا، لا تجعل البرنامج يختار أو يؤكد تلقائيًا. راجع [التعامل مع النتائج](/ar/docs/runtime-flow).

## ٧. حدّد الخطوة التالية حسب ما نجح

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

انتقل إلى [فهم `config.yml`](/ar/docs/configuration) لتعرف كيف توسّع الإعداد دون نسخ خيارات لا تحتاجها. ثم اقرأ [الـ Input](/ar/docs/tools) و[Mapping](/ar/docs/mapping) و[الـ Response](/ar/docs/responses). بعد نجاح الطلب المباشر، أضف [الـ AI Model](/ar/docs/providers) ليحوّل كلام المستخدم إلى استدعاء للـ Tool نفسها.

---

# افهم كيف يبني `config.yml` سلوك الـ Tool

يمكنك كتابة Config يدويًا أو استخدام [مولّد OpenAPI](/ar/docs/openapi) في إصدار يحتويه. يستخدم الناتج صيغة Config الأصلية نفسها؛ راجعه وأضف إعدادات تطبيقك.

بعد [البداية السريعة](/ar/docs/quick-start)، لديك طلب يعمل على API لديك. هدف هذا الفصل أن تستطيع قراءة الإعداد وتغييره بوعي: تعرف أين تضع الخيار، وما المشكلة التي يحلّها، وكيف تلاحظ أثره عند التشغيل.

الإعداد ليس وصفًا عامًا ترسله للـ AI Model وتنتظر منه التصرف. تقرأه المكتبة عند إنشاء Runtime، وتحوّله إلى تعريفات وقواعد تتحقق منها قبل التنفيذ. لذلك يمكن رفض الملف قبل إرسال أي طلب HTTP.

## ابدأ بثلاثة أسئلة، وليس بقائمة خيارات

**ماذا يستطيع المستخدم طلبه؟** هذا يحدد الـ Tool ووصفها. «ابحث عن خدمة» عملية مختلفة عن «احجز موعدًا»؛ وضعهما تحت وصف مبهم يجعل اختيار الـ Tool أصعب.

**ما البيانات المقبولة لتنفيذ العملية؟** هذا يحدد `tool.input_schema`. في البحث، قد تحتاج نصًا اسمه `query`. وفي الحجز، قد تحتاج تاريخًا وID للموعد. وجود حقل مطلوب لا يعني أن المستخدم يعرفه؛ سنحدد مصدر هذه الـ IDs عبر Dependencies لاحقًا.

**كيف تستقبل API هذه البيانات؟** هذا يحدد `request` و`request.map`. إذا كان اسم حقل الـ Tool `query` لكن API تستخدم `q`، فلا يكفي إعلان الحقل في Schema؛ يجب ربطهما.

في ملف البداية تكون العلاقة هكذا:

```text
arguments.query = "hair"
        |
        v
input_schema: accept a non-empty string
        |
        v
request.map.query: q <- $.query
        |
        v
GET /services?q=hair
```

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

## افهم مستويات الملف

الجذر هو المستوى الذي يحتوي `version` و`tools`. إعدادات الجذر تنطبق على Runtime كله. أما كل عنصر داخل `tools` فيصف Tool واحدة.

داخل الـ Tool يوجد `tool` لوصف الـ Input، و`request` لبناء HTTP. أضف `response` بجوارهما عندما تريد ضبط البيانات المعادة. وأضف `depends_on` أو `behavior` على مستوى الـ Tool أيضًا عند الحاجة.

هذا رسم لمواضع الأقسام، وليس ملفًا تنسخه للتشغيل:

```text
config.yml
├── version
├── models                  used by chat
├── assistant               conversation guidance
├── session                 conversation state limits
└── tools
    └── one tool
        ├── id
        ├── tool
        │   ├── description
        │   └── input_schema
        ├── request
        │   ├── method / url / auth
        │   └── map
        ├── response
        ├── depends_on
        └── behavior
```

المسافات في YAML جزء من البنية. مثلًا، `models` ليس داخل `tools`، و`input_schema` ليس مباشرةً بجانب `request`؛ مكانه داخل `tool`. إذا رفضت المكتبة مفتاحًا معروفًا، تأكد من موضعه قبل تغيير اسمه.

يقبل الجذر `version` و`assistant` و`routing` و`session` و`rate_limits` و`models` و`tools` فقط. الإصدار هو النص `"2"`، وقائمة الـ Tools لا يجوز أن تكون فارغة.

## أضف كل قسم عندما تظهر الحاجة إليه

عند نجاح أول طلب، أضف [`response.output_schema`](/ar/docs/responses) إذا تحققت من شكل الـ Response الذي تضمنه الـ API. ستلاحظ فرقًا مهمًا: HTTP 200 لا يكفي للنجاح إذا خالفت البيانات الشكل الذي يعتمد عليه تطبيقك.

عندما تريد استقبال كلام المستخدم، أضف [`models.routing`](/ar/docs/providers)، ثم استخدم `chat()`. دور هذا الإعداد اختيار الـ Tool واستخراج مدخلاتها. لا تحتاجه لمجرد إرسال الطلب المعروف عبر `invoke()`.

عندما تحتاج العملية إلى ID لا يعرفه المستخدم، أضف [`depends_on`](/ar/docs/dependencies) لجلبه من Tool قراءة. النتيجة قد تصبح `needs_selection`؛ أي إن المكتبة تنتظر اختيارًا، وليست معطّلة.

عندما تغيّر العملية بيانات، عرّف [`behavior.effect` والـ Confirmation](/ar/docs/effects). ستصبح النتيجة `needs_confirmation` قبل الكتابة. لا يكفي تغيير method من GET إلى POST.

وعندما يصبح التكامل جزءًا من تطبيق متعدد المستخدمين، راجع [Auth](/ar/docs/authentication) و[الـ Sessions](/ar/docs/sessions) و[سياسات الأعطال](/ar/docs/http). زيادة عدد الخيارات لا تعني أن الـ Tool أصبحت أفضل؛ كل خيار يجب أن يحل مشكلة تستطيع تسميتها.

## تحقّق مما يفحصه الإعداد وما لا يفحصه

يرفض التحقق مفاتيح غير معروفة، وIDs مكررة، وربطًا يقرأ مدخلًا غير معلن، والـ Dependencies تشير إلى Tool غير موجودة. لكنه لا يعرف أن Endpoint عندك تغيّرت أمس، ولا يثبت أن Token يملك صلاحية الوصول.

لذلك افصل بين تجربتين: إنشاء Runtime بنجاح يثبت صلاحية الإعداد وفق قواعد المكتبة؛ وتنفيذ Tool على API لديك يختبر صحة التكامل الفعلي. ثم تأتي تجربة ثالثة للمحادثة: هل اختار الـ AI Model الـ Tool المناسبة من كلام المستخدم؟

**توقّف هنا في القراءة الأولى، وانتقل إلى [تعريف الـ Input](/ar/docs/tools).** بقية الفصل مرجع لإعدادات الجذر. ارجع إلى القسم الذي تحتاجه عندما تبدأ ضبط المحادثة أو الـ Sessions أو حدود الاستهلاك.

## Assistant للغة والمحادثة، وليس لمنح صلاحية التنفيذ

```yaml
assistant:
  name: Booking assistant
  language: auto
  timezone: Asia/Hebron
  system_prompt: |
    Help users find and book appointments using the available tools.
    Do not invent availability or claim a booking before it completes.
  conversation:
    enabled: true
    include_capability_summary: true
    capability_summary:
      include_title: true
      include_description: true
    use_presentation_model: true
```

الحقل `name` اختياري، بطول 1 إلى 100 حرف. القيمة الافتراضية لـ `language` هي `auto`؛ تحدد لغة الردود، وتظهر رسائل Selection بالعربية عند ضبطها على `ar` أو اكتشاف العربية في آخر رسالة للمستخدم. القيمة الافتراضية لـ `timezone` هي `UTC`، ويجب أن تكون منطقة زمنية صالحة وفق IANA. اضبطها لفهم التواريخ النسبية، لكنها لا تعيد كتابة timestamps المخزنة في API.

يوجّه `system_prompt` المحادثة وعرض النتائج. لا يمنح إذن كتابة، ولا يتجاوز mapper. ضع قواعد التنفيذ في Schemas والسياسات.

> استخدم `system_prompt` لتحديد شخصية الـ AI Model ونبرة ردوده في المحادثة.

تكون `conversation.enabled` مفعّلة افتراضيًا. تعطيلها يحوّل حالة عدم اختيار Tool إلى `ROUTING_NO_MATCH` بدل المحادثة العادية؛ وللعمل المعلّق قواعده الخاصة في الحفاظ على الحالة. أبقها مفعّلة إذا كان المساعد يحتاج إلى الإجابة عن التحيات والأسئلة الجانبية.

تحدد خيارات capability summary ما إذا كانت المحادثة ترى أسماء الـ Tools وأوصافها. يقلل تعطيلها السياق، لكنه يقلل معرفة المساعد بقدراته؛ ولا يحذف الـ Tools من Routing. تكون `use_presentation_model` مفعّلة افتراضيًا؛ تستخدم المحادثة الـ AI Model المحدد لصياغة النتائج، أو الـ AI Model الخاص بالـ Routing إذا لم تحدد واحدًا منفصلًا.

## Routing للموازنة بين العثور على الـ Tool والكلفة والزمن

```yaml
routing:
  candidate_limit: 6
  fallback_attempts: 2
  lexical:
    enabled: true
    min_score: 1
    normalize_unicode: true
    normalize_arabic: true
    phrase_matching: true
  semantic_recall:
    enabled: true
    candidate_limit: 6
    max_catalog_tools: 100
    max_catalog_chars: 100000
    metadata:
      include_id: true
      include_title: true
      include_description: true
    allow_no_match: true
    multilingual: true
```

هذه القيم الافتراضية.

- يحدد `candidate_limit` أقصى عدد من الـ Tools المرشحة للـ Selection النهائي، من 1 إلى 100. ارفعه عندما تُستبعد Tools مناسبة؛ القوائم الأكبر قد تزيد الكلفة وتصعّب Selection.



- يسمح `fallback_attempts` بمحاولة إلى ثلاث محاولات بديلة للـ Selection عبر Structured Output أو JSON. لا يضبط Retry HTTP.
- يتحكم `lexical.enabled` في المطابقة قليلة الكلفة مع بيانات الـ Tools، ويحدد `min_score` عتبة الثقة. تعطيل normalization قد يفوّت تطابقات لصيغ Unicode أو عربية متكافئة. يعطي `phrase_matching` وزنًا إضافيًا لمطابقة العبارات المحددة في الـ Config.
- يسمح `semantic_recall.enabled` بالبحث بمساعدة الـ AI Model عندما تضعف المطابقة عبر Lexical Retrieval. قد يقلل تعطيله عدد الاستدعاءات، لكنه يفوّت إعادة الصياغة والمطابقة بين اللغات.
- يحد `semantic_recall.candidate_limit` الداخلي من IDs المسترجعة. ويحد `max_catalog_tools` و`max_catalog_chars` من بيانات الكتالوج المرسلة للـ AI Model؛ زيادتهما تسمح بكتالوج أوسع على حساب السياق. الحد الأعلى 10,000 Tool و1,000,000 حرف.
- خيارات `title` و`description` توازن بين وضوح الوصف وحجم prompt. أما `include_id` و`allow_no_match` و`multilingual` فلا تقبل حاليًا إلا `true`؛ ليست مفاتيح تعطيل.

قِس [نتائج Routing](/ar/docs/routing) على رسائل تمثل الاستخدام الفعلي قبل تعديل الحدود. غالبًا يفيد تحسين الوصف أكثر من رفع كل الأرقام.

## Session: مدة الصلاحية والسجل وحدود التنفيذ

| الإعداد | الافتراضي | أثر تغييره |
| --- | --- | --- |
| `ttl_ms` | 1,800,000 | مدة حياة الـ Session. زيادتها تبقي السياق مدة أطول، لكنها لا تمدد TTL موعد متاح. |
| `max_history_messages` | 20 | عدد رسائل السجل المتاحة للـ AI Model. الصفر يلغي الاحتفاظ بالسجل، والأعلى 1,000. ليس حد الرسائل طوال الـ Session. |
| `max_sessions` | 10,000 | سعة المخزن الافتراضي في الذاكرة. المخزن المخصص مسؤول عن سياسة سعته. |
| `max_steps` | 32 | يحد خطوات حسم [الـ Dependencies](/ar/docs/dependencies) لكل عملية، والأعلى 100. الزيادة تسمح بعمل أعمق، وقد تزيد زمنه. |

باستثناء طول السجل، يجب أن تكون القيم أعدادًا صحيحة موجبة. التخزين في الذاكرة يناسب process واحدة؛ لا تحوّله هذه القيم إلى تخزين مشترك. راجع [الـ Sessions](/ar/docs/sessions) قبل توزيع التطبيق.

## Rate limits تحسب العمل الفعلي

```yaml
rate_limits:
  ai_calls: 60
  tool_calls: 120
  messages: 120
  messages_per_session: 1000
  window_ms: 60000
```

يحسب التنفيذ الافتراضي حدود الاستهلاك لكل Session خلال الفترة المحددة. يشمل `ai_calls` استدعاءات الـ AI Model للـ Routing وSemantic Recall والمحادثة وصياغة النتائج؛ قد تحتاج الرسالة الواحدة إلى عدة استدعاءات. ويحسب `tool_calls` كل محاولة HTTP، بما فيها Retry. أما `messages` فيحسب العمليات الواردة خلال الفترة، ومنها `invoke()` و`select()` و`confirm()` و`cancel()`. ويحسب `messages_per_session` العمليات طوال عمر الـ Session.

إطالة `window_ms` مع إبقاء الميزانية كما هي تجعل المعدل المسموح أشد تقييدًا. وقد توقف الميزانية الصغيرة تدفقًا صحيحًا من عدة خطوات. تعامل مع `RATE_LIMITED` صراحةً. يحتاج التطبيق الموزع إلى limiter مشترك؛ هذه الإعدادات ليست سقف فوترة عامًّا للحساب.

## Models لتفعيل العمل الذي تحتاجه

يحدد `models.routing` الـ AI Model الذي تستخدمه `chat()` لاختيار الـ Tool، ويمكن استخدام `models.presentation` لفصل صياغة النتائج والمحادثة عن Routing. Providers المدعومون هم openai وanthropic وmistral وcohere وollama.

يحتاج كل إعداد إلى المفتاحين `provider` و`model`. تحتاج الخدمات المستضافة إلى `api_key: $ENV:NAME`، وغياب Environment Variable يفشل compilation. يحدد `base_url` الاختياري عنوان الـ Provider، و`timeout_ms` افتراضيًا 30,000. أما `temperature` فاختيارية بين صفر واثنين، ويجب أن يدعمها الـ AI Model.

تصف capability overrides قدرات حقيقية للـ Provider والـ AI Model؛ كتابة `nativeTools: true` لا تضيف tool calling إلى AI Model لا يدعمه. اقرأ [Providers](/ar/docs/providers) قبل تعديلها.

## تحقّق من الإعداد قبل التشغيل

```js
import { readFile } from "node:fs/promises";
import { compileConfig } from "agento-runtime";

const compiled = compileConfig(await readFile("./config.yml", "utf8"));
console.log(compiled.tools.values());
```

يفحص compilation علاقات لا يكشفها محلّل YAML وحده، مثل تكرار IDs، والـ Schemas غير الصالحة، وplaceholders غير المربوطة، والمسارات غير المعلنة، ودورات الـ Dependencies، وتعارض الوجهات، وبيانات الاعتماد غير الآمنة، وغياب Confirmation.

لكنه لا يثبت أن API تعيد الشكل المعلن، أو أن الـ AI Model يختار الـ Tool الصحيحة. نفّذ طلبًا على بيئة الاختبار في API لديك، ثم جرّب رسائل تمثل استخدام المنتج. تستطيع لاحقًا تثبيت حالات الخطأ في اختبارات آلية ببيانات محفوظة؛ يوضح [تمرين الحجز](/ar/docs/booking-example) هذا النوع من الاختبار.

---

# عرّف ما تقبله الـ Tool قبل أن ترسل الطلب

في [البداية السريعة](/ar/docs/quick-start) سمّينا مدخل البحث `query` وربطناه بحقل `q` في API. نركّز الآن على الـ Input نفسه: ما نوعه؟ هل يمكن حذفه؟ وما الذي يحدث إذا قدّم الـ AI Model قيمة غير صالحة؟

`input_schema` هي القواعد التي تجيب عن هذه الأسئلة. تستخدم JSON Schema؛ لا تكتب فيها عنوان Endpoint أو طريقة Auth.

## ابدأ من Input يعرفها المستخدم

للبحث، يعرف المستخدم الكلمات التي يريد البحث عنها. لذلك يمكن للـ Tool قبول `query` كنص. إذا كانت API تسمح بتحديد عدد النتائج، تستطيع إضافة `limit` كعدد صحيح، بشرط أن تطابق حدوده ما تدعمه API بالفعل.

المقطع التالي **يستبدل كتلة `tool` داخل Tool البحث**. احتفظ بـ`id` و`request` من إعدادك. يفترض المثال أن الـ API تقبل حدًا للنتائج من 1 إلى 20. اضبط هذه القيم وفق ما تدعمه الـ Endpoint لديك.

```yaml
tool:
  description: Search services by name. Use this to find services, not to create a booking.
  input_schema:
    type: object
    additionalProperties: false
    properties:
      query:
        type: string
        minLength: 1
        description: The search phrase supplied by the user.
      limit:
        type: integer
        minimum: 1
        maximum: 20
        default: 5
    required: [query]
```

تعني `type: object` أن الـ Input كائن ذو حقول، مثل `{ "query": "hair" }`. تحت `properties` نعرّف أسماء الحقول وقواعدها. ثم تحدد `required` أي هذه الحقول لا يمكن الاستغناء عنه.

لذلك، وجود `limit` داخل `properties` لا يجعله مطلوبًا. لا تضعه داخل `required` إلا إذا كان غيابه يمنع تنفيذ العملية. هنا اخترنا قيمة افتراضية تسمح للمستخدم بالبحث دون تحديد عدد.

## ميّز بين غياب القيمة وفسادها

عند إرسال `{}`، ينقص الحقل `query`. تعيد المكتبة `needs_input` كي يحصل تطبيقك على القيمة من المستخدم، ولا ترسل الطلب الناقص.

عند إرسال `{ "query": "" }`، الحقل موجود لكنه يخالف `minLength: 1`. هذه مشكلة تحقق، وليست حقلًا غائبًا. ستجد `INPUT_SCHEMA_VIOLATION` بدل إكمال الطلب.

عند إرسال `{ "query": "hair" }`، يملأ التحقق `limit` بالقيمة `5`. أما `{ "query": "hair", "limit": "5" }` فتفشل؛ علامات الاقتباس تجعل القيمة نصًا، والمكتبة لا تحوّل الأنواع تلقائيًا. ولا تستبدل القيمة الافتراضية قيمةً موجودةً لكنها خاطئة.

إذا أرسلت `limit: 50`، تُرفض القيمة بسبب `maximum: 20`. يحدد هذا القيد قيمة قيم Input المقبولة، ولا يجبر الـ API على إعادة عشرين عنصرًا؛ تبقى هي المسؤولة عن تقسيم النتائج.

> أضف `limit` فقط إذا كانت الـ API تدعمه، واضبط قيوده وفق توثيقها.


## الحقل المقبول لا يُرسل تلقائيًا

أضف الآن Mapping التالي تحت `request.map` لـ Tool البحث:

```yaml
query:
  q: $.query
  limit: $.limit
```

بعد التحقق تصبح الـ Input `{ "query": "hair", "limit": 5 }`. يرسل Mapping `q=hair&limit=5`. لو أضفت الحقل إلى Schema ونسيت هذا Mapping، فلن تُرسل قيمته إلى API.

قبل أن تضيف حقلًا، حدّد فائدته ومكانه في طلب HTTP. لا تضف خصائص لأن الـ AI Model قد يجدها مفيدة؛ يجب أن تكون لها وظيفة معروفة في Endpoint العملية.

## اكتب وصفًا يمكن التمييز به بين الـ Tools

وصف «إدارة الخدمات» لا يوضح هل الـ Tool تبحث أم تضيف أم تحذف. وصف «ابحث عن الخدمات بالاسم، ولا تستخدم هذه الـ Tool لإنشاء حجز» يوضح العملية والحد الفاصل بينها وبين Tool الحجز.

يحتاج الـ AI Model إلى هذا الوصف عند Routing، لكنه لا يغير ما تستطيع الـ Tool تنفيذه. يبقى العنوان والطريقة والـ Mapping تحت `request`. أما `title` فهو اسم اختياري أكثر ملاءمة للعرض، و`id` اسم ثابت يحدد الـ Tool في الاستدعاءات والـ Dependencies. لا يتجاوز `title` مقدار 200 حرف، أما `description` فهو مطلوب، وغير فارغ، ولا يتجاوز 8000 حرف.

اختر IDs واضحة مثل `search-services` و`book`. يجب أن تكون فريدة، وتبدأ بحرف أو underscore، وألا تتجاوز ٦٤ حرفاً. يسمح بعدها بالأحرف والأرقام وunderscores وhyphens. إذا غيّرت ID، حدّث كل المواضع التي تشير إليه.

## لا تطلب من الـ AI Model بيانات يملكها الخادم أو API

قد يتطلب الحجز `date` و`slot_token`. التاريخ يستطيع المستخدم تحديده، لكن Token يأتي من API بعد البحث عن موعد. عرّف الحقلين في قواعد التنفيذ، ثم استخدم [Dependency](/ar/docs/dependencies) لتحديد مصدر Token.

تخفي المكتبة حقول المستوى الأول التي تملؤها Dependencies أو References عن Schema التي يراها الـ AI Model. تبقى مطلوبة لتنفيذ العملية، لكنها ليست معلومات يُطلب من الـ AI Model اختراعها. افحص `runtime.listTools()` لترى الـ Tools والـ Input المتاحة له، ولا تفترض أنه يرى YAML كاملًا.

بيانات المستخدم الموثوقة، مثل الجهة التي ينتمي إليها، يوفّرها خادمك عبر `context` عند الحاجة؛ لا تجعل المستخدم يختار جهة أخرى بمجرد كتابة ID. وبيانات الاعتماد تمر عبر إعدادات Auth، لا عبر Schema. راجع [مصادر Mapping](/ar/docs/mapping) و[Auth](/ar/docs/authentication).

## استخدم قواعد إضافية حين يكون لها معنى

تمنع `additionalProperties: false` حقولًا غير معلنة. يضبطها التحقق على مستوى الجذر افتراضيًا إذا لم تكتبها. للكائنات المتداخلة، حدّد القاعدة صراحةً إذا أردت رفض حقولها الإضافية أيضًا.

تستخدم المكتبة JSON Schema draft-07 مع تحقق صارم عبر AJV، دون جلب Schema References بعيدة أو تحويل أنواع القيم تلقائيًا. يمكنك تعريف نوع رقم، وحدوده، وقائمة قيم مسموحة عبر `enum`، أو شكل كائن متداخل. اكتب القيود وفق متطلبات الـ API؛ لا تستنتجها من أسماء الحقول فقط.

لا تضف `trigger_hints` و`keywords` قبل نجاح التنفيذ. تساعد التلميحات والكلمات المفتاحية ذات الأوزان الموجبة في Lexical Retrieval. لا تمنح صلاحية التنفيذ ولا تضمن المطابقة؛ إعطاء كلمة عامة وزنًا كبيرًا قد يستبعد Tool أنسب. نشرحها في [اختيار الـ Tool](/ar/docs/routing) بعد إضافة الـ AI Model.

## جرب كل حالة قبل الانتقال إلى Mapping

جرّب مدخلًا صحيحًا، ثم احذف الحقل المطلوب، ثم مرّر نوعًا خاطئًا، ثم قيمة خارج الحدود. اختبر أيضًا الخصائص غير المعلنة والقيم التي لا تسمح بها `enum`. في كل مرة، حدّد هل يجب أن تصل العملية إلى الـ API أم تتوقف قبلها. ثم افحص `listTools()` للتأكد من إخفاء الحقول التي توفرها Dependencies، واختبر Routing بطلبات صحيحة ومتقاربة ومحادثة لا تحتاج إلى Tool.

ترفض المكتبة حقولًا مثل `access_token` لأنها تبدو كبيانات اعتماد. مرّر الـ Token عبر `auth` أو استخدم Environment Variable تحت `request.auth`. تثبت الـ Schema شكل البيانات وقيودها، لكنها لا تثبت ملكية المستخدم للـ ID أو بقاء الموعد متاحًا.

بعد ذلك تابع إلى [ربط القيم](/ar/docs/mapping). هناك سنقرأ الطلب من جهة API، ونحدد كيف تصل إليه القيم التي أصبحت تعرف كيف تتحقق منها.

---

# انقل كل قيمة إلى موضعها في طلب HTTP

في [فصل الـ Input](/ar/docs/tools)، تحقّقنا من أن `query` نص صالح وأن `limit` ضمن الحدود. هذه القيم ما زالت ضمن Input الخاص بالـ Tool داخل المكتبة. لكي تستفيد منها API، يجب تحديد أين تُرسل وبأي اسم.

هذا هو دور `request.map`. تخيّل أنك تكتب طلب curl يدويًا: قيمة داخل المسار، وأخرى بعد علامة الاستفهام، وثالثة في JSON body. Mapping يحدد هذه الوجهات دون أن يترك تركيب الطلب للـ AI Model.

## اقرأ أبسط سطر ربط من اليمين إلى اليسار

في Tool البحث كتبنا:

```yaml
# Inside request.map:
query:
  q: $.query
```

`$.query` يقرأ الـ Input الذي اسمه `query`. و`q` هو الاسم الذي تستقبله API. أما كلمة `query` التي تعلو السطرين فهي **نوع الوجهة في HTTP**؛ تشابه الاسمين لا يعني أنهما يشيران إلى الشيء نفسه.

لهذا يمكنك تسمية الـ Input `search_term`، ثم كتابة `q: $.search_term`، ويبقى طلب API نفسه. لكنك ستحتاج إلى تحديث `input_schema` و`input.json` لاستخدام الاسم الجديد.

المسار `$` يبدأ من مصدر القيمة المحدد. في الصيغة المختصرة هنا، المصدر هو Input الـ Tool. عندما نستخدم `source: session` لاحقًا، يبدأ المسار من `context` الذي مرّره تطبيقك. لا توجد مساحة متغيرات مشتركة يبحث فيها Runtime عن أي اسم يجده.

## انتقل إلى طلب فيه `path` و`query` معًا

الآن تخيّل أن API لديك توفر المواعيد لخدمة محددة. الطلب المباشر الناجح هو:

```http
GET /services/svc-color/slots?date=2026-10-01&limit=5
X-Locale: ar
```

الجزء `svc-color` من المسار، بينما `date` و`limit` معاملات query. و`X-Locale` header. لن نضع القيم الثلاث في `query` لمجرد أنها كلها Input.

في المثال التالي، `service_id` هو ID لخدمة موجودة في الـ API لديك؛ لا تفترض أن المستخدم يعرفه. إذا احتجت إلى إيجاده من اسم الخدمة، أضف [Dependency](/ar/docs/dependencies) بعد فهم Mapping.

هذا تعريف Tool تضيفه كعنصر في قائمة `tools`. استبدل العنوان والحقول وفق متطلبات الـ Endpoint لديك:

```yaml
- id: find-slots
  tool:
    description: Find available appointment slots for a service and date.
    input_schema:
      type: object
      properties:
        service_id: { type: string, minLength: 1 }
        date: { type: string, format: date }
        limit: { type: integer, minimum: 1, maximum: 20, default: 5 }
      required: [service_id, date]
  request:
    method: GET
    url: https://api.example.com/services/{service}/slots
    map:
      path:
        service: $.service_id
      query:
        date: $.date
        limit: $.limit
      headers:
        X-Locale:
          source: constant
          value: ar
```

عند تمرير `{ service_id: "svc-color", date: "2026-10-01" }`، يضيف التحقق القيمة الافتراضية `limit: 5`. يستبدل Mapping `{service}`، ويضيف `query` parameters، ويكتب header. لا يختار الـ AI Model عنوان الخادم، ولا يبني URL بنفسه.

جرّب إرسال `limit` بالقيمة `"5"`. يفشل التحقق؛ فلا يحوّل AGENTO النص إلى رقم تلقائيًا. وجرّب حذف `service_id`؛ سيطلب Runtime الـ Input الناقص قبل إرسال HTTP.

## افهم وجهات Mapping الأربع

| الوجهة | متى تستخدمها؟ | أثرها أثناء التنفيذ |
| --- | --- | --- |
| `request.map.path` | أجزاء URL مثل ID الخدمة | تستبدل `{placeholders}` بقيم بسيطة بعد ترميزها. إذا غابت القيمة، يبقى الـ Placeholder دون قيمة ويتوقف التنفيذ. |
| `request.map.query` | البحث والتاريخ والتصفية وتقسيم النتائج | تضيف Query Parameters إلى URL. إذا كانت القيمة مصفوفة، يتكرر اسم الـ Parameter لكل عنصر، مثل `tag=a&tag=b`. |
| `request.map.body` | حقول JSON لطلبات POST وPUT وPATCH وDELETE | تبني كائن JSON من الحقول المربوطة. يرفض compiler وجود `body` مع GET أو HEAD. |
| `request.map.headers` | بيانات غير سرية مثل اللغة | تحوّل القيم البسيطة إلى نصوص. بيانات الاعتماد و`headers` المحجوزة مرفوضة؛ استخدم `request.auth`. |

مفاتيح `body` تمثل حقول المستوى الأول. لإرسال كائن متداخل، اربط الكائن المعرّف في الـ Schema كاملًا بحقل واحد؛ كتابة نقاط في اسم الوجهة لا تبني كائنًا متداخلًا.

```yaml
# Fragment inside request.map of a confirmed write tool.
body:
  customer: $.customer
  channel:
    source: constant
    value: web
```

مع الـ Input `{ customer: { name: "Maya" } }`، يصبح `body` هو `{"customer":{"name":"Maya"},"channel":"web"}`. عرّف شكل `customer` في `tool.input_schema`، واضبط [Side Effects وConfirmation](/ar/docs/effects) لأن العملية تكتب بيانات.

## اختر مصدر القيمة عن قصد

المسار `$.date` اختصار لـ`{ source: tool-input, path: $.date }`. يبدأ المسار دائمًا من المصدر المختار، وليس من مساحة متغيرات عامة.

| المصدر | أين يبدأ المسار؟ | الاستخدام المناسب |
| --- | --- | --- |
| `tool-input` | `arguments` الـ Tool بعد التحقق وحسم الـ Dependencies | قيم المستخدم، والقيم التي ملأتها الـ Dependencies صراحةً. |
| `session` | كائن `context` الذي يمرّره التطبيق المضيف | tenant أو اللغة أو سياق موثوق آخر. لا يتيح قراءة سجل الـ Session الداخلي أو بيانات الاعتماد. |
| `dependency` | البيانات المحسومة للـ Dependency المباشرة المحددة | قيمة من استعلام معلن. بعد Selection، يكون المصدر هو العنصر المختار، وليس غلاف الـ Response الكامل. |
| `constant` | القيمة المكتوبة في `value` | عملة أو قناة تطبيق ثابتة. كتابة `SAR` وحدها لا تمثل مصدرًا صالحًا. |
| `generated` | UUID جديد | ربط الـ Request بالـ ID الخاص به لتتبّعه. يُولّد عند تجهيز الطلب، ولا يغني عن دعم idempotency في API. |
| `selection` | حقائق العرض المعلنة للـ Selection | معاينة Confirmation فقط. يُمنع استخدامه في HTTP أو `arguments` الـ Dependencies. |

في `source: session`، كلمة `session` تشير إلى `context` الذي مرّره تطبيقك في الاستدعاء، لا إلى كل ما تحفظه المكتبة عن المحادثة. إذا مرّرت `context: { tenantId: "t_1" }`، فالمسار `$.tenantId` يقرأ `t_1`. لا يقرأ سجل الرسائل أو Session ID تلقائيًا.


أما `source: constant` فيرسل قيمة ثابتة من الإعداد. و`source: generated` مع `generator: uuid` ينشئ UUID جديدًا؛ لا تستخدمه لاختراع ID لموعد أو عميل يجب أن يأتي من API.

المقاطع التالية داخل `request.map.body` لـ Tool تكتب بيانات لها سياسة Confirmation. لا تضف `body` إلى GET:

```yaml
# Under request.map.body of a confirmed write tool:
tenant_id: { source: session, path: $.tenantId }
currency: { source: constant, value: SAR }
request_id: { source: generated, generator: uuid }

# Under request.map.body when availability is a declared dependency:
slot_token:
  source: dependency
  tool: availability
  path: $.slot_token
```

مرّر السياق من خادم تطبيقك:

```ts
const result = await runtime.invoke({
  sessionId: RANDOWM_SESSION_ID,
  tool: "book",
  arguments: { date: "2026-10-01" },
  context: { tenantId: "t_1" },
  auth: { token: USER_ACCESS_TOKEN },
});
```

المتغيرات هنا تأتي من تطبيقك بعد التحقق من هوية المستخدم. لا يتم نسخ tenant ID من كلام المستخدم. مكان بيانات الاعتماد هو `auth`، وليس Schema الـ Tool أو ربط `context`.

## المسارات تقرأ الخصائص، ولا تنفّذ تعبيرات

تدعم المسارات `$` و`$.id` و`$.customer.address.city`، وكذلك أجزاء الخصائص الرقمية عندما يسمح المصدر بها. لا تدعم `$.items[*]` أو `$.items[0]` أو filters أو تركيب النصوص أو استدعاء الدوال أو تعبيرات JavaScript.

يفحص compiler أيضًا وجود مسار `tool-input` داخل Schema المعلنة. قد يكون المسار صحيح الصياغة، لكنه يُرفض لأنه يقرأ خاصية لم تعرّفها. المسارات المتعلقة بـprototype محظورة.

استخدم `depends_on.select` للـ Selection من قائمة، بدل افتراض أن عنصرًا في موضع معيّن هو المطلوب.

## غياب القيمة يختلف عن كونها فارغة

إذا غابت قيمة اختيارية من `tool-input`، يُحذف حقلها من الطلب. أما غياب قيمة من `session` أو Dependency فينتج `INPUT_MAPPING_MISSING`؛ لا يخترع الـ Runtime قيمة بديلة للـ Tenant أو الـ ID. القيمة `null` ليست غيابًا؛ يمكن إرسالها داخل JSON `body`، لكنها غير صالحة في URL أو header.

يجب أن تكون قيم `path` و`headers` بسيطة. لا تقبل `path` الشرطات المائلة أو المحارف التحكّمية أو المقطعين `.` و`..`. ولا تقبل `headers` فواصل الأسطر. يجب أن تحتوي مصفوفات `query` على قيم بسيطة فقط.

## ميّز بين أنواع Mapping الثلاثة

```yaml
# Fragment inside a dependency on services:
arguments:
  q: $.service_query
map:
  $.service_id: $.id

```

هذا مقطع مستقل داخل `request` للـ Tool الأم، وليس استمرارًا لمقطع الـ Dependency السابق:

```yaml
map:
  body:
    service: $.service_id
```

1. يمرّر `arguments` قيمة `service_query` من الـ Tool الأم إلى الـ Input `q` في Tool البحث.
2. ينسخ `map` الخاص بالـ Dependency قيمة `id` من نتيجتها إلى `service_id` في الـ Tool الأم. الطرف الأيسر هنا مسار وجهة.
3. يرسل `request.map.body` القيمة المحسومة باسم `service` إلى API. الطرف الأيسر هنا اسم حقل.

هذا الفرق أساس فهم [الـ Dependencies والـ Selection](/ar/docs/dependencies).

## اختبر حدود Mapping

قارن الطلب الذي يصل إلى الـ API في Staging بالـ Request المباشر الذي تعرف أنه يعمل. افحص URL وmethod وأسماء الحقول، دون تسجيل بيانات الاعتماد. يمكنك بعد ذلك استخدام Mock في الاختبارات الآلية لتثبيت هذا السلوك. اختبر غياب حقل اختياري، وغياب context مطلوب، ونوع بيانات غير صالح، وترميز أجزاء URL. يوفّر [مثال الحجز المتكامل](/ar/docs/booking-example) تجربة قابلة للتشغيل وتسلسل النتائج المتوقع.

---

# افحص الـ Response قبل عرضها للمستخدم

في الخطوات السابقة، أرسلنا طلب بحث إلى API. نفترض الآن أنه عاد بنجاح. بقي سؤالان مختلفان: هل البيانات بالشكل الذي يعتمد عليه تطبيقك؟ وأي جزء منها يحتاج المستخدم أو الـ AI Model إلى رؤيته؟

ضع إعداد `response` داخل الـ Tool، بجانب `tool` و`request`. لا تضعه داخل `request.map`؛ Mapping يخص الطلب الخارج، بينما هذا القسم يخص الـ Response العائدة.

## ابدأ من Response API لديك

لنواصل مثال البحث. نفترض أن الـ API تضمن Response بهذا الشكل:

```json
{
  "services": [
    { "id": "svc-color", "name": "Hair coloring", "price": 80 }
  ]
}
```

هنا الـ Response كائن، و`services` قائمة، وكل عنصر يحتوي ID واسمًا وسعرًا رقميًا. إذا كانت API لديك تستخدم `data.items` أو تعيد السعر كنص، فاكتب الـ Schema وفق شكل الـ Response الفعلي. عينة واحدة تساعدك على الفهم، لكنها لا تثبت وحدها أن جميع الحقول مطلوبة.

## تحقّق من الشكل الذي تعتمد عليه

أضف هذا القسم إلى Tool البحث إذا كان شكل الـ Response الذي تضمنه الـ API يطابق الوصف السابق:

```yaml
response:
  output_schema:
    type: object
    properties:
      services:
        type: array
        items:
          type: object
          properties:
            id: { type: string }
            name: { type: string }
            price: { type: number }
          required: [id, name, price]
    required: [services]
```

نبدأ بـ`type: object` لأن الـ Response الكاملة كائن. ثم نعرّف `services` كقائمة. القواعد داخل `items` تنطبق على **كل عنصر**، لا على القائمة نفسها.

`required: [services]` في آخر القسم تشترط وجود القائمة. أما `required: [id, name, price]` فتشترط هذه الحقول في كل خدمة. وجود حقول تحت `properties` لا يجعلها مطلوبة ما لم تذكرها في `required`.

لو أعادت API `{ "services": "unavailable" }` بدل قائمة، يفشل التحقق بالرمز `TOOL_OUTPUT_SCHEMA_VIOLATION` حتى لو كانت حالة HTTP هي 200. هذه فائدة القاعدة: تمنع تطبيقك من التعامل مع نص على أنه قائمة خدمات.

أما `{ "services": [] }` فتطابق القواعد السابقة؛ لم نشترط عنصرًا واحدًا على الأقل. لا تخلط بين نتيجة بحث فارغة وبنية Response مكسورة.

## اختر كيف تريد عرض النتيجة

في البداية استخدمنا `presentation: "raw"`. يعني ذلك أن التطبيق يستقبل البيانات ليعرضها بنفسه، دون الحاجة إلى AI Model يصوغ نصًا منها.

إذا أردت جملة مثل «وجدت خدمة صبغة الشعر بسعر…»، تستطيع استخدام `presentation: "ai"` بعد [إعداد الـ AI Model](/ar/docs/providers). وإذا كنت تحتاج النص والبيانات معًا، استخدم `"both"`.

هذه القيمة من خيارات إنشاء Runtime أو استدعائه، وليست حقلًا عشوائيًا داخل `response`:

```ts
const result = await runtime.invoke({
  sessionId: authenticatedConversationId,
  tool: "search-services",
  arguments: { query: "hair" },
  presentation: "both",
});
```

يفترض المقطع وجود Runtime وAI Model جاهزين في تطبيقك. وإذا لم تضبط AI Model، ابقَ على `raw` أثناء اختبار التكامل. في الوضع `ai` تُحذف `data` من نتيجة العرض عند نجاح الصياغة؛ أما تعثّر الصياغة فلا يستدعي إعادة طلب API الناجح. احتفظ بمعالجة البيانات المتاحة وحالة النتيجة.

## قلّل البيانات المرسلة إلى الـ AI Model الذي يصوغ النتائج

قد تحتوي كل خدمة على عشرات الخصائص، بينما يحتاج الرد إلى الاسم والسعر فقط. أضف هذا المقطع **داخل `response` بجانب `output_schema`**:

```yaml
model_view:
  path: $.services
  include: [name, price]
  max_items: 5
  max_chars: 4000
```

تقرأ `path` القائمة من الـ Response الكاملة. وتختار `include` الحقول اللازمة من كل عنصر. ويحد `max_items` عدد العناصر في البيانات المعدّة للـ AI Model، بينما يحد `max_chars` حجم التمثيل النصي المرسَل إليه.

بهذا لا يحتاج AI Model لصياغة النتائج إلى رؤية ID أو كل تفاصيل الخدمة لصياغة جواب بسيط. **لكن `model_view` لا تصفّي البيانات الخام التي يحصل عليها تطبيقك**. إذا طلبت `raw` أو `both`، لا تفترض أن `data` أصبحت مطابقة لقائمة `include`.

كذلك لا يعني `max_items: 5` أن API أعادت خمس نتائج فقط أو أن العدد الإجمالي هو خمسة. هو حد للبيانات التي تصل إلى الـ AI Model، وليس بديلًا عن Pagination في الـ API.

القيم الافتراضية هي المسار `$`، و20 عنصرًا، و12000 حرف. الحد الأقصى هو 1000 عنصر و100000 حرف. عندما يبقى المسار `$`، يمكن استخدام `response.items_path` كمسار بديل لبيانات العرض. أما Selection داخل Dependency فتستخدم `select.items_path` الخاص بها؛ لا تتبادل هذه الخيارات الأدوار.

## أخفِ الحقول الداخلية عند الحاجة

إذا كانت API تعيد حقلًا داخليًا مثل `internal_note` ولا ينبغي عرضه، أضف تحت `response`:

```yaml
private_fields: [internal_note]
```

هذا حذف بحسب اسم الحقل في البيانات المعروضة، ويشمل مواضع متداخلة. في التنفيذ الحالي تُجمع أسماء الحقول الخاصة عبر الـ Tools؛ لذلك اختر أسماء دقيقة، ولا تحجب اسمًا عامًا مثل `name` دون فهم أثره على بقية النتائج.

تختلف هذه السياسة عن `include`: الأولى لإخفاء بيانات خاصة من العرض، والثانية لتقليل المعلومات التي يحتاجها الـ AI Model الذي يصوغ النتائج. ويمكن أن تبقى قيم داخلية متاحة للـ Dependencies والـ Mapping قبل تنقيح العرض؛ إخفاء ID لا يعني أن التنفيذ فقده.

تبقى API مسؤولة عن صلاحيات الوصول. لا تجعل حذف حقل من الشاشة وسيلتك الوحيدة لحماية بيانات لا يحق للمستخدم طلبها أصلًا.

## افهم ترتيب المعالجة لتشخّص الخطأ

تقرأ المكتبة Response API وتتحقق من `output_schema` إن وُجدت. تستخدم البيانات اللازمة للتنفيذ الداخلي، وتحجب الحقول الخاصة من العرض، ثم تبني البيانات المحدودة للـ AI Model عند الحاجة إلى صياغة.

لذلك، إذا فشل التحقق فلا تعالج المشكلة بتغيير `model_view.path`؛ يجب أولًا مطابقة الـ Response الكاملة لقواعدها. وإذا كانت البيانات صحيحة لكن الرد اللغوي ينقصه السعر، افحص ما إذا كان السعر يصل إلى AI Model لصياغة النتائج أصلًا.

يمكنك أيضًا كتابة `response.instructions` لتوجيه الصياغة، مثل عدم اختراع سعر غائب. التعليمات لا تصلح مسارًا خاطئًا ولا تعوّض حقلًا حذفته من `include`.

## اختبر ما سيراه كل طرف

قارن Response API المباشرة بـ`result.data` في الوضع `raw`. ثم اختبر `both` وتأكد أن الرسالة لا تدّعي معلومات غير موجودة، وأنها تميّز بين عدم وجود نتائج وعدم عرض جميعها.

اختبر قائمة فارغة، وحقلًا مطلوبًا غائبًا، وسعرًا من النوع الخطأ، وحقلًا خاصًا داخل كائن متداخل. استخدم Staging أو Responses محفوظة بعد إزالة البيانات الحساسة لاختبار الأخطاء التي لا يمكنك إحداثها في API الفعلية.

بعد استقرار الطلب والاستجابة، انتقل إلى [إضافة AI Model للمحادثة](/ar/docs/providers). عندها تضيف فهم اللغة إلى Tool تعرف بالفعل كيف تعمل.

---

# Auth والأمان

## اختر بيانات الاعتماد حسب صاحب العملية

لا يحتاج عرض كتالوج عام إلى بيانات اعتماد. أما «حجوزاتي» فتحتاج إلى Token المستخدم المسجّل. وقد يحتاج استعلام بين خادمك وخدمة شريكة إلى Service API Key. اختر النوع الذي يملك صلاحية العملية المطلوبة؛ هذه الأنواع ليست بدائل متكافئة.

في [مثال الحجز](/ar/docs/booking-example)، يمرّر التطبيق `auth.token`. وعند ضبط `request.auth.type: session`، يضيف AGENTO الـ Bearer Token إلى Header عند التنفيذ. إذا غاب الـ Token، قد يُرسل الـ Request دون هذا الـ Header، وتبقى مسؤولية رفضه على الـ API. لذلك تحقّق من تسجيل الدخول داخل تطبيقك قبل العمليات المحمية.

لمفاتيح الخدمات، ضع اسم الـ Environment Variable تحت `env`؛ تغيير اسم `header` يغيّر الترويسة، وليس الصلاحيات التي يمنحها المفتاح. غياب مفتاح خدمة مطلوب يفشل compilation.

## اختبر الحدود مع API لديك

جرّب العملية نفسها باستخدام Token صحيحة، وأخرى منتهية، وID لمورد يخص مستخدمًا آخر، ودون Token. يجب أن تتحقق API من الملكية، وأن تتعامل مع 401 و403 عبر `AUTH_REJECTED`. ثم اختبر تبديل الحساب؛ أنشئ محادثة مصرّحًا بها جديدة أو امسح الـ Session القديمة بدل نقل موافقة معلّقة إلى هوية أخرى.

أبقِ بيانات الاعتماد خارج Config الذي يكتبه المتصفح، وقيم Input الـ AI Model، وmapped `headers`، و`context` العادي. يدعم redaction ضوابط الوصول في تطبيقك، ولا يحل محلها.


## AGENTO ليس خادم Auth لديك

لا يفك Runtime الحالي ترميز JWTs الخاصة بالمستخدمين، ولا يجلب مفاتيح JWKS، ولا يتحقق من issuers أو audiences، ولا يقرّر صلاحيات العمل. هذا الفصل مقصود.

تبقى API المستقبِلة هي المرجع في:

- صلاحية Token وانتهاء مدته.
- حالة تسجيل الدخول.
- الأدوار وscopes.
- حدود tenants.
- ملكية الموارد.
- صلاحيات إجراءات العمل.

## Tools تتطلب مصادقة المستخدم

```yaml
request:
  auth:
    type: session
```

يمرّر التطبيق المضيف الموثوق Token الخاص بالمتصل:

```ts
const result = await runtime.invoke({
  sessionId: serverOwnedSessionId,
  tool: "my-bookings",
  auth: { token: userAccessToken },
});
```

وعند التنفيذ، يرسل AGENTO:

```http
Authorization: Bearer <opaque-token>
```

لا يُفترض إدراج Token في prompts أو Schemas التي يراها الـ AI Model، أو سياق الـ Session العادي، أو النتائج، أو hooks.

## Responses 401 و403

تظهر Responses الخدمة المستقبِلة 401 و403 على هيئة `AUTH_REJECTED`، مع رمز الحالة ضمن تفاصيل الخطأ المحدودة. ويقرر التطبيق المضيف إن كان المطلوب تسجيل الدخول مجددًا، أو تجديد الـ Session، أو إبلاغ المستخدم بأنه لا يملك الصلاحية.

## Session ID لا يثبت هوية المستخدم

قاعدة أساسية في Production:

> يحدد `sessionId` المحادثة أو العملية التي تريد متابعتها، ولا يثبت هوية المستخدم.

يجب أن يربط التطبيق المضيف قيم Session ID التي يديرها الخادم بالمتصلين. لا تسمح للعميل باختيار Session مستخدم آخر عشوائيًا، أو ربط Token لمستخدم مختلف بـ Session قائمة.

عند تبديل الحساب، امسح الـ Session السابقة أو أنشئ Session ID جديدًا يديره الخادم.

## Tools تستخدم API key

لبيانات الاعتماد بين الخوادم:

```yaml
request:
  auth:
    type: api-key
    env: SERVICE_KEY
    header: X-API-Key
```

يتوقع compiler وجود Environment Variable. لا تضع مفاتيح الخدمات في URLs أو Input المستخدم أو prompts.

## الـ Tools العامة

```yaml
request:
  auth:
    type: none
```

استخدم هذا النوع فقط عندما تكون REST Endpoint عامة فعلًا.

## إعادة استخدام بيانات اعتماد الـ Session

يمكن لـAGENTO الاحتفاظ بـ Token دون تفسير محتواه للـ Session لتستخدمه استدعاءات `select()` و`confirm()` اللاحقة. تدعم واجهة Auth المصدّرة `SessionTokenStore` مخصصًا؛ وتُشفّر قيم Tokens قبل وصولها إلى المخزن.

عند استخدام مخزن مخصص، وفّر مفتاح تشفير مشتركًا بطول 32 بايت للـworkers التي تحتاج إلى استخدام بيانات الاعتماد نفسها عبر عدة instances.

## حجب البيانات الحساسة

يحمي Runtime البيانات الحساسة المعروفة والحقول الحساسة في النتائج والسجل وhooks والبيانات المتجهة إلى الـ AI Model. هذه حماية أثناء النقل، **وليست مصنّفًا شاملًا للبيانات الشخصية PII**.

يجب أن تظل API مسؤولة عن إعادة البيانات المناسبة للمتصل فقط، وتقليل البيانات وفق طبيعة مجالك.

## ضوابط النقل

- الطلبات التي تحمل بيانات اعتماد تتطلب HTTPS، إلا عند تعطيل ذلك صراحةً للتطوير المحلي.
- تُحظر redirects.
- يتحقق Runtime من تطابق origin النهائي للطلب مع الـ Origin المحدد في الـ Config.
- يوجد حد أقصى لحجم Response Body بالبايت.
- البيانات المقتطعة للعرض على الـ AI Model محدودة الحجم.
- لا يمكن تعريف بيانات الاعتماد كحقول Input عادية للـ AI Model.

---

# أضف فهم اللغة إلى الـ Tool التي اختبرتها

Tool البحث تعمل الآن عبر `invoke()`: أنت تختار اسمها وتقدّم مدخلاتها. نريد أن يكتب المستخدم طلبه بلغته، فتحدد المكتبة الـ Tool المناسبة وتستخرج مدخلاتها بمساعدة AI Model.

هذا هو دور `models.routing`. لا تحتاج إلى تغيير `request.url` أو Mapping لإضافة المحادثة؛ سنستخدم تعريف الـ Tool نفسه الذي نجح مع API.

## اختر Provider وAI Model، ثم ضع المفتاح في Environment Variable

يدعم الإصدار الموثّق Providers `openai` و`anthropic` و`mistral` و`cohere` و`ollama`. اسم الـ Provider يحدد بروتوكول الاتصال، بينما `model` يحدد الـ AI Model الفعلي في حسابك. لا يكفي كتابة اسم الـ Provider وحده.

أضف هذا القسم إلى **جذر `config.yml`**، بجانب `version` و`tools`. المثال يستخدم OpenAI؛ استخدم ID لـ AI Model متاح في حسابك ويدعم استخدام الـ Tools.

```yaml
models:
  routing:
    provider: openai
    model: YOUR_TOOL_CAPABLE_MODEL
    api_key: $ENV:OPENAI_API_KEY
    timeout_ms: 30000
```

`api_key` هنا مرجع إلى Environment Variable، وليس المفتاح نفسه. القيمة `$ENV:OPENAI_API_KEY` تطلب من المكتبة قراءة متغير اسمه `OPENAI_API_KEY` عند إنشاء Runtime. غيابه يمنع تشغيل إعداد يحتاج إليه.

ضع المفتاح في بيئة الخادم الذي سيشغّل AGENTO. المكتبة لا تقرأ ملف `.env` تلقائيًا؛ حمّله بآلية مشروعك أو خيار Node المناسب. لا ترسل مفتاح الـ AI Model إلى المتصفح أو تخلطه مع Token الذي تستخدمه API لديك.

`timeout_ms: 30000` يحدد مهلة استدعاء الـ AI Model بثلاثين ثانية. هذه ليست مهلة طلب API للـ Tool؛ تلك تُضبط داخل `request` في الـ Tool.

## جرّب رسالة بدل الـ Input الجاهزة

بعد حفظ الإعداد، أعد إنشاء Runtime ليقرأه. داخل تطبيقك، استبدل الاستدعاء المباشر برسالة:

```ts
const result = await runtime.chat({
  sessionId: authenticatedConversationId,
  message: "ابحث عن خدمات صبغة الشعر",
  presentation: "raw",
});
```

يفترض هذا المقطع أن `runtime` موجود في خادمك، وأن `authenticatedConversationId` Session ID مرتبط بالمستخدم الحالي. إذا كانت الـ Tool تحتاج Token أو `context`، مرّرهما كما فعلت في الاستدعاء المباشر.

في `invoke()` كتبت اسم الـ Tool و`arguments` بنفسك. في `chat()` ترسل الرسالة، ويختار الـ AI Model Tool من الـ Tools المسموحة ويقترح قيمًا لمدخلاتها. بعدها تطبّق المكتبة قواعد التحقق والـ Mapping نفسها.

افحص `result.tool` لترى ما إذا اختيرت الـ Tool المتوقعة، و`result.status` لتعرف الخطوة التالية. نجاح الرد اللغوي لا يثبت وحده أن HTTP نُفّذ؛ يمكن أن تنتهي تحية مثل «مرحبًا» دون Tool.

## افصل فهم الطلب عن صياغة النتيجة

ابدأ بـ`raw` كما في المثال؛ تستطيع اختبار اختيار الـ Tool دون إضافة صياغة لغوية للنتيجة. إذا أردت عرضًا لغويًا، استخدم `ai` أو `both` بعد ضبط [الـ Response](/ar/docs/responses).

يمكنك تعريف `models.presentation` لاستخدام AI Model مختلف لصياغة النتائج. هذا اختياري؛ عند غيابه تُستخدم إمكانات الـ AI Model الخاص بالـ Routing لصياغة النتائج. وتستخدم المحادثة العامة AI Model لصياغة النتائج افتراضيًا إذا كان موجودًا، وفق `assistant.conversation.use_presentation_model`.

لا تضف AI Model ثانيًا لمجرد أن القسم متاح. أضفه عندما تحتاج إلى اختلاف واضح في الكلفة أو زمن الرد أو جودة الصياغة.

## اختبر اللغة قبل تعديل القدرات المتقدمة

جرّب طلب بحث واضحًا، وصياغة أخرى له، وطلبًا تنقصه معلومة، وتحية لا تحتاج إلى API. إذا فشل `invoke()` بالقيم نفسها، أصلح التكامل أولًا. وإذا نجح وفشل اختيار الـ Tool من الرسالة، تابع [فصل Routing](/ar/docs/routing).

**بقية هذا الفصل مرجع للـ Providers والـ Adapters المخصصة.** لا تحتاج إلى كتابة `ModelAdapter` كي تبدأ باستخدام Provider مدعوم.

## تصف capability flags قدرات فعلية

تحدد `nativeTools` إمكانية استخدام Native Tool Calling. إذا لم تكن مدعومة، فقد يستخدم AGENTO Structured Output أو JSON يتحقق من صحته. وتصف `strictSchema` و`systemMessages` و`temperature` ميزات البروتوكول؛ لا تضمن هذه الخيارات جودة فهم الـ AI Model أو صحة قرارات العمل.

لا تفعّل flags لإخفاء مشكلة تكامل. اختبر اختيار Tool صحيحًا، وقيم Input غير صالحة، وإجابة دون Tool، وإلغاء طلب، وانتهاء مهلة. وعند بناء adapter مخصص، أعد البنية وقيم usage الموثقة؛ سيظل Runtime يتحقق من Selection والـ Input.


## IDs الخاصة بالـ Providers المدمجة

تقبل إعدادات AGENTO المدمجة:

- OpenAI
- Anthropic
- Mistral
- Cohere
- Ollama

تختلف القدرات بين Providers والـ AI Models. يصرّح adapter بالميزات التي يدعمها، بدل افتراض أن كل AI Model يدعم كل ميزات البروتوكول.

## واجهة تعريف القدرات

```ts
interface ModelCapabilities {
  nativeTools: boolean;
  structuredOutput: boolean;
  strictSchema: boolean;
  systemMessages: boolean;
  temperature: boolean;
}
```

تأخذ native tool calling الأولوية. وعند غيابها، قد يستخدم AGENTO Structured Output أو JSON بديلًا يجري التحقق منه، بحسب القدرات المعلنة.

## ملخص سلوك Providers

| Provider | مسار استدعاء الـ Tools الأصلي | البديل المنظّم |
|---|---|---|
| OpenAI | Chat Completions `tools` | صيغة Response JSON Schema |
| Anthropic | Messages tools/input_schema | JSON بديل يتحقق الـ Adapter من صحته |
| Mistral | Chat `tools` | صيغة Response JSON Schema |
| Cohere | v2 Chat `tools` | مسار JSON object/Schema |
| Ollama | `/api/chat` `tools` | JSON Schema عبر `format` |

يصف الجدول سلوك adapters، ولا يضمن أن كل AI Model منشور يدعم جميع الميزات.

## `ModelAdapter` مخصص

الواجهة العامة صغيرة:

```ts
interface ModelAdapter {
  capabilities(): ModelCapabilities;
  generateText(request: GenerationRequest): Promise<TextGenerationResult>;
  generateStructured(
    request: GenerationRequest & { schema: JsonSchema }
  ): Promise<StructuredGenerationResult>;
  selectTool(request: ToolSelectionRequest): Promise<ToolSelectionResult>;
}
```

لتمرير adapter:

```ts
const runtime = await AgentRuntime.create({
  config: configObject,
  model: myRoutingModel,
  presentationModel: myPresentationModel,
});
```

## متطلبات adapter

ينبغي أن يحقق adapter المخصص لـ Production ما يلي:

- احترام `AbortSignal`.
- إعادة بيانات استخدام محدودة الحجم.
- عدم جمع عدة استدعاءات Tools غير مراجعة في إجراء واحد.
- الحفاظ على Schemas الـ Tools التي يراها الـ AI Model.
- إظهار فشل Provider كخطأ، بدل تمرير نص الخطأ ضمن رسالة عادية من المساعد.
- التصريح بوضوح عن القدرات غير المدعومة.

## اختبر النشر الفعلي لديك

تتغير بروتوكولات Providers ودعم AI Models. شغّل حالات اختبار Routing والتقييم على IDs الخاصة بالـ AI Models والـ REST Endpoints نفسها التي تنوي استخدامها.

---

# اجلب البيانات التي تحتاجها العملية قبل تنفيذها

افترض أن API لإنشاء حجز تحتاج `date` و`slot_token`. يعرف المستخدم التاريخ الذي يناسبه، لكنه لا يعرف Token الداخلي للموعد. تأتي هذه القيمة من API المواعيد المتاحة، وقد تتغيّر عندما يُحجز الموعد أو تنتهي صلاحيتها.

لا تحل المشكلة بأن تطلب من الـ AI Model ملء الحقل المطلوب بأي قيمة. عرّف Tool قراءة تجلب التوافر، ثم اجعل Tool الحجز تعتمد عليها عبر `depends_on`. هذا معنى Dependency هنا: **عملية تستدعيها المكتبة للحصول على بيانات تحتاجها عملية أخرى**.

قبل المتابعة، يجب أن تكون قادرًا على [ربط مدخل بطلب HTTP](/ar/docs/mapping). سنضيف الآن مرحلة تسبق هذا الـ Mapping: كيف يصل الـ ID الصحيح إلى Input الخاص بـ Tool الحجز؟

## افصل بين بيانات المستخدم وبيانات API

نسمي Tool الحجز `book`، ونسمي استعلام المواعيد `availability`. هذه IDs لـ Tools يجب أن تكون معرّفة فعلًا في ملفك؛ كتابة أسمائها داخل `depends_on` لا تنشئها.

تحتاج `availability` إلى التاريخ فقط، وتعيد قائمة بالمواعيد. أما `book` فتحتاج التاريخ والـ Token الخاص بالموعد المختار. عرّف الحقلين في `input_schema` للحجز واجعلهما مطلوبين عبر `required: [date, slot_token]`. ثم حدد في الـ Dependency أن الـ Token يأتي من نتيجة الاستعلام.

تخفي المكتبة الحقول المباشرة التي تملؤها الـ Dependencies عن حقول الـ Input المعروضة للـ AI Model. لذلك يتعامل الـ AI Model مع التاريخ، بينما يحصل التنفيذ على Token من API. هذا الإخفاء لا يعني حذف الحقل من قواعد التنفيذ، ولا يضمن إخفاء كل حقل متداخل تلقائيًا.

## حدّد أولًا ما الذي يعيده الاستعلام

لشرح المسارات، سنستخدم Response بهذا الشكل:

```json
{
  "slots": [
    { "slot_token": "slot-a", "label": "10:00" },
    { "slot_token": "slot-b", "label": "11:00" }
  ]
}
```

لاحظ المستويات الثلاثة. الـ Response الكاملة كائن يحتوي `slots`. و`slots` قائمة. وكل عنصر داخلها كائن يحتوي `slot_token` و`label`.

هذا التمييز يحدد معنى كل مسار بعد قليل. إذا كانت API لديك تعيد `data.slots` أو تستخدم أسماء حقول مختلفة، غيّر المسارات بناءً على استجابتها؛ لا تغيّر backend ليطابق المثال.

## أضف الـ Dependency إلى Tool الحجز

هذا المقطع يوضع داخل عنصر `book`، بجانب `tool` و`request`. يفترض أنك عرّفت `availability` كـ Tool للقراءة تقبل التاريخ وتعيد الـ Response السابقة. للحصول على الملفين والأداتين في مثال مستقل، راجع [تجربة الحجز](/ar/docs/booking-example).

```yaml
depends_on:
  - tool: availability
    when: always
    arguments:
      date: $.date
    select:
      items_path: $.slots
      id_path: $.slot_token
      label_path: $.label
      id_sensitive: true
      facts:
        time: $.label
    map:
      $.slot_token: $.slot_token
    ttl_ms: 30000
```

اقرأ المقطع على ثلاث مراحل: **إرسال Input الاستعلام، ثم اختيار نتيجة، ثم نقل القيمة إلى الحجز**.

تحدد `tool: availability` Tool القراءة. ويأخذ `arguments.date: $.date` التاريخ من Input `book` ويرسله كمدخل اسمه `date` إلى `availability`. بعد ذلك تستخدم Tool التوافر إعداد `request` الخاص بها لإرسال التاريخ إلى API؛ هذه طبقة ربط مختلفة.

داخل `select`، يبدأ `items_path: $.slots` من **الـ Response الكاملة** ليجد القائمة. ثم يبدأ `id_path` و`label_path` من **عنصر واحد** في هذه القائمة. لذلك لا نكتب `$.slots.slot_token` في `id_path`.

تقرأ `label_path` النص الذي سيفهمه المستخدم، مثل الساعة العاشرة. وتقرأ `id_path` هوية الخيار. عند تفعيل `id_sensitive`، يعيد AGENTO للواجهة ID لا يكشف الـ Token الأصلي. مرّر هذا الـ ID كما هو إلى `select()`؛ تحتفظ المكتبة بالقيمة الأصلية داخليًا لاستخدامها في الحجز.

أما `facts.time` فتحفظ تسمية الوقت كي نستطيع عرضها في معاينة Confirmation. لا تحوّل هذه القيمة إلى مصدر لتنفيذ HTTP؛ قيم Selection Facts مخصصة للعرض.

## افهم لماذا يتكرر `slot_token` على طرفي `map`

السطر `$.slot_token: $.slot_token` لا ينقل القيمة إلى نفسها. الطرفان يقرآن كائنين مختلفين:

```text
Selected API item:
  { slot_token: "slot-b", label: "11:00" }
                |
                | depends_on.map
                v
Parent tool arguments:
  { date: "2026-10-01", slot_token: "slot-b" }
                |
                | request.map.body
                v
POST body:
  { date: "2026-10-01", slot_token: "slot-b" }
```

**يمين `depends_on.map`** يقرأ `slot_token` من العنصر الذي اختاره المستخدم. **يساره** يكتب القيمة في مدخل Tool الحجز. لو كانت API تعيد الحقل باسم `token`، يصبح السطر `$.slot_token: $.token`؛ لا يلزم أن يتطابق الاسمان.

بعدها فقط يأتي دور `request.map.body` في Tool الحجز:

```yaml
# Inside the booking tool's request.map:
body:
  date: $.date
  slot_token: $.slot_token
```

يقرأ هذا الـ Mapping الـ Input المكتمل للحجز ويبني JSON الذي سيرسله AGENTO إلى الـ API. وجود Dependency لا يعفيك من ربط الحقل بطلب الحجز. كما أن الحجز نفسه يحتاج [سياسة كتابة وConfirmation](/ar/docs/effects) قبل إرسال POST.

## تابع ما يراه تطبيقك في كل مرحلة

عندما تطلب تشغيل `book` بتاريخ صالح، تستدعي المكتبة التوافر أولًا. إذا غاب التاريخ، تحصل على `needs_input` قبل الاستعلام؛ ابدأ بجمع الـ Input الذي يعرفه المستخدم.

إذا أعاد الاستعلام عدة مواعيد، تكون النتيجة `needs_selection`. اعرض `selection.options` للمستخدم. لا يوجد حجز في هذه اللحظة، ولا ينبغي أن تظهر رسالة «تم الحجز».

عندما يختار المستخدم موعدًا، مرّر الـ ID الذي أعاده AGENTO لهذا الخيار:

```ts
// In your choice handler, after the user selects an option:
const next = await runtime.select({
  sessionId: authenticatedConversationId,
  selectionId: result.selection.id,
  choice: selectedOption.id,
});
```

`selectionId` يحدد قائمة الاختيارات المعلّقة، و`choice` يحدد عنصرًا منها. لا تمرّر ترتيب العنصر في المصفوفة بدل الـ ID، ولا تختَر أول عنصر تلقائيًا في الواجهة. يستطيع `chat()` أثناء انتظار Selection التعرف على رقم الخيار المعروض، بدءًا من 1، أو نصه المطابق تمامًا؛ أما `select()` فتتوقع الـ ID الذي أعاده AGENTO.

بعد Selection، تربط المكتبة Token وتتابع Tool الحجز. إذا كان Confirmation مطلوبًا، تعيد `needs_confirmation`. تعرض المعاينة وتنتظر الموافقة؛ لم يصل POST إلى API بعد. يشرح [التعامل مع الحالات](/ar/docs/runtime-flow) ما الذي تعرضه الواجهة في كل مرحلة.

## ماذا لو لم توجد عدة مواعيد؟

إذا كانت القائمة فارغة، تعود النتيجة `needs_input`، وقد تكون `missing` فارغة؛ الحقول مكتملة لكن لا توجد نتيجة مناسبة. اطلب تاريخًا آخر بدل البحث عن حقل ناقص غير موجود.

إذا وجد عنصر واحد ولا يوجد تفضيل صريح يخالفه، يمكن اختياره تلقائيًا. هذا يزيل خطوة Selection فقط، ولا يلغي Confirmation عملية الكتابة.

إذا كانت بنية القائمة أو IDs أو labels غير صالحة، فهذه مشكلة في شكل الـ Response المتفق عليه أو في المسارات، وليست حالة «لا توجد مواعيد». افحص `DEPENDENCY_SELECTION_INVALID`. وإذا غاب الحقل الذي يحاول `map` قراءته، افحص `DEPENDENCY_UNRESOLVED`.

## طابق التفضيل عندما يذكر المستخدم وقتًا

إذا كانت `book` تقبل مدخلًا اختياريًا اسمه `preferred_time`، يمكنك إضافة هذا المقطع داخل `select`:

```yaml
match:
  input_path: $.preferred_time
  item_path: $.label
```

يقرأ `input_path` تفضيل المستخدم من الـ Tool الأم، ويقارن به `item_path` في كل خيار. المقارنة للنصوص والأرقام، مع حذف الفراغات الطرفية وتوحيد حالة الأحرف؛ ليست فهمًا تقريبيًا للوقت. `4 PM` لا يساوي `16:00` لمجرد أن المقصود قد يكون واحدًا.

عند وجود تطابق وحيد يُختار. وعند وجود عدة تطابقات يلزم اختيار. وعندما لا يطابق التفضيل أي نتيجة، تُعرض البدائل حتى لو بقي خيار واحد.

## اختر متى تُجلب البيانات وكم تبقى صالحة

`when: always` هي القيمة الافتراضية. تعني أن الـ Dependency يجب حسمها حتى لو مرّر المستدعي Token. لكنها لا تعني طلب HTTP جديدًا في كل مرة؛ يمكن استخدام نتيجة مخزنة ما دامت صالحة ومدخلاتها وسياقها مطابقين.

أما `when: missing` فتسمح بتجاوز Dependency لم تُحسم بعد إذا كانت كل وجهات `map` موجودة مسبقًا. تناسب قيمة وفّرها تطبيق موثوق أو خطوة Navigation سابقة. لا تستخدمها كدليل أن Token الذي مرّره المستدعي ما زال صالحًا لدى API.

`ttl_ms: 30000` تعني ثلاثين ثانية. بعدها لا ينبغي الاعتماد على بيانات الاستعلام القديمة. القيمة الافتراضية ستون ثانية. إطالة المدة قد تقلل الاستعلامات، لكنها تزيد احتمال عرض موعد لم يعد متاحًا.

عمر Confirmation لا يتجاوز صلاحية البيانات التي يعتمد عليها. Confirmation مدته خمس دقائق لا يحافظ على صلاحية موعد جُلب قبل ثلاثين ثانية. عند انتهاء الصلاحية، أعد تجهيز العملية واطلب مراجعة مناسبة للبيانات الجديدة.

## توسّع بعد فهم Dependency واحدة

قد تحتاج `availability` نفسها إلى البحث عن الخدمة أولًا. عندها عرّف اعتمادها على Tool `services`؛ تنفّذ المكتبة أعمق Dependency أولًا ثم تعود نحو الحجز.

تُحسم الـ Dependencies بالترتيب، ولا تعمل بالتوازي. تستطيع Dependency لاحقة قراءة مدخل في الـ Tool الأم ملأته Dependency سابقة. لا تقرأ نتائج الـ Dependency مباشرةً عبر `source: dependency` داخل `arguments`؛ انقل القيمة إلى مدخل معلن أولًا.

إذا أعادت Tool القراءة كائنًا واحدًا ولا تحتاج إلى اختيار، احذف `select`. عندها يبدأ يمين `map` من الـ Response كاملة، لا من عنصر مختار. مثل Response بالشكل `{ customer: { id: "cus-42" } }`، تقرأ منه القيمة عبر `$.customer_id: $.customer.id`. عرّف `customer_id` في الـ Tool الأم وعرّف Tool القراءة. إضافة `select` أو حذفها يغيّر نقطة بداية المسارات في `map`.

## اختبر النتائج قبل Production

اختبر غياب التاريخ، وعدم وجود مواعيد، ووجود موعد واحد أو عدة مواعيد، وتكرار IDs، وغياب الـ Token، وانتهاء Selection، وتغيّر سياق التطبيق. تأكد من عدم إرسال أي كتابة قبل Confirmation. ثم اختبر Recovery بعد `409` أو `410`؛ يجب أن يطلب مراجعة جديدة، ولا يحجز موعدًا بديلًا تلقائيًا.

ترفض المكتبة العلاقات الدائرية والـ Dependencies المكررة والـ Tools غير الموجودة والـ Dependencies التي تكتب بيانات ووجهات Mapping المتعارضة. يحدد `session.max_steps` أقصى عدد من خطوات التنفيذ، وليس عدد الـ Tools في الملف. لا تحتاج إلى سلسلة معقدة في البداية؛ أثبت أولًا أن Token الصحيح وصل من الاستعلام إلى الحجز، ثم أضف خطوة أخرى.

---

# اطلب موافقة المستخدم قبل تغيير البيانات

الـ Side Effect هي عملية تغيّر بيانات العمل، مثل إنشاء حجز أو تعديل ملف أو خصم مبلغ من حساب أو حذف عنصر. في البحث كان إرسال الطلب يعيد معلومات فقط؛ أما هنا فقد تنشأ نتيجة فعلية لا يريدها المستخدم إذا أُرسلت بالخطأ.

يحدد `behavior.effect` نوع العملية، وتحدد `confirmation` متى تنتظر الموافقة. اختيار الـ AI Model لـ Tool الحجز يعبّر عن فهمه للطلب، لكنه لا يستدعي `confirm()` نيابة عن تطبيقك. سنفصل تجهيز العملية عن تنفيذها.

## اختر التصنيف الذي يصف العملية

| `behavior.effect` | مثال | النتيجة الملزمة |
| --- | --- | --- |
| `read-only` | GET للتوافر أو HEAD للبيانات الوصفية | الافتراضي، ويتيح استخدامها كـ Dependency تلقائية. |
| `side-effect` | POST للحجز أو PATCH للملف | يتطلب `confirmation.required: true`، ولا يصلح كـ Dependency تلقائية. |
| `destructive` | DELETE للحجز أو إزالة لا يمكن التراجع عنها | يتطلب Confirmation وضوابط الكتابة نفسها، لكنه لا يضيف شاشة موافقة ثانية تلقائيًا. |

يرفض الـ Compiler الحالي تصنيف أي Method غير GET وHEAD على أنها `read-only`. حتى Endpoint تستخدم POST للبحث تحتاج إلى تصنيف Side Effect في هذا الإصدار. لا تستخدم `read-only` لتجاوز Confirmation.

## ابدأ بعملية الحجز

هذا قسم `behavior` من [إعداد الحجز الكامل](/ar/docs/booking-example):

```yaml
behavior:
  effect: side-effect
  confirmation:
    required: true
    ttl_ms: 30000
    preview:
      date: $.date
      time: { source: selection, path: $.time }
      notes: $.notes
```

تخبر `effect: side-effect` المكتبة أن العملية تغيّر البيانات. وتفرض `required: true` التوقف قبل التنفيذ. أما `ttl_ms: 30000` فتعطي المستخدم ثلاثين ثانية للمراجعة ما لم تنتهِ بيانات يعتمد عليها الطلب قبل ذلك. وتحدد `preview` المعلومات التي سيوافق عليها.

إذا لم تحدد `behavior.effect` المناسب، يرفض الـ Compiler عملية POST بالخطأ `CONFIG_EFFECT_INVALID`. وإذا حددت Side Effect دون فرض Confirmation، يعيد `CONFIG_CONFIRMATION_INVALID`. هذه أخطاء بدء تشغيل، وليست تلميحات للواجهة.

تغيّر إضافة هذا القسم التنفيذ فعليًا؛ بعد حسم الـ Input والـ Dependencies، يجهّز AGENTO طلب HTTP ويخزنه، ثم يعيد `needs_confirmation` وينتظر الموافقة. لم يُرسل الحجز بعد.

## صمّم معاينة يستطيع المستخدم مراجعتها

المعاينة كائن بيانات تستطيع واجهتك عرضه، وليست نصًا يُنفّذ أو طلبًا جديدًا إلى API. في المثال، يقرأ `date` و`notes` من Input الحجز، ويقرأ `time` من حقيقة العرض التي سمّيناها `facts.time` في فصل الـ Dependencies. هكذا يرى المستخدم التاريخ والوقت الفعليين بدل Token لا يفهمه.

يسار كل سطر في `preview` اسم الحقل الذي سيظهر في كائن المعاينة، ويمينه مصدر القيمة. تغيير اسم حقل المعاينة لا يغيّر `body` الحجز؛ ذلك ما زال تحت `request.map.body`.

القيم المحفوظة في Selection Facts **للعرض فقط**. استخدم ربط الـ Dependencies لتمرير Token الحقيقي إلى HTTP. يُمنع كشف بيانات اعتماد أو حقول private في المعاينة، كما تُمنع القيم generated. قراءة حقيقة اختيار غير معلنة تفشل compilation.

إذا حذفت `preview`، يعرض Runtime حقول الـ Input التي يراها الـ AI Model. قد يكون ذلك صالحًا تقنيًا لكنه ناقص المعنى؛ فالحقول التي تنتجها الـ Dependencies مخفية عن الـ AI Model، وقد لا يظهر وقت الموعد المختار. عرّف معاينة صريحة للعمليات المهمة.

## التطبيق مسؤول عن إجراء الموافقة

تحتوي النتيجة على ID Confirmation وموعد انتهائه ومعاينته. اعرض المعاينة مع إجراء واضح للـ Confirmation. داخل server handler المرتبط به:

```ts
// Called only after the user approves the displayed action.
const next = await runtime.confirm({
  sessionId: authenticatedConversationId,
  confirmationId: displayedConfirmation.id,
  auth: { token: userAccessToken },
});
```

استخدم الـ Session المصرّح بها والسياق نفسيهما. لا تقبل Session ID عشوائيًا من المتصفح. أعد الحالة التالية إلى الواجهة؛ فقد ترفض API العملية حتى بعد Confirmation.

إرسال «نعم» أو «yes» عبر `chat()` يعيد الـ Confirmation المعلّق ولا يرسل عملية الكتابة. بعد موافقة المستخدم، يستدعي تطبيقك `confirm()` ويمرّر قيمة `confirmation.id` في `confirmationId`. عند الرفض استخدم `cancel()`؛ إخفاء البطاقة وحده لا يلغي العملية.

## افهم الانتهاء والإبطال

القيمة الافتراضية لـ`confirmation.ttl_ms` خمس دقائق، وتُكتب في الإعداد بالمللي ثانية: `300000`. لكن الانتهاء الفعلي هو أقرب موعد بين هذه المدة وانتهاء الاختيارات أو الـ Dependencies. Confirmation مدته خمس دقائق لا يجعل الـ Token الخاص بالموعد صالحًا لخمس دقائق إذا انتهى بعد ثلاثين ثانية.

يربط Runtime Confirmation بالطلب المجهّز وسياق التنفيذ. تغيّر العملية أو السياق، أو انتهاء الـ Dependency، أو ID غير صحيح، أو إعادة الموافقة، يجعل الـ Confirmation غير صالح للمتابعة. لا تكرر ID نفسه؛ أعد تجهيز العملية المطلوبة واطلب مراجعة جديدة.

## استهلاك Confirmation لا يضمن التسليم مرة واحدة

يحفظ AGENTO استهلاك Confirmation قبل إرسال HTTP، فلا يُفترض أن تنفّذ الموافقة نفسها العملية مرارًا. لكن قد تنتهي المهلة بعد قبول API للحجز، أو يتوقف worker بين الإرسال وتسجيل النجاح.

للعمليات المهمة، وفّر idempotency في backend وطريقة للاستعلام عن حالة العملية. عند ظهور `TOOL_EXECUTION_UNCERTAIN`، تحقق من حالة العملية لدى الـ API قبل محاولة إنشاء الحجز من جديد.

```yaml
# request fragment, only when the API honors this header:
idempotency:
  header: Idempotency-Key
  source: generated
retry:
  attempts: 3
```

يُضاف المفتاح إلى الطلب المجهّز، ويُعاد استخدامه في محاولات النقل التابعة له. أما invocation جديد بالكامل فيجهّز مفتاحًا جديدًا. هذا الخيار لا ينفّذ منع التكرار داخل خادمك. اقرأ [سياسة HTTP](/ar/docs/http).

## حدّث الخيار غير المتاح ثم اطلب الموافقة من جديد

إذا أعادت API الحالة 409 أو 410 بسبب اختفاء الموعد، أضف:

```yaml
# Merge into behavior on the parent write tool.
recovery:
  refresh_dependency: availability
  statuses: [409, 410]
```

يجب أن تكون الـ Dependency المحددة معلنة مباشرةً في الـ Tool. يحذف Runtime قيمها القديمة، ويعيد الاستعلام للقراءة فقط، ثم يعيد اختيارًا أو Confirmation جديدًا. لا يوافق على البديل تلقائيًا.

استخدم recovery عندما تعني هذه الحالات فعلًا أن بيانات الـ Dependency أصبحت قديمة. لا تفسّر 409 الناتجة عن قاعدة عمل أخرى تلقائيًا على أنها طلب لتغيير الموعد.

## اختبر السياسة نفسها

شغّل [مثال الحجز](/ar/docs/booking-example)، ثم تحقق من هذه الحدود: لا POST قبل الموافقة، وPOST واحد بعدها، وعدم إرسال POST ثانٍ عند تكرار ID، ورفض Confirmation المنتهي، وطلب مراجعة جديدة بعد إعادة جلب الخيارات بعد تلقي الحالة `409`. تثبت هذه الاختبارات السياسة أكثر من مجرد ظهور بطاقة Confirmation.
