从零构建Gemini 3 Agent:工具调用与循环实战手记 几个月前我在某个技术社区刷到一个问题帖主说自己读了一堆Agent教程结果连第一个能自动调用搜索的Demo都没跑通。下面的回复五花八门但仔细看下来核心问题只有一个——大部分人把Agent想得太复杂了。以Gemini 3现在的工具调用能力和长上下文处理能力从零构建一个能用、能调试、能持续迭代的Agent门槛已经比一年前低了一个数量级。这篇文章是我多次从空白项目开始搭建Agent后沉淀下来的完整手记按照理解原理、搭骨架、加工具、管记忆、处理复杂任务、排查问题这条路线来展开。你不需要懂分布式系统不需要自己训练模型只要会Python基础语法、用过一次大模型API就能跟着这套思路跑出一个真正干活的Agent。先说结论Agent本质就是“大模型 工具 一个循环”。Gemini 3负责理解和决策工具负责动手执行循环则负责把两者的结果反复喂回去直到任务完成。听起来简单但里面每一个环节都有无数的坑等着你踩。下面我把每个部分拆开讲清楚。1. 动手之前先把Agent翻译成一个看得懂的程序1.1 核心需求解析你真正要构建的东西是什么我见过太多人一上来就套各种高大上框架结果连最基本的请求都没调通。我自己更习惯把Agent拆成三件套来看模型、工具、循环。模型就是大脑负责理解用户的原话、拆解意图、决定下一步调用哪个工具、传什么参数。Gemini 3这代模型在函数调用上的结构化输出做得比较稳意味着你不需要做任何微调只要在请求里把工具清单传进去它就能返回一个类似“我要调用某个函数参数如下”的决策结果。工具就是手脚负责执行模型给出的指令。比如查数据库、请求外部接口、读文件、发邮件甚至调用另一个模型。工具可以是任意一段代码只要它接收结构化输入、给出结构化输出就行。循环就是把大脑和手脚串起来的机制。一次请求根本算不上Agent真正的Agent是一个循环模型输出决策程序执行决策把执行结果放回上下文再让模型观察结果、决定下一步。这个循环一直持续到模型认为任务完成或者达到你设置的最大步数。用代码来表达的话最小的Agent骨架就是这个样子def run_agent(user_input, max_steps10): messages [{role: user, content: user_input}] for step in range(max_steps): response client.chat(messagesmessages, toolsTOOL_LIST) if response.finish_reason stop: return response.content messages.append(response.message) messages.extend(execute_tool_calls(response.tool_calls)) raise TimeoutError(agent 超过最大步数)很多人一听Agent就是循环会觉得我在简化问题但实际上这就是全部真相。那些看起来复杂的产品无非是在循环里加了更多工具、更多记忆、更多安全策略。底层逻辑没变。1.2 该不该做Agent先判断场景值不值得并不是所有需求都适合套Agent。我自己的判断标准有三个任务是否有多个子步骤、子步骤之间是否依赖中间结果、是否允许有几十秒到几分钟的执行时间。如果三个都是肯定答案那Agent很合适如果只是单次问答、固定公式计算、延迟要求几百毫秒的场景用传统程序或者直接调一次模型就够了别给架构加戏。举个例子有个朋友曾经想把整个客服系统全部做成Agent让机器直接操作后台给用户退款。我把他的需求拆开一看大部分工单只需要一个规则查询根本不需要Agent直接写SQL就能搞定。后来我们把真正需要多轮推理和工具调用的场景剥出来做Agent剩下的事情仍然走传统接口整体又稳又省成本。做Agent之前先冷静一下如果这个问题用十行代码就能解决就别折腾模型了。Agent的价值在于处理“事先无法穷举步骤”的任务而不是处理“你确定下一步是什么”的任务。2. 环境与骨架用一个最小循环把Agent跑起来2.1 准备API Key与开发环境把密钥藏好依赖隔离干净实战的第一步永远是环境。我自己习惯新建一个项目目录用虚拟环境把依赖隔离起来避免不同的项目互相污染。命令就那么几条mkdir agent-starter cd agent-starter python -m venv .venv source .venv/bin/activate pip install gemini3-sdk python-dotenv然后把API Key放进.env文件而不是写死在代码里。这个习惯非常关键。我见过不止一次有人图省事把密钥写进脚本转头就把代码传到公共仓库几分钟后密钥就被扫描机器人抓走了。用dotenv加载的话代码里只有一行from dotenv import load_dotenv load_dotenv()之后你就可以通过os.getenv(GEMINI3_API_KEY)来读取密钥。做这一步的意义不只是安全也方便换密钥、换环境不用改代码。2.2 第一段Agent代码Hello Agent依赖装好之后先写一个最简单的版本目的只有两个验证密钥能不能用、验证请求链路是否通畅。import os from dotenv import load_dotenv from gemini3 import Gemini3Client load_dotenv() client Gemini3Client(api_keyos.getenv(GEMINI3_API_KEY)) response client.chat( messages[{role: user, content: 你好请用一句话回答Agent是什么}] ) print(response.content)跑通这一步你的Agent之路才算正式开始。如果连这个都报错先检查密钥、网络、模型名别急着写业务逻辑。2.3 理解Agent循环的四个步骤跑通基础请求以后要做的事情就是给循环里加入工具处理。一个完整的Agent循环可以分为四步第一步把对话历史、系统提示词、工具清单一起发给模型。模型不是直接告诉你答案而是可能返回一个工具调用请求也可能直接返回最终文本。第二步程序解析模型的输出。如果finish_reason是stop说明模型认为任务完成可以直接把结果交给用户如果是tool_call说明模型想要执行某个工具。第三步执行工具。程序根据返回的函数名和参数调用本地对应的Python函数把执行结果转成可以被模型理解的结构化数据。第四步把工具执行结果追加到对话历史里再发回给模型。模型会看到工具返回了什么然后继续判断是继续调用工具还是最终收尾。这一步我建议你打印出每一轮的调用信息比如模型选择了哪个工具、传了什么参数、工具返回了什么。肉眼看到整个决策链条之后很多“为什么Agent会这么干”的疑惑会直接消失。3. 给Agent加工具从“能聊天”到“能干活”的分水岭3.1 什么是工具调用让模型学会动手而非空谈一个只有对话能力的模型充其量是陪聊机器人。Agent和聊天机器人的最大区别就是工具调用。工具调用的本质是把“需要人为编码的逻辑”以函数的形式暴露给模型模型通过结构化的方式表达执行意图然后由你的程序真正去执行。打个比方你问模型“某城市今天适合穿什么衣服”模型自己是不知道天气数据的。如果你给它一个天气查询工具它就会输出“我要调用get_weather参数是城市名”。你的程序收到这个意图后去请求天气服务把数据返回给模型模型再基于真实数据生成穿衣建议。Gemini 3在工具调用上的返回格式是结构化的JSON典型长这样{ tool_call: { name: get_weather, arguments: {city: 某城市, date: 2026-05-01} } }你的代码不用去解析自然语言直接根据这个JSON做映射就可以。3.2 工具函数设计描述写得好成功率才能上来工具函数的设计质量直接影响Agent成功率的百分之八十。我自己总结了几条硬规矩工具名称要动词开头、含义准确比如query_order_status、send_email_notification别用什么do_thing之类的模糊名称。工具描述要像写接口文档一样详细。模型是通过描述来理解“这个工具什么时候该用”的。描述里最好写清楚适用条件、参数含义、返回字段的说明。比如tools [ { name: get_weather, description: 根据城市名和日期查询未来三天的天气。适用于用户询问气温、降雨、穿衣建议等场景。, parameters: { type: object, properties: { city: {type: string, description: 城市中文名例如上海}, date: {type: string, description: 日期格式YYYY-MM-DD} }, required: [city, date] } } ]你看到这里可能会觉得麻烦但我的经验是描述越具体模型选错工具的概率越低。尤其当工具变多的时候两个功能相近的工具最容易让模型混淆。此时你要在描述里明确写出“什么情况用这个而不是那个”。工具返回必须是结构化数据最好直接用JSON。返回内容不要啰嗦不要给模型大段渲染后的文本而是给原始数据加最少量的说明。模型擅长从结构化数据中提取信息你给它大段文本反而容易增加幻觉风险。3.3 参数校验与异常返回别让Agent在同一个坑里反复摔模型再聪明也会有传错参数的时候。比如用户说的是“某城市”三个字模型可能在参数里带上了“天气”两个字。这属于正常现象你不需要去怪模型而是要在代码层做双重保险。我推荐在工具函数内部做参数校验用简单的类型检查和必填字段检查就够了。如果参数不合法直接抛出一个自定义异常然后把这个异常信息转换为结构化结果返回给模型。关键点在于返回给模型的错误信息不要太短至少要说清楚“错在哪、正确的参数应该是什么样子”。这里有一个我踩过很多次的坑工具异常返回后模型会倾向于反复调用同一个工具因为它以为自己刚才只是运气不好。为了减少这种死循环我会在错误信息里明确给出替代方案例如{ status: error, message: 查询失败订单号格式不正确。请检查订单号是否为13位数字或者提醒用户重新提供订单号。 }这样一来模型就有了下一步的动作依据而不是原地打转。4. 记忆与上下文管理别让Agent的“脑子”被撑爆4.1 大上下文不等于无限能力Gemini 3拥有比前代更大的上下文窗口这让很多开发者产生了“我可以把整个产品文档全塞进去”的错觉。但实际用下来你会很快发现上下文很长的时候模型在两个位置容易出问题一是注意力被海量历史稀释模型忘掉了最初的用户约束二是Token成本和响应延迟急剧上升。我曾经在一个测验项目里把十几页产品需求原文全部塞进上下文结果Agent在执行到第五步时完全不记得用户最开始说过“只要电子表格不要PDF”。这个现象不是偶然而是长上下文带来的典型问题。上下文管理不是优化项而是必需品。4.2 三种常用记忆方案怎么选我整理了一张对照表日常决策基本看这个就够记忆方案适用场景优点缺点全量历史记录会话短、任务步骤少实现最简单不丢任何信息长会话后Token成本高注意力被稀释摘要压缩中等长度会话、多轮任务信息密度高上下文占用大幅降低压缩过程会丢失细节模型摘要本身有误差向量检索记忆知识问答、长期个人助理可扩展按需拉取相关片段需要额外搭建检索组件有索引和存储成本我自己的经验是大部分业务场景用“摘要压缩 滑动窗口”组合就足够了不一定非要上向量检索。向量检索适合的是“知识量非常大、需要在海量资料中捞证据”的场景比如企业知识库问答。普通业务Agent硬上向量检索反而增加了复杂度收益很有限。4.3 实战做法滑动窗口加摘要压缩先说滑动窗口不管对话多长只把最近几轮消息原样保留更早的内容全部移出上下文。这个方案能有效防止模型忽视近期的用户指令。但纯滑动窗口有一个问题你可能把重要背景信息丢掉了。这时候就需要摘要压缩来弥补。思路是当对话历史超过N条时让模型把旧内容整理成一段摘要摘要里保留关键决策、用户偏好、已经完成的事项然后把摘要放在系统提示词里近几轮消息放在对话里。写个示意代码def compact_memory(messages, keep_last6, summary): if len(messages) keep_last: return messages, summary recent messages[-keep_last:] history messages[:-keep_last] if history: comp client.chat([ {role: system, content: 把用户与助手的对话压缩成中文摘要保留关键事实、决策和未完成事项。不要遗漏用户明确提出的要求。}, {role: user, content: json.dumps(history, ensure_asciiFalse)} ]).content summary comp system_prompt f以下是此前对话的摘要作为长期记忆参考{summary} return [{role: system, content: system_prompt}] recent, summary这个函数每次对话结束后调用既能保住重要信息又不会让上下文无限膨胀。不过我给你提个醒摘要过程本身会消耗一定Token不要在每个环节都压缩而是设置一个阈值比如历史超过十二轮再触发。5. 复杂任务与多轮规划Agent自己怎么拆解问题5.1 让Agent先规划再执行当一个任务足够复杂时最怕的就是模型走一步看一步走到一半丢了主线。我建议你在系统提示词里明确要求模型在执行前先做任务拆解。这不是什么高深技巧就是一次提示工程。一段我经常用的系统提示词是这样的你是一个能调用工具的智能助手。在收到复杂任务时请先输出任务拆解计划说明你将分几步完成、每一步依赖哪些工具。 执行过程中每完成一步请用以下格式汇报 第X步 / 执行结果 / 下一步计划 直到所有步骤完成再输出最终结论。有人说这个要求模型不一定遵守实测下来Gemini 3对这类结构化指令的遵循度确实不错。更重要的是一旦模型输出了拆解计划你可以把这段计划保存在上下文里这样它后续就算跑偏也比较容易在提示下回到主线。5.2 循环上限与中断策略别让Agent无限空转Agent循环如果没有任何约束遇到工具异常或者模型误判时可能陷入长时间空转。我的习惯是一开始就给循环设三个保险。第一个保险是max_steps当作硬性上限。比如最多执行十五步超过直接终止。第二个保险是重复工具检测统计同一个工具被调用的次数连续调用超过三次且结果一直失败就停止循环并转入人工接入流程。第三个保险是超时控制整个Agent执行设置一个总超时时间比如三十秒超过就返回当前进度而不是继续傻等。终止策略里有个容易被忽视的细节当Agent提前终止时不要只返回一句“抱歉我失败了”。更好的做法是让它先把当前已经完成的部分整理成进度报告再说明卡在哪一步、需要用户提供什么补充信息。这样用户面对的不是黑盒而是清晰的问题边界。5.3 让Agent学会“停下来问人”很多研发人员做Agent时会有一种执念让模型自己搞定一切。这其实是个危险倾向。对于删除操作、发送消息、修改订单、支付退款这类敏感动作Agent不应该有“最终决定权”。我推荐的方式是把“人工确认”设计成一个工具。比如def ask_user_for_confirmation(description: str): # 向用户展示操作说明等待用户输入确认或拒绝 return {status: waiting, confirmation_required: description}当Agent判断某个操作需要用户确认时它会先调用这个工具你的程序把确认问题推给用户拿到用户答复后再把结果放回上下文Agent收到“用户已同意”才能继续后续动作。这套机制的好处是安全策略从提示词层面落到了代码层面而不是寄希望于模型自觉。6. 常见问题排查与避坑速查6.1 模型返回的工具调用参数不规范这个问题出现的频率非常高。现象是模型在arguments里多传字段、少传字段、类型搞错。原因往往是工具的JSON Schema描述不够严格或者用户需求本身存在歧义。我的解决办法有三层第一层把工具Schema里的每个字段都写清格式约束枚举值直接用enum限定第二层在代码里做参数校验不要模型传什么就信什么第三层把校验失败信息返回给模型让模型自己修正。三层叠加之后参数出错率能降到很低。6.2 Agent反复调用同一个工具陷入死循环这是我被问得最多的问题之一。最常见的一个案例是Agent查询订单状态失败后不换思路连续五次调用同一个查询接口最后把Token花光任务还没完成。问题出在工具返回信息里缺少“下一步该做什么”。工具返回错误时系统要主动给模型提供新的路径。比如原本查询订单接口返回“订单不存在”错误信息里就要加上“可以选择创建新订单、查看历史订单列表、或者询问用户订单号是否填正确”。有了这些提示模型大概率会换一个方向继续尝试而不是原地重试。6.3 Agent忘记了最初的用户约束长会话里用户最初说的一句话可能决定整个任务的走向。比如用户说“不要发送邮件只生成草稿”结果Agent在后半段直接点了发送按钮这就是严重事故。我的做法是把关键约束“制度化”一开始从用户的原话里提取出限制条件写入系统提示词并且每次执行敏感工具之前由代码自动把约束和这次操作放在一起做一次检查。不要指望模型能在几十轮对话后还牢牢记住最初的细粒度约束要用代码兜底。整理一个速查表方便你遇到问题时直接查阅现象常见原因快速解决模型选错工具工具描述含糊工具间功能重叠重写描述明确适用边界参数类型或字段缺漏Schema约束太松增加格式限制加入参数校验反复调用同一个工具错误信息里缺少替代方案在错误返回中补充下一步建议长对话忘记用户约束关键信息被历史淹没把固定约束放入系统提示词代码层检查任务做到一半完全跑偏缺少计划与主线追踪要求先输出任务拆解每步汇报进度工具异常导致整个会话崩溃异常没有返回给模型把异常包装成结构化结果让模型可理解6.4 安全兜底权限最小化最后必须强调安全。给Agent的工具权限要做到最小化只把它当前任务需要的权限给到位。永远不要用数据库管理员的账号去连接数据库更不要给Agent直接执行删除表的SQL能力。业务上的危险操作一律通过审批接口或人工确认工具来做。表面上看这会拖慢Agent的响应速度但比起一次不可逆的误操作这点延迟完全值得。7. 从Demo到生产几个我拿真金白银换来的经验7.1 别急着上框架先手写一遍循环很多初学者特别热衷于引入各种Agent框架好像上了框架就专业了。但我自己一直建议第一个项目一定要手写Agent循环。只有手写一遍你才会理解消息列表怎么维护、工具结果怎么回填、错误到底在哪一层发生。我见过太多人一上来就用框架出问题后完全不知道日志里报错是什么意思因为框架把流程藏得太深了。手写一遍以后你未来用任何框架都心里有数因为你清楚框架在底层做了什么。7.2 日志是Agent项目的生命线传统程序讲究日志Agent项目是几十倍地讲究日志。因为Agent每走一步都有“模型决策”和“工具执行”两个环节任何一环出错都可能导致结果偏离。我看日志的时候通常关注四个信息模型收到的完整消息模型输出的决策和工具调用工具执行的真实返回每一轮消耗的Token数量。写好Agent日志之后还有一个习惯我建议你养成把每轮执行轨迹完整保存下来。这样当业务方质疑某个结果“为什么这样”的时候你能拿出完整的决策链给人家看。这比任何解释都有说服力。7.3 增量构建从三个工具开始跑通主链路最后分享一个我坚持了很长时间的习惯每次从零构建Agent我先不管未来要接多少系统先把当前主流程最核心的三个工具写出来跑通完整链路。比如做一个订单助手就先写查询订单、修改订单备注、生成订单报告这三个工具把主链路跑顺。等主链路稳定了再一个一个叠加新能力每加一个工具就重新回归测试一遍核心场景。这种增量构建方式让我避免了很多“框架搭得很好、业务根本跑不通”的尴尬局面。某种程度上构建Agent和写普通软件一样越晚集成痛苦越深越早跑通端到端后面的路越顺。这套方法不是唯一答案但如果你看过太多高大上的方案却迟迟没法落地我建议你直接照这个思路试一遍。等第一个Agent在你的终端里真正自动跑完一套工具调用、拿到结果的那一刻你会发现这东西比想象中容易也比想象中有趣得多。