GEMINI LABJP
V0.60.0 — The gemini-cli stable release is still v0.60.0. Almost all of it is security work: web fetch destination checks, MCP OAuth issuer validation, sandbox isolation9/30 — gemini-omni-flash-preview shuts down on September 30, nine days from now. The replacement is gemini-omni-1.1-flashCODE13 — Uploading the same video repeatedly returns success and a code 13 failure in turn. With no visible trigger, it is worth deciding your retry policy up frontNEW — Three lines that decide image features in the Gemini app: thirteen, eighteen, and your administrator2.5GA — Gemini 2.5 Pro, Flash and Flash-Lite still have no announced shutdown date. The deprecation table reads No shutdown date announced3.8FLASH — Gemini 3.8 Flash pricing is introductory. It holds until December 31, 2026, and both input and output double on January 1, 2027V0.60.0 — The gemini-cli stable release is still v0.60.0. Almost all of it is security work: web fetch destination checks, MCP OAuth issuer validation, sandbox isolation9/30 — gemini-omni-flash-preview shuts down on September 30, nine days from now. The replacement is gemini-omni-1.1-flashCODE13 — Uploading the same video repeatedly returns success and a code 13 failure in turn. With no visible trigger, it is worth deciding your retry policy up frontNEW — Three lines that decide image features in the Gemini app: thirteen, eighteen, and your administrator2.5GA — Gemini 2.5 Pro, Flash and Flash-Lite still have no announced shutdown date. The deprecation table reads No shutdown date announced3.8FLASH — Gemini 3.8 Flash pricing is introductory. It holds until December 31, 2026, and both input and output double on January 1, 2027
Articles/API / SDK
API / SDK/2026-05-04Intermediate

Implementing Structured Output with Gemini Function Calling — Multi-Tool Design Patterns

A practical guide to reliable structured output with Gemini API Function Calling — covering tool definition best practices, multi-tool coordination, and error handling.

Gemini API240Function Calling17structured output8tool usePython47

When I first tried Gemini's Function Calling, I assumed it was just a formalized version of asking for JSON output. Once I used it in production, the difference became clear.

The key distinction: the model decides when and whether to call tools based on context. It selects which data it needs, fetches it, and integrates the results — autonomously. This guide explains that mechanism with practical code you can adapt directly.

The Basic Structure of Function Calling

Function Calling follows a three-step cycle:

  1. Send a request with tool definitions attached
  2. The model returns a function_call — it has not executed the tool yet
  3. Your code runs the tool, sends the result back to the model

The separation — the model signals tool use, your application executes it — is architecturally important. You retain full control of what actually runs.

import google.generativeai as genai
 
genai.configure(api_key="YOUR_GEMINI_API_KEY")
 
tools = [
    {
        "function_declarations": [
            {
                "name": "get_weather",
                "description": "Get current weather for a specified city",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city": {
                            "type": "string",
                            "description": "City name (e.g., Tokyo, New York)"
                        },
                        "unit": {
                            "type": "string",
                            "enum": ["celsius", "fahrenheit"],
                            "description": "Temperature unit"
                        }
                    },
                    "required": ["city"]
                }
            }
        ]
    }
]
 
model = genai.GenerativeModel("gemini-2.0-flash", tools=tools)
response = model.generate_content("What is the weather in Tokyo today?")
 
for part in response.candidates[0].content.parts:
    if hasattr(part, "function_call"):
        print(f"Tool called: {part.function_call.name}")
        print(f"Arguments: {dict(part.function_call.args)}")

Writing Tool Definitions That Actually Work

The quality of description fields directly affects model decision accuracy. This is mentioned briefly in official docs, but the impact in practice is significant.

Weak: "description": "Get weather"

Strong: "description": "Retrieve current temperature, weather condition, and humidity for a city. Use this when the user asks about weather, or when weather conditions are needed to make a decision."

The "use this when..." clause suppresses over-triggering — the model calling a tool when it's not needed. When multiple tools have overlapping capabilities, this differentiation instruction is particularly effective.

Using enum to constrain parameter values is also worth the effort. Specifying enum: ["celsius", "fahrenheit"] for the unit parameter eliminates the risk of the model passing unexpected values.

Coordinating Multiple Tools

Most real applications combine multiple tools. Gemini can return multiple tool calls in a single response (parallel function calling).

def handle_function_calls(response, tool_implementations):
    """Process function calls and return results"""
    function_results = []
 
    for part in response.candidates[0].content.parts:
        if not hasattr(part, "function_call"):
            continue
        
        fc = part.function_call
        func_name = fc.name
        func_args = dict(fc.args)
        
        if func_name not in tool_implementations:
            function_results.append({
                "function_response": {
                    "name": func_name,
                    "response": {"error": f"Unknown function: {func_name}"}
                }
            })
            continue
        
        try:
            result = tool_implementations[func_name](**func_args)
            function_results.append({
                "function_response": {
                    "name": func_name,
                    "response": {"result": result}
                }
            })
        except Exception as e:
            function_results.append({
                "function_response": {
                    "name": func_name,
                    "response": {"error": str(e)}
                }
            })
    
    return function_results
 
def get_weather(city: str, unit: str = "celsius") -> dict:
    return {"city": city, "temperature": 22, "condition": "Sunny", "unit": unit}
 
def get_news(topic: str, max_results: int = 3) -> list:
    return [{"title": f"{topic} news {i}", "url": f"https://example.com/{i}"} for i in range(max_results)]
 
tool_implementations = {
    "get_weather": get_weather,
    "get_news": get_news
}

When Gemini returns multiple tool calls at once, they're independent of each other and safe to execute in parallel. Using asyncio.gather() to run them concurrently meaningfully reduces latency.

Handling Errors and Uncertainty

When a tool returns an error, Gemini adjusts its response accordingly — but only if you pass error information in a structured, informative way. Vague errors cause the model to speculate.

def safe_tool_response(func_name: str, result=None, error=None) -> dict:
    if error:
        return {
            "function_response": {
                "name": func_name,
                "response": {
                    "success": False,
                    "error": str(error),
                    "suggestion": "Try an alternative approach"
                }
            }
        }
    return {
        "function_response": {
            "name": func_name,
            "response": {
                "success": True,
                "data": result
            }
        }
    }

Including a success flag and suggestion field lets the model make informed decisions — "this tool failed, let me try a different path" — rather than silently producing a hallucinated response.

A Real-World Use Case

I built an app review analysis tool using this approach. I passed App Store review text to Gemini and defined three tools:

  • categorize_review — classify into UI/bug/feature request/etc.
  • extract_sentiment — score sentiment and extract key phrases
  • flag_urgent — mark reviews reporting crashes or critical issues

Implementing this with plain JSON mode would require 3 separate API calls per review. With Function Calling, Gemini infers from context that all three tools should run and returns the calls together. API call count dropped to one-third, with a corresponding reduction in cost and latency.

Where to Start

Begin with a single tool and experiment with description phrasing. Watching how changes to the "use this when..." clause affect when the model calls the tool is the fastest way to build intuition.

The official Gemini API documentation on Function Calling covers the fundamentals. The parallel execution pattern and structured error responses described here are practical additions I arrived at through real usage — consider them field notes alongside the official guide.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API / SDK2026-04-23
Parallel Function Calling in Gemini API: Production Patterns, Pitfalls, and Monitoring
A production guide to Parallel Function Calling in the Gemini API: DAG tool design, partial failure handling, rate limits, and monitoring — with working code.
API / SDK2026-09-12
gemini-2.5-flash-image stops on October 2, and the replacement the table names retired in June
gemini-2.5-flash-image shuts down on October 2, 2026, but the recommended replacement listed in the official table, gemini-3.1-flash-image-preview, was already retired on June 25. Here is the script I wrote to follow replacement chains to their end, and what it found across every row of the table.
API / SDK2026-09-07
The day Lyria 3.5 landed, I changed how my audio folders are laid out
When Lyria 3.5 brought full-length generation, my generated takes were sitting in the same folder as the tracks I had chosen by hand. Here is the forty-line ledger gate that draws the line by hash, not by filename.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links