Google Gemini 函数调用(Function calling)使用笔记

下面给你一份「从零开始」的 Google Gemini 函数调用(Function calling)使用笔记,以 Python 代码为主,涵盖最常用的「自动生成 JSON Schema→注册函数→让模型决定调不调用→把结果喂回去继续生成」四步流程。不管你用的是免费版 Gemini 1.5 Flash / Pro,还是 v1beta 新模型,流程都是一致的。

────────────────────

  1. 前置准备

────────────────────

pip install -q google-generativeai

如果你在中国大陆,代理 / VPN 是必须条件。

export GOOGLE_API_KEY="你的KEY"

────────────────────

  1. 声明可被调用的函数

────────────────────

记住,Gemini 只负责「产生 JSON 调用参数」,真正的执行代码一定要由你在本地或云端跑。

下面例子写两个道具函数:实时查汇率、查当前天气。

  
import google.generativeai as genai
  
import datetime, requests
  

  
genai.configure(api_key="你的KEY")
  

  
# ------------ 函数1:汇率 ------------
  
def get_exchange_rate(base: str, target: str) -> float:
  
    """
  
    查询 base->target 的实时汇率(依赖免费 demo API)
  
    """
  
    url = f"https://api.exchangerate.host/latest?base={base}"
  
    data = requests.get(url, timeout=5).json()
  
    return data["rates"].get(target.upper())
  

  
# ------------ 函数2:天气 ------------
  
def get_current_weather(location: str, unit="celsius") -> str:
  
    """
  
    伪·天气查询;真正生产环境应调用和风、OpenWeatherMap 等
  
    """
  
    mock_weather = {
  
        "北京": {"temp": 32, "unit": unit},
  
        "上海": {"temp": 30, "unit": unit},
  
    }
  
    info = mock_weather.get(location, {"temp": 22, "unit": unit})
  
    return f"{location} 当前 {info['temp']}°{info['unit'][0].upper()}"
  

────────────────────

  1. 写成 OpenAPI-like JSON Schema

────────────────────

这是 2024 年 6 月新 API 的一致做法:把每个函数元数据写进 tools=[{"function_declarations": [...]}] 里即可。

  
tools = [
  
    {
  
        "function_declarations": [
  
            {
  
                "name": "get_exchange_rate",
  
                "description": "查询两种货币之间的实时汇率。",
  
                "parameters": {
  
                    "type": "object",
  
                    "properties": {
  
                        "base": {"type": "string", "description": "基准货币代码,如 USD、CNY"},
  
                        "target": {"type": "string", "description": "目标货币代码"}
  
                    },
  
                    "required": ["base", "target"]
  
                }
  
            },
  
            {
  
                "name": "get_current_weather",
  
                "description": "根据城市名称查询当前天气",
  
                "parameters": {
  
                    "type": "object",
  
                    "properties": {
  
                        "location": {"type": "string"},
  
                        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius"}
  
                    },
  
                    "required": ["location"]
  
                }
  
            }
  
        ]
  
    }
  
]
  

────────────────────

  1. 建立模型 & 聊天并“截获”调用意图

────────────────────

核心就是 loop:

(1) 发用户问题 → (2) 收到 function_call → (3) 本地跑函数 → (4) 把函数结果以 function_response 形式再发回去 → (5) Gemini 基于返回值继续生成自然语言答案。

  
model = genai.GenerativeModel(
  
    model_name="gemini-1.5-flash",
  
    tools=tools
  
)
  

  
chat = model.start_chat(enable_automatic_function_calling=False)   # 我们手动控制循环
  
user_message = "10 USD 能换多少人民币?顺便告诉我北京现在多少度?"
  

  
# ---------- 第一回合 ----------
  
resp1 = chat.send_message(user_message)
  
print("第一轮模型回复候选:", resp1.candidates[0].content.parts)
  

  
# Gemini 可能一次就想调用多个函数,所以 parts 里会有多个 FunctionCall
  
for part in resp1.candidates[0].content.parts:
  
    if fn_call := part.function_call:
  
        name = fn_call.name
  
        args = {k: v for k, v in fn_call.args.items()}
  
        print(f"模型想调用的函数:{name}({args})")
  

  
        # 真正在本地跑
  
        if name == "get_exchange_rate":
  
            ret = get_exchange_rate(**args)
  
        elif name == "get_current_weather":
  
            ret = get_current_weather(**args)
  

  
        # 把结果封装成 function_response,发送回模型
  
        chat.send_message(
  
            genai.protos.Content(
  
                parts=[genai.protos.Part(
  
                    function_response=genai.protos.FunctionResponse(
  
                        name=name,
  
                        response={"result": ret}
  
                    )
  
                )]
  
            )
  
        )
  

  
# ---------- 第二回合(自然语言回答) ----------
  
final = chat.send_message("请把上面结果用中文一句话总结。")
  
print(final.text)
  

运行示例输出:

  
10 USD ≈ 72.4 CNY, 北京目前 32°C
  

────────────────────

  1. 让模型“全自动”执行(可选)

────────────────────

如果你不想手写循环,可以:

  
model = genai.GenerativeModel(
  
    model_name="gemini-1.5-flash",
  
    tools=tools
  
)
  
chat = model.start_chat(enable_automatic_function_calling=True)   # 让它自己循环!
  
print(chat.send_message("同样的问题").text)
  

只是要注意真正的函数还是在你本地/云函数里执行,自动模式也只是帮你把上面的四步隐藏掉而已。

────────────────────

  1. 「工具链」小贴士

────────────────────

  • 当一次问题触发多个函数(上面示例就是 2 个),Gemini 1.5 Flash 已经支持并行 function_call

  • 如果模型对参数理解不对,你可以在 description 里加「绝对不要使用简称,必须传完整参数名」等提示词。

  • 为了提升稳定性,强烈把「系统提示」写出来:

    
    system_instruction="你是一个懂得在必要时调用工具提高准确度的助手。"
    
  • token 额度:函数声明本身也会占输入 token(但不会占用 Assistant 轮次)。如果接口返回 429,适当减少参数数量、缩短 description。

  • json 返回值尽量扁平,不要嵌套三层字典,否则长上下文里容易触发 MalformedFunctionResponse 报错。

────────────────────

一句话总结

────────────────────

写函数 → 配 JSON Schema → 让 Gemini 生成 FunctionCall → 你写代码执行 → 把返回值扔回聊天上下文。跑来跑去就这么 4 步,剩下的就是工程封装 & 错误兜底。祝你玩得开心!