في الخطوات السابقة، أرسلنا طلب بحث إلى API. نفترض الآن أنه عاد بنجاح. بقي سؤالان مختلفان: هل البيانات بالشكل الذي يعتمد عليه تطبيقك؟ وأي جزء منها يحتاج المستخدم أو الـ AI Model إلى رؤيته؟
ضع إعداد response داخل الـ Tool، بجانب tool وrequest. لا تضعه داخل request.map؛ Mapping يخص الطلب الخارج، بينما هذا القسم يخص الـ Response العائدة.
ابدأ من Response API لديك
لنواصل مثال البحث. نفترض أن الـ API تضمن Response بهذا الشكل:
{
"services": [
{ "id": "svc-color", "name": "Hair coloring", "price": 80 }
]
}
هنا الـ Response كائن، وservices قائمة، وكل عنصر يحتوي ID واسمًا وسعرًا رقميًا. إذا كانت API لديك تستخدم data.items أو تعيد السعر كنص، فاكتب الـ Schema وفق شكل الـ Response الفعلي. عينة واحدة تساعدك على الفهم، لكنها لا تثبت وحدها أن جميع الحقول مطلوبة.
تحقّق من الشكل الذي تعتمد عليه
أضف هذا القسم إلى Tool البحث إذا كان شكل الـ Response الذي تضمنه الـ API يطابق الوصف السابق:
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. وإذا كنت تحتاج النص والبيانات معًا، استخدم "both".
هذه القيمة من خيارات إنشاء Runtime أو استدعائه، وليست حقلًا عشوائيًا داخل response:
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:
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:
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 للمحادثة. عندها تضيف فهم اللغة إلى Tool تعرف بالفعل كيف تعمل.