ترکیب ابزارهای داخلی و فراخوانی تابع


‫Gemini با حفظ و آشکار کردن سابقه زمینه‌ای تماس‌های ابزار، امکان ترکیب ابزارهای داخلی، مانند google_search، و فراخوانی تابع (که به‌عنوان ابزارهای سفارشی نیز شناخته می‌شود) را در یک تولید واحد فراهم می‌کند. ترکیب‌های ابزار سفارشی و داخلی امکان گردش‌های کار پیچیده و عامل‌محور را فراهم می‌کنند که در آن‌ها، برای مثال، مدل می‌تواند پیش‌از فراخواندن منطق کسب‌وکار خاص شما، خود را در داده‌های وب هم‌زمان مستقر کند.

در اینجا نمونه‌ای ارائه شده است که ترکیب‌های ابزار داخلی و سفارشی را با google_search و یک تابع سفارشی getWeather فعال می‌کند:

Python

from google import genai
from google.genai import types

client = genai.Client()

getWeather = {
    "name": "getWeather",
    "description": "Gets the weather for a requested city.",
    "parameters": {
        "type": "object",
        "properties": {
            "city": {
                "type": "string",
                "description": "The city and state, e.g. Utqiaġvik, Alaska",
            },
        },
        "required": ["city"],
    },
}

# Turn 1: Initial request with Google Search (built-in) and getWeather (custom) tools enabled
response = client.models.generate_content(
    model="gemini-3.8-flash",
    contents="What is the northernmost city in the United States? What's the weather like there today?",
    config=types.GenerateContentConfig(
        tools=[
            types.Tool(
                google_search=types.GoogleSearch(),  # Built-in tool
                function_declarations=[getWeather],  # Custom tool
            ),
        ],
        tool_config=types.ToolConfig(
            include_server_side_tool_invocations=True
        )
    ),
)
function_call_id = None
for part in response.candidates[0].content.parts:
    if part.function_call:
        print(f"Function call: {part.function_call.name} (ID: {part.function_call.id})")
        function_call_id = part.function_call.id

# Turn 2: Manually build history to circulate both tool and function context
history = [
    types.Content(
        role="user",
        parts=[types.Part(text="What is the northernmost city in the United States? What's the weather like there today?")]
    ),
    # Response from Turn 1 includes tool_call, tool_response, and thought_signatures
    response.candidates[0].content,
    # Return the function_response
    types.Content(
        role="user",
        parts=[types.Part(
            function_response=types.FunctionResponse(
                name="getWeather",
                response={"response": "Very cold. 22 degrees Fahrenheit."},
                id=function_call_id # Match the ID from the function_call
            )
        )]
    )
]

response_2 = client.models.generate_content(
    model="gemini-3.8-flash",
    contents=history,
    config=types.GenerateContentConfig(
        tools=[
            types.Tool(
                google_search=types.GoogleSearch(),
                function_declarations=[getWeather]
            ),
        ],
        # This flag needs to be enabled for built-in tool context circulation and tool combination
        tool_config=types.ToolConfig(
            include_server_side_tool_invocations=True
        )
    ),
)

for part in response_2.candidates[0].content.parts:
    if part.text:
        print(part.text)

جاوا اسکریپت

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

const getWeather = {
    name: "getWeather",
    description: "Get the weather in a given location",
    parameters: {
        type: "OBJECT",
        properties: {
            location: {
                type: "STRING",
                description: "The city and state, e.g. San Francisco, CA"
            }
        },
        required: ["location"]
    }
};

async function run() {
    const tools = [
      { googleSearch: {} },
      { functionDeclarations: [getWeather] }
    ];
    // This flag needs to be enabled for built-in tool context circulation and tool combination
    const toolConfig = { includeServerSideToolInvocations: true };

    // Turn 1: Initial request with Google Search (built-in) and getWeather (custom) tools enabled
    const response1 = await client.models.generateContent({
        model: "gemini-3.8-flash",
        contents: [{role: "user", parts: [{text: "What is the northernmost city in the United States? What's the weather like there today?"}]}],
        config: {
            tools: tools,
            toolConfig: toolConfig,
        },
    });

    for (const part of response1.candidates[0].content.parts) {
        if (part.functionCall) {
            console.log(`Function call: ${part.functionCall.name} (ID: ${part.functionCall.id})`);
        }
    }

   const functionCallId = response1.candidates[0].content.parts.find(p => p.functionCall)?.functionCall?.id;

    // Turn 2: Manually build history to circulate both tool and function context
    const history = [
        {
            role: "user",
            parts:[{text: "What is the northernmost city in the United States? What's the weather like there today?"}]
        },
        // Response from Turn 1 includes tool_call, tool_response, and thought_signatures
        response1.candidates[0].content,
        // Return the function_response
        {
            role: "user",
            parts: [{
                functionResponse: {
                    name: "getWeather",
                    response: {response: "Very cold. 22 degrees Fahrenheit."},
                    id: functionCallId // Match the ID from the function_call
                }
            }]
        }
    ];

    const response2 = await client.models.generateContent({
        model: "gemini-3.8-flash",
        contents: history,
        config: {
            tools: tools,
            toolConfig: toolConfig,
        },
    });

    for (const part of response2.candidates[0].content.parts) {
        if (part.text) {
            console.log(part.text);
        }
    }
}

run();

رفتن

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/google/generative-ai-go/genai"
    "google.golang.org/api/option"
)

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("GEMINI_API_KEY")))
    if err != nil {
        log.Exit(err)
    }
    defer client.Close()

    getWeather := &genai.FunctionDeclaration{
        Name:        "getWeather",
        Description: "Get the weather in a given location",
        Parameters: &genai.Schema{
            Type: genai.Object,
            Properties: map[string]*genai.Schema{
                "location": {
                    Type:        genai.String,
                    Description: "The city and state, e.g. San Francisco, CA",
                },
            },
            Required: []string{"location"},
        },
    }

    model := client.GenerativeModel("gemini-3.8-flash")
    model.Tools = []*genai.Tool{
        {GoogleSearch: &genai.GoogleSearch{}}, // Built-in tool
        {FunctionDeclarations: []*genai.FunctionDeclaration{getWeather}}, // Custom tool
    }
    ist := true
    model.ToolConfig = &genai.ToolConfig{
        IncludeServerSideToolInvocations: &ist, // This flag needs to be enabled for built-in tool context circulation and tool combination
    }

    chat := model.StartChat()

    // Turn 1: Initial request with Google Search (built-in) and getWeather (custom) tools enabled
    prompt := genai.Text("What is the northernmost city in the United States? What's the weather like there today?")
    resp1, err := chat.SendMessage(ctx, prompt)
    if err != nil {
        log.Exitf("SendMessage failed: %v", err)
    }

    if resp1 == nil || len(resp1.Candidates) == 0 || resp1.Candidates[0].Content == nil {
        log.Exit("empty response from model")
    }

    var functionCallID string
    for _, part := range resp1.Candidates[0].Content.Parts {
        switch p := part.(type) {
        case genai.FunctionCall:
            fmt.Printf("Function call: %s (ID: %s)\n", p.Name, p.ID)
            if p.Name == "getWeather" {
                functionCallID = p.ID
            }
        }
    }

    if functionCallID == "" {
        log.Exit("no getWeather function call in response")
    }

    // Turn 2: Provide function result back to model.
    // Chat history automatically includes tool_call, tool_response, and thought_signatures from Turn 1.
    fr := genai.FunctionResponse{
        Name: "getWeather",
        ID:   functionCallID,
        Response: map[string]any{
            "response": "Very cold. 22 degrees Fahrenheit.",
        },
    }

    resp2, err := chat.SendMessage(ctx, fr)
    if err != nil {
        log.Exitf("SendMessage for turn 2 failed: %v", err)
    }

    if resp2 == nil || len(resp2.Candidates) == 0 || resp2.Candidates[0].Content == nil {
        log.Exit("empty response from model in turn 2")
    }

    for _, part := range resp2.Candidates[0].Content.Parts {
        if txt, ok := part.(genai.Text); ok {
            fmt.Println(string(txt))
        }
    }
}

REST

# Turn 1: Initial request with Google Search (built-in) and getWeather (custom) tools enabled
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
  "contents": [{
    "role": "user",
    "parts": [{
      "text": "What is the northernmost city in the United States? What'\''s the weather like there today?"
    }]
  }],
  "tools": [{
    "googleSearch": {}
  }, {
    "functionDeclarations": [{
      "name": "getWeather",
      "description": "Get the weather in a given location",
      "parameters": {
          "type": "OBJECT",
          "properties": {
              "location": {
                  "type": "STRING",
                  "description": "The city and state, e.g. San Francisco, CA"
              }
          },
          "required": ["location"]
      }
    }]
  }],
  "toolConfig": {
    "includeServerSideToolInvocations": true
  }
}'

# Turn 2: Manually build history to circulate both tool and function context
# The following request assumes you have captured candidates[0].content from Turn 1 response,
# and extracted function_call.id for getWeather.
# Replace FUNCTION_CALL_ID and insert candidate content from turn 1.
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
  "contents": [
    {
      "role": "user",
      "parts": [{"text": "What is the northernmost city in the United States? What'\''s the weather like there today?"}]
    },
    YOUR_CANDIDATE_CONTENT_FROM_TURN_1_RESPONSE,
    {
      "role": "user",
      "parts": [{
        "functionResponse": {
          "name": "getWeather",
          "id": "FUNCTION_CALL_ID",
          "response": {"response": "Very cold. 22 degrees Fahrenheit."}
        }
      }]
    }
  ],
  "tools": [{
    "googleSearch": {}
  }, {
    "functionDeclarations": [{
      "name": "getWeather",
      "description": "Get the weather in a given location",
      "parameters": {
          "type": "OBJECT",
          "properties": {
              "location": {
                  "type": "STRING",
                  "description": "The city and state, e.g. San Francisco, CA"
              }
          },
          "required": ["location"]
      }
    }]
  }],
  "toolConfig": {
    "includeServerSideToolInvocations": true
  }
}'

روش کار

مدل‌های Gemini 3 از گردش بافت ابزار برای فعال کردن ترکیب‌های ابزار داخلی و سفارشی استفاده می‌کنند. گردش زمینه ابزار امکان حفظ و نمایش زمینه ابزارهای داخلی و هم‌رسانی آن با ابزارهای سفارشی در همان تماس را از نوبت به نوبت فراهم می‌کند.

فعال کردن ترکیب ابزار

  • برای فعال کردن گردش زمینه ابزار، باید پرچم include_server_side_tool_invocations را روی true تنظیم کنید.
  • function_declarations را به‌همراه ابزارهای داخلی موردنظرتان برای استفاده اضافه کنید تا عملکرد ترکیبی را راه‌اندازی کنید.
    • اگر function_declarations را اضافه نکنید، گردش زمینه ابزار همچنان روی ابزارهای داخلی گنجانده‌شده عمل خواهد کرد، به شرطی که پرچم تنظیم شده باشد.

میانای برنامه‌سازی کاربردی بخش‌هایی را برمی‌گرداند

در یک پاسخ واحد، API بخش‌های toolCall و toolResponse را برای فراخوانی ابزار داخلی برمی‌گرداند. برای تماس تابع (ابزار سفارشی)، API بخش تماس functionCall را برمی‌گرداند که کاربر در نوبت بعدی بخش functionResponse را ارائه می‌کند.

  • ‫toolCall و toolResponse: این API این بخش‌ها را برمی‌گرداند تا زمینه ابزارهایی که در سمت سرور اجرا می‌شوند و نتیجه اجرای آن‌ها برای نوبت بعدی حفظ شود.
  • functionCall و functionResponse: API فراخوانی تابع را برای کاربر ارسال می‌کند تا آن را تکمیل کند، و کاربر نتیجه را در پاسخ تابع برمی‌گرداند (این بخش‌ها برای همه فراخوانی تابع در Gemini API استاندارد است و مختص ویژگی ترکیب ابزار نیست).
  • (فقط ابزار اجرای کد) executableCode و codeExecutionResult: هنگام استفاده از ابزار «اجرای کد»، به‌جای functionCall و functionResponse، این API executableCode (کد تولیدشده توسط مدلی که قرار است اجرا شود) و codeExecutionResult (نتیجه کد اجرایی) را برمی‌گرداند.

باید همه بخش‌ها، ازجمله همه فیلدهایی که دربردارند، را در هر نوبت به مدل برگردانید تا بافت حفظ شود و ترکیب ابزارها امکان‌پذیر شود.

فیلدهای مهم در بخش‌های برگشتی

بخش‌های خاصی که توسط API برگردانده می‌شوند شامل id، tool_type، و thought_signature فیلد خواهند بود. این فیلدها برای حفظ بافتار ابزار بسیار مهم هستند (و بنابراین برای ترکیب ابزارها بسیار مهم هستند)؛ باید همه بخش‌ها را همان‌طور که در پاسخ داده شده است در درخواست‌های بعدی‌تان برگردانید.

  • id: شناسه یکتایی که تماس را به پاسخ آن نگاشت می‌کند. id در همه پاسخ‌های فراخوانی تابع تنظیم شده است، صرف‌نظر از گردش زمینه ابزار. باید همان id را که میانای برنامه‌سازی کاربردی در فراخوانی تابع ارائه می‌دهد در پاسخ تابع ارائه دهید. ابزارهای داخلی به‌طور خودکار id را بین فراخوانی ابزار و پاسخ ابزار هم‌رسانی می‌کنند.
    • در همه بخش‌های مربوط به ابزار پیدا شد: toolCall، toolResponse، functionCall، functionResponse، executableCode، codeExecutionResult
  • tool_type: ابزار خاصی را که استفاده می‌شود شناسایی می‌کند؛ ابزار داخلی حرفی یا (مثلاً URL_CONTEXT) یا نام تابع (مثلاً getWeather).
    • در toolCall و toolResponse بخش پیدا شد.
  • thought_signature: زمینه رمزگذاری‌شده واقعی که در هر بخش برگشتی ازسوی API جاسازی شده است. بدون امضاهای فکری نمی‌توان بافت را بازسازی کرد؛ اگر امضاهای فکری را برای همه بخش‌ها در هر نوبت برنگردانید، مدل خطا خواهد داد.
    • در همه بخش‌ها پیدا شد.

داده‌های مختص ابزار

برخی‌از ابزارهای داخلی آرگومان‌های داده قابل‌مشاهده برای کاربر را که مختص نوع ابزار است برمی‌گردانند.

ابزار آرگومان‌های فراخوانی ابزار قابل‌مشاهده کاربر (درصورت وجود) پاسخ ابزار نمایان برای کاربر (درصورت وجود)
GOOGLE_SEARCH queries search_suggestions
GOOGLE_MAPS queries places
google_maps_widget_context_token
URL_CONTEXT ‫urls
نشانی وب برای مرور شدن
url_metadata
retrieved_url: نشانی‌های وب مرور شد
url_retrieval_status: وضعیت مرور
FILE_SEARCH خالی خالی

ساختار درخواست ترکیب ابزار نمونه

ساختار درخواست زیر ساختار درخواست پیام‌واره را نشان می‌دهد: «What is the northernmost city in the United States?‎» (شمالی‌ترین شهر ایالات متحده کدام است؟) امروز هوا در آنجا چطور است؟». این مهارت سه ابزار را ترکیب می‌کند: ابزارهای داخلی Gemini google_search و code_execution، و یک تابع سفارشی get_weather.

{
  "model": "models/gemini-3.8-flash",
  "contents": [{
    "parts": [{
      "text": "What is the northernmost city in the United States? What's the weather like there today?"
    }],
    "role": "user"
  }, {
    "parts": [{
      "thoughtSignature": "...",
      "toolCall": {
        "toolType": "GOOGLE_SEARCH_WEB",
        "args": {
          "queries": ["northernmost city in the United States"]
        },
        "id": "a7b3k9p2"
      }
    }, {
      "thoughtSignature": "...",
      "toolResponse": {
        "toolType": "GOOGLE_SEARCH_WEB",
        "response": {
          "search_suggestions": "..."
        },
        "id": "a7b3k9p2"
      }
    }, {
      "functionCall": {
        "name": "getWeather",
        "args": {
          "city": "Utqiaġvik, Alaska"
        },
        "id": "m4q8z1v6"
      },
      "thoughtSignature": "..."
    }],
    "role": "model"
  }, {
    "parts": [{
      "functionResponse": {
        "name": "getWeather",
        "response": {
          "response": "Very cold. 22 degrees Fahrenheit."
        },
        "id": "m4q8z1v6"
      }
    }],
    "role": "user"
  }],
  "tools": [{
    "functionDeclarations": [{
      "name": "getWeather"
    }]
  }, {
    "googleSearch": {
    }
  }, {
    "codeExecution": {
    }
  }],
  "toolConfig": {
    "includeServerSideToolInvocations": true
  }
}

کدها و قیمت‌گذاری

توجه داشته باشید که toolCall و toolResponse بخش در درخواست‌ها برای prompt_token_count محاسبه می‌شوند. ازآنجایی‌که مراحل ابزار واسطه اکنون قابل‌مشاهده است و به شما برگردانده می‌شود، این مراحل بخشی از سابقه مکالمه است. این فقط برای درخواست‌ها صدق می‌کند، نه پاسخ‌ها.

ابزار «جستجوی Google» از این قانون مستثنا است. «جستجوی Google» ازقبل مدل قیمت‌گذاری خود را در سطح پُرسمان اعمال می‌کند، بنابراین هزینه توکن‌ها دوبرابر نمی‌شود (صفحه قیمت‌گذاری را ببینید).

برای اطلاعات بیشتر، صفحه نشان‌ها را بخوانید.

محدودیت‌ها

  • وقتی پرچم include_server_side_tool_invocations فعال باشد، به‌طور پیش‌فرض روی حالت VALIDATED تنظیم می‌شود (از حالت AUTO پشتیبانی نمی‌شود)
  • ابزارهای داخلی مثل google_search به مکان و زمان فعلی اطلاعات متکی هستند، بنابراین اگر system_instruction یا function_declaration.description شما اطلاعات مکان و زمان متعارضی داشته باشد، ویژگی ترکیب ابزار ممکن است خوب کار نکند.

ابزارهای پشتیبانی‌شده

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

ابزار طرف اجرا پشتیبانی گردش زمینه‌ای
جستجوی Google سمت سرور پشتیبانی می‌شود
Google Maps سمت سرور پشتیبانی می‌شود
بافت نشانی وب سمت سرور پشتیبانی می‌شود
جستجوی فایل سمت سرور پشتیبانی می‌شود
اجرای کد سمت سرور پشتیبانی می‌شود (درون‌ساخت، از قطعات executableCode و codeExecutionResult استفاده می‌کند)
استفاده از رایانه سمت کارخواه پشتیبانی می‌شود (درون‌ساخت، از قطعات functionCall و functionResponse استفاده می‌کند)
توابع سفارشی سمت کارخواه پشتیبانی می‌شود (درون‌ساخت، از قطعات functionCall و functionResponse استفاده می‌کند)

قدم بعدی چیست