Gemini Function Calling 工程实践
Gemini Function Calling 的入门示例通常只有四步:声明函数、让模型选择、在本地执行、把结果传回模型。真正上线后,问题却集中在示例没有展开的地方:模型选错函数怎么办,参数通过 JSON 解析却不符合业务规则怎么办,多个调用怎样并发,写操作重试会不会重复扣款,以及工具结果多大才不会挤爆上下文。
先明确责任边界:模型返回的是结构化的调用建议,不会替应用安全地执行你的业务代码。函数注册、参数校验、权限判断、超时、重试和审计都属于应用层。以下字段与调用模式依据 Google 官方 Gemini API 文档整理;模型支持情况可能变化,接入时仍应以所用模型的官方说明为准。
一、把函数声明当作机器可读接口契约
函数名和 description 不是注释,而是模型路由工具时的主要信号。get_order 与 create_order 必须在名称和描述中体现读取、写入差异;“处理订单”这种宽泛描述会增加误选概率。参数应使用清楚的 JSON Schema 子集,能用 enum 表达的状态不要退化成任意字符串。
const tools = [{
functionDeclarations: [{
name: "get_order_status",
description: "按订单号查询状态。只读,不创建或修改订单。",
parameters: {
type: "OBJECT",
properties: {
order_id: {
type: "STRING",
description: "系统订单号,例如 ORD_123;不是物流单号"
}
},
required: ["order_id"]
}
}]
}];
不要让一个函数承担多个意图,例如用 manage_order(action, payload) 同时查询、取消和退款。虽然声明更短,但权限、必填字段和失败语义全被挤进一个模糊入口。更好的做法是按副作用拆分,让函数名本身帮助模型和审计系统判断风险。
二、AUTO、ANY、NONE 与 VALIDATED 怎么选
Gemini 的 function calling config 可控制模型是否以及如何调用函数。AUTO 允许模型在自然语言与函数调用之间选择,适合普通助理;ANY 要求输出函数调用,并可配合 allowedFunctionNames 缩小候选;NONE 禁止调用;VALIDATED 允许自然语言或函数调用,同时对调用进行受约束的 schema 验证。具体模式支持需查看当前 API 与模型文档。
| 场景 | 建议模式 | 额外约束 |
|---|---|---|
| FAQ 与工具混合客服 | AUTO 或 VALIDATED | 写操作二次确认 |
| 表单转结构化动作 | ANY | 只开放当前步骤函数 |
| 只读总结阶段 | NONE | 防止意外再次调用 |
| 高风险操作确认后 | ANY | allowlist 仅保留目标函数 |
模式不能替代授权。即使 ANY 只允许 refund_order,应用仍必须检查当前用户是否拥有该订单、金额是否符合规则、确认是否仍在有效期。模型约束的是输出形状,不是业务权限。
三、参数必须经过三层校验
第一层是 schema 校验,检查类型、必填字段、枚举和格式;第二层是业务校验,例如库存是否足够、日期是否在可预约区间;第三层是授权校验,确认调用者能否对目标资源执行动作。三层都通过后才能调用真实服务。
建议把模型参数视为不可信外部输入。不要把参数直接拼接到 SQL、shell 命令或 URL;不要允许模型传入内部租户 ID 覆盖认证上下文;也不要因为参数“看起来合理”就跳过服务端现有校验。
校验失败时,返回结构化且可修正的错误,而不是原始异常堆栈:
{
"ok": false,
"error": {
"code": "INVALID_DATE_RANGE",
"message": "end_date 必须晚于 start_date",
"retryable": true
}
}
这样模型可以基于明确字段修正一次。若连续产生同类无效参数,应停止工具循环并向用户澄清,避免无限自我重试。
四、并行调用要按依赖图执行
Gemini 可以在一个回合返回多个函数调用,也能组合成先后依赖的调用链。应用不能看到“多个调用”就全部 Promise.all。查询北京和上海天气彼此独立,可以并行;先查用户 ID 再读取该用户订单,后者依赖前者,必须串行。
每个调用使用模型返回的 call ID 关联结果。并行执行时,完成顺序可能与返回顺序不同,不要靠数组下标配对。为每个工具设置独立超时与并发上限,并保留部分成功信息:三个只读查询中一个超时,不一定要丢弃另外两个有效结果。
对写操作应默认串行,除非业务明确支持并发和补偿。两个看似独立的写请求可能竞争同一库存或余额;模型并不知道数据库隔离级别,执行器必须知道。
五、写操作必须具备幂等与确认机制
网络超时无法证明操作失败。调用支付服务后连接中断,盲目重试可能造成重复扣款。每个有副作用的函数都应接收或由服务端生成稳定的 idempotencyKey,并把调用结果持久化。相同业务意图再次到来时先查历史结果,而不是重新执行。
高风险动作建议分成“准备”和“提交”两步。模型先调用只读的 prepare_refund,返回金额、对象与影响;应用把确认摘要展示给用户;收到明确确认后,再把服务端生成的短期 confirmation token 交给 commit_refund。token 应绑定用户、资源、金额和过期时间,模型不能自行构造。
工具调用循环还应设置最大轮数、总耗时和累计成本预算。结束条件包括:模型返回最终文本、出现不可重试错误、达到循环上限,或需要用户补充信息。更完整的多轮编排可参考Claude 多轮 Tool Use 循环设计,其状态机原则同样适用于 Gemini。
六、工具结果应为模型裁剪,而非原样倾倒
内部 API 常返回大量字段,但模型通常只需要少数事实。把完整数据库记录、HTML 或日志原样塞回上下文,会增加成本,也可能泄露内部字段。为每个工具定义专门的 model-facing response DTO,只包含完成当前任务需要的内容。
结果应区分 ok、稳定错误码和可读摘要。分页查询返回当前页、总数近似值和下一页游标;长列表先聚合,再允许模型按需请求下一页。敏感字段在进入模型前脱敏,访问令牌、内部备注和其他租户数据永远不应成为工具结果。
同时保留两份记录:原始响应写入受控审计存储,裁剪后的响应进入模型上下文。排障时可以追溯真实执行结果,又不会让上下文承担日志仓库的职责。
七、观测指标要覆盖“选、填、执、答”
仅记录最终 HTTP 200 无法判断 Function Calling 是否可靠。完整链路至少包括四段:模型是否选对函数,参数是否有效,工具是否成功执行,最终回答是否忠实使用结果。每段都要有独立指标。
建议记录 traceId、模型请求 ID、function call ID、函数名、参数 schema 版本、校验结果、权限结果、耗时、重试次数和结果状态。参数日志需要脱敏。核心指标包括函数选择准确率、参数一次通过率、工具 P95 延迟、超时率、重复写入拦截数、平均工具轮数,以及“工具成功但最终回答错误”的比例。
离线评测集要覆盖近义工具、缺失参数、恶意参数、用户改口、多工具依赖、部分超时与重复回放。函数声明每次改动都跑同一批用例,才能知道描述优化是否真的改善选择,而不是只让某个演示问题通过。
八、上线前的工程检查表
上线前确认:函数职责单一;声明有版本;参数通过 schema、业务和权限三层校验;写操作具备幂等键;高风险动作需要显式确认;调用按依赖图调度;call ID 与结果正确关联;工具有超时、并发和循环上限;进入模型的结果已经裁剪脱敏;日志可以从用户请求追踪到最终工具结果。
最后准备降级路径。Gemini 暂时无法生成有效调用时,系统应能返回可理解的提示或转人工,而不是把内部错误展示给用户。Function Calling 的生产质量,最终取决于执行器能否把模型的不确定建议包进确定的工程约束。
九、相关阅读
如果你的应用需要用统一方式接入多家模型的工具调用能力,YoTradeApi 可提供兼容常见 SDK 的 API 接入方式,便于集中管理鉴权与用量。