افترض أن API لإنشاء حجز تحتاج date وslot_token. يعرف المستخدم التاريخ الذي يناسبه، لكنه لا يعرف Token الداخلي للموعد. تأتي هذه القيمة من API المواعيد المتاحة، وقد تتغيّر عندما يُحجز الموعد أو تنتهي صلاحيتها.
لا تحل المشكلة بأن تطلب من الـ AI Model ملء الحقل المطلوب بأي قيمة. عرّف Tool قراءة تجلب التوافر، ثم اجعل Tool الحجز تعتمد عليها عبر depends_on. هذا معنى Dependency هنا: عملية تستدعيها المكتبة للحصول على بيانات تحتاجها عملية أخرى.
قبل المتابعة، يجب أن تكون قادرًا على ربط مدخل بطلب HTTP. سنضيف الآن مرحلة تسبق هذا الـ 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 بهذا الشكل:
{
"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 السابقة. للحصول على الملفين والأداتين في مثال مستقل، راجع تجربة الحجز.
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 لا ينقل القيمة إلى نفسها. الطرفان يقرآن كائنين مختلفين:
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 الحجز:
# Inside the booking tool's request.map:
body:
date: $.date
slot_token: $.slot_token
يقرأ هذا الـ Mapping الـ Input المكتمل للحجز ويبني JSON الذي سيرسله AGENTO إلى الـ API. وجود Dependency لا يعفيك من ربط الحقل بطلب الحجز. كما أن الحجز نفسه يحتاج سياسة كتابة وConfirmation قبل إرسال POST.
تابع ما يراه تطبيقك في كل مرحلة
عندما تطلب تشغيل book بتاريخ صالح، تستدعي المكتبة التوافر أولًا. إذا غاب التاريخ، تحصل على needs_input قبل الاستعلام؛ ابدأ بجمع الـ Input الذي يعرفه المستخدم.
إذا أعاد الاستعلام عدة مواعيد، تكون النتيجة needs_selection. اعرض selection.options للمستخدم. لا يوجد حجز في هذه اللحظة، ولا ينبغي أن تظهر رسالة «تم الحجز».
عندما يختار المستخدم موعدًا، مرّر الـ ID الذي أعاده AGENTO لهذا الخيار:
// 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 بعد. يشرح التعامل مع الحالات ما الذي تعرضه الواجهة في كل مرحلة.
ماذا لو لم توجد عدة مواعيد؟
إذا كانت القائمة فارغة، تعود النتيجة needs_input، وقد تكون missing فارغة؛ الحقول مكتملة لكن لا توجد نتيجة مناسبة. اطلب تاريخًا آخر بدل البحث عن حقل ناقص غير موجود.
إذا وجد عنصر واحد ولا يوجد تفضيل صريح يخالفه، يمكن اختياره تلقائيًا. هذا يزيل خطوة Selection فقط، ولا يلغي Confirmation عملية الكتابة.
إذا كانت بنية القائمة أو IDs أو labels غير صالحة، فهذه مشكلة في شكل الـ Response المتفق عليه أو في المسارات، وليست حالة «لا توجد مواعيد». افحص DEPENDENCY_SELECTION_INVALID. وإذا غاب الحقل الذي يحاول map قراءته، افحص DEPENDENCY_UNRESOLVED.
طابق التفضيل عندما يذكر المستخدم وقتًا
إذا كانت book تقبل مدخلًا اختياريًا اسمه preferred_time، يمكنك إضافة هذا المقطع داخل select:
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 الصحيح وصل من الاستعلام إلى الحجز، ثم أضف خطوة أخرى.