Ultra Tech النسخة الإنجليزية

تصويب

  1. أُعيدت كتابة المقال دليلاً كاملاً على منصّة OpenAI، بثلاثة أمثلة منفَّذة وستّة عشر مصدراً مستشهَداً به؛ والشرح المختصر السابق لاستخدام الأدوات واستشهاداه استُبدلا.

البرمجيات الدليل 1 من 3

ما هو استخدام الأدوات

آخر تحقّق من المصادر في .

عندك سؤال لا يستطيع نموذج اللغة أن يجيب عنه ممّا تعلّمه: أين طلبيّتك الآن، وكم سعر اليوم، وماذا يقول قاعدة بياناتك. النموذج لم يرَ بياناتك يوماً، ولا يستطيع تشغيل شيفرتك. استخدام الأدوات هو الطريقة التي تسلّمه بها الاثنين: تصف له عمليّة يجوز له أن يطلبها، فيطلبها، فتنفّذها شيفرتك أنت، ثمّ تخبره بما رجع. كلّ ما يفعله «الوكيل» مبنيّ على هذا التبادل الواحد.

على منصّة OpenAI اسم هذه الآليّة استدعاء الدوالّ. استدعاء الدوالّ — ويُسمّى أيضاً استدعاء الأدوات — طريقة قويّة ومرنة تتيح لنماذج OpenAI أن تتعامل مع أنظمة خارجيّة وتصل إلى بيانات خارج بيانات تدريبها. والكلمتان تردان معاً عن قصد:الأداة أيّ عمليّة تسمح للنموذج بطلبها، والدالّة أداة تصفها بمخطّط JSON — أي ببيان مقروء للآلة يقول ما الذي تحتاجه العمليّة مُدخلاً. كلّ أداة في هذا الدليل دالّة.

في هذا الدليل نبني أداة واحدة من الصفر: بحث عن طلبيّات متجر متخيَّل. تكتب ملفّاً واحداً، وتشغّله ثلاث مرّات، وتقرأ ما يرجع في كلّ مرّة. والمخرجات التي تراها هنا ليست توضيحيّة: كلّ واحدة منها هي ما طبعه هذا الملفّ حين شُغِّل، في اليوم الذي يحمله ختمه. وفي النهاية تكون بين يديك الحلقة التي يتكوّن منها كلّ برنامج يستخدم الأدوات، وتعرف لماذا هي بهذا الشكل.

بنهاية هذا الدليل ستقدر

  • تعرّف أداة واحدة، وترسلها مع سؤال، وترى النموذج يطلبها بدل أن يخمّن — في ملفّ كتبته وشغّلته بنفسك.
  • تنفّذ طلب النموذج في شيفرتك وتعيد إليه النتيجة ليُكمل جوابه.
  • تُشغّل الحلقة التي تستمرّ حتى لا يبقى للنموذج ما يطلبه، بما فيها دور يطلب فيه بحثَين معاً.
  • تميّز الأخطاء الثلاثة التي تجعل برنامجاً يستخدم الأدوات يفشل بصمت، أو يدور بلا نهاية، أو يتجاهل أداته.

قبل أن تبدأ

تحتاج أربعة أشياء. تحقّق من كلّ واحد قبل أن تكتب سطراً.

  • Node.js إصدار 22 أو أحدث. شغّل node --version. يطبع إصداراً مثل v24.14.1، وهو الإصدار الذي جرت عليه تشغيلات هذه الصفحة. إن طبع رقماً أقلّ أو رسالة خطأ، نزّل Node.js من موقعه الرسميّ.
  • مفتاح API من OpenAI. أنشئه من صفحة مفاتيح API في لوحة تحكّمك واحفظه في مكان آمن. لا يدخل الشيفرة أبداً.
  • مجلّد فيه حزمة SDK. في الطرفيّة: mkdir tool-use-demo ثمّ cd tool-use-demo ثمّ npm init -y ثمّ npm install openai@7.15.0. الأمر الأخير يثبّت حزمة openai بالإصدار الذي جرت عليه هذه التشغيلات.
  • المفتاح في بيئتك، في نفس نافذة الطرفيّة التي ستشغّل منها الملفّ. على PowerShell في ويندوز: $env:OPENAI_API_KEY = "ضع مفتاحك هنا". على macOS أو لينكس: export OPENAI_API_KEY="ضع مفتاحك هنا". الحزمة تقرأ هذا المتغيّر بنفسها، ولهذا لا تذكر الشيفرة أدناه المفتاح إطلاقاً. وإن فتحت نافذة جديدة، اضبطه من جديد.

وشيء أخير قبل اختيار النموذج. كلّ ما في هذه الصفحة جرى على gpt-5.6-luna. النموذج GPT-5.6 Luna، ومعرّفه gpt-5.6-luna، هو نموذج GPT-5.6 الذي تصفه OpenAI بأنّه مُحسَّن لأحمال العمل الحسّاسة للتكلفة. والخطوات نفسها على أيّ نموذج من OpenAI يدعم أدوات الدوالّ؛ لكنّ المخرجات التي تقارن بها مخرجاتك هي مخرجات Luna.

الخطوة 1 — عرّف أداة وشاهد النموذج يطلبها

أنشئ ملفّاً اسمه tool-use.mjs وضع فيه هذا، حرفاً بحرف:

مثال first-request
import OpenAI from 'openai';
import { toResponseInputItems } from 'openai/lib/responses/ResponseInputItems';

const client = new OpenAI(); // reads OPENAI_API_KEY from the environment; the key is never in the code

const tools = [
  {
    type: 'function',
    name: 'get_order_status',
    description:
      'Look up one order in the shop\'s order system by its order number. Returns the order\'s current ' +
      'status (packed, shipped or delivered), the carrier once it has shipped, and the expected delivery ' +
      'date. Use it whenever the user asks where an order is or when it will arrive: the order system is ' +
      'the only place that information exists. Returns an error for an order number that does not exist.',
    parameters: {
      type: 'object',
      properties: {
        order_id: { type: 'string', description: 'The order number as the customer sees it, digits only, for example "1042".' },
      },
      required: ['order_id'],
      additionalProperties: false,
    },
    strict: true,
  },
];

const question = 'Where is my order 1042?';

const first = await client.responses.create({
  model: 'gpt-5.6-luna',
  tools,
  input: [{ role: 'user', content: question }],
});

for (const item of first.output) {
  if (item.type === 'function_call') console.log(`function_call: ${item.name} ${item.arguments}`);
  else if (item.type === 'message') console.log(`message: ${first.output_text}`);
}
console.log(`status: ${first.status}`);

المخرجات

function_call: get_order_status {"order_id":"1042"}
status: completed

شغّلناه في node@24.14.1 openai 7.15.0

شغّله بالأمر node tool-use.mjs. يرجع سطران، اقرأهما واحداً واحداً.

  • function_call: get_order_status {"order_id":"1042"} — النموذج لم يجب عن السؤال، بل أصدر استدعاء دالّة: اسم أداتك والمُعامِلات التي يريدها، نصّاً بصيغة JSON. والرقم 1042 جاء من السؤال؛ لا شيء في شيفرتك ذكره. هذه هي الحيلة كلّها: النموذج لا يستطيع البحث عن شيء، فيطلب منك أن تبحث، بالشكل الذي أخبرته أنّ البحث يأخذه.
  • status: completed — انتهى الردّ انتهاءً طبيعيّاً. و«مكتمل» لا تعني أنّ السؤال أُجيب، بل تعني أنّ النموذج قال كلّ ما يريد قوله في هذا الدور، والذي يريده بحثٌ في الطلبيّات.

انظر الآن ماذا فعلت الشيفرة لتصل إلى هذا.

tools قائمة فيها مُدخل واحد هو الأداة. نوعها function، وname هو ما سيقوله النموذج حين يطلبها، وdescription أربع جمل: ماذا تفعل الأداة، وماذا تُرجع، ومتى تُستخدم، وماذا يحدث مع مُدخل خاطئ. هذا الوصف هو كلّ ما يعرفه النموذج عن أداتك، ولذلك هو أهمّ نصّ في الملفّ. وparameters مخطّط JSON للمُدخل: خاصّيّة واحدة order_id، نصّ له وصفه الخاصّ، مذكورة في required، ومعها additionalProperties: false حتّى لا يخترع النموذج حقولاً. وstrict: trueفي آخره يحوّل المخطّط من اقتراح إلى وعد. ضبط strict على true يضمن التزام استدعاءات الدوالّ بمخطّط الدالّة التزاماً موثوقاً بدل أن يكون بذل جهد فحسب، وتوصي OpenAI بتفعيل الوضع الصارم دائماً. وللوضع الصارم شرطان يستوفيهما المخطّط أعلاه: في الوضع الصارم يجب ضبط additionalProperties على false لكلّ كائن في المُعامِلات، ويجب أن تكون كلّ الحقول في properties مطلوبة.

وclient.responses.create(...) هو الطلب نفسه: يحمل النموذج والأدوات وinput، أي قائمة فيها رسالة واحدة من user هي السؤال. وnew OpenAI() فوقه يقرأ مفتاحك من البيئة.

أمّا حلقة for في الأسفل فتقرأ الردّ. first.outputقائمة العناصر التي أنتجها النموذج. تحمل قائمة output في الردّ عنصراً نوعه function_call، ولكلّ عنصر منها call_id يُستخدم لاحقاً لتسليم نتيجة الدالّة، واسم، ومُعامِلات مُرمَّزة بصيغة JSON. فتطبع حلقتك اسم كلّ عنصرfunction_call ومُعامِلاته، ونصّ أيّ عنصر message عبر first.output_text. في هذه التشغيلة لم تكن هناك رسالة: ذهب النموذج إلى البحث مباشرةً. والسطر الأخير يطبع first.status.

انتبه إلى ما لم يحدث: لم يُبحث عن أيّ طلبيّة. النموذج لا يعرف أنّ الطلبيّة 1042 موجودة، ولن يعرف ما لم تخبره. والخطوة ٢ هي حيث تخبره.

الخطوة 2 — نفّذ الأداة وأعد النتيجة

أضف ما يلي أسفل ما كتبته في الخطوة ١، في الملفّ نفسه:

مثال return-the-result
const ORDERS = {
  '1042': { status: 'shipped', carrier: 'DHL', expected_delivery: '2026-09-14' },
  '1043': { status: 'packed', carrier: null, expected_delivery: '2026-09-16' },
};

function getOrderStatus(args) {
  const order = ORDERS[args.order_id];
  return JSON.stringify(order ?? { error: `no order numbered ${args.order_id}` });
}

const input = [{ role: 'user', content: question }, ...toResponseInputItems(first.output)];
for (const item of first.output) {
  if (item.type !== 'function_call') continue;
  const output = getOrderStatus(JSON.parse(item.arguments));
  console.log(`function_call_output: ${output}`);
  input.push({ type: 'function_call_output', call_id: item.call_id, output });
}

const second = await client.responses.create({ model: 'gpt-5.6-luna', tools, input });

console.log(`status: ${second.status}`);
console.log(second.output_text);

المخرجات

function_call_output: {"status":"shipped","carrier":"DHL","expected_delivery":"2026-09-14"}
status: completed
Order **1042** has shipped via **DHL**. It’s expected to arrive on **September 14, 2026**.

شغّلناه في node@24.14.1 openai 7.15.0

شغّل الملفّ من جديد بالأمر node tool-use.mjs. لأنّ الملفّ يعمل من أوّله، يُطبع سطرا الخطوة ١ أوّلاً كما هما، ثمّ تتبعهما ثلاثة أسطر جديدة. اقرأ الجديدة:

  • function_call_output: {"status":"shipped","carrier":"DHL","expected_delivery":"2026-09-14"} — شيفرتك أجابت عن طلب النموذج. ORDERS هنا تنوب عن نظام طلبيّات حقيقيّ: طلبيّتان في كائن بسيط. وgetOrderStatus تقرأ order_idالذي طلبه النموذج، وتجد الطلبيّة، وتُرجعها نصّاً بصيغة JSON — أو كائن خطأ لرقم غير موجود. والشكل متروك لك. النتيجة التي تمرّرها في رسالة function_call_output تكون عادةً نصّاً، وصيغته متروكة لك — JSON أو رموز أخطاء أو نصّ عادي — والنموذج يفسّر هذا النصّ حسب الحاجة.
  • status: completed — انتهى الردّ الثاني.
  • Order **1042** has shipped via **DHL**. It’s expected to arrive on **September 14, 2026**. — جواب النموذج، مطبوعاً عبر second.output_text. والنجمتان حول الكلمات ترميز Markdown للتغليظ؛ النموذج يكتب Markdown ما لم تطلب غير ذلك. ولاحظ أنّ كلّ حقيقة في هذه الجملة — شركة الشحن، والتاريخ، وكلمة «شُحنت» — موجودة لأنّ شيفرتك أرجعتها. النموذج حوّل JSON إلى جملة ولم يضف شيئاً.

الطلب الثاني هو الموضع الذي يخطئ فيه الناس، فانظر كيف بُني input. يبدأ بالسؤال الأصليّ. ثمّ يحمل عناصر خرج النموذج نفسها من الردّ الأوّل، بعد أن حوّلها toResponseInputItemsإلى الشكل الذي تقبله الواجهة مُدخلاً؛ واستدعاء الدالّة الذي أصدره النموذج بينها، وكذلك كلّ ما أنتجه غيره. في نماذج الاستدلال، أيّ عناصر استدلال ترجع في ردود تحمل استدعاءات أدوات يجب أن تُعاد هي أيضاً مع نواتج الاستدعاءات. والدالّة المساعِدة تتكفّل بذلك. ثمّ تدفع الحلقة، لكلّfunction_call في الردّ الأوّل، عنصر function_call_output واحداً يحمل شيئين: call_id منسوخاً من الاستدعاء، وoutputأي النصّ الذي أرجعته أداتك. ناتج الأداة إمّا JSON مُهيكَل وإمّا نصّ عادي، وعليه أن يحمل إشارة إلى استدعاء بعينه من استدعاءات النموذج، عبر call_id. وهذا المعرّف هو ما يربط جوابك بسؤاله حين يكون قد سأل أكثر من سؤال.

ثمّ يخرج الطلب ومعه قائمة tools نفسها. الأدوات تُرسَل في كلّ مرّة: النموذج لا يتذكّر طلبك السابق إلّا بقدر ما يحمله input.

الخطوة 3 — أطلق الحلقة حتى يتوقّف النموذج عن الطلب

الخطوتان ١ و٢ رحلة ذهاب وإياب واحدة، مكتوبة باليد. والبرنامج الحقيقيّ لا يعرف مسبقاً كم رحلة يحتاج السؤال، فيدور في حلقة. أضف هذا أسفل شيفرة الخطوة ٢:

مثال the-loop
async function runAgent(userQuestion) {
  const history = [{ role: 'user', content: userQuestion }];
  for (let turn = 1; turn <= 5; turn += 1) {
    const response = await client.responses.create({ model: 'gpt-5.6-luna', tools, input: history });
    history.push(...toResponseInputItems(response.output));
    const calls = response.output.filter((item) => item.type === 'function_call');
    if (calls.length === 0) {
      console.log(`turn ${turn}: no function call, status ${response.status}`);
      return response.output_text;
    }
    for (const call of calls) {
      const output = getOrderStatus(JSON.parse(call.arguments));
      console.log(`turn ${turn}: ${call.name}(${call.arguments}) -> ${output}`);
      history.push({ type: 'function_call_output', call_id: call.call_id, output });
    }
  }
  throw new Error('the model was still asking for tools after 5 turns');
}

console.log(await runAgent('Which of my orders 1042 and 1043 arrives first, and how many days apart are they?'));

المخرجات

turn 1: get_order_status({"order_id":"1042"}) -> {"status":"shipped","carrier":"DHL","expected_delivery":"2026-09-14"}
turn 1: get_order_status({"order_id":"1043"}) -> {"status":"packed","carrier":null,"expected_delivery":"2026-09-16"}
turn 2: no function call, status completed
Order **1042** arrives first, with expected delivery on **September 14, 2026**. Order **1043** is expected on **September 16, 2026**, so they are **2 days apart**.

شغّلناه في node@24.14.1 openai 7.15.0

شغّل الملفّ مرّة ثالثة. بعد الأسطر الخمسة من الخطوتين ١ و٢، تطبع الحلقة سجلّها هي:

  • turn 1: get_order_status({"order_id":"1042"}) -> … ثمّ، في السطر التالي، مثله للطلبيّة 1043 — في دوره الأوّل طلب النموذج بحثَين معاً، واحداً لكلّ طلبيّة في السؤال. وحلقتك أجابت عنهما قبل أن ترسل شيئاً. قد يختار النموذج استدعاء عدّة دوالّ في دور واحد، ويمكنك منع ذلك بضبط parallel_tool_calls على false، فيُستدعى حينها صفر أو أداة واحدة بالضبط. ولا داعي للمنع هنا: بحثان في دور واحد يعنيان طلباً أقلّ.
  • turn 2: no function call, status completed — في الدور الثاني كانت النتيجتان بين يدي النموذج ولم يبقَ ما يطلبه. لم ترَ الحلقة عنصر function_call فأرجعت النصّ.
  • Order **1042** arrives first, with expected delivery on **September 14, 2026**. … — الجواب النهائيّ. التاريخان جاءا من أداتك، والطرح أجراه النموذج.

اقرأ runAgent مرّة واحدة من أعلاها ويكون شكل كلّ برنامج يستخدم الأدوات أمامك. history قائمة جارية بكلّ ما قيل: السؤال، ثمّ دوراً بعد دور، عناصر النموذج ونواتجك. وكلّ دور يرسل historyكاملة مع الأدوات. حين يستدعي النموذج دالّة عليك أن تنفّذها وتعيد النتيجة، وبما أنّ ردّ النموذج قد يحمل صفر استدعاءات أو واحداً أو عدّة، فالأفضل أن تفترض أنّها عدّة. ولهذا تجمع الحلقة كلّ عناصرfunction_call في الدور داخل calls، وشرط الخروج هو calls.length === 0: دورٌ بلا استدعاءات هو جواب النموذج النهائيّ. ودورٌ فيه استدعاءات تنفّذ كلّ واحد منها، وتدفع ناتجه تحت call_idالخاصّ به، ثمّ تدور من جديد. مع واجهة Responses يستطيع تطبيقك مواصلة هذا المسار بعدد الاستدعاءات الذي تتطلّبه المهمّة. أمّا سقف الأدوار الخمسة في حلقةfor فهو الشيء الوحيد الذي لا تعطيك إيّاه المنصّة، وقسم «هون بيغلط الناس» يقول لماذا تريده.

اجمع الخطوات الثلاث تجد أمامك خطوات المنصّة الخمس كما تصفها هي. مسار استدعاء الأدوات خمس خطوات: ترسل طلباً إلى النموذج ومعه الأدوات التي يستطيع استدعاءها، فيصلك منه استدعاء أداة، فتنفّذ الشيفرة عندك بالمُدخل الذي طلبه، ثم ترسل طلباً ثانياً ومعه ناتج الأداة، فيصلك الجواب النهائي — أو استدعاءات أخرى. الخطوة ١ كانت أوّل اثنتين، والخطوة ٢ الثلاث التي تليها، والخطوة ٣ ما بين القوسين.

القاعدة الوحيدة

النموذج لا ينفّذ شيئاً أبداً. كلّ استدعاء أداة رحلة ذهاب وإياب — النموذج يطلب، وشيفرتك تنفّذ، وأنت تُبلّغ تحت call_id نفسه، ثمّ يواصل النموذج — والنموذج، لا شيفرتك، هو من يقرّر متى انتهى. وكلّ ما عدا ذلك في هذا الدليل مسك دفاتر حول هذه القاعدة.

الدور ماذا يرسل النموذج ماذا تفعل أنت
١ عنصر function_call واحداً أو أكثر نفّذ كلّ واحد، وادفع function_call_output لكلّ استدعاء، وأرسل السجلّ كاملاً
٢ استدعاءات أخرى، أو رسالة بلا استدعاءات استدعاءات: كرّر ما فعلت؛ بلا استدعاءات: تلك الرسالة هي الجواب

وإذا حفظت القاعدة صار الخطآن الأشيع بديهيَّين: أن تجيب عن استدعاء لم يصدر، وألّا تجيب عن استدعاء صدر.

متى تستخدمه ومتى لا

الحالة القرار السبب
الجواب في بيانات لم يرَها النموذج قطّ — طلبيّاتك، سعر لحظيّ، ملفّ على قرصك استخدمه لا سبيل آخر للنموذج إليها؛ الأداة هي الجسر الوحيد
السؤال يحتاج فعلاً له أثر — إرسال بريد، ردّ مبلغ، كتابة ملفّ استخدمه بحذر النموذج يصف الفعل فقط؛ شيفرتك تنفّذه، وشيفرتك تستطيع أن ترفض
تحتاج شكلاً مضموناً للجواب — كائن JSON بحقول ثابتة استخدمه مخطّط الدالّة الصارم يجعل الشكل ضماناً
تلخيص أو ترجمة أو سؤال من معرفة عامّة لا تستخدمه لا شيء يُنفَّذ؛ رحلة الأداة تضيف طلباً بلا مقابل
جواب سريع لمرّة واحدة، تكلفة الطلب الثاني فيه أكبر من فائدته لا تستخدمه كلّ استدعاء أداة طلب إضافيّ على الأقلّ قبل الجواب
عشرات الأدوات على النموذج أن يختار بينها في كلّ دور ليس بعد الدقّة تقلّ كلّما طالت القائمة؛ ابدأ صغيراً وزِد

وثمّة كلفة يسهل نسيانها حين يقول الجدول «استخدمه». تُحقَن الدوالّ خلف الكواليس في رسالة النظام بصيغة دُرِّب عليها النموذج، ولهذا تُحسب تعريفات الدوالّ ضمن حدّ سياق النموذج وتُحاسَب بوصفها رموز إدخال. الوصف الطويل يستحقّ ثمنه؛ أمّا أداة لا يحتاجها النموذج فلا.

مصطلحات مرّت معنا

  • الأداة — عمليّة تسمح للنموذج بطلبها. تعمل في شيفرتك أنت، لا في النموذج أبداً.
  • الدالّة — أداة موصوفة بمخطّط JSON؛ وهي نوع الأدوات المستخدَم في هذا الدليل.
  • مخطّط JSON — وصف مقروء للآلة لشكل قيمة: حقولها وأنواعها وأيّها مطلوب.
  • استدعاء الدالّة (function_call) — عنصر في خرج النموذج يسمّي أداة ويعطي مُعامِلاتها نصّاً بصيغة JSON.
  • ناتج الاستدعاء (function_call_output) — العنصر الذي ترسله بالنتيجة، تحت call_id الاستدعاء.
  • call_id — المعرّف الذي يربط ناتجاً واحداً باستدعاء واحد؛ وللاستدعاء أيضاً id، وهو حقل آخر.
  • الوضع الصارم (strict: true) — الضبط الذي يجعل مُعامِلات النموذج مطابقة لمخطّطك دائماً.
  • الدور — طلب واحد وردّه؛ والبرنامج الذي يستخدم الأدوات يأخذ عدّة أدوار لسؤال واحد.
  • الحلقة — الشيفرة التي ترسل السجلّ، وتجيب عن كلّ استدعاء، وتكرّر حتى يخلو دور من الاستدعاءات.
  • واجهة Responses — نقطة OpenAI التي يناديها هذا الدليل، client.responses.create، وقائمة output فيها تحمل العناصر وoutput_text يجمع النصّ.
  • status — الحقل الذي يقول هل انتهى الردّ؛ وcompleted تعني أنّ الدور انتهى انتهاءً طبيعيّاً، سواء طلب أداة أم لا.

الخلاصة

استخدام الأدوات تبادل واحد يتكرّر: النموذج يطلب عمليّة لا يستطيع تنفيذها، وشيفرتك تنفّذها وتُبلّغ تحت معرّف الاستدعاء، ويواصل النموذج حتى يخلو دور من الطلبات. والملفّ الذي شغّلته ثلاث مرّات في هذه الصفحة هو هذا التبادل، مكتوباً باليد مرّة وفي حلقة مرّة. صِف الأداة وصفاً جيّداً، وأجب عن كلّ استدعاء، وضع سقفاً للحلقة، والباقي شيفرتك أنت.

ما تغيّر مؤخراً

لم يتغيّر أيّ مصدر من مصادر هذا الدليل منذ آخر فحص.