مدل زبانی فقط متن می‌نویسد. نمی‌تواند وضعیت سفارش مشتری را از پایگاه داده بخواند، موجودی انبار را ببیند یا تیکتی ثبت کند. ولی اگر بخواهید دستیار سایتتان واقعاً کار کند، به همین کارها نیاز دارد.

فراخوانی ابزار (Function Calling یا Tool Use) راه حل استاندارد است. شما فهرست تابع‌هایی را که برنامه‌تان می‌تواند اجرا کند به مدل معرفی می‌کنید. مدل هر وقت لازم دید، به‌جای جواب متنی، درخواستی ساخت‌یافته می‌فرستد: «این تابع را با این آرگومان‌ها صدا بزن». برنامه‌ی شما آن را اجرا می‌کند و نتیجه را برمی‌گرداند. اگر هنوز اولین اتصال را نزده‌اید، اول اتصال به API هوش مصنوعی با PHP را ببینید.

چرخه چطور کار می‌کند؟#

نمودار چرخه‌ی فراخوانی ابزار: برنامه پرسش و فهرست ابزار را به مدل می‌دهد، مدل ابزار لازم را می‌خواهد، کد برنامه اعتبارسنجی و اجرا می‌کند، نتیجه به مدل برمی‌گردد و در پایان پاسخ نهایی ساخته می‌شود

قدم‌ها به این ترتیب‌اند:

  1. پرسش کاربر را همراه فهرست ابزارها به API می‌فرستید.
  2. مدل یا مستقیم جواب می‌دهد، یا یک یا چند فراخوانی ابزار درخواست می‌کند.
  3. برنامه‌ی شما نام ابزار و آرگومان‌ها را بررسی و اجرا می‌کند.
  4. نتیجه را در قالب پیام «نتیجه‌ی ابزار» به مدل برمی‌گردانید.
  5. مدل با دیدن نتیجه، پاسخ نهایی را می‌نویسد یا ابزار دیگری می‌خواهد.

نکته‌ی کلیدی: مدل هیچ‌چیز را اجرا نمی‌کند. او فقط پیشنهاد می‌دهد و تصمیم نهایی با کد شماست. این مرز، پایه‌ی امنیت کل سیستم است.

قدم اول: تعریف ابزار#

هر ابزار یک نام، توضیح و شرح پارامترها با JSON Schema دارد. توضیح مهم‌ترین بخش است، چون مدل بر اساس آن تصمیم می‌گیرد چه وقت ابزار را صدا بزند:

JSON
{
  "name": "get_order_status",
  "description": "وضعیت یک سفارش را با شماره‌ی سفارش برمی‌گرداند. فقط وقتی استفاده شود که کاربر درباره‌ی پیگیری سفارش خودش می‌پرسد.",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "شماره‌ی سفارش، فقط ارقام"
      }
    },
    "required": ["order_id"]
  }
}

نام کلیدها بین سرویس‌ها کمی فرق می‌کند؛ مثلاً بعضی به‌جای parameters از input_schema استفاده می‌کنند و ساختار پیام‌ها هم یکسان نیست. پس قالب دقیق را حتماً از مستندات سرویس خودتان بردارید. منطق کلی ولی همه‌جا همین است. برای ابزارهای استانداردشده‌ای که به چند برنامه وصل می‌شوند، MCP چیست را هم ببینید.

توضیح خوب بنویسید#

  • بگویید ابزار چه کاری می‌کند و چه وقت باید صدا زده شود.
  • بگویید چه وقت نباید استفاده شود.
  • هر پارامتر توضیح و قالب مشخص داشته باشد.
  • نام ابزارها شفاف و متمایز باشند؛ دو ابزار با کار مشابه مدل را سردرگم می‌کنند.

قدم دوم: اجرای امن ابزار در PHP#

بخش مهم کار اینجاست. آرگومان‌هایی که مدل می‌فرستد ورودی غیرقابل‌اعتماد است، درست مثل داده‌ی فرم کاربر:

PHP
<?php
function run_tool( string $name, array $args, int $userId ): array
{
    switch ( $name ) {
        case 'get_order_status':
            $orderId = $args['order_id'] ?? null;

            if ( ! is_string( $orderId ) || ! preg_match( '/^\d{1,12}$/', $orderId ) ) {
                return [ 'error' => 'invalid_order_id' ];
            }

            // سفارش باید متعلق به همین کاربر باشد، نه هر کاربری
            $order = find_order_for_user( (int) $orderId, $userId );

            if ( ! $order ) {
                return [ 'error' => 'not_found' ];
            }

            return [
                'status'     => $order->status,
                'updated_at' => $order->updated_at,
            ];

        default:
            return [ 'error' => 'unknown_tool' ];
    }
}

سه اصل در این کد رعایت شده:

  • فهرست سفید. فقط ابزارهای شناخته‌شده اجرا می‌شوند؛ نام ناشناخته خطا می‌دهد.
  • اعتبارسنجی آرگومان. نوع و قالب بررسی می‌شود، نه این که مقدار مستقیم وارد پرس‌وجو شود. در کوئری هم از پارامتر بسته‌بندی‌شده استفاده کنید.
  • بررسی دسترسی. تابع سفارش را با شناسه‌ی کاربر فعلی می‌گیرد. حتی اگر مدل (یا مهاجم) شماره‌ی سفارش دیگری بفرستد، چیزی نشت نمی‌کند.

قدم سوم: حلقه‌ی گفت‌وگو#

حلقه باید سقف تعداد دور داشته باشد. تابع call_model در کد پایین پوشش خودتان روی API سرویس است که پاسخ را به قالبی یکسان درمی‌آورد؛ این بخش به قالب سرویس بستگی دارد:

PHP
<?php
function answer( array $messages, array $tools, int $userId ): string
{
    for ( $round = 0; $round < 5; $round++ ) {
        $reply = call_model( $messages, $tools ); // پوشش خودتان روی API

        if ( empty( $reply['tool_calls'] ) ) {
            return $reply['text'];
        }

        $messages[] = $reply['raw_message']; // پیام مدل را در تاریخچه نگه دارید

        foreach ( $reply['tool_calls'] as $call ) {
            $args   = json_decode( $call['arguments'], true );
            $result = is_array( $args )
                ? run_tool( $call['name'], $args, $userId )
                : [ 'error' => 'bad_arguments' ];

            $messages[] = [
                'role'         => 'tool',
                'tool_call_id' => $call['id'],
                'content'      => json_encode( $result, JSON_UNESCAPED_UNICODE ),
            ];
        }
    }

    return 'متأسفانه الان نمی‌توانم این درخواست را کامل انجام دهم.';
}

نام نقش پیام و کلیدها در هر سرویس ممکن است متفاوت باشد، ولی ساختار همین است: درخواست مدل را در تاریخچه نگه می‌دارید و نتیجه‌ی هر ابزار را با شناسه‌ی همان فراخوانی برمی‌گردانید.

امنیت#

فراخوانی ابزار قدرت مدل را زیاد می‌کند و سطح حمله را هم. رعایت این موارد ضروری است:

  • ابزارها را حداقلی و ترجیحاً فقط‌خواندنی بگیرید.
  • برای کارهای مهم، مثل پرداخت، حذف یا ارسال پیام، تأیید کاربر بگیرید.
  • متنی که ابزار برمی‌گرداند (مثلاً نظر کاربران یا صفحه‌ی وب) می‌تواند حاوی دستور پنهان باشد. تزریق پرامپت در سایت این خطر را توضیح داده است.
  • همه‌ی فراخوانی‌ها را برای عیب‌یابی و ردیابی لاگ کنید، بدون ذخیره‌ی داده‌ی حساس.
  • سقف تعداد دور و زمان‌بندی (timeout) بگذارید.

برای تصویر کلی‌تری از این‌که مدل با ابزارها چگونه به یک عامل تبدیل می‌شود، ایجنت هوش مصنوعی چیست را بخوانید. اگر فقط قالب ثابت خروجی می‌خواهید و نیاز به ابزار نیست، گرفتن خروجی JSON از هوش مصنوعی ساده‌تر است.

چه وقت ابزار نسازیم؟#

هر ابزار یک وعده و یک ریسک است. قبل از تعریف ابزار بپرسید واقعاً لازم است یا نه:

  • اگر داده‌ی لازم کوچک و ثابت است، مستقیم در پرامپت بدهید؛ نیازی به رفت‌وبرگشت نیست.
  • اگر فقط قالب خروجی می‌خواهید، خروجی ساخت‌یافته کافی است.
  • اگر کار همیشه یک مسیر ثابت دارد، آن را در کد خودتان بنویسید و مدل را فقط برای بخش زبانی به کار بگیرید.
  • اگر کار برگشت‌ناپذیر و پرخطر است، مرحله‌ی تأیید انسانی بگذارید یا ابزار را اصلاً نسازید.

عیب‌یابی رایج#

وقتی ابزار درست کار نمی‌کند، معمولاً یکی از این‌هاست:

نشانهعلت محتمل
مدل ابزار را هیچ‌وقت صدا نمی‌زندتوضیح ابزار مبهم است یا با پرسش‌های کاربر نمی‌خواند
مدل ابزار نامناسب را صدا می‌زنددو ابزار شبیه هم‌اند یا توضیح «چه وقت نه» ندارند
آرگومان‌ها نامعتبرندشرح پارامتر کافی نیست؛ مثال و قالب را در توضیح بیاورید
حلقه تمام نمی‌شودنتیجه‌ی ابزار برای مدل بی‌معنا یا ناقص است
پاسخ نهایی نتیجه را نادیده می‌گیردنتیجه در تاریخچه با شناسه‌ی درست برنگشته است

یک روش مؤثر: نام ابزار، آرگومان و نتیجه‌ی هر دور را در لاگ بنویسید و نگاه کنید مدل در کدام قدم مسیر را گم می‌کند.

نتیجه‌ی ابزار را کوتاه و روشن بدهید#

نتیجه‌ای که برمی‌گردانید خودش وارد پنجره‌ی متن مدل می‌شود. جدول بزرگ یا HTML خام برنگردانید؛ فقط همان فیلدهایی را بدهید که برای جواب لازم است. هر چه داده‌ی اضافی کمتر باشد، مصرف توکن پایین‌تر و احتمال حواس‌پرتی مدل کمتر است. درباره‌ی هزینه و توکن، محاسبه و کاهش هزینه‌ی API را ببینید.

خلاصه#

  • فراخوانی ابزار یعنی مدل درخواست می‌دهد و برنامه‌ی شما اجرا می‌کند.
  • هر ابزار با نام، توضیح روشن و JSON Schema معرفی می‌شود.
  • آرگومان‌ها را مثل ورودی کاربر اعتبارسنجی کنید و دسترسی را بسنجید.
  • حلقه را با سقف تعداد دور و timeout محدود کنید.
  • ابزارهای حساس به تأیید انسانی نیاز دارند.
  • قالب دقیق درخواست و پاسخ را از مستندات سرویس خودتان بگیرید.