
谷歌AI 最近的产品节奏可以读成一次典型的分权与收权动作搜索、浏览器、办公套件、手机系统都还在但原本分散在各自产品里的智能入口正在逐步收拢到 Gemini 这个统一模型层。历史故事里的“杯酒释兵权”是皇帝用一场酒宴拿回各地兵权谷歌AI 做的事情很像不颠覆原有产品形态却把真正的决策权交还给一个统一模型让页面、按钮、接口都变成执行层。对开发者来说这不是一条新闻标题而是 AI 应用架构的具体变化。以前做 AI 功能核心工作是“调一个聊天接口”现在做 AI 功能核心工作是“设计一个有边界的 Agent”。模型需要被允许调用工具工具需要被限制在业务规则内调用过程还要能被日志追踪、被成本控制、被异常处理兜底。这篇文章把这条主线落到代码上基于 Gemini 的 Function Calling 能力从环境准备开始实现一个最小可运行的 AI Agent并说明参数、排查和上线前加固的关键点。1. 从“杯酒释兵权”理解谷歌AI的技术主线1.1 这不是产品改名是控制权迁移“杯酒释兵权”里的核心动作不是把将领免职而是把分散在地方将领手里的军事权力收回中央再用一套新的制度重新分配。放在谷歌AI 语境里模型层就是新的中央旧产品就是原来的地方势力。最典型的变化是用户看到的是搜索框、浏览器工具栏、文档里的“帮我写”但背后真正完成意图理解的都是同一个模型入口。以前不同产品各自训练模型、各自维护提示词、各自处理上下文后续演变成统一模型后产品层的职责从“理解用户”变成了“承接用户输入并展示结果”。对应用开发者来说这种控制权迁移意味着不要把模型当成一个黑盒接口而要把它当成一个“会做决策的调度者”。真正决定业务正确性的是你给这个调度者提供了哪些工具、设置了多少轮循环、在什么条件下允许它执行操作。1.2 AI Agent 是新的“兵权单元”传统应用里用户的每次操作都对应一段确定逻辑点按钮、查数据库、返回结果。AI Agent 不太一样它先接收一个目标再由模型判断该调用哪个工具、观察工具返回结果、决定是否继续下一步。下面用表格看传统 AI 应用和 Agent 应用的区别维度传统 AI 应用Agent 应用用户输入明确的指令或表单目标型描述可能含糊处理方式代码写死流程模型规划 工具调用输出结果固定格式动态文本或真实操作可控性高需要边界和权限约束排查方式看代码分支看模型决策和工具日志这就是“兵权”的转移模型拥有决策权执行权放在工具函数里。工具函数是兵模型是将军开发者才是最终授权的人。你不把不安全的操作声明给模型模型就永远无法调用它。1.3 对开发者的三个直接要求围绕这种变化开发工作不再只是写提示词。至少有三个点必须重新设计。第一工具边界。每个工具函数都必须有明确用途、参数校验和权限校验。模型只会根据函数名和描述决定是否调用它不会替你做安全判断。第二循环控制。Agent 会有多轮“模型决策 - 工具执行 - 结果回填”的循环。没有最大轮数、超时时间和终止条件模型可能会反复调用工具造成成本和资源浪费。第三可观测性。模型内部怎么思考无法完全控制但“模型返回了什么 functionCall”“工具执行后返回了什么结果”这些信息是可记录的。把这条链路记录下来线上出问题才能定位。2. 准备 Gemini 开发环境API Key、模型与连通性2.1 环境要求清单下面示例使用 Gemini API 的原生 HTTP 方式先把原理跑通再考虑 SDK 或 Spring AI 封装。这样能避免不同 SDK 版本差异影响对核心逻辑的理解。项目要求网络能访问generativelanguage.googleapis.com的 API 端点API Key在 Google AI Studio 控制台创建Python3.10 或更高版本依赖requests库模型名以官方模型列表为准例如gemini-2.0-flash安装依赖pip install requests如果是公司网络或本地网络对 API 端点有访问限制要先解决网络可达性再用下面的 curl 命令验证。网络不通时后续所有请求都会卡在连接阶段和代码无关。2.2 用 curl 验证模型连通性先设置环境变量避免把密钥写死在命令记录里export GEMINI_API_KEY你的 API Key然后发送一个最简单的对话请求curl -s -X POST \ https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent \ -H Content-Type: application/json \ -H x-goog-api-key: ${GEMINI_API_KEY} \ -d { contents: [ { role: user, parts: [ {text: 用一个短句解释 AI Agent} ] } ] }正常响应会包含candidates数组里面是模型生成的内容{ candidates: [ { content: { role: model, parts: [ { text: AI Agent 是一个能自主规划、调用工具并根据结果继续执行的智能程序。 } ] }, finishReason: STOP } ] }这里的关键点是x-goog-api-key请求头。很多新手把它放在 URL 上也能工作但统一用请求头传递更利于后续接入网关和密钥管理。2.3 先在 AI Studio 里验证功能和提示词写代码之前建议先在 AI Studio 的界面里试一遍提示词和工具声明。AI Studio 能直接看到模型在什么输入下会调用工具、什么输入下不会调用这对于调整工具描述非常有帮助。学习阶段的重点是快速跑通不需要考虑高并发和高可用。但要注意AI Studio 创建的 API Key 是开发阶段的密钥进入生产环境前需要纳入正式的密钥管理流程并定期轮换。3. 用 Function Calling 实现一个最小 AI Agent3.1 Agent 需要哪些组件一个最小 Agent 不需要很复杂但必须包含四部分模型入口负责接收对话上下文返回文本或函数调用指令。工具声明用 JSON Schema 描述函数名、参数和用途。工具执行器在本地真实调用函数并返回给模型。循环控制在最大轮数内重复“模型调用 - 工具执行 - 结果回填”。下面用一个“服务状态查询和重启”的例子演示完整流程。3.2 定义工具声明工具声明是一份给模型看的说明书。模型不会读取你的 Python 代码它只根据这段 JSON 结构决定要不要调用函数。tools [ { functionDeclarations: [ { name: get_service_status, description: 查询指定服务的当前状态返回 running、degraded 或 stopped。, parameters: { type: object, properties: { service_name: { type: string, description: 服务名例如 user-service、payment-service } }, required: [service_name] } }, { name: restart_service, description: 重启指定服务。仅当服务状态为 degraded 或 stopped 时使用。, parameters: { type: object, properties: { service_name: { type: string, description: 要重启的服务名 } }, required: [service_name] } } ] } ]这里要注意两点。第一description要写清楚“什么条件下该调用”。第二个工具的描述里明确写了仅在 degraded 或 stopped 时使用这能减少模型乱调用。第二参数尽量用required约束。缺参会让模型在后续生成里强行猜测增加错误结果概率。3.3 实现 Agent 循环完整脚本如下import requests GEMINI_API_KEY YOUR_API_KEY MODEL_NAME gemini-2.0-flash GEMINI_URL fhttps://generativelanguage.googleapis.com/v1beta/models/{MODEL_NAME}:generateContent MAX_AGENT_ROUNDS 3 tools [ { functionDeclarations: [ { name: get_service_status, description: 查询指定服务的当前状态返回 running、degraded 或 stopped。, parameters: { type: object, properties: { service_name: { type: string, description: 服务名例如 user-service、payment-service } }, required: [service_name] } }, { name: restart_service, description: 重启指定服务。仅当服务状态为 degraded 或 stopped 时使用。, parameters: { type: object, properties: { service_name: { type: string, description: 要重启的服务名 } }, required: [service_name] } } ] } ] def call_model(contents): response requests.post( GEMINI_URL, headers{Content-Type: application/json, x-goog-api-key: GEMINI_API_KEY}, json{contents: contents, tools: tools}, timeout60 ) response.raise_for_status() return response.json() def get_service_status(service_name): fake_status { user-service: degraded, payment-service: running, ai-service: stopped } return {result: fake_status.get(service_name, unknown)} def restart_service(service_name): return {result: f{service_name} restart request accepted} def execute_tool(name, args): if name get_service_status: return get_service_status(args[service_name]) if name restart_service: return restart_service(args[service_name]) return {error: funknown tool: {name}} def run_agent(user_text): contents [{role: user, parts: [{text: user_text}]}] for _ in range(MAX_AGENT_ROUNDS): data call_model(contents) candidate data[candidates][0] model_content candidate[content] parts model_content.get(parts, []) function_calls [part for part in parts if functionCall in part] if not function_calls: return .join(part.get(text, ) for part in parts) # 把模型的 functionCall 放回会话上下文 contents.append(model_content) # 执行模型请求的每个函数 for fc in function_calls: fname fc[functionCall][name] fargs fc[functionCall].get(args, {}) result execute_tool(fname, fargs) contents.append({ role: function, parts: [ {functionResponse: {name: fname, response: result}} ] }) raise RuntimeError(fAgent 在 {MAX_AGENT_ROUNDS} 轮内没有结束) if __name__ __main__: query user-service 现在是什么状态如果它 degraded就重启它。 print(run_agent(query))这个脚本的核心是run_agent里的循环。模型第一次返回的通常是一个functionCall脚本拿到函数名和参数后执行本地函数再把functionResponse追加回contents。下一次请求模型时模型能看到工具执行结果从而决定是继续调用工具还是输出最终文本。生产环境要注意工具函数里的模拟数据必须替换成真实服务并且在执行操作前加上权限校验。示例里的restart_service是危险操作真实项目绝不能无条件执行。3.4 运行与预期输出保存为gemini_agent.py后执行python gemini_agent.py正常输出类似user-service 当前状态为 degraded已发送重启请求。如果模型返回的不是一句话而是 JSON 里的functionCall也不要紧张。这是正常流程。模型在一次请求中说“我需要调用某个函数”应用层执行完后再把这个结果交还给模型模型才能给出最终回复。验证时建议打开调试在call_model返回后加一行print(data)。这样能清楚看到模型是先返回函数调用还是直接返回文本。很多 Agent 问题都出在这一步你以为模型没调用工具其实工具结果没有正确回填。4. 关键参数与工具配置详解4.1 generationConfig 中的关键参数Gemini API 请求体里可以携带generationConfig用来控制模型输出的确定性、长度等表现。参数作用建议temperature控制随机性值越高输出越发散工具调用场景建议设置为 0 到 0.3topP核采样控制候选词累计概率与 temperature 不要同时大力调整maxOutputTokens限制单次输出最大 token 数Agent 多步推理可用 1024复杂场景调大stopSequences遇到指定字符串停止生成用于解析结构化输出时很有用工具调用场景中随机性太强会导致模型频繁选择错误工具。只要能稳定完成任务temperature越低越好。一个带参数的请求体示例{ contents: [], tools: [], generationConfig: { temperature: 0.2, topP: 0.8, maxOutputTokens: 1024 }, toolConfig: { functionCallingConfig: { mode: AUTO } } }4.2 toolConfig 控制模型调用工具的力度toolConfig.functionCallingConfig.mode控制模型是否必须调用工具常用三种模式模式行为适用场景AUTO模型自主决定调用哪个工具或不调用默认场景推荐ANY模型必须调用其中一个工具跳过思考直接执行工具时使用NONE禁止调用任何工具只做普通文本生成时使用在排查“模型不调用工具”的问题时可以把AUTO临时改成ANY确认工具链路本身没有问题。但最终生产环境还是推荐AUTO给模型判断余地。4.3 工具声明越细Agent 越稳定工具声明本质上是给模型看的接口文档。描述含糊模型就会猜。对比一下错误描述查询服务状态。 正确描述查询指定服务的当前状态返回 running、degraded 或 stopped。查询前不要修改任何数据。正确描述里既包含返回值格式也包含行为边界。参数也尽量用具体枚举或正则提示例如“只允许字母、数字、中划线”。这样能显著减少模型生成非法参数的概率。尽量不要把多个操作塞进一个函数。比如“查询并重启服务”看起来省事但会让模型丧失区分场景的能力。每个工具只做一件事描述和参数都会更清晰。5. 常见问题排查从现象倒推根因5.1 API Key 与网络问题如果请求返回 401 或连接超时先不要调试模型逻辑按顺序检查三个点网络能否到达 API 端点、API Key 是否正确、请求头是否携带了x-goog-api-key。问题现象可能原因检查方式处理建议401 UNAUTHENTICATEDAPI Key 无效或未传递打印请求头确认 key 值在 AI Studio 重新创建 Key连接超时网络无法访问 API 端点curl -v观察连接阶段先解决网络可达性429 RESOURCE_EXHAUSTED免费额度用完或触发限流查看用量和错误体 timeout等待后重试启用计费增加退避5.2 Function Calling 返回异常如果模型返回了functionCall但后续请求报400 INVALID_ARGUMENT绝大多数情况是contents顺序或 role 不对。正确的结构必须是user - model(带 functionCall) - function(带 functionResponse) - model(最终文本)model的 functionCall 和function的 functionResponse 必须成对出现。打印完整contents是最快的定位方式import json print(json.dumps(contents, ensure_