If you've gotten the Gemini API quickstart running and are now wondering "what do I actually build with this?", Function Calling is probably the next concept that will unlock real-world use cases.
Text generation is great. Streaming works. But practical applications almost always need AI to interact with the outside world — look up current data, perform calculations, or call a third-party API. That's where Function Calling comes in.
What Is Function Calling and Why Does It Matter
LLMs are powerful at language tasks, but they can't tell you the current time accurately or retrieve live stock prices. Ask Gemini "What's the weather in Tokyo right now?" and it will generate a plausible-sounding answer — not a real one.
Function Calling bridges that gap. When the model decides it needs to call a specific function, instead of hallucinating the answer, it returns a structured JSON object with the function name and arguments. Your app executes the function, returns the result to the model, and the model generates a final answer grounded in real data.
The division of responsibility:
- Your app: defines the functions and actually runs them
- The model: decides when and which function to call, and generates the arguments
This separation is what makes Function Calling so useful and composable.
Setting Up in Antigravity
Open Antigravity and create the project from the integrated terminal:
# Create project folder
mkdir gemini-function-calling
cd gemini-function-calling
# Set up virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# Install dependencies
pip install google-generativeai python-dotenvAntigravity's Python extension should auto-detect the virtual environment interpreter. If it doesn't, press Ctrl+Shift+P → Python: Select Interpreter → choose .venv/bin/python.
Add your API key to a .env file:
GEMINI_API_KEY=YOUR_GEMINI_API_KEYA Minimal Function Calling Example
The fastest way to understand the mechanics is a simple "get current time" function:
# basic_function_calling.py
import os
import json
import datetime
import google.generativeai as genai
from dotenv import load_dotenv
load_dotenv()
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
# ① Define the actual function
def get_current_time(timezone: str = "Asia/Tokyo") -> dict:
"""Returns the current date and time."""
now = datetime.datetime.now()
return {
"time": now.strftime("%Y-%m-%d %H:%M:%S"),
"timezone": timezone
}
# ② Define the tool schema for Gemini (JSON Schema format)
tools = [
{
"function_declarations": [
{
"name": "get_current_time",
"description": "Get the current date and time",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "Timezone string (e.g., Asia/Tokyo, UTC)"
}
},
"required": []
}
}
]
}
]
# ③ Initialize model and start chat
model = genai.GenerativeModel("gemini-2.0-flash", tools=tools)
chat = model.start_chat()
# ④ Send user message
response = chat.send_message("What time is it right now?")
# ⑤ Check if the model wants to call a function
for part in response.parts:
if hasattr(part, "function_call"):
func_call = part.function_call
print(f"Model requested function: {func_call.name}")
print(f"Arguments: {dict(func_call.args)}")
# ⑥ Execute the function
result = get_current_time(**dict(func_call.args))
# ⑦ Return the result to the model
final_response = chat.send_message(
genai.protos.Part(
function_response=genai.protos.FunctionResponse(
name=func_call.name,
response={"result": result}
)
)
)
print(f"\nAI response: {final_response.text}")Expected output:
Model requested function: get_current_time
Arguments: {}
AI response: The current time is 2026-04-20 09:00:00 (Asia/Tokyo).
Step ⑤ is the key insight: the model is not executing anything. It's telling your app "please run this function with these arguments." Your app runs the function and reports back. The model then uses that real data to formulate its answer.
Real-World Example: Multiple Tools
Most real applications define several functions. Here's a multi-tool setup with a calculator and unit converter:
# multi_tool_example.py
import os
import google.generativeai as genai
from dotenv import load_dotenv
load_dotenv()
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
# Tool implementations
def calculate(expression: str) -> dict:
"""Evaluate a math expression (safe characters only)."""
allowed_chars = set("0123456789+-*/()., ")
if not all(c in allowed_chars for c in expression):
return {"error": "Expression contains invalid characters", "result": None}
try:
result = eval(expression) # Use a safer eval in production
return {"result": result, "expression": expression}
except Exception as e:
return {"error": str(e), "result": None}
def convert_units(value: float, from_unit: str, to_unit: str) -> dict:
"""Simple unit conversion (km↔miles, kg↔lbs)."""
conversions = {
("km", "miles"): lambda v: v * 0.621371,
("miles", "km"): lambda v: v * 1.60934,
("kg", "lbs"): lambda v: v * 2.20462,
("lbs", "kg"): lambda v: v / 2.20462,
}
key = (from_unit.lower(), to_unit.lower())
if key in conversions:
result = conversions[key](value)
return {"result": round(result, 4), "from": f"{value} {from_unit}", "to": f"{result:.4f} {to_unit}"}
return {"error": f"Conversion from {from_unit} to {to_unit} is not supported"}
# Map tool names to functions
TOOL_FUNCTIONS = {
"calculate": calculate,
"convert_units": convert_units,
}
tools = [
{
"function_declarations": [
{
"name": "calculate",
"description": "Evaluate a mathematical expression",
"parameters": {
"type": "object",
"properties": {
"expression": {"type": "string", "description": "Math expression to evaluate (e.g., 2 + 3 * 4)"}
},
"required": ["expression"]
}
},
{
"name": "convert_units",
"description": "Convert between units (km↔miles, kg↔lbs)",
"parameters": {
"type": "object",
"properties": {
"value": {"type": "number", "description": "The value to convert"},
"from_unit": {"type": "string", "description": "Source unit"},
"to_unit": {"type": "string", "description": "Target unit"}
},
"required": ["value", "from_unit", "to_unit"]
}
}
]
}
]
def chat_with_tools(user_message: str) -> str:
"""General-purpose chat with tools — handles multi-step function calls."""
model = genai.GenerativeModel("gemini-2.0-flash", tools=tools)
chat = model.start_chat()
response = chat.send_message(user_message)
# Loop to handle multiple rounds of function calls
max_iterations = 5
for _ in range(max_iterations):
func_calls = [p for p in response.parts if hasattr(p, "function_call")]
if not func_calls:
break # No more function calls — we have the final answer
# Execute all requested functions and return results
function_responses = []
for part in func_calls:
fc = part.function_call
func = TOOL_FUNCTIONS.get(fc.name)
if func:
result = func(**dict(fc.args))
else:
result = {"error": f"Unknown function: {fc.name}"}
function_responses.append(
genai.protos.Part(
function_response=genai.protos.FunctionResponse(
name=fc.name,
response={"result": result}
)
)
)
response = chat.send_message(function_responses)
return response.text
# Test it
print(chat_with_tools("Calculate 42 * 1.08, then convert that result from km to miles"))
# Expected: calculates 45.36, then converts to ~28.18 milesThe max_iterations loop is critical. Gemini may request multiple function calls in sequence — calculate first, then convert. Without a loop and a cap, you risk running indefinitely if something goes wrong. In production, always set a reasonable upper bound.
Debugging Function Calling in Antigravity
The most common frustration is "why isn't the function being called?" Antigravity's debug panel helps, but first add this inspection utility to your code:
def debug_response(response):
"""Print the internal structure of a Gemini response for debugging."""
print("--- Response structure ---")
print(f"Number of candidates: {len(response.candidates)}")
for i, candidate in enumerate(response.candidates):
print(f"Candidate {i}: finish_reason={candidate.finish_reason}")
for j, part in enumerate(candidate.content.parts):
if hasattr(part, "text"):
print(f" Part {j}: text = {part.text[:100]}")
elif hasattr(part, "function_call"):
print(f" Part {j}: function_call = {part.function_call.name}({dict(part.function_call.args)})")A finish_reason of STOP means normal completion. MAX_TOKENS means you've hit the token limit. If functions aren't being called when you expect them to be, the most likely culprit is a vague description in your tool schema. The more specific you are about when and why the function should be used, the more reliably the model will call it.
Where to Go From Here
Function Calling opens up a whole category of "AI + live data" applications that aren't possible with text generation alone. Take the code from this guide and connect it to an API you already use — a weather service, your own database, or a company REST API.
The Google GenAI SDK for Python documentation has additional examples, including tool_config for forcing the model to always call a specific tool — useful for keeping chatbot behavior consistent and predictable.