یکی از مهمترین موانع در ساخت عاملهای هوش مصنوعی قدرتمند، همواره عدم هماهنگی بین ماهیت سیال و احتمالی مدلهای زبانی بزرگ (LLM) و الزامات قطعی و ساختاریافته سیستمهای خارجی بوده است. چگونه مدلی که کلمات بعدی را پیشبینی میکند، میتواند دقیقاً با یک REST API تعامل داشته باشد، یک پایگاه داده SQL را جستجو کند یا یک گردش کار خاص را بدون ایجاد پارامترهای توهمی (hallucinating) اجرا کند؟ پاسخ در فراخوانی تابع (Function Calling) نهفته است (که به آن استفاده از ابزار یا Actions نیز گفته میشود).
برای توسعهدهندگان متوسط و پیشرفته، عبور از تکمیلهای ساده چت به سمت عاملهای کاربردی نیازمند تغییر نگرش است. شما دیگر فقط به مدل دستورالعمل نمیدهید؛ بلکه یک رابط اجرایی تعریف میکنید. این پست به بررسی معماری فراخوانی تابع، نمایش استراتژیهای پیادهسازی و برجسته کردن بهترین شیوهها برای اطمینان از قابلیت اطمینان میپردازد.
درک مکانیسم
در هسته خود، فراخوانی تابع به LLM اجازه میدهد تا دادههای ساختاریافته (معمولاً JSON) را که نام یک تابع و آرگومانهای آن را نشان میدهد، خروجی دهد، نه اینکه فقط متن زبان طبیعی تولید کند. برنامه این پاسخ را دریافت میکند، کد واقعی را اجرا میکند و سپس نتیجه را به مدل باز میگرداند.
این فرآیند، LLM را از یک تولیدکننده متن ایستا به یک عامل فعال تبدیل میکند که قادر به استدلال درباره تغییرات وضعیت و بازیابی داده است. گردش کار معمولاً این حلقه را دنبال میکند:
- ورودی کاربر: کاربر یک درخواست ارسال میکند.
- تصمیم مدل: LLM تعیین میکند که یک ابزار خارجی مورد نیاز است و یک شیء فراخوانی تابع را خروجی میدهد.
- اجرا: بکاند JSON را تجزیه میکند، تابع خاص را فراخوانی میکند و خروجی را ثبت میکند.
- پاسخ: نتیجه تابع به تاریخچه مکالمه اضافه میشود.
- خروجی نهایی: LLM یک پاسخ زبان طبیعی بر اساس خروجی ابزار تولید میکند.
تعریف طرحواره: قرارداد
موفقیت عامل شما به شدت به نحوه تعریف توابع شما بستگی دارد. APIهای مدرن (مانند OpenAI، Anthropic و Mistral) به شما اجازه میدهند یک تعریف طرحواره JSON برای هر ابزار موجود ارائه دهید. این طرحواره به عنوان یک قرارداد سختگیرانه عمل میکند.
فرض کنید یک عامل هوش مصنوعی نیاز دارد دادههای آب و هوا را دریافت کند. شما باید نام تابع، توضیحی درباره عملکرد آن و پارامترهای مورد انتظار را تعریف کنید. در اینجا یک مثال عملی با استفاده از یک ساختار شبه-API مشابه OpenAI آورده شده است:
const tools = [
{
type: "function",
function: {
name: "get_weather",
description: "Get the current weather in a given location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g., San Francisco, CA"
},
unit: {
type: "string",
enum: ["celsius", "fahrenheit"],
description: "The temperature unit to use"
}
},
required: ["location"]
}
}
}
];
به استفاده از enum برای واحد توجه کنید. این امر برای محدود کردن فضای جستجوی مدل و کاهش خطاها حیاتی است. اگر پارامترها را خیلی مبهم رها کنید، مدل ممکن است رشتههای دلخواهی را ارسال کند که بکاند شما قادر به پردازش آنها نیست.
مدیریت حلقه: پیادهسازی مقاوم
پیادهسازی حلقه اجرا جایی است که بسیاری از توسعهدهندگان با موارد حاشیهای (edge cases) مواجه میشوند. به ندرت پیش میآید که یک LLM بار اول همه چیز را درست انجام دهد، به ویژه با استدلال پیچیده چندمرحلهای. شما باید مدیریت خطا را برای JSON نامعتبر یا فراخوانیهای ناموفق API پیادهسازی کنید.
هنگامی که مدل یک فراخوانی تابع را خروجی میدهد، کد شما باید چیزی شبیه به این باشد:
async function handleModelResponse(response) {
if (response.choices[0].finish_reason === 'function_call') {
const call = response.choices[0].message.function_call;
// 1. Execute the function
let result;
try {
result = await executeTool(call.name, call.arguments);
} catch (error) {
result = { error: "Failed to execute function" };
}
// 2. Append result to conversation
const conversationHistory = [
...previousMessages,
response.choices[0].message,
{
role: "function",
name: call.name,
content: JSON.stringify(result)
}
];
// 3. Send back to model for final answer
return await getModelFinalAnswer(conversationHistory);
}
}
با ارسال صریح role: "function" به مدل، حلقه بسته میشود. مدل اکنون زمینه دادههای بازیابی شده را دارد و میتواند یک پاسخ منسجم مانند "آب و هوای فعلی در سان فرانسیسکو ۷۲ درجه فارنهایت است" را فرمولبندی کند.
نتیجهگیری
فراخوانی تابع، ستون فقرات عامل هوش مصنوعی مدرن است. این امکان را به LLMها میدهد تا از تولید متن فراتر رفته و به طور مؤثر با دنیای دیجیتال تعامل داشته باشند. نکته کلیدی برای توسعهدهندگان این است که ساختار بسیار مهم است. با ارائه طرحوارههای واضح، مدیریت خطای مقاوم و یک حلقه اجرای به خوبی تعریف شده، میتوانید عاملهایی بسازید که نه فقط دموهای خیرهکننده، بلکه ابزارهای قابل اعتماد و آماده برای تولید باشند.
با بهبود مدلها در تواناییهای استدلال خود، اصطکاک فراخوانی تابع کاهش خواهد یافت، اما معماری اساسی همان خواهد ماند: ابزار را تعریف کنید، قصد را اجرا کنید و نتیجه را سنتز کنید.