skip to content

Function Calling

FunctionDeclaration tools plus a tool_config mode — AUTO, ANY or NONE — that controls how hard the model is pushed toward calling one. The loop is the familiar one: read FunctionCall parts, execute your code, return a FunctionResponse part and continue the turn.

on this pageshow

questions

6

In Google's Gemini API, what does the model return when it decides to call a declared function?

level: juniorimportance: must knowfreq 70%

answer

  1. The model asks, it never runs
  2. Look inside the candidate's parts list
  3. name plus args, not a string
  4. args arrives already parsed
  5. Several calls can share one turn

basics

~20 s

Gemini returns an ordinary response whose candidate content holds a FunctionCall part: a function name plus an args object of already-parsed arguments. The model never runs your code — your application executes the call and sends the result back.

solid answer

~40 s

Function calling in Gemini is a *request*, not an execution. You call `generate_content` with your `FunctionDeclaration` tools, and if the model wants a tool it emits a part whose `function_call` field carries `name` and `args`. In the `google-genai` SDK, `args` is already a parsed dict — you do not parse a JSON string as you would with some other vendors — and `response.function_calls` is a convenience list of every call in the turn. A turn's `parts` list can mix text and function calls, and Gemini may emit several calls at once, so always iterate `response.candidates[0].content.parts` rather than assuming a single part. `response.text` may be empty when the turn is a pure tool request. Nothing runs on Google's servers: you dispatch to your own code and continue the conversation with a FunctionResponse.

code

python · 27 lines
python
from google import genai
from google.genai import types

client = genai.Client()

get_weather = types.FunctionDeclaration(
    name="get_weather",
    description="Get the current weather for a city.",
    parameters=types.Schema(
        type=types.Type.OBJECT,
        properties={"city": types.Schema(type=types.Type.STRING)},
        required=["city"],
    ),
)

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="What is the weather in Oslo?",
    config=types.GenerateContentConfig(
        tools=[types.Tool(function_declarations=[get_weather])],
        automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True),
    ),
)

for part in response.candidates[0].content.parts:
    if part.function_call:
        print(part.function_call.name, dict(part.function_call.args))

go deeper

for a junior

Be able to say that Gemini returns a FunctionCall part with a name and an args object, and that your own code runs the function and sends the result back.

for a middle

Explain the traversal: candidates, content, parts, and the function_call field; note that args is already decoded and that a turn may contain text plus several calls.

for a senior

Show that you treat model-chosen arguments as untrusted input and that your dispatcher handles unknown names, multiple calls and missing text without crashing.

for a principal

Frame the boundary: the model proposes, your process authorizes and executes. Argue where validation, rate limiting and audit logging of tool invocations belong in the system.

## The core idea When you attach tools to a Gemini request, you are not giving the model the ability to run code. You are giving it a vocabulary. The model can answer in words, or it can answer with a structured message that means "please run `get_weather` with `{"city": "Oslo"}` and tell me what you got". Executing that call is entirely your application's job, and so is deciding whether the call is safe to make at all. ## Where the call lives in the response A Gemini response contains `candidates`, each with a `content` object, and that content has a list of `parts`. A part is a union: it may hold `text`, `inline_data`, a `function_call`, a `function_response`, and so on. A tool request appears as a part whose `function_call` field is populated, with two fields that matter: - `name` — the exact `name` you gave the matching `FunctionDeclaration`. - `args` — the arguments the model chose, shaped by the parameter schema you declared. In the `google-genai` Python SDK, `args` arrives as a Python mapping of already-decoded values, not as a JSON string that you must `json.loads`. That is a real difference from OpenAI-shaped APIs, where tool-call arguments arrive as a string, and it is a frequent source of copy-paste bugs when porting code between vendors. Some function calls also carry an optional `id`. When one is present you should echo it back on the corresponding function response so the model can pair them unambiguously. ## Reading it without assuming shape Three assumptions break in production: 1. **"The function call is `parts[0]`."** Gemini can put a short piece of narration text before the call, so iterate the whole list. 2. **"There is at most one call."** Gemini can emit several `function_call` parts in a single turn when the requests are independent. 3. **"`response.text` always works."** When the turn is purely a tool request, there may be no text at all; accessing `.text` gives you nothing useful and can warn about non-text parts. The SDK gives you `response.function_calls`, a flattened list of every `FunctionCall` in the turn, which is the shortest correct way to check "did the model ask for a tool?". ## The model does not execute anything This is worth saying plainly because juniors often assume the provider runs the function. It does not. Google's servers never see your function body, never open a socket on your behalf, and never learn what your tool returned unless you send it. Everything the model knows about the outcome comes from the FunctionResponse you choose to put in the next turn. That is also your security boundary: validate `args` before you use them, exactly as you would validate any untrusted input, because the values were produced by a language model and may be wrong, out of range, or adversarially influenced by the user's text. There is one nuance for the Python SDK: if you pass plain Python callables as tools instead of `FunctionDeclaration` objects, the SDK's automatic function calling will execute them for you *client-side* and hand you only the final text. That is still your process running your code — the execution model has not changed, only who writes the loop. ## What happens next Once you have `name` and `args`, the loop is the familiar one: dispatch to your implementation, then append two things to the conversation — the model's own turn containing the function call, and a new turn containing a function response part carrying your result. You then call the model again with the whole history and the same tool declarations. The model reads the result and either answers in text or asks for another call. ## Common failure modes - Treating `args` as a JSON string and calling `json.loads` on a dict. - Dropping the model's function-call turn from the history, so the response you send has nothing to attach to. - Trusting `args` blindly — passing a model-chosen file path or SQL fragment straight into a privileged operation. - Assuming an empty `response.text` means the request failed, when it simply means the turn was a tool request. ## Mental model Think of Gemini as a colleague who can only speak. When it needs the weather it writes you a note that says `get_weather(city="Oslo")`. It cannot pick up the phone. You make the call, write the answer on the note, and hand it back.

  • How does reading a Gemini function call differ from reading an OpenAI-style tool call?
    Shape and encoding both differ. Gemini puts the request in a content part's `function_call` field, with `args` already decoded into a mapping. OpenAI-shaped APIs return a `tool_calls` array on the message where the arguments are a JSON *string* you must parse yourself, and each call carries an id you must echo on the reply. Porting code between them means changing both the traversal and the argument decoding.
  • If a turn contains both text and a function call, do you show the text to the user?
    Usually you can, but treat it as optional narration rather than the answer. The authoritative answer comes after the tool result is returned, so the safest UX is to render such text as a transient status line and let the final post-tool turn produce the message you persist.
  • What should you do before passing args into your implementation?
    Validate them as untrusted input. The values were generated by a model that may have been steered by user text, so enforce types, ranges, allow-lists and authorization on your side even though you declared a schema. A schema constrains shape, not intent.

saying these in an interview costs you the question

  • Believing Google's servers execute your declared function
  • Calling json.loads on the args mapping
  • Assuming the call is always parts[0]
  • Thinking an empty response.text means an error
  • Using model-supplied args without any validation

context

open as a page

How do you define a FunctionDeclaration for the Gemini API?

level: middleimportance: must knowfreq 62%

basics

~20 s

A FunctionDeclaration carries a name, a description, and a parameters schema written in the OpenAPI subset Gemini accepts — object type, properties, required, enum, items. Declarations are grouped into a Tool and passed on the request config's tools list.

open as a page

How do you return a function result to Gemini and continue the tool-use turn?

level: middleimportance: must knowfreq 58%

basics

~20 s

Append the model's own turn containing the FunctionCall to the conversation, then append a turn whose part is a FunctionResponse carrying the same function name and a JSON-object result. Re-send the whole history with the same tools and loop until no more calls appear.

open as a page

What is automatic function calling in the Gemini Python SDK, and when do you disable it?

level: middleimportance: should knowfreq 40%

basics

~20 s

When you pass plain Python callables as Gemini tools, the SDK derives the declarations from their signatures and then executes them locally on your behalf, looping until the model produces text. Disable it with AutomaticFunctionCallingConfig(disable=True) whenever you need approval, custom error handling or tracing.

open as a page

Gemini returned three FunctionCall parts in one turn — how must your app respond?

level: seniorimportance: should knowfreq 38%

basics

~10 s

Execute all three, then send one turn containing three FunctionResponse parts, each naming the function it answers. Splitting them across turns or answering only some leaves calls unresolved and the model typically re-issues them.

open as a page

What does Gemini's tool_config mode ANY do, and when does it trap your loop?

level: seniorimportance: should knowfreq 48%

basics

~20 s

ANY forces Gemini to emit a function call on that turn instead of free text, optionally restricted to allowed_function_names. Left on for the whole conversation it prevents the model from ever writing the final answer, so the loop keeps calling tools until you switch back to AUTO.

open as a page