模型决定“建议调用什么函数”,应用决定“是否真的调用”。这句话是 Agent 安全的底线。以“给客户发送回访邮件”为例,模型可能正确识别了意图,也可能被一段网页文本诱导去调用删除工具。解决办法不是写更长的提示词,而是给工具调用加独立的白名单、参数校验、金额/次数预算和人工审批。下面的 Python 程序不依赖真实密钥就能跑通这层门禁。
Gemini API 的函数调用文档把流程拆成声明函数、模型提出调用、应用执行函数、回传结果四步,并明确工具由应用执行。也正因为如此,函数调用不是授权系统。本文的示例模拟模型输出;接入 API 后只需把模型返回的函数名与参数送入 authorize,不要跳过它。

运行前准备
Python 3.10+,无第三方依赖。保存为 tool_gate.py 后运行 python tool_gate.py。若接入 Gemini,安装 python -m pip install google-genai,并把密钥放在 GEMINI_API_KEY;本示例没有调用线上 API,已在本机实际运行并通过三条断言。当前函数调用文档适用于模型把工具建议交给应用处理的流程。
from dataclasses import dataclass
from typing import Any
@dataclass
class Decision:
allowed: bool
reason: str
ALLOWED = {"search_kb", "draft_email"}
MAX_SEARCHES = 3
def authorize(name: str, args: dict[str, Any], searches: int) -> Decision:
if name not in ALLOWED:
return Decision(False, "工具不在白名单")
if name == "search_kb":
query = args.get("query", "")
if not isinstance(query, str) or not query.strip() or len(query) > 120:
return Decision(False, "查询参数非法")
if searches >= MAX_SEARCHES:
return Decision(False, "搜索预算已耗尽")
if name == "draft_email":
if set(args) != {"customer_id", "template"}:
return Decision(False, "邮件参数必须精确匹配")
if args["template"] not in {"follow_up", "receipt_request"}:
return Decision(False, "模板未批准")
return Decision(True, "允许;发送前仍需人工确认")
def execute(call: dict, searches: int) -> dict:
decision = authorize(call["name"], call["args"], searches)
if not decision.allowed:
return {"status": "blocked", "reason": decision.reason}
return {"status": "needs_approval", "tool": call["name"], "reason": decision.reason}
if __name__ == "__main__":
print(execute({"name":"search_kb", "args":{"query":"退款政策"}}, 0))
assert execute({"name":"delete_customer", "args":{}}, 0)["status"] == "blocked"
assert execute({"name":"search_kb", "args":{"query":"x"}}, 3)["status"] == "blocked"
assert execute({"name":"draft_email", "args":{"customer_id":"c1","template":"follow_up"}}, 0)["status"] == "needs_approval"
这段代码的关键是不信任模型:ALLOWED 把可调用范围缩小为两个工具;邮件工具要求参数集合完全匹配,避免多塞一个收件人或“立即发送”开关;搜索有累计上限。execute 即使允许,也只产生 needs_approval,不发送邮件。实际调用 API 时,还应给每个 tool call 关联用户身份、租户、请求ID和幂等键。
预期输出第一行是 needs_approval;删除客户和超预算搜索均为 blocked。常见错误:把工具描述写得很细就以为安全;只校验函数名不校验参数;重试时重置预算;把模型输出当作可信 JSON。适合知识检索、草稿生成和低风险查询;不适合无审批地付款、删除数据、修改权限或向外部发送不可撤回消息。
工程化可进一步加入 JSON Schema、审计事件、租户级速率限制、审批时效和回放测试。5分钟实践:新增 create_refund_draft,规定金额超过100元或缺少订单号时必须返回 needs_approval。
在你的业务里,哪一个工具调用必须永远保留人工确认?
关注「蜗牛聊AI」,一起看懂技术变化背后的真正机会。
本文首发于 java4u.cn,转载请注明出处。

