مقالهٔ قبلی این مجموعه توضیح داد که MCP چطور راه ارتباط Agent با ابزارهای بیرونی را استاندارد میکند. اما یک لایه پایینتر باید دید: وقتی یک مدل زبانی تصمیم میگیرد «الان باید یک لید در CRM بسازم»، دقیقاً چه اتفاقی در سطح فنی میافتد؟ پاسخ این سؤال Tool Calling نام دارد؛ مکانیزمی که به مدل اجازه میدهد بهجای فقط تولید متن، یک اقدام مشخص را با ورودیهای ساختیافته درخواست کند. این مقاله همان چرخه را قدمبهقدم، با تمرکز روی یک CRM واقعی، باز میکند.
لیست مطالب
Tool Calling دقیقاً چیست
یک مدل زبانی بهخودیخود فقط متن تولید میکند؛ نه میتواند رکورد بسازد، نه ایمیل بفرستد. Tool Calling این محدودیت را با یک قرارداد ساده دور میزند: به مدل فهرستی از ابزارهای موجود، همراه با Schema دقیق هرکدام، داده میشود. مدل بهجای پاسخ آزاد، میتواند تصمیم بگیرد که «الان باید ابزار X را با این ورودیها صدا بزنم» و این تصمیم را در قالب یک شیء ساختیافته (معمولاً JSON) برمیگرداند. سیستم میزبان همان شیء را میگیرد، ابزار واقعی را اجرا میکند و نتیجه را دوباره به مدل برمیگرداند تا ادامهٔ مکالمه یا اقدام بعدی را تعیین کند.
چرخهٔ کامل یک فراخوانی
- کاربر یک درخواست طبیعی میدهد: «برای سارا احمدی یک لید جدید بساز، شمارهاش ۰۹۱۲…، از اینستاگرام اومده.»
- مدل با در نظر گرفتن Tool های موجود، تشخیص میدهد این درخواست با ابزار crm.create_lead قابل انجام است.
- مدل ورودی را طبق Schema آن ابزار میسازد و به سیستم میزبان اعلام میکند «این ابزار را با این آرگومانها صدا بزن».
- سیستم میزبان (نه خود مدل) واقعاً فراخوانی را روی CRM اجرا میکند.
- نتیجهٔ اجرا — موفق، ناموفق، یا خطا — به مدل برگردانده میشود.
- مدل بر اساس نتیجه، پاسخ نهایی به کاربر را میسازد یا در صورت نیاز، گام بعدی را (مثلاً فراخوانی ابزار دیگر) تصمیم میگیرد.
نکتهٔ مهم اینجاست که مدل هیچوقت مستقیماً به CRM دسترسی ندارد؛ او فقط «درخواست فراخوانی» تولید میکند و اجرای واقعی همیشه دست کد میزبان است. همین جداسازی است که امکان بازبینی، تأیید انسانی و لاگگیری را فراهم میکند.
نمونه؛ تعریف ابزار و پاسخ مدل
تعریف ابزار برای ساخت لید، چیزی شبیه این است:
{
"name": "crm.create_lead",
"description": "Create a lead record from an inbound contact",
"input_schema": {
"type": "object",
"properties": {
"full_name": { "type": "string" },
"phone": { "type": "string" },
"source": { "type": "string", "enum": ["instagram", "telegram", "form", "call"] }
},
"required": ["full_name", "phone", "source"]
}
}
وقتی مدل تصمیم به فراخوانی میگیرد، خروجیاش چیزی شبیه این است:
{
"tool_call": {
"name": "crm.create_lead",
"arguments": {
"full_name": "سارا احمدی",
"phone": "0912xxxxxxx",
"source": "instagram"
}
}
}
و سیستم میزبان بعد از اجرا، نتیجه را به این شکل برمیگرداند تا مدل بتواند دنبالهٔ کار را تصمیم بگیرد:
{
"tool_result": {
"name": "crm.create_lead",
"status": "success",
"lead_id": "LD-4821"
}
}
وقتی یک زنجیره از چند فراخوانی لازم است
خیلی از درخواستهای واقعی با یک فراخوانی تمام نمیشوند. «این لید را به مرحلهٔ Negotiation ببر و یک پیگیری هم برایش بفرست» یعنی حداقل دو ابزار پشت سر هم: crm.update_deal_stage و message.send. مدل باید بعد از دیدن نتیجهٔ فراخوانی اول، تصمیم بگیرد که فراخوانی دوم را با چه ورودیهایی انجام دهد — مثلاً شناسهٔ Deal که در نتیجهٔ گام اول برگشته را در گام دوم استفاده کند. این همان چیزی است که در معماری کلی محصول Opsless، لایهٔ Orchestration نامیده میشود.
خطا، Retry و Idempotency
در دنیای واقعی، فراخوانیها همیشه موفق نمیشوند: شبکه قطع میشود، CRM خطای موقتی میدهد، یا ورودی نامعتبر است. یک پیادهسازی جدی Tool Calling باید این موارد را از قبل در نظر بگیرد:
- پیام خطای قابلفهم برای مدل: بهجای یک کد خطای خام، باید توضیح داد چرا فراخوانی شکست خورد تا مدل بتواند تصمیم درست بگیرد (مثلاً شمارهٔ تلفن را دوباره از کاربر بپرسد).
- Retry با سقف مشخص: خطاهای موقتی شبکه باید چند بار دوباره امتحان شوند، اما نه بهصورت نامحدود و نه برای خطاهایی که ناشی از ورودی نادرستاند.
- Idempotency: اگر یک فراخوانی بهخاطر Timeout دوباره اجرا شود، نباید دو لید تکراری ساخته شود. راهحل معمول این است که هر فراخوانی یک کلید یکتا (Idempotency Key) داشته باشد تا CRM بفهمد این درخواست قبلاً پردازش شده یا نه.
فراخوانی موازی در برابر ترتیبی
نکتهٔ دیگری که در پیادهسازی واقعی اهمیت دارد، تفاوت بین فراخوانیهای موازی (Parallel) و ترتیبی (Sequential) است. اگر درخواست کاربر «برای این سه لید، اطلاعات تماس را چک کن» باشد، سه فراخوانی crm.get_lead هیچ وابستگی به یکدیگر ندارند و میتوانند همزمان اجرا شوند تا زمان پاسخ کوتاهتر شود. اما وقتی خروجی یک فراخوانی، ورودی فراخوانی بعدی است — مثل مثال بالا که شناسهٔ Deal از گام اول در گام دوم استفاده میشود — اجرای موازی نهتنها کمکی نمیکند، بلکه میتواند باعث خطا شود. تشخیص درست این وابستگی، یکی از وظایف اصلی لایهٔ Orchestration است، نه چیزی که بشود آن را نادیده گرفت.
مشاهدهپذیری؛ چرا باید هر فراخوانی را دید
یک سیستم Tool Calling که فقط کار میکند اما قابل مشاهده نیست، دیر یا زود مشکلساز میشود. تیم فنی باید بتواند برای هر مکالمه ببیند دقیقاً کدام ابزارها با چه ورودیهایی صدا زده شدهاند، هرکدام چقدر طول کشیدهاند و کدامها شکست خوردهاند. این لاگ ساختاریافته دو کاربرد دارد: برای دیباگ کردن رفتار غیرمنتظرهٔ مدل، و برای اثبات به کاربر که واقعاً چه اتفاقی افتاده — همان چیزی که در معماری کلی محصول Opsless به آن لایهٔ Verification میگوییم.
جمعبندی
- Tool Calling راهی است که مدل بهجای متن آزاد، یک اقدام ساختیافته با ورودی مشخص درخواست میکند.
- اجرای واقعی همیشه دست کد میزبان است، نه خود مدل؛ این جداسازی امکان بازبینی و کنترل را میدهد.
- زنجیرههای چندمرحلهای نیاز به مدیریت State بین فراخوانیها دارند تا خروجی یک گام، ورودی گام بعد شود.
- خطا باید به زبان قابلفهم برای مدل برگردد، نه فقط یک کد وضعیت خام.
- Idempotency Key برای هر فراخوانی، از تکرار ناخواسته یک اقدام حساس مثل ساخت لید یا ارسال پیام جلوگیری میکند.
این مقاله بخشی از دورهٔ Opsless است.
