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-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 반환 부분
단일 응답에서 API는 내장 도구 호출의 toolCall 및 toolResponse 부분을 반환합니다. 함수 (맞춤 도구) 호출의 경우 API는 functionCall 호출 부분을 반환하며, 사용자는 다음 턴에서 functionResponse 부분을 제공합니다.
toolCall및toolResponse: API는 다음 턴을 위해 서버 측에서 실행되는 도구의 컨텍스트와 실행 결과를 보존하기 위해 이러한 부분을 반환합니다.functionCall및functionResponse: API는 사용자가 작성할 함수 호출을 사용자에게 전송하고 사용자는 함수 응답에서 결과를 다시 전송합니다. 이러한 부분은 Gemini API의 모든 함수 호출에 표준이며 도구 조합 기능에만 고유한 것은 아닙니다.- (코드 실행 도구만 해당)
executableCode및codeExecutionResult: 코드 실행 도구를 사용할 때 API는functionCall및functionResponse대신executableCode(실행되도록 모델에서 생성된 코드) 및codeExecutionResult(실행 가능한 코드의 결과)를 반환합니다.
컨텍스트를 유지하고 도구 조합을 사용 설정하려면 각 턴에서 모든 필드를 포함한 모든 파트를 모델에 반환해야 합니다.
반환된 파트의 중요 필드
API에서 반환되는 특정 부분에는 id, tool_type, thought_signature 필드가 포함됩니다. 이러한 필드는 도구 컨텍스트를 유지하는 데 중요하므로 도구 조합에 중요합니다. 후속 요청에서 대답에 제공된 대로 모든 부분을 반환해야 합니다.
id: 호출을 응답에 매핑하는 고유 식별자입니다.id는 도구 컨텍스트 순환과 관계없이 모든 함수 호출 응답에 설정됩니다. 함수 응답에서 API가 함수 호출에 제공하는 것과 동일한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 |
placesgoogle_maps_widget_context_token |
| URL_CONTEXT | urls탐색할 URL |
urls_metadataretrieved_url: 탐색한 URLurl_retrieval_status: 탐색 상태 |
| FILE_SEARCH | 없음 | 없음 |
도구 조합 요청 구조의 예
다음 요청 구조는 '미국에서 가장 북쪽에 있는 도시는 어디야?'라는 프롬프트의 요청 구조를 보여줍니다. 오늘 날씨가 어때?' 기본 제공 Gemini 도구 google_search 및 code_execution과 맞춤 함수 get_weather의 세 가지 도구를 결합합니다.
{
"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에 충돌하는 위치 및 시간 정보가 있으면 도구 조합 기능이 제대로 작동하지 않을 수 있습니다.
지원되는 도구
표준 도구 컨텍스트 순환은 서버 측 (내장) 도구에 적용됩니다. 코드 실행도 서버 측 도구이지만 컨텍스트 순환을 위한 자체 내장 솔루션이 있습니다. Computer Use와 함수 호출은 클라이언트 측 도구이며 컨텍스트 순환을 위한 내장 솔루션도 있습니다.
| 도구 | 실행 측 | 컨텍스트 순환 지원 |
|---|---|---|
| Google 검색 | 서버 측 | 지원됨 |
| Google 지도 | 서버 측 | 지원됨 |
| URL 컨텍스트 | 서버 측 | 지원됨 |
| 파일 검색 | 서버 측 | 지원됨 |
| 코드 실행 | 서버 측 | 지원됨 (내장, executableCode 및 codeExecutionResult 파트 사용) |
| 컴퓨터 사용 | 클라이언트 측 | 지원됨 (내장, functionCall 및 functionResponse 파트 사용) |
| 맞춤 함수 | 클라이언트 측 | 지원됨 (내장, functionCall 및 functionResponse 파트 사용) |