اگر می‌خواهید خروجی مدل زبانی فقط حرف نزند و واقعا کاری انجام دهد، از Function Calling در OpenAI استفاده کنید: با تعریف توابع خودتان و یک اسکیما JSON، مدل در زمان مناسب آن توابع را صدا می‌زند و با نتیجه آن پاسخ می‌سازد. در پایتون، این کار با تعریف tools، گرفتن درخواست model برای اجرای تابع، اجرای امن کد شما و ارسال نتیجه به مدل تکمیل می‌شود.

Function Calling دقیقا چه می‌کند و چه زمانی به کار می‌آید؟

Function Calling به مدل اجازه می‌دهد به جای حدس زدن پاسخ، یک یا چند «ابزار» شما را صدا بزند؛ مثلا آب‌وهوا را از API بگیرد، در پایگاه داده جستجو کند یا پرداخت را بررسی کند. وقتی درخواست کاربر نیازمند داده واقعی یا عملیاتی خارج از متن است (اطلاعات به‌روز، محاسبات دقیق، تعامل با سیستم داخلی)، Function Calling مسیر درست است.

پیش‌نیازها و آماده‌سازی

  • یک کلید API از OpenAI و تنظیم متغیر محیطی OPENAI_API_KEY
  • نصب کتابخانه پایتون OpenAI: pip install openai
  • انتخاب یک مدل سازگار با ابزارها (مثل gpt-4o-mini)

نمونه کد پایه: اتصال مدل به یک تابع ساده

در این مثال، یک ابزار ساده به نام get_current_weather تعریف می‌کنیم که ظاهرا آب‌وهوا را برمی‌گرداند (می‌تواند به API واقعی متصل شود). مدل اگر تشخیص دهد به این ابزار نیاز دارد، آن را صدا می‌زند.

import os
import json
from openai import OpenAI

# 1) کلاینت
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# 2) تعریف تابع واقعی شما
def get_current_weather(location: str, unit: str = "celsius"):
    # در عمل اینجا به API هواشناسی وصل شوید. این فقط نمونه شبیه‌سازی‌شده است.
    fake_db = {"تهران": {"celsius": 24, "fahrenheit": 75}}
    temp = fake_db.get(location, {}).get(unit)
    if temp is None:
        return {"ok": False, "message": "location or unit not supported"}
    return {"ok": True, "location": location, "unit": unit, "temp": temp}

# 3) اسکیما ابزارها برای مدل
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Return current weather for a given location.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string", "description": "City name in Persian, e.g., تهران"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
                },
                "required": ["location"]
            }
        }
    }
]

# 4) گفت‌وگو
messages = [
    {"role": "user", "content": "هوای تهران الان چطور است؟ دما را به سلسیوس بگو."}
]

# 5) درخواست اول: آیا مدل می‌خواهد ابزاری را فراخوانی کند؟
resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages,
    tools=tools,
    temperature=0  # برای رفتار قابل‌پیش‌بینی‌تر
)

msg = resp.choices[0].message
tool_calls = getattr(msg, "tool_calls", None)

if tool_calls:
    # 6) اجرای امن توابع خواسته‌شده و افزودن نتیجه به گفت‌وگو
    for call in tool_calls:
        fn_name = call.function.name
        try:
            args = json.loads(call.function.arguments or "{}")
        except json.JSONDecodeError:
            args = {}

        result = None
        if fn_name == "get_current_weather":
            location = str(args.get("location", "")).strip()
            unit = args.get("unit") or "celsius"
            if unit not in ["celsius", "fahrenheit"]:
                unit = "celsius"
            result = get_current_weather(location=location, unit=unit)
        else:
            result = {"ok": False, "message": f"unknown tool: {fn_name}"}

        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result, ensure_ascii=False)
        })

    # 7) درخواست دوم: مدل با توجه به خروجی ابزار، پاسخ نهایی را می‌سازد
    final = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages,
        temperature=0
    )
    print(final.choices[0].message.content)
else:
    # مدل بدون ابزار پاسخ داده است
    print(msg.content)

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

کنترل رفتار مدل با tool_choice

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

# اجبار به استفاده از یک تابع مشخص
resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages,
    tools=tools,
    tool_choice={"type": "function", "function": {"name": "get_current_weather"}},
    temperature=0
)

# جلوگیری از هرگونه فراخوانی ابزار
resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages,
    tools=tools,
    tool_choice="none",
    temperature=0
)

وقتی داده حیاتی باید از منبع معتبر شما بیاید، با tool_choice جلوی حدس‌زدن مدل را بگیرید.

چند ابزار و مسیریابی ساده

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

import json
from openai import OpenAI

client = OpenAI()

def add(a: float, b: float):
    return {"ok": True, "result": a + b}

tools = [
    {
        "type": "function",
        "function": {
            "name": "add",
            "description": "Add two numbers precisely.",
            "parameters": {
                "type": "object",
                "properties": {"a": {"type": "number"}, "b": {"type": "number"}},
                "required": ["a", "b"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Return current weather for a given location.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
                },
                "required": ["location"]
            }
        }
    }
]

messages = [{"role": "user", "content": "۲۳.۵ را با ۴ جمع کن و فقط نتیجه عددی بده."}]
resp = client.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools, temperature=0)

msg = resp.choices[0].message
if msg.tool_calls:
    for call in msg.tool_calls:
        name = call.function.name
        args = json.loads(call.function.arguments or "{}")

        if name == "add":
            a = float(args.get("a", 0))
            b = float(args.get("b", 0))
            out = add(a, b)
        elif name == "get_current_weather":
            out = {"ok": False, "message": "weather not available in this demo"}
        else:
            out = {"ok": False, "message": f"unknown tool: {name}"}

        messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(out)})

    final = client.chat.completions.create(model="gpt-4o-mini", messages=messages, temperature=0)
    print(final.choices[0].message.content)

برای سناریوهای واقعی، این الگو را به یک «روتر ابزار» با دیکشنری نام→تابع تعمیم دهید و گزارش‌گیری (logging) را اضافه کنید.

اعتبارسنجی ورودی و مدیریت خطا

مدل معمولا آرگومان‌ها را به‌صورت JSON صحیح بازمی‌گرداند؛ اما همیشه باید اعتبارسنجی کنید:

  • پارامترهای لازم را بررسی کنید و برای مقادیر خارج از دامنه (مثلا واحد دما) مقدار امن پیش‌فرض بگذارید.
  • هرگز آرگومان‌ها را مستقیم به کوئری پایگاه داده یا شل سیستم ندهید.
  • در خطاهای قابل‌پیش‌بینی (اتصال API، محدودیت نرخ)، پیام فنی را به لاگ بفرستید و خلاصه‌ای امن به مدل بدهید.
try:
    args = json.loads(call.function.arguments or "{}")
except Exception as e:
    log_id = "err-json-parse"
    messages.append({"role": "tool", "tool_call_id": call.id,
                     "content": json.dumps({"ok": False, "message": "invalid arguments", "code": log_id})})
    continue

چگونه نتیجه درست را بسنجیم؟

  • دمای پایین (temperature=0) را برای تکرارپذیری تنظیم کنید.
  • در لاگ، آرگومان‌های دریافتی و خروجی ابزار را ذخیره کنید تا اگر مدل مسیر اشتباهی رفت، علت را بیابید.
  • برای ورودی‌های تست، پاسخ هدف را از قبل مشخص کنید و بخشی از تست‌های واحد (unit tests) پروژه قرار دهید.

نکات امنیتی ضروری

  • فقط ابزارهای مورد نیاز را در tools معرفی کنید. معرفی یک تابع یعنی به مدل اجازه پیشنهاد آن را داده‌اید.
  • محدودیت نرخ و زمان‌سنج (timeout) برای تماس با APIهای بیرونی بگذارید.
  • داده حساس کاربر را قبل از ارسال به مدل حداقل‌سازی کنید (data minimization).
  • خروجی ابزارها را قابل نمایش برای کاربر کنید ولی خطاهای داخلی را لو ندهید.

الگوی اجرای دو مرحله‌ای؛ چرا مهم است؟

الگوی استاندارد این است: 1) مدل درخواست ابزار می‌دهد، 2) شما ابزار را اجرا می‌کنید، 3) نتیجه ابزار را به مدل برمی‌گردانید تا پاسخ نهایی را بسازد. حذف مرحله 3 باعث می‌شود مدل نتواند نتیجه خام ابزار را به زبان طبیعی و مطابق با درخواست کاربر تبدیل کند.

چه زمانی گزینه دیگری بهتر است؟

خطاهای رایج و راه‌حل‌ها

  • JSON نامعتبر در arguments: temperature را کم کنید، اسکیما را دقیق‌تر کنید، و قبل از اجرا JSON را اعتبارسنجی کنید.
  • ابزار اشتباه انتخاب می‌شود: توضیح (description) ابزار و نام آن را شفاف‌تر کنید و در صورت لزوم از tool_choice استفاده کنید.
  • حلقه بی‌پایان بین ابزار و مدل: پس از یک یا دو دور پاسخ، شرط توقف بگذارید.
  • نشت اطلاعات حساس: ورودی به مدل را مینیمال نگه دارید و لاگ‌ها را پاکسازی کنید.

گام بعدی چیست؟

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