Function calling
The model can't look up your data or change anything in your systems. It can only ask. Follow one function call through the Gemini API with no framework, from declaration to final answer.
A model knows a lot, but it doesn’t know where your customer’s parcel is right now. That fact lives in your database. Function calling connects the two without giving the model any access to your systems.
It works in four steps, and only one of them happens inside the model:
- Declare the function: its name, what it does, and a schema for its parameters.
- Call the model with the user’s question and the declaration.
- Run the function yourself. The model returns a
function_callwith the arguments it chose. Your code decides whether to run it. - Send the result back. The model uses it to write the final answer.
This lesson uses the Gemini API directly, with no agent framework, so you can see every message. Press Play for a single lookup, then switch the Try it selector to see two calls at once, and a question that needs no call at all.
- Model asks
- Your code runs
- Result goes back
- Answer
Press Play to watch it run, or step through with ← →.
import json
from google import genai
# 1. Describe the function to the model: name, purpose, parameters.
get_order_status_declaration = {
"type": "function",
"name": "get_order_status",
"description": "Gets the live delivery status of a customer's order.",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "Order ID, e.g. ORD-4417"},
},
"required": ["order_id"],
},
}
def get_order_status(order_id: str) -> dict:
"""Your real code: a database query or an internal API call."""
return {"order_id": order_id, "status": "out_for_delivery", "eta": "today 6 pm"}
client = genai.Client()
# 2. Turn 1: send the question together with the declaration.
interaction = client.interactions.create(
model="gemini-3.8-flash",
input="Where is my order ORD-4417?",
tools=[get_order_status_declaration],
)
# 3. Your code runs every function the model asked for.
results = []
for step in interaction.steps:
if step.type == "function_call" and step.name == "get_order_status":
result = get_order_status(**step.arguments)
results.append({
"type": "function_result",
"name": step.name,
"call_id": step.id,
"result": [{"type": "text", "text": json.dumps(result)}],
})
# 4. Turn 2: send the results back, linked to turn 1.
if results:
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=results,
tools=[get_order_status_declaration],
previous_interaction_id=interaction.id,
)
print(interaction.output_text)Interaction steps
No steps yet. Press Play.
What to notice
- The model only asks. A
function_callis a structured request: a name, arguments and anid. Running it, validating it or refusing it is your code’s job. Google’s best practices include validating function calls before you execute them. - Two API calls, linked together. Turn 1 returns the
function_call. Turn 2 sends afunction_resultwith the matchingcall_id, plusprevious_interaction_idso the model sees turn 1. (If you setstore=False, you resend the full history yourself instead.) - The declaration is the model’s only view of your function. A clear
descriptionand strong types (string,integer,enum) lead to better calls. Google suggests keeping the active set to around 10 to 20 tools. - One turn can hold several calls. Independent lookups come back together as parallel calls. Calls that depend on each other (find the city, then get its weather) come back one after another. This is called compositional calling.
- You can control when it calls.
tool_choiceingeneration_configsets the mode:
| Mode | Behaviour |
|---|---|
auto (default) | The model decides: call a function or answer directly |
any | The model must call a function |
none | The model can’t call functions |
validated | The model makes sure its calls match the function schema |