فراخوانی تابع
به مدلها امکان دهید با تعریف توابعی که مدل میتواند فراخوانی کند، با کد سفارشی یا APIهای خارجی شما تعامل داشته باشند.
مقدمه
فراخوانی تابع به مدلهای AvalAI اجازه میدهد تا بر اساس ورودی کاربر، به طور هوشمند تصمیم بگیرند که چه زمانی توابع خاصی را که شما تعریف کردهاید فراخوانی کنند. به جای تولید صرف متن، مدل میتواند یک شی JSON ساختاریافته حاوی آرگومانها را برای فراخوانی یک یا چند تابع شما خروجی دهد. این امر ساخت برنامههایی را امکانپذیر میسازد که میتوانند:
- واکشی دادهها: اطلاعات بلادرنگ (مانند وضعیت آب و هوا، قیمت سهام) یا دادهها را از پایگاههای دانش داخلی خود بازیابی کنند تا پاسخ مدل را غنیتر سازند (RAG).
- انجام عملیات: عملیاتی مانند ارسال ایمیل، بهروزرسانی پایگاه داده، فراخوانی سرویسهای خارجی یا تعامل با رابط کاربری یا بکاند برنامه شما را انجام دهند.
(منبع نمودار: OpenAI)
مراحل فراخوانی تابع
گردش کار معمول برای استفاده از فراخوانی تابع با AvalAI به شرح زیر است:
- تعریف توابع: لیستی از توابع موجود (ابزارها) شامل نام، توضیحات و طرحواره یا اسکیمای پارامترهای آنها را در درخواست API خود به مدل ارائه دهید. به تعریف توابع (پارامتر
tools) مراجعه کنید. - تصمیمگیری مدل: مدل ورودی کاربر را پردازش میکند و تصمیم میگیرد که آیا فراخوانی یک یا چند تابع مناسب است یا خیر. در صورت مثبت بودن، یک شی
tool_callsدر پیام پاسخ برمیگرداند. - اجرای تابع: کد برنامه شما پیام
tool_callsرا تجزیه میکند، تابع(های) مشخص شده را با آرگومانهای ارائه شده اجرا میکند و نتیجه(ها) را بازیابی میکند. به مدیریت فراخوانیهای تابع (tool_calls) مراجعه کنید. - ارسال نتیجه بازگشتی: مدل را دوباره فراخوانی کنید، پیام دستیار اصلی (با
tool_calls) و پیام(های) جدید با نقشtoolحاوی نتایج تابع را به آن اضافه کنید. به ارسال نتایج بازگشتی (نقش:"tool") مراجعه کنید. - پاسخ مدل: مدل نتیجه(های) تابع را در پاسخ نهایی خود به کاربر لحاظ میکند.
مسیر مناسب Tool Calling را انتخاب کنید
منطق کسبوکار در هر دو route یکسان است، اما wire format را بر اساس app خود انتخاب کنید:
| کاربرد | Route پیشنهادی | دلیل |
|---|---|---|
| integration چت موجود | /v1/chat/completions | messages، tool_calls و پیام نتیجه با role: "tool" را با کمترین migration حفظ میکند. |
| workflow عاملمحور جدید | /v1/responses | آیتمهای تایپشده response.output، آیتم function_call_output، آیتمهای reasoning و مسیر تمیزتر برای workflowهای stateful میدهد. |
| کاتالوگ بزرگ ابزار | /v1/responses در صورت پشتیبانی route | namespaceها را با tool_search ترکیب کنید یا وقتی tool_search در دسترس نیست، لیست مستقیم tools را کوچک نگه دارید. |
| عملیات state-changing یا پولی | هر دو route | از strict: true استفاده کنید، argumentها را در کد validate کنید و وقتی ترتیب یا تایید مهم است parallel_tool_calls: false بگذارید. |
در Responses با مدلهای دارای reasoning، وقتی conversation را دستی ادامه میدهید، آیتمهای reasoning و function-call برگشتی را حفظ کنید. حذف این آیتمها میتواند باعث شود turn بعدی context مربوط به استفاده از ابزار را از دست بدهد. برای appهای Chat Completions موجود، جریان قدیمی /v1/chat/completions را نگه دارید و بهجای جایگزینی یکباره کد سالم، هنگام migration نسخه Responses را کنار آن اضافه کنید.
چکلیست Schema برای Production
پیش از انتشار workflow دارای ابزار، contract تابع را هم از دید مدل و هم از دید handler برنامه بررسی کنید:
- نام action را واضح بگذارید: نام و توضیح تابع را مشخص بنویسید و format هر parameter و معنای خروجی ابزار را توضیح دهید.
- Contract سختگیرانه را ترجیح دهید:
strict: trueبگذارید، ازadditionalProperties: falseاستفاده کنید، همه propertyها را درrequiredبیاورید و فیلدهای اختیاری را با union شامل null مثل["string", "null"]نمایش دهید. - مقادیر شناختهشده برنامه را در کد نگه دارید: از مدل نخواهید IDها، مجوزها، قیمتها یا state انتخابشده در UI را که برنامه از قبل میداند حدس بزند؛ این مقادیر را در handler تزریق کنید.
- دوباره در سمت سرور validate کنید:
argumentsتولیدشده را JSON غیرقابل اعتماد بدانید. آن را parse، validate و authorize کنید و بهجای اجرای کورکورانه، خطای ساختاریافته برگردانید. - مجموعه ابزار فعال را محدود کنید: لیست اولیه
toolsرا کوچک نگه دارید، catalogهای بزرگ را با namespace گروهبندی کنید و فقط وقتی route/model انتخابی AvalAI پشتیبانی میکند ازtool_searchاستفاده کنید. - Side effectها را کنترل کنید: برای write، payment، inventory، approval یا workflowهای ترتیبی
parallel_tool_calls: falseبگذارید؛ در Responses مقدارهای متناظرcall_idو آیتمهای reasoning/function-call را بین turnها حفظ کنید.
مثال: دریافت وضعیت آب و هوا
بیایید با یک تابع get_current_weather این موضوع را نشان دهیم.
مرحله ۱ و ۲: تعریف تابع و فراخوانی مدل
یک درخواست به API تکمیل چت شامل تعریف(های) تابع در پارامتر tools ارسال کنید.
TOOLS_JSON=$(
cat <<'JSON'
[
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "دریافت وضعیت آب و هوای فعلی در یک مکان مشخص",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "شهر و استان، به عنوان مثال San Francisco, CA"
},
"unit": {
"type": ["string", "null"],
"enum": ["celsius", "fahrenheit", null]
}
},
"required": ["location", "unit"],
"additionalProperties": false
},
"strict": true
}
}
]
JSON
)
curl https://api.avalai.ir/v1/chat/completions \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [
{"role": "user", "content": "هوای بوستون چطور است؟"}
],
"tools": '"$TOOLS_JSON"',
"tool_choice": "auto"
}'import os
from openai import OpenAI
import json
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1", # آدرس پایه
)
# تعریف طرحواره یا اسکیمای تابع
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "دریافت وضعیت آب و هوای فعلی در یک مکان مشخص",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "شهر و استان، به عنوان مثال San Francisco, CA",
},
"unit": {
"type": ["string", "null"],
"enum": ["celsius", "fahrenheit", None],
},
},
"required": ["location", "unit"],
"additionalProperties": False,
},
"strict": True,
},
}
]
messages = [{"role": "user", "content": "هوای بوستون چطور است؟"}] # پیام کاربر به فارسی
try:
response = client.chat.completions.create(
model="gpt-5.5", # از مدلی استفاده کنید که از فراخوانی تابع از طریق AvalAI پشتیبانی میکند
messages=messages,
tools=tools,
tool_choice="auto", # پیشفرض: اجازه دهید مدل تصمیم بگیرد
)
response_message = response.choices[0].message
tool_calls = response_message.tool_calls
# منطق مرحله ۳ در ادامه میآید...
if tool_calls:
print("مدل میخواهد توابع زیر را فراخوانی کند:")
print(tool_calls)
# response_message و tool_calls را برای مرحله ۳ و ۴ ذخیره کنید
else:
print("مدل درخواست فراخوانی تابع نداد.")
print(response_message.content)
except Exception as e:
print(f"یک خطای API رخ داد: {e}")import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.AVALAI_API_KEY,
baseURL: "https://api.avalai.ir/v1", // از URL پایه AvalAI استفاده کنید
});
async function callWeatherFunction() {
const tools = [
{
type: "function",
function: {
name: "get_current_weather",
description: "دریافت وضعیت آب و هوای فعلی در یک مکان مشخص",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "شهر و استان، به عنوان مثال San Francisco, CA",
},
unit: {
type: ["string", "null"],
enum: ["celsius", "fahrenheit", null],
},
},
required: ["location", "unit"],
additionalProperties: false,
},
strict: true,
},
},
];
const messages = [{ role: "user", content: "هوای بوستون چطور است؟" }]; // پیام کاربر به فارسی
try {
const response = await client.chat.completions.create({
model: "gpt-5.5", // از مدلی استفاده کنید که از فراخوانی تابع از طریق AvalAI پشتیبانی میکند
messages: messages,
tools: tools,
tool_choice: "auto", // پیشفرض: اجازه دهید مدل تصمیم بگیرد
});
const responseMessage = response.choices[0].message;
const toolCalls = responseMessage.tool_calls;
// منطق مرحله ۳ در ادامه میآید...
if (toolCalls) {
console.log("مدل میخواهد توابع زیر را فراخوانی کند:");
console.log(toolCalls);
// responseMessage و toolCalls را برای مرحله ۳ و ۴ ذخیره کنید
} else {
console.log("مدل درخواست فراخوانی تابع نداد.");
console.log(responseMessage.content);
}
} catch (error) {
console.error("یک خطای API رخ داد:", error);
}
}
callWeatherFunction();package main
import (
"context"
"fmt"
"os"
openai "github.com/openai/openai-go"
)
func main() {
apiKey := os.Getenv("AVALAI_API_KEY")
baseURL := "https://api.avalai.ir/v1" // از URL پایه AvalAI استفاده کنید
config := openai.DefaultConfig(apiKey)
config.BaseURL = baseURL
client := openai.NewClientWithConfig(config)
tools := []openai.Tool{
{
Type: openai.ToolTypeFunction,
Function: &openai.FunctionDefinition{
Name: "get_current_weather",
Description: "دریافت وضعیت آب و هوای فعلی در یک مکان مشخص",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"location": map[string]interface{}{
"type": "string",
"description": "شهر و استان، به عنوان مثال San Francisco, CA",
},
"unit": map[string]interface{}{
"type": []string{"string", "null"},
"enum": []interface{}{"celsius", "fahrenheit", nil},
},
},
"required": []string{"location", "unit"},
"additionalProperties": false,
},
},
},
}
messages := []openai.ChatCompletionMessage{
{Role: openai.ChatMessageRoleUser, Content: "هوای بوستون چطور است؟"}, // پیام کاربر به فارسی
}
resp, err := client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: "gpt-5.5", // از مدلی استفاده کنید که از فراخوانی تابع از طریق AvalAI پشتیبانی میکند
Messages: messages,
Tools: tools,
ToolChoice: "auto", // پیشفرض: اجازه دهید مدل تصمیم بگیرد
},
)
if err != nil {
fmt.Printf("خطای تکمیل چت: %v\n", err)
return
}
responseMessage := resp.Choices[0].Message
toolCalls := responseMessage.ToolCalls
// منطق مرحله ۳ در ادامه میآید...
if len(toolCalls) > 0 {
fmt.Println("مدل میخواهد توابع زیر را فراخوانی کند:")
// برای مرحله ۳ و ۴ در toolCalls حلقه بزنید
for _, toolCall := range toolCalls {
fmt.Printf(" شناسه: %s, نوع: %s, تابع: %s, آرگومانها: %s\n",
toolCall.ID, toolCall.Type, toolCall.Function.Name, toolCall.Function.Arguments)
}
// responseMessage و toolCalls را برای مرحله ۳ و ۴ ذخیره کنید
} else {
fmt.Println("مدل درخواست فراخوانی تابع نداد.")
fmt.Println(responseMessage.Content)
}
}<?php
require 'vendor/autoload.php'; // اطمینان حاصل کنید که کلاینت PHP OpenAI نصب شده است
$apiKey = getenv('AVALAI_API_KEY');
$baseURL = 'https://api.avalai.ir/v1'; // از URL پایه AvalAI استفاده کنید
$client = OpenAI::client($apiKey);
// توجه: تنظیم URL پایه ممکن است به نسخه خاص کتابخانه کلاینت PHP بستگی داشته باشد.
// مستندات کتابخانه خود را بررسی کنید. برخی ممکن است از یک فکتوری یا شی پیکربندی استفاده کنند.
// مثال با استفاده از پیکربندی factory-style:
// $client = OpenAI::factory()
// ->withApiKey($apiKey)
// ->withBaseUri($baseURL)
// ->make();
$tools = [
[
'type' => 'function',
'function' => [
'name' => 'get_current_weather',
'description' => 'دریافت وضعیت آب و هوای فعلی در یک مکان مشخص',
'parameters' => [
'type' => 'object',
'properties' => [
'location' => [
'type' => 'string',
'description' => 'شهر و استان، به عنوان مثال San Francisco, CA',
],
'unit' => [
'type' => ['string', 'null'],
'enum' => ['celsius', 'fahrenheit', null],
],
],
'required' => ['location', 'unit'],
'additionalProperties' => false,
],
'strict' => true,
],
]
];
$messages = [['role' => 'user', 'content' => "هوای بوستون چطور است؟"]]; // پیام کاربر به فارسی
try {
$response = $client->chat()->create([
'model' => 'gpt-5.5', // از مدلی استفاده کنید که از فراخوانی تابع از طریق AvalAI پشتیبانی میکند
'messages' => $messages,
'tools' => $tools,
'tool_choice' => 'auto', // پیشفرض: اجازه دهید مدل تصمیم بگیرد
]);
$responseMessage = $response->choices[0]->message;
$toolCalls = $responseMessage->toolCalls ?? null; // برای ایمنی از null coalescing استفاده کنید
// منطق مرحله ۳ در ادامه میآید...
if ($toolCalls) {
echo "مدل میخواهد توابع زیر را فراخوانی کند:\n";
print_r($toolCalls); // یا در آنها حلقه بزنید
// $responseMessage و $toolCalls را برای مرحله ۳ و ۴ ذخیره کنید
} else {
echo "مدل درخواست فراخوانی تابع نداد.\n";
echo $responseMessage->content;
}
} catch (Exception $e) {
echo "یک خطای API رخ داد: " . $e->getMessage() . "\n";
}نسخه معادل Responses API
وقتی مدل انتخابی از /v1/responses پشتیبانی میکند، این نسخه را کنار مثال Chat Completions استفاده کنید. در Responses، ابزار تابع بهصورت داخلی tag میشود: type، name، description، parameters و strict روی خود شی ابزار قرار میگیرند. برای پیدا کردن فراخوانی تابع، آیتمهای response.output را بر اساس type بررسی کنید.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
tools = [
{
"type": "function",
"name": "get_current_weather",
"description": "Get the current weather in a given location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. Boston, MA",
}
},
"required": ["location"],
"additionalProperties": False,
},
"strict": True,
}
]
response = client.responses.create(
model="gpt-5.5",
input="هوای بوستون چطور است؟",
tools=tools,
)
for item in response.output:
if item.type == "function_call":
print("Function:", item.name)
print("Arguments:", item.arguments)import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.AVALAI_API_KEY,
baseURL: "https://api.avalai.ir/v1",
});
const tools = [
{
type: "function",
name: "get_current_weather",
description: "Get the current weather in a given location.",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g. Boston, MA",
},
},
required: ["location"],
additionalProperties: false,
},
strict: true,
},
];
const response = await client.responses.create({
model: "gpt-5.5",
input: "هوای بوستون چطور است؟",
tools,
});
for (const item of response.output) {
if (item.type === "function_call") {
console.log("Function:", item.name);
console.log("Arguments:", item.arguments);
}
}curl https://api.avalai.ir/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-d '{
"model": "gpt-5.5",
"input": "هوای بوستون چطور است؟",
"tools": [
{
"type": "function",
"name": "get_current_weather",
"description": "Get the current weather in a given location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. Boston, MA"
}
},
"required": ["location"],
"additionalProperties": false
},
"strict": true
}
]
}'messages→input- پیکربندی ابزار در Chat Completions یعنی
{ type: "function", function: {...} }→ پیکربندی ابزار در Responses یعنی{ type: "function", name, description, parameters, strict } choices[0].message.tool_calls→ آیتمهایresponse.outputباtype: "function_call"- آرگومانهای ابزار همچنان رشته JSON هستند؛ قبل از فراخوانی کد خود آنها را parse کنید.
خروجی مورد انتظار مدل (درخواست فراخوانی تابع):
اگر مدل تصمیم به فراخوانی تابع بگیرد، فیلد tool_calls در پیام پاسخ (مثلا response_message.tool_calls در پایتون/جاوااسکریپت، resp.Choices[0].Message.ToolCalls در Go) چیزی شبیه به این خواهد بود:
[
{
"id": "call_abc123", // شناسه منحصر به فرد برای این فراخوانی خاص
"type": "function",
"function": {
"name": "get_current_weather",
"arguments": "{\"location\": \"Boston, MA\", \"unit\": \"fahrenheit\"}" // آرگومانها به صورت رشته JSON
}
}
](توجه: فیلد id برای تطبیق فراخوانی با نتیجه آن در مرحله ۴ حیاتی است)
مرحله ۳: اجرای تابع
کد برنامه شما باید فراخوانی تابع را بر اساس name و arguments ارائه شده در tool_calls مدیریت کند.
(توجه: کد پایتون زیر منطق را نشان میدهد. شما باید منطق مشابهی را در زبان انتخابی خود (جاوااسکریپت، Go، PHP و غیره) با استفاده از tool_calls دریافت شده در مرحله ۲ پیادهسازی کنید.)
# تابع جایگزین برای پیادهسازی واقعی شما در پایتون
import json
def get_current_weather(location, unit="fahrenheit"):
"""تابع ساختگی برای دریافت وضعیت آب و هوا"""
unit = unit or "fahrenheit"
print(
f"--- تابع get_current_weather(location='{location}', unit='{unit}') فراخوانی شد ---"
)
if not isinstance(location, str):
raise ValueError("مقدار location باید رشته باشد")
if not location.strip():
raise ValueError("مقدار location نمیتواند خالی باشد")
if "boston" in location.lower():
weather_info = {
"location": location,
"temperature": "72",
"unit": unit,
"forecast": "آفتابی",
}
else:
weather_info = {"location": location, "temperature": "نامشخص"}
return json.dumps(weather_info, ensure_ascii=False)
def process_tool_calls(tool_calls, available_functions):
"""پردازش فراخوانیهای ابزار و تولید نتایج"""
if not tool_calls:
return []
results_for_next_call = []
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions.get(function_name)
result = {"tool_call_id": tool_call.id, "role": "tool", "name": function_name}
if function_to_call:
try:
function_args = json.loads(tool_call.function.arguments)
function_response = function_to_call(**function_args)
result["content"] = function_response
except json.JSONDecodeError:
error_msg = "خطا در تجزیه پارامترهای ورودی"
result["content"] = json.dumps({"error": error_msg}, ensure_ascii=False)
print(f"خطا: {error_msg}")
except Exception as e:
error_msg = f"خطا در اجرای تابع {function_name}: {str(e)}"
result["content"] = json.dumps({"error": error_msg}, ensure_ascii=False)
print(f"خطا: {error_msg}")
else:
error_msg = f"تابع {function_name} پیادهسازی نشده است"
result["content"] = json.dumps({"error": error_msg}, ensure_ascii=False)
print(f"خطا: {error_msg}")
results_for_next_call.append(result)
return results_for_next_call
# تنظیم توابع در دسترس
available_functions = {
"get_current_weather": get_current_weather,
}
# اطمینان از وجود متغیرهای مورد نیاز
if "messages" not in locals():
messages = []
if "response_message" in locals():
messages.append(response_message)
# پردازش فراخوانیهای ابزار
if "tool_calls" in locals():
results_for_next_call = process_tool_calls(tool_calls, available_functions)
print("\nنتایج آماده شده برای فراخوانی API بعدی:")
print(json.dumps(results_for_next_call, ensure_ascii=False, indent=2))مرحله ۴ و ۵: ارسال نتایج بازگشتی و دریافت پاسخ نهایی
نتیجه(های) تابع را به عنوان پیام(های) جدید با نقش role: "tool" به تاریخچه مکالمه خود اضافه کنید و یک فراخوانی API دیگر انجام دهید. مدل از نتایج برای تولید پاسخ نهایی خود استفاده خواهد کرد.
# فرض کنید $ASSISTANT_MSG_JSON حاوی رشته JSON پیام دستیار از مرحله ۲ است
# فرض کنید $TOOL_RESULTS_JSON حاوی رشته آرایه JSON پیامهای نتیجه ابزار از مرحله ۳ است
# فرض کنید $USER_MSG_JSON حاوی رشته JSON پیام اصلی کاربر است
# رشته JSON آرایه کامل پیامها را بسازید
MESSAGES_JSON=$(echo "[$USER_MSG_JSON, $ASSISTANT_MSG_JSON]" | jq -c '. + '"$TOOL_RESULTS_JSON")
curl https://api.avalai.ir/v1/chat/completions \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": '"$MESSAGES_JSON"'
}'# فرض کنید 'messages' حاوی تاریخچه تا پیام tool_calls دستیار است
# فرض کنید 'results_for_next_call' حاوی لیست پیامهای نتیجه ابزار از مرحله ۳ است
if results_for_next_call:
messages.extend(results_for_next_call) # نتایج ابزار را به تاریخچه پیام اضافه کنید
print("\nدر حال ارسال نتایج به مدل...")
try:
second_response = client.chat.completions.create(
model="gpt-5.5",
messages=messages,
# در اینجا نیازی به ابزار نیست مگر اینکه بخواهید فراخوانیهای بعدی انجام شود
)
# مرحله ۵: دریافت پاسخ نهایی
final_response = second_response.choices[0].message.content
print("\nپاسخ نهایی مدل:")
print(final_response)
except Exception as e:
print(f"یک خطای API در فراخوانی دوم رخ داد: {e}")// فرض کنید 'messages' حاوی تاریخچه تا پیام tool_calls دستیار است
// فرض کنید 'resultsForNextCall' حاوی آرایه پیامهای نتیجه ابزار از مرحله ۳ است
async function sendResultsAndGetResponse(messages, resultsForNextCall) {
if (resultsForNextCall && resultsForNextCall.length > 0) {
// نتایج ابزار را به تاریخچه پیام اضافه کنید
const updatedMessages = messages.concat(resultsForNextCall);
console.log("\nدر حال ارسال نتایج به مدل...");
try {
const secondResponse = await client.chat.completions.create({
model: "gpt-5.5",
messages: updatedMessages,
// در اینجا نیازی به ابزار نیست مگر اینکه بخواهید فراخوانیهای بعدی انجام شود
});
// مرحله ۵: دریافت پاسخ نهایی
const finalResponse = secondResponse.choices[0].message.content;
console.log("\nپاسخ نهایی مدل:");
console.log(finalResponse);
} catch (error) {
console.error("یک خطای API در فراخوانی دوم رخ داد:", error);
}
}
}
// مثال استفاده (با فرض اینکه messages و resultsForNextCall از مراحل قبلی پر شدهاند)
// اطمینان حاصل کنید که 'messages' شامل پیام کاربر و پیام دستیار با tool_calls است
// sendResultsAndGetResponse(messages, resultsForNextCall);// فرض کنید 'messages' حاوی تاریخچه تا پیام tool_calls دستیار است
// فرض کنید 'resultsForNextCall' حاوی اسلایس پیامهای نتیجه ابزار از مرحله ۳ است
func sendResultsAndGetResponse(client *openai.Client, messages []openai.ChatCompletionMessage, resultsForNextCall []openai.ChatCompletionMessage) {
if len(resultsForNextCall) > 0 {
// نتایج ابزار را به تاریخچه پیام اضافه کنید
messages = append(messages, resultsForNextCall...)
fmt.Println("\nدر حال ارسال نتایج به مدل...")
resp, err := client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: "gpt-5.5",
Messages: messages,
// در اینجا نیازی به ابزار نیست مگر اینکه بخواهید فراخوانیهای بعدی انجام شود
},
)
if err != nil {
fmt.Printf("خطای تکمیل چت در فراخوانی دوم: %v\n", err)
return
}
// مرحله ۵: دریافت پاسخ نهایی
finalResponse := resp.Choices[0].Message.Content
fmt.Println("\nپاسخ نهایی مدل:")
fmt.Println(finalResponse)
}
}
// مثال استفاده (با فرض اینکه client, messages و resultsForNextCall پر شدهاند)
// اطمینان حاصل کنید که 'messages' شامل پیام کاربر و پیام دستیار با tool_calls است
// sendResultsAndGetResponse(client, messages, resultsForNextCall)<?php
// فرض کنید $messages حاوی تاریخچه تا پیام tool_calls دستیار است
// فرض کنید $resultsForNextCall حاوی آرایه پیامهای نتیجه ابزار از مرحله ۳ است
if (!empty($resultsForNextCall)) {
// نتایج ابزار را به تاریخچه پیام اضافه کنید
$updatedMessages = array_merge($messages, $resultsForNextCall);
echo "\nدر حال ارسال نتایج به مدل...\n";
try {
$secondResponse = $client->chat()->create([
'model' => 'gpt-5.5',
'messages' => $updatedMessages,
// در اینجا نیازی به ابزار نیست مگر اینکه بخواهید فراخوانیهای بعدی انجام شود
]);
// مرحله ۵: دریافت پاسخ نهایی
$finalResponse = $secondResponse->choices[0]->message->content;
echo "\nپاسخ نهایی مدل:\n";
echo $finalResponse . "\n";
} catch (Exception $e) {
echo "یک خطای API در فراخوانی دوم رخ داد: " . $e->getMessage() . "\n";
}
}نسخه معادل Responses API
برای بازگرداندن نتیجه ابزار از آیتمهای function_call_output استفاده کنید. مقدار call_id باید با call_id آیتم function_call خروجی مدل یکی باشد؛ سپس دوباره /v1/responses را با آیتمهای خروجی قبلی مدل و نتیجه ابزار فراخوانی کنید.
import json
input_items = [{"role": "user", "content": "هوای بوستون چطور است؟"}]
response = client.responses.create(
model="gpt-5.5",
input=input_items,
tools=tools,
)
input_items += response.output
for item in response.output:
if item.type != "function_call":
continue
if item.name == "get_current_weather":
args = json.loads(item.arguments)
tool_result = get_current_weather(**args)
input_items.append(
{
"type": "function_call_output",
"call_id": item.call_id,
"output": tool_result,
}
)
final_response = client.responses.create(
model="gpt-5.5",
input=input_items,
tools=tools,
)
print(final_response.output_text)let input = [{ role: "user", content: "هوای بوستون چطور است؟" }];
let response = await client.responses.create({
model: "gpt-5.5",
input,
tools,
});
input = input.concat(response.output);
for (const item of response.output) {
if (item.type !== "function_call") continue;
if (item.name === "get_current_weather") {
const args = JSON.parse(item.arguments);
const toolResult = getCurrentWeather(args.location);
input.push({
type: "function_call_output",
call_id: item.call_id,
output: toolResult,
});
}
}
const finalResponse = await client.responses.create({
model: "gpt-5.5",
input,
tools,
});
console.log(finalResponse.output_text);# در فراخوانی اول Responses، آیتم function_call و call_id را بگیرید.
# سپس آیتم خروجی مدل را همراه function_call_output متناظر ارسال کنید.
curl https://api.avalai.ir/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-d '{
"model": "gpt-5.5",
"input": [
{"role": "user", "content": "هوای بوستون چطور است؟"},
{
"type": "function_call",
"call_id": "call_abc123",
"name": "get_current_weather",
"arguments": "{\"location\":\"Boston, MA\"}"
},
{
"type": "function_call_output",
"call_id": "call_abc123",
"output": "{\"temperature\":\"72\",\"unit\":\"fahrenheit\",\"forecast\":\"sunny\"}"
}
],
"tools": [
{
"type": "function",
"name": "get_current_weather",
"description": "Get the current weather in a given location.",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
"additionalProperties": false
},
"strict": true
}
]
}'- Chat Completions نتیجه ابزار را به شکل پیامهای
role: "tool"همراهtool_call_idمیفرستد. - Responses نتیجه ابزار را به شکل آیتم ورودی
type: "function_call_output"همراهcall_idمتناظر میفرستد. - وقتی state را دستی مدیریت میکنید، آیتمهای قبلی
response.outputمدل را حفظ کنید؛ بهویژه آیتمهای reasoning و function-call. - برای مدلهای دارای reasoning، هر آیتم reasoning که همراه فراخوانی ابزار برگشته را همراه خروجی تابع دوباره بفرستید؛ حذف این آیتمها میتواند مرحله reasoning بعدی را خراب کند.
خروجی نهایی مورد انتظار (مرحله ۵):
هوای فعلی در بوستون، MA ۷۲ درجه فارنهایت و آفتابی است.تعریف توابع (پارامتر tools)
شما توابع را در پارامتر tools درخواست API خود تعریف میکنید. هر ابزار از نوع function نیاز به یک طرحواره دارد که هدف و پارامترهای آن را توصیف میکند.
همین schema تابع بسته به route دو شکل wire متفاوت دارد:
| Route | شکل تابع | فراخوانی برگشتی | نتیجه ابزار |
|---|---|---|---|
/v1/chat/completions | با tag بیرونی: { "type": "function", "function": { ... } } | choices[0].message.tool_calls[] | پیام role: "tool" همراه tool_call_id |
/v1/responses | با tag داخلی: { "type": "function", "name": "...", "description": "...", "parameters": {...}, "strict": true } | آیتمی در response.output[] با type: "function_call" | آیتم ورودی با type: "function_call_output" و call_id متناظر |
برای ادغامهای چت موجود از شکل Chat Completions استفاده کنید. برای جریانهای عاملی جدید، مخصوصا وقتی به آیتمهای خروجی تایپشده، زمینه reasoning یا مسیر مهاجرت تمیزتر به ابزارهای داخلی نیاز دارید، شکل Responses را ترجیح دهید.
{
"type": "function",
"function": {
"name": "your_function_name", // نام تابع شما
"description": "توضیح واضحی از کاری که تابع انجام میدهد و زمان استفاده از آن.",
"parameters": {
"type": "object",
"properties": {
"param1": {
"type": "string",
"description": "توضیح پارامتر اول."
},
"param2": {
"type": ["number", "null"],
"description": "توضیح پارامتر دوم. وقتی ارسال نمیشود از null استفاده کنید."
},
"param3": {
"type": "string",
"enum": ["value1", "value2"],
"description": "پارامتر با مقادیر مجاز خاص."
}
},
"required": ["param1", "param2", "param3"],
"additionalProperties": false
},
"strict": true
}
}type: باید"function"باشد.function.name: نامی که کد شما برای شناسایی تابع استفاده خواهد کرد (مثلاget_current_weather).function.description: برای کمک به مدل در درک زمان و دلیل فراخوانی تابع حیاتی است. دقیق باشید.function.parameters: یک شی JSON Schema که آرگومانهایی را که تابع میپذیرد تعریف میکند.type: باید"object"باشد.properties: هر پارامتر را تعریف میکند (نام، نوع، توضیحات، enum اختیاری).required: آرایهای که پارامترهای اجباری را لیست میکند.additionalProperties: false: از ابداع پارامترهای اضافی توسط مدل جلوگیری میکند. توصیه میشود.
function.strict: هر زمان میخواهید مدل بهصورت قابل اعتماد با طرحواره شما منطبق شود، مقدار آن راtrueبگذارید؛ مشابهresponse_formatباjson_schemaدر خروجیهای ساختاریافته. نیاز بهadditionalProperties: falseو لیست شدن تمام ویژگیها درrequiredدارد (برای پارامترهای اختیاری از{"type": ["string", "null"]}استفاده کنید). بخش حالت سختگیرانه را در زیر ببینید.
ابزارهای کمکی برای نوشتن Schema
مستندات OpenAI دو مسیر کمکی برای ساخت schema تابع معرفی میکند: helperهای SDK که objectهای سبک Pydantic/Zod را به schema ابزار تبدیل میکنند، و تولید/تکرار schema در Playground. این ابزارها را شتابدهنده بدانید، نه جایگزین review. schemaهای تولیدشده ممکن است edge caseها را جا بیندازند و همه قابلیتهای Pydantic یا Zod به زیرمجموعه JSON Schema پشتیبانیشده نگاشت نمیشود.
پیش از استفاده از schema تولیدشده در AvalAI:
description،enum،requiredوadditionalPropertiesرا دستی review کنید.- وقتی
strict: trueفعال است، مطمئن شوید فیلدهای اختیاری از union شامل null مثل{"type": ["string", "null"]}استفاده میکنند. - برای آرگومانهای malformed، فیلدهای گمشده و شکست ابزار، eval یا تست integration نماینده اضافه کنید.
- تعریف type اصلی، schema تولیدشده و validator سمت سرور را همگام نگه دارید تا مدل و handler شما روی یک contract مشترک توافق داشته باشند.
بهترین شیوهها برای تعریف توابع
- توضیحات واضح: برای تابع و هر پارامتر توضیح دقیق بنویسید. هدف، قالب مورد انتظار، عوارض جانبی و معنای مقدار برگشتی را توضیح دهید.
- قواعد استفاده در پیام developer: به مدل بگویید چه زمانی از تابع استفاده کند، چه زمانی استفاده نکند و وقتی اطلاعات لازم وجود ندارد چه کند.
- آزمون کارآموز: یک کارآموز انسانی باید بتواند فقط با نام، توضیح و schema تابع را درست صدا بزند. اگر سؤال میپرسد، پاسخ آن را به توضیح یا پرامپت اضافه کنید.
- طراحی شهودی: تابعها را واضح و سختاشتباه طراحی کنید. مثلا
refund_order({reason})بهتر از booleanهای جدا مثلrefund: trueوdo_not_refund: falseاست. - استفاده از enum و schema سختگیرانه: برای انتخابهای ثابت از
enumاستفاده کنید؛ برای ابزارهای production ازstrict: true،additionalProperties: falseو فیلدهای required همراه union شاملnullبرای مقدارهای اختیاری استفاده کنید. - ترکیب فراخوانیهای متوالی: اگر همیشه تابع B را بعد از تابع A فراخوانی میکنید، آنها را در یک ابزار ادغام کنید تا مدل مجبور نباشد workflow داخلی شما را یاد بگیرد.
- مدیریت آرگومانهای شناختهشده در کد: مدل را مجبور نکنید آرگومانهایی را که برنامه شما از قبل میداند حدس بزند. مثلا اگر UI از قبل
order_idرا انتخاب کرده، آن را از schema حذف کنید و در handler تزریق کنید. - بازگرداندن خروجی مفید ابزار: JSON کوتاه یا متن سادهای برگردانید که facts مورد نیاز مدل را داشته باشد و هنگام شکست ابزار، کد خطا یا توضیح کوتاه بدهد. برای ابزارهایی که خروجی طبیعی ندارند، یک رشته کوتاه موفقیت/شکست کافی است.
مقیاسدادن سطح ابزارهای بزرگتر
هر تعریف ابزار بخشی از context را مصرف میکند و میتواند دقت انتخاب ابزار را پایین بیاورد. فهرست ابزارهای فعال را کوچک نگه دارید و با اضافه شدن ابزارها، دقت را eval کنید.
- تعریف ابزارها از پنجره context مدل مصرف میکند و بهعنوان input token محاسبه میشود، پس توضیحها را مفید اما فشرده نگه دارید.
- در شروع هر نوبت، کمتر از حدود ۲۰ تابع فعال را هدف بگیرید.
- ابزارهای مرتبط را در کد خود بر اساس دامنه گروهبندی کنید (
billing،crm،shipping) تا پرامپتها و handlerها قابل فهم بمانند. - وقتی یک route/model در AvalAI بارگذاری deferred مثل
tool_searchرا ارائه میدهد، توضیح namespace را کوتاه نگه دارید و راهنمای دقیق استفاده را در توضیح تابعهایی قرار دهید که بعدا load میشوند. - از
allowed_toolsبرای محدود کردن ابزارهای قابل فراخوانیِ از قبل load شده استفاده کنید، بدون اینکه کل فهرستtoolsرا تغییر دهید؛ این الگو در صورت پشتیبانی میتواند prompt caching را پایدارتر نگه دارد. - در
tool_searchاجراشده توسط client، هنگام برگرداندنtool_search_outputمقدارtool_search_call.call_idرا حفظ کنید؛ در جستجوی hosted انتظارexecution: "server"وcall_id: nullداشته باشید. - از
additional_toolsفقط وقتی استفاده کنید که ابزارها باید در نقطه مشخصی از state مکالمه replay شده در دسترس شوند، و در turnهای بعدی ترتیب همان آیتم را حفظ کنید. - برای عملیات state-changing یا پولی،
parallel_tool_calls: falseرا تنظیم کنید و در هر نوبت فقط یک تصمیم ابزار صریح بخواهید.
Namespaceها و بارگذاری Deferred ابزارها
برای برنامههای بزرگ، ابزارهای مرتبط را در namespaceهایی مثل crm، billing، shipping یا support گروهبندی کنید. Namespace یک نقشه فشرده از دامنه به مدل میدهد، در حالی که قواعد دقیق استفاده کنار توضیح هر تابع باقی میماند. وقتی route انتخابی AvalAI از الگوی OpenAI برای tool_search پشتیبانی کند، میتوانید تابعهایی را که کمتر استفاده میشوند با defer_loading: true علامتگذاری کنید تا مدل فقط هنگام نیاز آنها را load کند.
این الگو برای زمانی مناسب است که ابزارهای زیادی دارید، schemaها بزرگ هستند یا چند سیستم backend با نامهای شبیه به هم دارید. توضیح namespace را کوتاه نگه دارید، توضیح تابعها را دقیق بنویسید و در کد برنامه، handlerها را بر اساس namespace و نام تابع route کنید.
from openai import OpenAI
import os
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
crm_namespace = {
"type": "namespace",
"name": "crm",
"description": "CRM tools for customer lookup and order management.",
"tools": [
{
"type": "function",
"name": "get_customer_profile",
"description": "Fetch a customer profile by customer ID.",
"parameters": {
"type": "object",
"properties": {"customer_id": {"type": "string"}},
"required": ["customer_id"],
"additionalProperties": False,
},
"strict": True,
},
{
"type": "function",
"name": "list_open_orders",
"description": "List open orders for a customer ID.",
"defer_loading": True,
"parameters": {
"type": "object",
"properties": {"customer_id": {"type": "string"}},
"required": ["customer_id"],
"additionalProperties": False,
},
"strict": True,
},
],
}
response = client.responses.create(
model="gpt-5.5",
input="سفارشهای باز مشتری CUST-12345 را فهرست کن.",
tools=[crm_namespace, {"type": "tool_search"}],
parallel_tool_calls=False,
)
for item in response.output:
print(item)import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.AVALAI_API_KEY,
baseURL: "https://api.avalai.ir/v1",
});
const crmNamespace = {
type: "namespace",
name: "crm",
description: "CRM tools for customer lookup and order management.",
tools: [
{
type: "function",
name: "get_customer_profile",
description: "Fetch a customer profile by customer ID.",
parameters: {
type: "object",
properties: { customer_id: { type: "string" } },
required: ["customer_id"],
additionalProperties: false,
},
strict: true,
},
{
type: "function",
name: "list_open_orders",
description: "List open orders for a customer ID.",
defer_loading: true,
parameters: {
type: "object",
properties: { customer_id: { type: "string" } },
required: ["customer_id"],
additionalProperties: false,
},
strict: true,
},
],
};
const response = await client.responses.create({
model: "gpt-5.5",
input: "سفارشهای باز مشتری CUST-12345 را فهرست کن.",
tools: [crmNamespace, { type: "tool_search" }],
parallel_tool_calls: false,
});
console.log(response.output);curl https://api.avalai.ir/v1/responses \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"input": "سفارشهای باز مشتری CUST-12345 را فهرست کن.",
"parallel_tool_calls": false,
"tools": [
{
"type": "namespace",
"name": "crm",
"description": "CRM tools for customer lookup and order management.",
"tools": [
{
"type": "function",
"name": "get_customer_profile",
"description": "Fetch a customer profile by customer ID.",
"parameters": {
"type": "object",
"properties": {"customer_id": {"type": "string"}},
"required": ["customer_id"],
"additionalProperties": false
},
"strict": true
},
{
"type": "function",
"name": "list_open_orders",
"description": "List open orders for a customer ID.",
"defer_loading": true,
"parameters": {
"type": "object",
"properties": {"customer_id": {"type": "string"}},
"required": ["customer_id"],
"additionalProperties": false
},
"strict": true
}
]
},
{"type": "tool_search"}
]
}'اگر مدل یا route انتخابی از tool_search پشتیبانی نمیکند، همچنان از یک آرایه کوچکتر tools بهصورت مستقیم استفاده کنید. با این حال میتوانید handlerها را در کد خود بر اساس namespace سازماندهی کنید و با allowed_tools و parallel_tool_calls: false محدوده هر نوبت را محدود نگه دارید.
Tool search زمانی بیشترین ارزش را دارد که کاتالوگ اولیه تابعها آنقدر بزرگ است که latency، هزینه یا دقت انتخاب ابزار را بدتر میکند. OpenAI مستند کرده که ابزارهای load شده نزدیک انتهای context اضافه میشوند تا cache حفظ شود؛ بنابراین توضیح namespaceها را پایدار نگه دارید و مجموعه ابزارهای load شده را در میانه مکالمه تغییر ندهید، مگر اینکه برنامه شما عمدا مجموعه قابلیتهای در دسترس را تغییر داده باشد.
ابزارهای سفارشی و Grammarها (وابسته به Route)
در مستندات OpenAI برای Responses API، custom tools هم معرفی شدهاند؛ یعنی ابزارهایی که به جای آرگومان JSON، یک payload متن آزاد دریافت میکنند. این الگو برای اجرای کد، تولید SQL، تولید config یا runtimeهایی مفید است که بستهبندی payload در JSON برایشان اصطکاک ایجاد میکند. در AvalAI، دسترسی به این قابلیت به مدل و route وابسته است؛ برای بیشترین سازگاری، تا وقتی route انتخابی صریحا custom tools را پشتیبانی نکرده، از ابزارهای تابع مبتنی بر schema استفاده کنید.
اگر custom tools فعال بود، input متنی آن را مثل آرگومان JSON غیرقابل اعتماد بدانید: نام ابزارها را allowlist کنید، payload را validate یا sandbox کنید و هیچ کد یا SQL تولیدشده توسط مدل را بدون بررسی policy روی سیستم production اجرا نکنید. وقتی route از ابزارهای سفارشی محدودشده با grammar پشتیبانی میکند، grammarهای کوچک و صریح (lark یا regex) را ترجیح دهید و پیش از ترافیک production با promptهای نماینده تست کنید.
Grammarها را ساده و bounded نگه دارید. پشتیبانی grammar در OpenAI از lark یا regex استفاده میکند؛ از lookaroundها، modifierهای lazy در regex، قواعد %ignore بیش از حد باز و free-text spanهای نامحدود دوری کنید. syntax مربوط به regex grammar شبیه رفتار regex در Rust است، نه ماژول re پایتون؛ بنابراین patternها را قبل از ترافیک production تست کنید.
مدیریت فراخوانیهای تابع
handler خود را همیشه طوری بنویسید که مدل بتواند در یک نوبت صفر، یک یا چند فراخوانی ابزار برگرداند. نام فیلدها بر اساس route متفاوت است:
- در Chat Completions، پیام پاسخ مدل (
response.choices[0].messageدر پایتون/جاوااسکریپت،resp.Choices[0].Messageدر Go) ممکن استtool_callsداشته باشد. - در Responses، آیتمهای
response.outputرا بررسی کنید و دنبال آیتمهایی باشید کهtypeآنهاfunction_callاست. آیتم function call شاملname، رشته JSON درarguments، مقدارcall_idو گاهیnamespaceبرای ابزارهای namespaced است.
در Chat Completions، هر مورد در tool_calls دارای موارد زیر خواهد بود:
id: یک شناسه منحصر به فرد برای این فراخوانی خاص (مثلاcall_abc123). شما باید از این شناسه هنگام ارسال نتیجه بازگشتی در فیلدtool_call_idاستفاده کنید.type:"function"خواهد بود.function: شیئی حاوی:name: نام تابعی که باید فراخوانی شود.arguments: یک رشته JSON حاوی آرگومانها. شما باید این رشته را تجزیه کنید (مثلاjson.loads()در پایتون،JSON.parse()در جاوااسکریپت).
کد شما باید:
- بررسی کند که آیا
tool_callsدر Chat یا آیتمهایfunction_callدر Responses وجود دارند. - در هر آیتم فراخوانی پیمایش کند.
- نام ابزار را شناسایی کند (
function.nameدر Chat وnameدر Responses). - رشته JSON در
argumentsرا به یک شی/دیکشنری بومی تجزیه کند. - تابع برنامه مربوطه خود را با استفاده از آرگومانهای تجزیه شده اجرا کند.
- نتیجه (یا یک پیام خطا) مرتبط با شناسه فراخوانی را ذخیره کند. برای فراخوانی بعدی API، یا پیام Chat با
role: "tool"بسازد یا آیتم Responses از نوعfunction_call_outputآماده کند.
چکلیست Handler ابزار
پیش از اینکه یک فراخوانی ابزار روی داده production اثر بگذارد، این موارد را بررسی کنید:
- تابعها را allowlist کنید: فقط مقدارهای شناختهشده
function.nameرا به کدی که مالک آن هستید route کنید. هیچوقت نام تابعی را که مدل تولید کرده بهصورت پویا evaluate نکنید. - آرگومانها را parse و validate کنید:
argumentsرا متن JSON غیرقابل اعتماد بدانید. آن را parse کنید، فیلدهای required و enumها را دوباره در برنامه اعتبارسنجی کنید و اگر اعتبارسنجی شکست خورد، یک خطای ساختاریافته برگردانید. - چند فراخوانی را مدیریت کنید: handlerها را برای صفر، یک یا چند فراخوانی ابزار بنویسید. اگر ترتیب مهم است یا عملیات پول، موجودی، مجوز یا state قابل مشاهده برای کاربر را تغییر میدهد،
parallel_tool_calls: falseرا تنظیم کنید. - نتیجه کوتاه برگردانید: facts مورد نیاز مدل را بهصورت یک رشته کوتاه یا رشته JSON ارسال کنید. برای عملیات بدون خروجی طبیعی، بهجای مقدار خالی، پیام موفقیت/شکست روشن برگردانید.
- شناسهها را حفظ کنید: در Chat Completions هر نتیجه را با
tool_call_idمتناظر بفرستید. در Responses،function_call_outputرا باcall_idمتناظر ارسال کنید و هنگام مدیریت دستی state، آیتمهای قبلیresponse.outputرا نگه دارید. - برای side effectها gate بگذارید: برای refund، خرید، تغییر حساب، اعلانها و عملیات برگشتناپذیر، پیش از اجرای تابع یک مرحله تایید در محصول اضافه کنید.
طراحی خروجی ابزار
خروجی ابزار ورودی جدید مدل است، پس آن را محدود و صریح نگه دارید. فقط فیلدهایی را برگردانید که مدل برای گام بعدی لازم دارد؛ ردیف خام دیتابیس، secret، stack trace یا داده نامرتبط مشتری را برنگردانید.
- از envelope قابل پیشبینی مثل
{"ok": true, "data": ...}یا{"ok": false, "error_code": "not_found"}استفاده کنید تا مدل success و failure را یکسان مدیریت کند. - برای factهای ساختاریافته، رشته JSON فشرده را ترجیح دهید و برای خلاصه انسانی از متن ساده استفاده کنید؛ دستور hidden داخل خروجی ابزار نگذارید.
- در Responses،
function_call_outputباید باcall_idهمانfunction_callتطبیق داشته باشد. شکل سازگار با OpenAI معمولا خروجی رشتهای را میپذیرد و بعضی routeها میتوانند آرایهای از شیهای فایل یا تصویر را نیز بپذیرند. خروجی فایل/تصویر ابزار را در AvalAI وابسته به route بدانید؛ فقط وقتی گام بعدی مدل واقعا به artifact نیاز دارد، یک خلاصه کوتاه متن/JSON را همراهfile_id،file_urlیا مرجع تصویر بفرستید. - وقتی ابزار سند، جدول یا تصویر بزرگ برمیگرداند، payload خام را داخل خروجی ابزار نریزید. artifact را ذخیره کنید، یک reference پایدار بدهید و وقتی مدل باید محتوا را بررسی کند از ورودیهای فایل، ورودیهای vision یا workflow بازیابی استفاده کنید.
- نام ابزار،
call_idیاtool_call_id، نتیجه validation، وضعیت اجرا، latency و اندازه خروجی redacted را برای debugging و review incidentها log کنید.
ارسال نتایج بازگشتی
پس از اجرای تمام توابع درخواستی، یک فراخوانی API دوم همراه خروجی ابزارها انجام دهید:
- Chat Completions: پیامهای
role: "tool"را بعد از پیام دستیار حاویtool_callsاضافه کنید. - Responses: آیتمهای قبلی
response.outputرا همراه یک آیتمfunction_call_outputبرای هر نتیجه تابع اضافه کنید. وقتی state را دستی مدیریت میکنید، آیتمهای reasoning و آیتمهای اصلیfunction_callرا حفظ کنید.
در Chat Completions، موارد زیر را به تاریخچه پیام خود بعد از پیام کاربر و پیام دستیار حاوی tool_calls اضافه کنید:
- یک پیام جدید برای هر نتیجه فراخوانی تابع، با:
role:"tool"tool_call_id:idازtool_callمربوطه که مدل ارسال کرد. این برای تطبیق حیاتی است.name:nameتابعی که فراخوانی شد.content: مقدار بازگشتی تابع شما، معمولا به رشته تبدیل میشود (رشتههای JSON رایج و توصیه شده هستند).
سپس مدل از این نتایج برای فرموله کردن پاسخ متنی نهایی خود استفاده خواهد کرد.
پیکربندیهای اضافی
انتخاب ابزار (tool_choice)
نحوه انتخاب ابزارها توسط مدل را با استفاده از پارامتر tool_choice در درخواست خود کنترل کنید:
"auto"(پیشفرض): مدل تصمیم میگیرد که آیا صفر، یک یا چند تابع را فراخوانی کند."required": مدل را مجبور میکند حداقل یک تابع ازtoolsارائه شده را فراخوانی کند."none": از فراخوانی هرگونه تابعی توسط مدل جلوگیری میکند، حتی اگرtoolsارائه شده باشند.- اجبار یک تابع مشخص:
/v1/responses:{"type": "function", "name": "my_specific_function"}/v1/chat/completions:{"type": "function", "function": {"name": "my_specific_function"}}
{"type": "allowed_tools", "mode": "auto", "tools": [...]}: مجموعه ابزارهای قابل فراخوانی را بدون تغییر کل لیستtoolsمحدود میکند؛ در صورت پشتیبانی، این الگو میتواند رفتار prompt caching را پایدارتر نگه دارد.
وقتی از allowed_tools استفاده میکنید، ابزارها را با همان شکلی لیست کنید که route انتظار دارد. Responses از ابزارهای دارای tag داخلی مثل {"type": "function", "name": "get_weather"} استفاده میکند. Chat Completions هنگام اجبار یک تابع از شکل قدیمی wrap شده استفاده میکند، در حالی که پاسخ مدل همچنان choices[0].message.tool_calls را برمیگرداند.
فراخوانی تابع موازی (parallel_tool_calls)
به طور پیشفرض (parallel_tool_calls: true یا حذف شده در درخواست API، اگرچه برخی کتابخانههای کلاینت ممکن است پیشفرض متفاوتی داشته باشند)، مدلهایی مانند gpt-5.4 میتوانند تصمیم بگیرند چندین تابع را به طور همزمان در یک پیام پاسخ واحد فراخوانی کنند (موارد متعدد در لیست tool_calls).
شما میتوانید با تنظیم parallel_tool_calls: false در درخواست API این را محدود کنید تا مدل در هر نوبت صفر یا یک تابع فراخوانی کند. برای workflowهایی که state را تغییر میدهند، سیستمهای پولی را فراخوانی میکنند یا به تایید مرحلهبهمرحله نیاز دارند، از این گزینه استفاده کنید.
فراخوانی موازی برای ابزارهای تابع سفارشی کاربرد دارد. ابزارهای داخلی provider ممکن است قواعد توالی خودشان را داشته باشند، و ابزارهای داخلی OpenAI از parallel function calling استفاده نمیکنند. اگر از طریق AvalAI به مدل/ارائهدهندهای با ابزار داخلی route میکنید، پیش از فرض گرفتن چند فراخوانی همزمان، همان route را تست کنید.
اگر از مدل fine-tuned استفاده میکنید، به schema سختگیرانه و چند فراخوانی موازی در یک نوبت تکیه نکنید؛ providerها ممکن است وقتی مدل fine-tuned چند فراخوانی تولید میکند، تضمینهای strict mode را غیرفعال کنند.
حالت سختگیرانه (strict: true)
افزودن "strict": true از مدل میخواهد آرگومانهایی تولید کند که بهصورت قابل اعتماد با JSON Schema شما مطابق باشند و برای فراخوانی ابزار در production توصیه میشود. در Chat Completions، مقدار strict داخل شی "function" قرار میگیرد. در Responses، مقدار strict روی خود ابزار تابع و کنار name، description و parameters قرار میگیرد. برای پاسخ ساختاریافته نهایی بهجای فراخوانی ابزار، در Responses از text.format و در Chat Completions از response_format استفاده کنید.
OpenAI Responses API ممکن است schemaهای سازگار را به strict mode normalize کند و اگر schema قابل strict شدن نباشد به tool calling best-effort برگردد؛ Chat Completions تا وقتی strict: true نگذارید non-strict میماند. در AvalAI رفتار route انتخابی را verify کنید و فقط وقتی عمدا آرگومانهای best-effort میخواهید strict: false بگذارید.
الزامات برای حالت سختگیرانه:
additionalPropertiesباید در شیparametersبرای آن تابع برابرfalseباشد.- تمام ویژگیهای تعریف شده در
parameters.propertiesباید درparameters.requiredلیست شوند. - برای نمایش پارامترهای اختیاری، از یک اجتماع نوع شامل
nullاستفاده کنید، به عنوان مثال"type": ["string", "null"].
مزایا: ساختارهای آرگومان قابل اطمینانتر برای تابع خاص. محدودیتها: ممکن است در اولین فراخوانی تاخیر کمی بیشتر داشته باشد؛ طرحوارهها کش میشوند و واجد شرایط حفظ داده صفر نیستند؛ از زیرمجموعهای از ویژگیهای JSON Schema پشتیبانی میکند (به راهنمای خروجیهای ساختاریافته مراجعه کنید).
جریانسازی (Streaming)
میتوانید با تنظیم stream: true فراخوانیهای تابع را به صورت جریانی دریافت کنید. در Chat Completions، تکههای delta.tool_calls[index].function.arguments را جمع کنید. در Responses، رویدادهای SSE تایپشده را گوش کنید: response.output_item.added شروع فراخوانی تابع را نشان میدهد، response.function_call_arguments.delta متن آرگومانها را جریان میدهد و response.function_call_arguments.done فراخوانی کاملشده را ارائه میکند. دلتاهای آرگومان را بر اساس item_id جمع کنید یا پیش از اجرای ابزار منتظر رویداد done بمانید.
فراخوانی تابع در مقابل خروجیهای ساختاریافته
- از فراخوانی تابع (
tools) استفاده کنید زمانی که: میخواهید مدل JSON را به طور خاص برای فعال کردن کد برنامه شما (APIها، توابع داخلی، پرسوجوهای پایگاه داده) خروجی دهد. مدل تصمیم میگیرد کدام تابع(ها) را بر اساس توضیحات آنها فراخوانی کند. - از خروجیهای ساختاریافته (
text.formatدر Responses وresponse_formatدر Chat Completions) استفاده کنید زمانی که: میخواهید پاسخ متنی نهایی مدل به کاربر به یک ساختار JSON خاص که شما تعریف میکنید محدود شود (مانند استخراج دادههای قابل اعتماد، نمایش در رابط کاربری). مدل متنی را تولید میکند که با طرحواره مطابقت دارد، نه لزوما تصمیم به فراخوانی یک تابع.
مدلهای پشتیبانی شده
فراخوانی تابع توسط چندین مدل پیشرفته موجود از طریق AvalAI پشتیبانی میشود، از جمله:
gpt-5.4و اسنپشاتهای آنgpt-5.5و اسنپشاتهای آن- برای آخرین اطلاعات سازگاری برای مدلهای دیگر (مانند مدلهای Anthropic، Google، Cohere)، بررسی اجمالی مدلهای AvalAI را بررسی کنید.
مدلهای قدیمیتر ممکن است پشتیبانی محدودی از پارامتر tools یا ویژگیهای پیشرفته مانند حالت سختگیرانه و فراخوانیهای موازی داشته باشند یا اصلا پشتیبانی نکنند.