Kết hợp các công cụ tích hợp và tính năng gọi hàm


Gemini cho phép kết hợp các công cụ tích hợp, chẳng hạn google_search, và lệnh gọi hàm (còn gọi là công cụ tuỳ chỉnh) trong một lần tạo bằng cách giữ nguyên và hiển thị nhật ký ngữ cảnh của các lệnh gọi công cụ. Các kiểu kết hợp công cụ tích hợp và tuỳ chỉnh cho phép các quy trình công việc phức tạp, có tính đại diện, trong đó, ví dụ: mô hình có thể dựa vào dữ liệu web theo thời gian thực trước khi gọi logic kinh doanh cụ thể của bạn.

Sau đây là ví dụ về cách bật các kiểu kết hợp công cụ tích hợp và tuỳ chỉnh bằng google_search và hàm tuỳ chỉnh 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.5-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.5-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)

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.5-flash",
    });

    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.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.5-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.5-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.5-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
  }
}'

Cách hoạt động

Các mô hình Gemini 3 sử dụng lưu thông ngữ cảnh công cụ để bật các kiểu kết hợp công cụ tích hợp và tuỳ chỉnh. Lưu thông ngữ cảnh công cụ giúp giữ nguyên và hiển thị ngữ cảnh của các công cụ tích hợp, đồng thời chia sẻ ngữ cảnh đó với các công cụ tuỳ chỉnh trong cùng một lệnh gọi từ lượt này sang lượt khác.

Bật tính năng kết hợp công cụ

  • Bạn phải đặt cờ include_server_side_tool_invocations thành true để bật tính năng lưu thông ngữ cảnh công cụ.
  • Đưa function_declarations vào cùng với các công cụ tích hợp mà bạn muốn sử dụng để kích hoạt hành vi kết hợp.
    • Nếu bạn không đưa function_declarations vào, tính năng lưu thông ngữ cảnh công cụ vẫn sẽ hoạt động trên các công cụ tích hợp được đưa vào, miễn là bạn đã đặt cờ.

API trả về các phần

Trong một phản hồi duy nhất, API sẽ trả về các phần toolCalltoolResponse cho lệnh gọi công cụ tích hợp. Đối với lệnh gọi hàm (công cụ tuỳ chỉnh), API sẽ trả về phần lệnh gọi functionCall, mà người dùng cung cấp phần functionResponse trong lượt tiếp theo.

  • toolCalltoolResponse: API trả về các phần này để giữ nguyên ngữ cảnh của các công cụ được chạy ở phía máy chủ và kết quả thực thi của các công cụ đó cho lượt tiếp theo.
  • functionCallfunctionResponse: API gửi lệnh gọi hàm cho người dùng điền thông tin và người dùng gửi kết quả trở lại trong phản hồi hàm (các phần này là tiêu chuẩn cho tất cả lệnh gọi hàm trong Gemini API, không chỉ dành riêng cho tính năng kết hợp công cụ).
  • (Chỉ công cụThực thi mã) executableCodecodeExecutionResult: Khi sử dụng công cụ Thực thi mã, thay vì functionCallfunctionResponse, API sẽ trả về executableCode (mã do mô hình tạo ra để thực thi) và codeExecutionResult (kết quả của mã thực thi).

Bạn phải trả về tất cả các phần, bao gồm cả tất cả các trường mà các phần đó chứa, cho mô hình trong mỗi lượt để duy trì ngữ cảnh và bật tính năng kết hợp công cụ.

Các trường quan trọng trong các phần được trả về

Một số phần do API trả về sẽ bao gồm các trường id, tool_type, và thought_signature. Các trường này rất quan trọng để duy trì ngữ cảnh công cụ (và do đó, rất quan trọng đối với các kiểu kết hợp công cụ); bạn cần trả về tất cả các phần như trong phản hồi trong các yêu cầu tiếp theo.

  • id: Giá trị nhận dạng duy nhất ánh xạ một lệnh gọi đến phản hồi của lệnh gọi đó. id **được** đặt trên tất cả các phản hồi lệnh gọi hàm, bất kể tính năng lưu thông ngữ cảnh công cụ. Bạn phải cung cấp cùng một id trong phản hồi hàm mà API cung cấp trong lệnh gọi hàm. Các công cụ tích hợp sẽ tự động chia sẻ id giữa lệnh gọi công cụ và phản hồi công cụ.
    • Có trong tất cả các phần liên quan đến công cụ: toolCall, toolResponse, functionCall, functionResponse, executableCode, codeExecutionResult
  • tool_type: Xác định công cụ cụ thể đang được sử dụng; công cụ tích hợp theo nghĩa đen hoặc tên hàm (ví dụ: URL_CONTEXT) hoặc hàm (ví dụ: getWeather).
    • Có trong các phần toolCalltoolResponse.
  • thought_signature: Ngữ cảnh được mã hoá thực tế được nhúng trong mỗi phần do API trả về. Không thể tái tạo ngữ cảnh nếu không có chữ ký ý tưởng; nếu bạn không trả về chữ ký ý tưởng cho tất cả các phần trong mỗi lượt, mô hình sẽ báo lỗi.
    • Có trong tất cả các phần.

Dữ liệu dành riêng cho công cụ

Một số công cụ tích hợp trả về các đối số dữ liệu mà người dùng có thể thấy, dành riêng cho loại công cụ.

Công cụ Đối số lệnh gọi công cụ mà người dùng có thể thấy (nếu có) Phản hồi công cụ mà người dùng có thể thấy (nếu có)
GOOGLE_SEARCH queries search_suggestions
GOOGLE_MAPS queries places
google_maps_widget_context_token
URL_CONTEXT urls
URL cần duyệt
urls_metadata
retrieved_url: URL đã duyệt
url_retrieval_status: Trạng thái duyệt
FILE_SEARCH Không có Không có

Cấu trúc yêu cầu kết hợp công cụ mẫu

Cấu trúc yêu cầu sau đây cho thấy cấu trúc yêu cầu của lời nhắc: "Thành phố cực bắc ở Hoa Kỳ là gì? Thời tiết ở đó hôm nay như thế nào?". Cấu trúc này kết hợp 3 công cụ: các công cụ Gemini tích hợp google_searchcode_execution, cùng với hàm tuỳ chỉnh get_weather.

{
  "model": "models/gemini-3.5-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
  }
}

Mã thông báo và giá

Xin lưu ý rằng các phần toolCalltoolResponse trong yêu cầu được tính vào prompt_token_count. Vì các bước công cụ trung gian này hiện có thể thấy và được trả về cho bạn, nên chúng là một phần của nhật ký cuộc trò chuyện. Điều này chỉ áp dụng cho yêu cầu, không áp dụng cho phản hồi.

Công cụ Google Tìm kiếm là một trường hợp ngoại lệ đối với quy tắc này. Google Tìm kiếm đã áp dụng mô hình giá riêng ở cấp truy vấn, vì vậy, mã thông báo không bị tính phí gấp đôi (xem trang Giá).

Đọc trang Mã thông báo để biết thêm thông tin.

Các điểm hạn chế

  • Mặc định ở chế độ VALIDATED (AUTO không được hỗ trợ) khi bật cờ include_server_side_tool_invocations
  • Các công cụ tích hợp như google_search dựa vào thông tin về vị trí và thời gian hiện tại. Vì vậy, nếu system_instruction hoặc function_declaration.description có thông tin về vị trí và thời gian xung đột, thì tính năng kết hợp công cụ có thể hoạt động không hiệu quả.

Các công cụ được hỗ trợ

Tính năng lưu thông ngữ cảnh công cụ tiêu chuẩn áp dụng cho các công cụ phía máy chủ (tích hợp). Thực thi mã cũng là một công cụ phía máy chủ, nhưng có giải pháp tích hợp riêng cho tính năng lưu thông ngữ cảnh. Sử dụng máy tính và lệnh gọi hàm là các công cụ phía máy khách, đồng thời cũng có các giải pháp tích hợp cho tính năng lưu thông ngữ cảnh.

Công cụ Phía thực thi Hỗ trợ lưu thông ngữ cảnh
Google Tìm kiếm Phía máy chủ Được hỗ trợ
Google Maps Phía máy chủ Được hỗ trợ
Ngữ cảnh URL Phía máy chủ Được hỗ trợ
Tìm kiếm tệp Phía máy chủ Được hỗ trợ
Thực thi mã Phía máy chủ Được hỗ trợ (tích hợp, sử dụng các phần executableCodecodeExecutionResult)
Sử dụng máy tính Phía máy khách Được hỗ trợ (tích hợp, sử dụng các phần functionCallfunctionResponse)
Hàm tuỳ chỉnh Phía máy khách Được hỗ trợ (tích hợp, sử dụng các phần functionCallfunctionResponse)

Bước tiếp theo