組み込みツールと関数呼び出しを組み合わせる

Gemini では、ツール呼び出しのコンテキスト履歴を保持して公開することで、組み込みツールgoogle_searchなど)と関数呼び出しカスタムツールとも呼ばれます)を 1 回の生成で組み合わせることができます。組み込みツールとカスタムツールの組み合わせにより、複雑なエージェント ワークフローが可能になります。たとえば、モデルは特定のビジネス ロジックを呼び出す前に、リアルタイムのウェブデータに基づいて自己をグラウンディングできます。

組み込みツールとカスタムツールの組み合わせを有効にする例を次に示します。この例では、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-flash-preview",
    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.ToolGoogleSearch(),  # Built-in tool
          function_declarations=[getWeather]       # Custom tool
        ),
      ],
      include_server_side_tool_invocations=True
    ),
)

for part in response.candidates[0].content.parts:
    if part.tool_call:
        print(f"Tool call: {part.tool_call.tool_type} (ID: {part.tool_call.id})")
    if part.tool_response:
        print(f"Tool response: {part.tool_response.tool_type} (ID: {part.tool_response.id})")
    if part.function_call:
        print(f"Function call: {part.function_call.name} (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=response.candidates[0].content.parts[2].function_call.id # Match the ID from the function_call
            )
        )]
    )
]

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

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

JavaScript

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 model = client.getGenerativeModel({
        model: "gemini-3-flash-preview",
    });

    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 result1 = await model.generateContent({
        contents: [{role: "user", parts: [{text: "What is the northernmost city in the United States? What's the weather like there today?"}]}],
        tools: tools,
        toolConfig: toolConfig,
    });

    const response1 = result1.response;

    for (const part of response1.candidates[0].content.parts) {
        if (part.toolCall) {
            console.log(`Tool call: ${part.toolCall.toolType} (ID: ${part.toolCall.id})`);
        }
        if (part.toolResponse) {
            console.log(`Tool response: ${part.toolResponse.toolType} (ID: ${part.toolResponse.id})`);
        }
        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 result2 = await model.generateContent({
        contents: history,
        tools: tools,
        toolConfig: toolConfig,
    });

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

run();

Go

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-flash-preview")
    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
            }
        case genai.ToolCallPart:
            fmt.Printf("Tool call: %s (ID: %s)\n", p.ToolType, p.ID)
        case genai.ToolResponsePart:
            fmt.Printf("Tool response: %s (ID: %s)\n", p.ToolType, 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-flash-preview: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-flash-preview: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 がパーツを返す

1 つのレスポンスで、API は組み込みツールの呼び出しに対して toolCall パーツと toolResponse パーツを返します。関数(カスタムツール)の呼び出しの場合、API は functionCall 呼び出しパーツを返します。ユーザーは次のターンで functionResponse パーツを提供します。

  • toolCalltoolResponse: API は、サーバーサイドで実行されるツールのコンテキストと実行結果を次のターンで保持するために、これらのパーツを返します。
  • functionCallfunctionResponse: API は、ユーザーが入力する関数呼び出しを送信します。ユーザーは関数レスポンスで結果を返します(これらのパーツは、ツール組み合わせ機能に固有のものではなく、Gemini API のすべての 関数呼び出しに共通です)。
  • コード実行ツールのみ) executableCodecodeExecutionResult: コード実行ツールを使用する場合、API は functionCallfunctionResponse ではなく、executableCode(実行されるモデルによって生成されたコード)と codeExecutionResult( 実行可能コードの結果)を返します。

コンテキストを維持してツールの組み合わせを有効にするには、各ターンで、含まれるすべてのフィールドを含むすべてのパーツをモデルに返す必要があります。

返されるパーツの重要なフィールド

API から返される特定の パーツには、idtool_type、および thought_signature フィールドが含まれます。これらのフィールドは、ツール コンテキストの維持に不可欠であり(したがって、ツールの組み合わせに不可欠です)、後続のリクエストでは、レスポンスで提供されたとおりに すべてのパーツを返す必要があります。

  • id: 呼び出しをレスポンスにマッピングする一意の識別子。id は、ツール コンテキストの循環に関係なく、すべての関数呼び出しレスポンスに設定 されます。API が関数呼び出しで提供するのと同じ id を関数レスポンスで提供する必要があります。 組み込みツールは、ツール呼び出しとツールレスポンスの間で id を自動的に共有します。
    • すべてのツール関連パーツ(toolCalltoolResponsefunctionCallfunctionResponseexecutableCodecodeExecutionResult)にあります。
  • tool_type: 使用されている特定のツールを識別します。リテラルの組み込みツールまたは関数名(URL_CONTEXTgetWeather など)。
    • toolCall パーツと toolResponse パーツにあります。
  • thought_signature: API から返される各パーツ に埋め込まれた実際の暗号化されたコンテキスト。思考署名がないとコンテキストを再構築できません。すべてのターンのすべてのパーツの思考署名を返さないと、モデルでエラーが発生します。
    • すべてのパーツにあります。

ツール固有のデータ

一部の組み込みツールは、ツールタイプに固有のユーザーに表示されるデータ引数を返します。

ツール ユーザーに表示されるツール呼び出し引数(ある場合) ユーザーに表示されるツールレスポンス(ある場合)
GOOGLE_SEARCH queries search_suggestions
GOOGLE_MAPS queries places
google_maps_widget_context_token
URL_CONTEXT urls
閲覧する URL
urls_metadata
retrieved_url: 閲覧した URL
url_retrieval_status: 閲覧ステータス
FILE_SEARCH なし なし

ツールの組み合わせリクエスト構造の例

次のリクエスト構造は、「米国最北端の都市はどこですか?今日の天気はどうですか?」というプロンプトのリクエスト構造を示しています。組み込みの Gemini ツール google_searchcode_execution、カスタム関数 get_weather の 3 つのツールを組み合わせます。

{
  "model": "models/gemini-3-flash-preview",
  "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 マップ サーバーサイド サポート対象
URL コンテキスト サーバーサイド サポート対象
ファイル検索 サーバーサイド サポート対象
コードの実行 サーバーサイド サポート対象(組み込み、executableCode パーツと codeExecutionResult パーツを使用)
コンピュータの使用 クライアントサイド サポート対象(組み込み、functionCall パーツと functionResponse パーツを使用)
カスタム関数 クライアントサイド サポート対象(組み込み、functionCall パーツと functionResponse パーツを使用)

次のステップ