AGENTO
v1.1.0 · التوثيق
التوثيق/اربط خطوات العمل/مثال حجز متكامل

مثال حجز متكامل

2026-10-15·المصدر 4bebed4·6 دقائق للقراءة·15 / 27

ستتعلم: تابع مثالًا اختياريًا لاختبار Selection والـ Confirmation وأعطال الحجز.

هذا تمرين اختياري بعد فهم Mapping والـ Dependencies وConfirmation. يستخدم بيانات ثابتة كي تختبر انتهاء Selection وتكرار Confirmation وحالات الخطأ دون إنشاء حجوزات حقيقية. لربط API لديك للمرة الأولى، ابدأ بـالدليل العملي الذي يرسل طلبًا فعليًا.

السيناريو صغير عن قصد: يقدّم المستخدم التاريخ، وتعطي API المواعيد المتاحة، ويختار المستخدم وقتًا، ثم يطلب التطبيق موافقته قبل الحجز.

متطلبات الـ API

  • يعيد GET /availability?date=2026-10-01 الشكل { slots: [{ slot_token, label }] }.
  • يقبل POST /bookings الشكل { date, slot_token, notes? }.
  • يستقبل POST قيمة Bearer Token للمستخدم، ويعيد { booking_id, time }.

هذه Endpoints مخصصة للمثال، وليست خدمة تجريبية عامة. يعترض البرنامج الطلبات، لذلك لا ينشئ حجزًا عبر الشبكة.

١. احفظ الإعداد الكامل

نزّل config.yml. هذا ملف كامل؛ لا يحتاج إلى تجميع مقاطع من صفحات أخرى.

version: "2"
assistant:
  language: auto
  timezone: Asia/Hebron
tools:
  - id: availability
    tool:
      description: Find available appointment times for a date. This tool does not create a booking.
      input_schema:
        type: object
        properties:
          date: { type: string, format: date }
        required: [date]
    request:
      method: GET
      url: https://api.example.com/availability
      map:
        query:
          date: $.date
    response:
      private_fields: [slot_token]
      output_schema:
        type: object
        properties:
          slots:
            type: array
            items:
              type: object
              properties:
                slot_token: { type: string }
                label: { type: string }
              required: [slot_token, label]
        required: [slots]

  - id: book
    tool:
      description: Book an appointment for a date after selecting an available time and confirming.
      input_schema:
        type: object
        properties:
          date: { type: string, format: date }
          slot_token: { type: string, minLength: 1 }
          notes: { type: string, maxLength: 500 }
        required: [date, slot_token]
    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
    behavior:
      effect: side-effect
      confirmation:
        required: true
        ttl_ms: 30000
        preview:
          date: $.date
          time: { source: selection, path: $.time }
          notes: $.notes
      recovery:
        refresh_dependency: availability
        statuses: [409, 410]
    request:
      method: POST
      url: https://api.example.com/bookings
      auth:
        type: session
      retry:
        attempts: 1
      map:
        body:
          date: $.date
          slot_token: $.slot_token
          notes: $.notes

تتبّع البيانات:

  1. يملك المستخدم book.date، ويُمرّر إلى availability.date.
  2. تجلب availability المواعيد من الـ API، وتتحقق Output Schema من وجود slot_token وlabel لكل موعد.
  3. يصف select طريقة عرض المواعيد، ويخفي id_sensitive قيم Tokens الحقيقية باستخدام IDs للواجهة لا تكشف الـ Tokens الأصلية.
  4. يكتب ربط الـ Dependency Token المختار في book.slot_token. هذا الحقل غير موجود في Schema الحجز التي يراها الـ AI Model.
  5. توفّر حقيقة Selection time معاينة مفهومة للمستخدم، بينما يستخدم POST قيمة Token الحقيقية.
  6. يوقف confirmation.required التدفق قبل الكتابة. ويمرّر auth.type: session قيمة Token من التطبيق، لتتحقق API من الصلاحية.

يحذف private_fields قيمة slot_token من بيانات العرض، لكنها تبقى متاحة لحسم الـ Dependencies داخل Runtime. حماية بيانات العرض لا ينبغي أن تزيل القيمة اللازمة للتنفيذ.

٢. شغّل برنامج الاختبار

ثبّت حزمة npm وفق دليل التثبيت، واحفظ البرنامج بجانب config.yml باسم demo.mjs. نزّل البرنامج.

import assert from "node:assert/strict";
import { AgentRuntime } from "agento-runtime";

const calls = [];
const runtime = await AgentRuntime.create({
  configPath: "./config.yml",
  presentation: "raw",
  fetch: async (url, options) => {
    const path = new URL(url).pathname;
    calls.push({ path, method: options.method });
    if (path === "/availability") {
      return Response.json({
        slots: [
          { slot_token: "slot-a", label: "10:00" },
          { slot_token: "slot-b", label: "11:00" },
        ],
      });
    }
    assert.equal(path, "/bookings");
    assert.equal(options.method, "POST");
    assert.equal(options.headers.authorization, "Bearer demo-user-token");
    assert.deepEqual(JSON.parse(options.body), {
      date: "2026-10-01", slot_token: "slot-b", notes: "Window seat",
    });
    return Response.json({ booking_id: "booking-42", time: "11:00" });
  },
});
const request = {
  sessionId: "booking-demo",
  auth: { token: "demo-user-token" },
};
try {
  let result = await runtime.invoke({
    ...request, tool: "book",
    arguments: { date: "2026-10-01", notes: "Window seat" },
  });
  assert.equal(result.status, "needs_selection");
  assert.equal(calls.length, 1);
  assert(!result.selection.options.some(option => option.id === "slot-b"));
  console.log("1.", result.status, result.selection.options);

  // Test fixture: simulate the user choosing 11:00.
  const choice = result.selection.options.find(option => option.label === "11:00");
  result = await runtime.select({
    ...request, selectionId: result.selection.id, choice: choice.id,
  });
  assert.equal(result.status, "needs_confirmation");
  assert.equal(calls.length, 1);
  assert.equal(result.confirmation.preview.time, "11:00");
  assert(!JSON.stringify(result.confirmation.preview).includes("slot-b"));
  console.log("2.", result.status, result.confirmation.preview);

  // Test fixture only: a real UI waits for explicit user approval here.
  result = await runtime.confirm({
    ...request, confirmationId: result.confirmation.id,
  });
  assert.equal(result.status, "completed");
  assert.equal(calls.filter(call => call.method === "POST").length, 1);
  console.log("3.", result.status, result.data);
  console.log("HTTP calls:", calls);
} finally {
  runtime.dispose();
}

node demo.mjs

يحاكي البرنامج Selection والموافقة داخل هذا الاختبار فقط. في منتجك، يجب أن ينتظر handler Selection إجراء المستخدم، ثم ينتظر handler Confirmation موافقته المنفصلة.

٣. اقرأ تسلسل النتائج

الخطوةالنتيجةطلبات HTTP حتى اللحظة
استدعاء book بالتاريخ والملاحظاتneeds_selection مع 10:00 و11:00GET واحد، ودون POST.
اختيار 11:00needs_confirmation مع التاريخ والوقت والملاحظاتما زال GET واحدًا، ودون POST.
استدعاء confirm() بقيمة confirmationId التي أعادها AGENTOcompleted مع booking-42GET واحد وPOST واحد.

يتحقق البرنامج من body النهائي بدقة، ومن عدم ظهور Token الحقيقي ضمن الخيارات أو المعاينة، ومن إرسال POST مرة واحدة فقط.

٤. غيّر خيارًا واحدًا في كل تجربة

احذف التاريخ. توقّع needs_input قبل أي GET. يميّز Runtime بين مدخل يقدّمه المستخدم وحقل تنتجه الـ Dependency.

أعد موعدًا واحدًا. إذا لم يحدد المستخدم تفضيلًا يخالف هذا الموعد، يُختار الموعد تلقائيًا. تصبح النتيجة الأولى needs_confirmation؛ عدّل assertions لتوافق التجربة. Selection التلقائي لا يعني الموافقة التلقائية.

أعد قائمة فارغة. توقّع needs_input مع رسالة تشرح غياب التوافر. لا تعرض نموذج إدخال بعنوان «التاريخ ناقص» لمجرد أن الحالة هي needs_input.

خفّض TTL الـ Dependency إلى مللي ثانية واحدة. قد ينتهي Selection قبل الطلب التالي. توقّع INPUT_SELECTION_STALE أو CONFIRMATION_STALE حسب لحظة الانتهاء. جدّد التدفق بدل إعادة إرسال ID قديم.

أعد 409 مرة واحدة من POST. يمسح recovery البيانات القديمة ويجلب مواعيد جديدة. تعامل مع Selection أو Confirmation الجديد؛ لا تؤكد البديل بصمت. يجب ألا يحدث POST الثاني إلا بعد موافقة جديدة.

غيّر when إلى missing ومرّر الـ Token مباشرةً. يمكن تجاوز الاستعلام. استخدم ذلك فقط لمسار موثوق من التطبيق أو Navigation، وليس للتحقق من Tokens التي يرسلها المستخدم.

٥. اتصل بـAPI الفعلية

استبدل URL للأداتين وoutput Schema، ثم احذف fetch المخصص. مرّر بيانات الاعتماد من backend بعد التحقق من المستخدم. طابق Schema ومسارات Selection والـ Mapping ومعاينة Confirmation مع Responses حقيقية.

أنشئ Runtime طويل العمر لكل process، واستخدم conversation ID ثابتًا ومصرّحًا به في كل متابعة. لا تسمح للعميل باختيار sessionId لمستخدم آخر.

عند تشغيل عدة instances، وفّر تخزين Sessions مشتركًا يسلسل المعاملات لكل Session، وrate limiter مشتركًا مناسبًا. وإذا دعمت API آلية idempotency، فاضبطها قبل السماح بإعادة محاولة الكتابة. راجع الجاهزية لـ Production.

٦. وسّع المثال إلى حجز خدمة

الخطوة التالية هي book → availability → services. تحتاج availability إلى ID لخدمة؛ لذلك عرّف اعتمادها على services. أضف اسم الخدمة إلى Selection facts للمعاينة، ثم أضف تفضيلات المستخدم بعد نجاح تدفق Selection الأساسي.

أضف سلوكًا واحدًا يمكن ملاحظته واختباره في كل مرة، ثم انتقل إلى السياسة التالية.