فاز 13 · درس 2

بررسی عمیق Function Calling در OpenAI، Anthropic و Gemini

نوع
ساختنی
زمان
حدود 75 دقیقه
زبان‌ها
Python (stdlib, schema translators)
پیش‌نیاز
فاز 13 · 01 (رابط ابزار)
سه آداپتور یا رابط فیزیکی متفاوت با پایه‌های گوناگون که همگی به یک کابل و ترمینال مشترک متصل می‌شوند.
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفاهیم پایه
  4. ساختار مشترک
  5. مقایسه‌ی جزئیات ساختار، فیلد به فیلد
  6. محدودیت‌هایی که در عمل با آن‌ها روبه‌رو می‌شوید
  7. رفتار tool_choice
  8. فراخوانی‌های موازی
  9. استریمینگ (Streaming)
  10. خطاها و ترمیم آن‌ها
  11. الگوی مترجم (Translator Pattern)
  12. استفاده از ابزارهای واقعی
  13. خروجی این درس
  14. تمرین‌ها
  15. اصطلاحات کلیدی
  16. برای مطالعه‌ی بیشتر

بسیاری از توسعه‌دهندگان کار خود را با نوشتن ایجنت‌ها روی API مدل‌های OpenAI شروع می‌کنند؛ اما وقتی نیاز می‌شود مدل را به کلود (Anthropic Claude) یا جمینای (Google Gemini) تغییر دهند، با انبوهی از تفاوت‌های ریز ساختاری مواجه می‌شوند. اگرچه منطق فراخوانی تابع (Function Calling) در هر سه یکسان است، نام کلیدها، نحوه بازگرداندن آرگومان‌ها (رشته در برابر شیء) و ساختار ارسال نتیجه کاملاً با یکدیگر فرق دارند.

این ناسازگاری‌ها معمولاً ساعت‌ها وقت تیم‌های مهندسی را صرف بازنویسی لایه‌های ورودی و خروجی می‌کند. در این درس ساختار پِی‌لودهای هر سه ارائه‌دهنده را کنار هم می‌گذاریم و با طراحی یک الگوی مترجم (Translator Pattern)، روشی اصولی برای نوشتن کدهای قابل‌حمل و مستقل از ارائه‌دهنده یاد می‌گیریم.

سه ارائه‌دهنده‌ی پیشرو در سال 2024 روی یک چرخه‌ی فراخوانی ابزار (tool-call loop) به توافق رسیدند، اما در تمام جزئیات دیگر راه خود را جدا کردند. OpenAI از tools و tool_calls استفاده می‌کند؛ Anthropic سراغ بلوک‌های tool_use و tool_result رفته است و Gemini از functionDeclarations و تطبیق با شناسه‌ی یکتا بهره می‌گیرد. در این درس، تفاوت‌های ساختاری این سه مدل را کنار هم بررسی می‌کنیم تا کدی که برای یک ارائه‌دهنده نوشته‌اید، هنگام پورت کردن روی دیگری از کار نیفتد.

اهداف یادگیری

  • بیان سه تفاوت ساختاری میان پِی‌لودهای فراخوانی تابع (function calling) در OpenAI، Anthropic و Gemini (تعریف، فراخوانی، نتیجه).
  • تبدیل تعریف یک ابزار به فرمت‌های هر سه ارائه‌دهنده و پیش‌بینی تفاوت‌ها در محدودیت‌های حالت دقیق (strict mode).
  • استفاده از tool_choice در هر ارائه‌دهنده برای اجبار، منع یا انتخاب خودکار فراخوانی ابزارها.
  • تسلط بر محدودیت‌های قطعی هر ارائه‌دهنده (تعداد ابزار، عمق اسکیما، طول آرگومان‌ها) و ساختار خطاهایی که هنگام نقض این محدودیت‌ها برمی‌گردانند.

صورت مسئله

ساختار درخواست فراخوانی تابع در هر ارائه‌دهنده فرق می‌کند. به سه نمونه‌ی واقعی از استک‌های پروداکشن سال 2026 نگاه کنید:

APIهای Chat Completions / Responses در OpenAI. شما پِی‌لود tools: [{type: "function", function: {name, description, parameters, strict}}] را ارسال می‌کنید. پاسخ مدل شامل choices[0].message.tool_calls: [{id, type: "function", function: {name, arguments}}] است که در آن arguments یک رشته‌ی JSON است و باید آن را پارس کنید. حالت دقیق (strict: true) تطابق با اسکیما (schema compliance) را از طریق دیکود مقید (constrained decoding) تضمین می‌کند.

API پیام‌های Anthropic (یا همان Messages API). شما پِی‌لود tools: [{name, description, input_schema}] را ارسال می‌کنید. پاسخ به شکل content: [{type: "text"}, {type: "tool_use", id, name, input}] بازمی‌گردد. فیلد input از قبل پارس شده است (یک آبجکت است، نه رشته). شما در پاسخ، یک پیام جدید با نقش user می‌فرستید که شامل بلوک {type: "tool_result", tool_use_id, content} است.

API مدل Google Gemini. شما پِی‌لود tools: [{functionDeclarations: [{name, description, parameters}]}] را ارسال می‌کنید (تودرتو داخل functionDeclarations). پاسخ به شکل candidates[0].content.parts: [{functionCall: {name, args, id}}] می‌رسد که در آن id از Gemini 3 به بعد برای تطبیق فراخوانی‌های موازی، مقداری یکتا دارد. شما با پِی‌لود {functionResponse: {name, id, response}} پاسخ می‌دهید.

چرخه‌ی کار یکسان است، اما نام فیلدها، ساختار تودرتو، تفاوت رشته در برابر آبجکت و سازوکارهای تطبیق فرق دارند. تیمی که یک ایجنت (agent) هواشناسی بر پایه‌ی OpenAI می‌نویسد، باید دو روز برای پورت کردن به Anthropic و یک روز دیگر برای Gemini فقط صرف لوله‌کشی و هماهنگی کدها کند.

در این درس مترجمی می‌سازیم که این سه فرمت را در یک تعریف ابزار کانونیکال (canonical) یکپارچه می‌کند و مسیریابی را در لایه‌ی مرزی (edge) انجام می‌دهد. در فاز 13 · 17 همین الگو را به یک گیت‌وی مدل‌های زبانی (LLM gateway) تعمیم می‌دهیم.

مفاهیم پایه

ساختار مشترک

هر ارائه‌دهنده‌ای به پنج بخش نیاز دارد:

  1. فهرست ابزارها (Tool list): نام، توضیحات و اسکیمای ورودی برای هر ابزار.
  2. انتخاب ابزار (Tool choice): اجبار به فراخوانی یک ابزار خاص، منع استفاده از ابزارها یا واگذاری تصمیم به مدل.
  3. صدور فراخوانی (Call emission): خروجی ساختاریافته‌ای (structured output) که نام ابزار و آرگومان‌ها را مشخص می‌کند.
  4. شناسه‌ی فراخوانی (Call id): تطبیق پاسخ با فراخوانی درست (در فراخوانی‌های موازی حیاتی است).
  5. تزریق نتیجه (Result injection): پیام یا بلوکی که نتیجه را به فراخوانی مربوطه پیوند می‌دهد.

مقایسه‌ی جزئیات ساختار، فیلد به فیلد

جنبه OpenAI Anthropic Gemini
پوشش تعریف ابزار {type: "function", function: {...}} {name, description, input_schema} {functionDeclarations: [{...}]}
فیلد اسکیما parameters input_schema parameters
محفظه‌ی پاسخ tool_calls[] روی پیام assistant content[] از نوع tool_use parts[] از نوع functionCall
نوع آرگومان‌ها رشته‌ی JSON آبجکت پارس‌شده آبجکت پارس‌شده
فرمت شناسه call_... (تولیدشده توسط OpenAI) toolu_... (Anthropic) UUID (نسخه‌ی Gemini 3+)
بلوک نتیجه نقش tool با tool_call_id نقش user با tool_result و tool_use_id functionResponse با id منطبق
اجبار به ابزار tool_choice: {type: "function", function: {name}} tool_choice: {type: "tool", name} tool_config: {function_calling_config: {mode: "ANY"}}
منع ابزارها tool_choice: "none" tool_choice: {type: "none"} mode: "NONE"
اسکیمای دقیق strict: true strict: true به ازای هر ابزار (اختیاری) responseSchema در سطح درخواست

محدودیت‌هایی که در عمل با آن‌ها روبه‌رو می‌شوید

  • OpenAI: حداکثر 128 ابزار در هر درخواست. عمق اسکیما حداکثر 5. طول رشته‌ی آرگومان‌ها کمتر یا مساوی 8192 بایت. در حالت دقیق، نباید از $ref استفاده شود، استفاده از oneOf/anyOf/allOf با هم‌پوشانی ممنوع است و تمام مشخصه‌ها باید در required فهرست شوند.
  • Anthropic: حداکثر 64 ابزار در هر درخواست. عمق اسکیما روی کاغذ نامحدود است اما سقف تجربی آن به 10 می‌رسد. فراخوانی دقیق ابزار، انتخابی است و با فعال‌سازی strict: true روی تعریف ابزار (tool definition) اعمال می‌شود. بدون آن، اسکیما صرفاً قراردادی است که مدل تمایل دارد رعایت کند، نه تضمینی قطعی.
  • Gemini: حداکثر 64 تابع در هر درخواست. انواع داده در اسکیما، زیرمجموعه‌ای از OpenAPI 3.0 هستند (کمی با JSON Schema نسخه 2020-12 تفاوت دارد). فراخوانی‌های موازی از Gemini 3 شناسه‌ی یکتا دارند.

رفتار tool_choice

هر سه ارائه‌دهنده از سه حالت اصلی پشتیبانی می‌کنند، فقط نام‌گذاری‌هایشان فرق دارد:

  • Auto: مدل بین ابزار یا متن یکی را انتخاب می‌کند (حالت پیش‌فرض).
  • Required / Any: مدل باید حداقل یک ابزار را فراخوانی کند.
  • None: مدل نباید هیچ ابزاری را فراخوانی کند.

علاوه بر این، هر ارائه‌دهنده یک حالت اختصاصی هم دارد:

  • OpenAI: اجبار به فراخوانی یک ابزار مشخص با نام آن.
  • Anthropic: اجبار به فراخوانی یک ابزار مشخص با نام آن؛ فلگ disable_parallel_tool_use فراخوانی تکی را از چندگانه جدا می‌کند.
  • Gemini: حالت mode: "VALIDATED" فارغ از نیت (intent) مدل، همه‌ی پاسخ‌ها را از اعتبارسنج اسکیما رد می‌کند.

فراخوانی‌های موازی

تنظیم parallel_tool_calls: true (پیش‌فرض) در OpenAI چند فراخوانی را در قالب یک پیام assistant برمی‌گرداند. شما همه‌ی آن‌ها را اجرا می‌کنید و پاسخی دسته‌ای با نقش tool می‌فرستید که به ازای هر tool_call_id یک ورودی دارد. Anthropic قبلاً فقط فراخوانی تکی انجام می‌داد؛ اما با disable_parallel_tool_use: false (پیش‌فرض از Claude 3.5 به بعد) حالت چندگانه را فعال کرده است. Gemini 2 امکان فراخوانی موازی داشت ولی شناسه‌ی پایداری نمی‌داد؛ Gemini 3 شناسه‌های UUID را اضافه کرده تا پاسخ‌هایی که نامرتب می‌رسند، بدون مشکل تطبیق پیدا کنند.

استریمینگ (Streaming)

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

  • OpenAI: چانک‌های دلتا از tool_calls[i].function.arguments به‌مرور می‌رسند. داده‌ها را تا رسیدن به finish_reason: "tool_calls" تجمیع می‌کنید.
  • Anthropic: رویدادهای block-start ،block-delta و block-stop ارسال می‌شوند. چانک‌های input_json_delta حامل آرگومان‌های ناقص هستند.
  • Gemini: قابلیت streamFunctionCallArguments (جدید در Gemini 3) چانک‌ها را همراه با یک functionCallId می‌فرستد تا چند فراخوانی موازی بتوانند لابه‌لای هم جریان پیدا کنند.

در فاز 13 · 03 مباحث فراخوانی موازی و سرهم‌بندی پاسخ‌های استریمینگ را مفصل باز می‌کنیم. این درس روی ساختار تعریف ابزار و فراخوانی تکی تمرکز دارد.

خطاها و ترمیم آن‌ها

خطاهای مربوط به آرگومان نامعتبر هم ظاهر متفاوتی دارند:

  • OpenAI (حالت عادی): مدل arguments: "{bad json}" برمی‌گرداند؛ پارس JSON شما با خطا مواجه می‌شود؛ یک پیام خطا تزریق می‌کنید و مدل را دوباره صدا می‌زنید.
  • OpenAI (حالت دقیق): اعتبارسنجی حین دیکود انجام می‌شود؛ تولید JSON نامعتبر غیرممکن است، اما ممکن است پاسخ از نوع امتناع (refusal) باشد.
  • Anthropic (حالت عادی): فیلد input ممکن است حاوی فیلدهای پیش‌بینی‌نشده باشد؛ اسکیما الزام‌آور نیست و باید در سمت سرور اعتبارسنجی کنید. با strict: true، نمونه‌برداری به اسکیما مقید می‌شود.
  • Gemini: رفتار خاص OpenAPI 3.0: فیلد enum روی فیلدهای از نوع شیء بدون اعلام خطا نادیده گرفته می‌شود؛ باید خودتان آن را اعتبارسنجی کنید.

الگوی مترجم (Translator Pattern)

یک تعریف ابزار استاندارد و کانونیکال در کد شما به این شکل است (طراحی ساختار دست خودتان است):

Tool(
    name="get_weather",
    description="Use when ...",
    input_schema={"type": "object", "properties": {...}, "required": [...]},
    strict=True,
)

سه تابع کوچک آن را به ساختارهای سه‌گانه‌ی این ارائه‌دهنده‌ها ترجمه می‌کنند. محیط تست در code/main.py دقیقاً همین کار را پیاده می‌کند و سپس یک فراخوانی ابزار شبیه‌سازی‌شده را در ساختار پاسخ هر ارائه‌دهنده تست و ارزیابی می‌کند. نیازی به اتصال شبکه نیست؛ این درس ساختارها را آموزش می‌دهد، نه درخواست‌های HTTP را.

تیم‌های فنی در محیط پروداکشن، این مترجم را داخل کلاس‌هایی مثل AbstractToolset (در Pydantic AI)، UniversalToolNode (در LangGraph) یا BaseTool (در LlamaIndex) کپسوله می‌کنند. در فاز 13 · 17 گیت‌ویی می‌سازیم که لایه‌ی بیرونی آن رابطی به سبک OpenAI دارد و در پشت صحنه درخواست‌ها را به هر یک از این سه مدل هدایت می‌کند.

آرگومان‌های فراخوانی تابعبرای تکمیل اسکیما بکشید
FUNCTION CALL ARGSFunction calling is two jobs in one. First the model selects which tool to invoke from the available set, then it populates that tool's typed argument schema by extracting values from the user request. The output is not prose but a structured call: a tool name plus a JSON object whose fields match the declared types, ready to execute.درخواست: رزرو 2 صندلی از بمبئی به سان‌فرانسیسکو برای 1 ژوئیهtool : enum = book_flightfrom : string = "BOM"to : string = "SFO"date : date = ?pax : int = ?
فراخوانی تابع دو وظیفه در یک فرآیند است: ابتدا مدل از میان ابزارهای موجود، ابزار مناسب را انتخاب می‌کند؛ سپس با استخراج مقادیر از درخواست کاربر، اسکیمای آرگومان‌های نوع‌دار آن ابزار را مقداردهی می‌کند. خروجی یک متن ساده نیست، بلکه فراخوانی ساختاریافته‌ای است شامل نام ابزار و یک آبجکت JSON آماده‌ی اجرا که فیلدهای آن با انواع داده‌ای تعریف‌شده مطابقت دارند.

استفاده از ابزارهای واقعی

در فایل code/main.py یک دیتاکلاس کانونیکال به نام Tool و سه مترجم تعریف شده‌اند که JSON تعریف ابزار را برای OpenAI، Anthropic و Gemini تولید می‌کنند. این اسکریپت سپس پاسخ دستی ساخته‌شده‌ی هر یک از ارائه‌دهنده‌ها را به همان آبجکت فراخوانی کانونیکال تبدیل می‌کند تا نشان دهد منطق کار در پس ظاهر متفاوت فیلدها، کاملاً یکسان است. آن را اجرا کنید و تفاوت‌های این سه تعریف را کنار هم بسنجید.

مواردی که باید دقت کنید:

  • سه بلوک تعریف ابزار صرفاً در پوشش بیرونی و نام فیلدها با هم فرق دارند.
  • سه بلوک پاسخ در محل قرارگیری فراخوانی تفاوت دارند (فیلد سطح بالای tool_calls، بلوک content[]، یا ورودی parts[]).
  • یک تابع واحد به نام canonical_call() فیلدهای {id, name, args} را از هر سه فرمت پاسخ استخراج می‌کند.

خروجی این درس

خروجی این درس فایل outputs/skill-provider-portability-audit.md است. این مهارت با گرفتن کد پیاده‌سازی فراخوانی ابزار برای یک ارائه‌دهنده، یک گزارش ارزیابی قابلیت انتقال (portability audit) تولید می‌کند: کدهای شما به کدام محدودیت‌های ارائه‌دهنده وابسته است، چه فیلدهایی باید تغییر نام دهند، و هنگام انتقال به مدل‌های دیگر چه بخش‌هایی از کار می‌افتند.

تمرین‌ها

  1. اسکریپت code/main.py را اجرا کنید و مطمئن شوید که خروجی JSON تعریف ابزار برای هر سه مدل، از یک آبجکت پایه‌ی Tool ساخته می‌شود. ابزار کانونیکال را طوری تغییر دهید که یک پارامتر enum به آن اضافه شود و بررسی کنید که چرا فقط مترجم Gemini نیاز به مدیریت رفتار خاص OpenAPI دارد.

  2. برای هر ارائه‌دهنده یک پارسر ListToolsResponse بنویسید که فهرست ابزارهای بازگردانده‌شده از سوی مدل را پس از فراخوانی list_tools یا کشف ابزار استخراج کند. OpenAI این قابلیت را به‌صورت پیش‌فرض ندارد؛ به این عدم تقارن دقت کنید.

  3. تبدیل tool_choice را پیاده‌سازی کنید: یک ساختار کانونیکال به‌صورت ToolChoice(mode="force", tool_name="x") را به فرمت هر سه مدل تبدیل کنید. سپس همین کار را برای mode="any" و mode="none" تکرار کنید. از جدول مقایسه‌ای درس کمک بگیرید.

  4. یکی از سه ارائه‌دهنده را انتخاب کرده و مستندات فراخوانی تابع آن را از ابتدا تا انتها بخوانید. فیلدی در مشخصات اسکیمای آن پیدا کنید که دو ارائه‌دهنده‌ی دیگر از آن پشتیبانی نمی‌کنند. گزینه‌های پیشنهادی: strict در OpenAI، فلگ disable_parallel_tool_use در Anthropic، یا function_calling_config.allowed_function_names در Gemini.

  5. یک ورودی تست بنویسید: فراخوانی ابزاری که آرگومان‌های آن، اسکیمای تعریف‌شده را نقض کند. آن را به اعتبارسنج هر ارائه‌دهنده بدهید (می‌توانید از اعتبارسنج کتابخانه‌ی استاندارد در درس 01 استفاده کنید) و خطاهای صادرشده را ثبت کنید. مستند کنید که برای تضمین دقت و سخت‌گیری در پروداکشن، کدام مدل را انتخاب می‌کنید.

اصطلاحات کلیدی

اصطلاح در گفت‌وگوها معنای دقیق فنی
Function calling «فراخوانی ابزار» API در سطح ارائه‌دهنده برای تولید ساختاریافته‌ی فراخوانی ابزار
Tool declaration «مشخصات ابزار» نام + توضیحات + پِی‌لود ورودی بر اساس اسکیمای JSON (JSON Schema)
tool_choice «اجبار / منع» حالت‌های auto / required / none / نام خاص
Strict mode «اعمال اسکیما» فلگی در OpenAI که دیکود را برای انطباق با اسکیما مقید می‌کند
tool_use block «فرمت فراخوانی در Anthropic» بلوک محتوایی درون‌خطی شامل id، name و input
functionCall part «فرمت فراخوانی در Gemini» یک ورودی در آرایه‌ی parts[] شامل name، args و id
Arguments-as-string «JSON رشته‌ای» بازگرداندن آرگومان‌ها به شکل رشته‌ی JSON توسط OpenAI، نه یک آبجکت
Parallel tool calls «ارسال هم‌زمان در یک نوبت» چند فراخوانی ابزار درون یک نوبت گفت‌وگو (turn) در پیام assistant
Refusal «امتناع مدل» بلوک امتناع فقط در حالت دقیق، به‌جای فراخوانی ابزار
OpenAPI 3.0 subset «رفتار خاص اسکیما در Gemini» استفاده‌ی Gemini از لهجه‌ای شبیه به JSON Schema با تفاوت‌های جزئی

برای مطالعه‌ی بیشتر

سؤالات متداول

تفاوت اصلی در قالب ارسال آرگومان‌های ابزار بین OpenAI و Anthropic چیست؟

مدل‌های OpenAI آرگومان‌های ابزار را به صورت رشته متنی خام JSON درون فیلد arguments بازمی‌گردانند که باید پارس شود؛ اما Anthropic در بلوک tool_use آرگومان‌ها را از قبل پارس کرده و به شکل شیء در فیلد input تحویل می‌دهد.

Gemini چگونه فراخوانی‌های موازی ابزارها را تطبیق می‌دهد؟

از نسخه Gemini 3 به بعد، هر فراخوانی تابع یک شناسه یکتا از نوع UUID دریافت می‌کند که در بخش functionResponse برای بازگرداندن نتیجه همان فراخوانی استفاده می‌شود.

حالت strict mode در OpenAI چه محدودیتی در اسکیما ایجاد می‌کند؟

در حالت دقیق، استفاده از ارجاعات ref$ و هم‌پوشانی‌های oneOf/anyOf/allOf ممنوع است، حداکثر عمق اسکیما به 5 محدود می‌شود و تمام مشخصه‌ها باید الزماً در آرایه required تعریف شوند.

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.