使用 Gemini 3 从零开始构建 Agent 实用开发指南
看到一个 AI 代理编辑多个文件、运行命令、处理错误并迭代解决问题时,可能会觉得这像魔法一样复杂。但其实不然。构建代理的秘诀在于:它根本没有秘密。
一个 Agent 的核心原理非常简单:它是一个大型语言模型(LLM)在一个循环中运行,并配备了它可以选择使用的工具。
只要你会用 Python 编写循环,你就能构建一个 Agent。本指南将带你一步步完成整个过程,从简单的 API 调用到功能完备的命令行接口(CLI)代理。
Agent 的真正定义是什么?
传统的软件工作流是预设的,遵循预定义的路径(步骤 A -> 步骤 B -> 步骤 C)。而 Agent 是一个系统,它利用 LLM 动态地决定应用程序的控制流程,以达成用户的目标。
一个 Agent 通常由以下核心组件构成:
- 模型(大脑): 推理引擎,在本例中是 Gemini 模型。它负责处理歧义、规划步骤,并决定何时需要外部帮助。
- 工具(手和眼): 代理可以执行的功能,用于与外部世界/环境交互(例如,搜索网页、读取文件、调用 API)。
- 上下文/记忆(工作区): 代理在任何时刻都能获取的信息。有效地管理这些信息,也就是所谓的“上下文工程(Context Engineering)”。
- 循环(生命): 一个
while循环,允许模型:观察 → 思考 → 行动 → 再次观察,直到任务完成。

“循环”的工作流程
几乎所有 Agent 的“循环”都是一个迭代过程:
- 定义工具: 你使用结构化的 JSON 格式向模型描述你可用的工具(例如,
get_weather)。 - 调用 LLM: 你将用户的提示词和工具定义发送给模型。
- 模型决策: 模型分析请求。如果需要工具,它会返回一个结构化的工具调用请求,其中包含工具名称和参数。
- 执行工具(客户端责任): 客户端/应用程序代码拦截这个工具调用请求,执行实际的代码或 API 调用,并捕获结果。
- 响应与迭代: 你将结果(工具响应)发回给模型。模型利用这一新信息来决定下一步,可能是调用另一个工具,也可能是生成最终的回答。
构建 Agent
让我们使用 Gemini 3 Pro 和 Python SDK 一步步构建一个 Agent,从基本的文本生成逐步过渡到功能完备的 CLI 代理。
先决条件: 安装 SDK (pip install google-genai) 并设置你的 GEMINI_API_KEY 环境变量(可在 AI Studio 中获取)。
步骤 1:基础文本生成和抽象
第一步是创建与 LLM(即 Gemini 3 Pro)交互的基线。我们将创建一个简单的 Agent 类抽象来组织代码,并在后续步骤中对其进行扩展。首先从一个维护会话历史的简单聊天机器人开始。
from google import genai
from google.genai import types
class Agent:
def __init__(self, model: str):
self.model = model
self.client = genai.Client()
self.contents = [] # 用于存储会话历史
def run(self, contents: str):
# 1. 将用户输入添加到历史记录
self.contents.append({"role": "user", "parts": [{"text": contents}]})
# 2. 调用模型生成内容
response = self.client.models.generate_content(model=self.model, contents=self.contents)
# 3. 将模型的响应添加到历史记录
self.contents.append(response.candidates[0].content)
return response
agent = Agent(model="gemini-3-pro-preview")
response1 = agent.run(
contents="Hello, What are top 3 cities in Germany to visit? Only return the names of the cities."
)
print(f"Model: {response1.text}")
# 输出: Berlin, Munich, Cologne
response2 = agent.run(
contents="Tell me something about the second city."
)
print(f"Model: {response2.text}")
# 输出: Munich is the capital of Bavaria and is known for its Oktoberfest.
请注意,这还不是一个 Agent。它是一个标准的聊天机器人。它能保持状态(记忆),但不能采取行动,没有“手和眼”。
步骤 2:赋予它手和眼(工具使用)
要开始将它转变为 Agent,我们需要工具使用或函数调用能力。我们为 Agent 提供工具。这需要定义实现(Python 代码)和定义(LLM 看见的 Schema)。如果 LLM 认为某个工具将有助于解决用户的提示,它将返回一个结构化的请求来调用该函数,而不是仅仅返回文本。
我们将创建 3 个工具:read_file、write_file 和 list_dir。工具定义是一个 JSON Schema,它定义了工具的 name、description 和 parameters。
最佳实践: 使用 description 字段来解释何时以及如何使用该工具。模型高度依赖这些描述来理解工具的使用时机和方法。务必清晰明了。
import os
import json
# --- 工具定义(Schema) ---
read_file_definition = {
"name": "read_file",
"description": "Reads a file and returns its contents.",
# ... 省略其余参数定义 ...
}
list_dir_definition = {
"name": "list_dir",
"description": "Lists the contents of a directory.",
# ... 省略其余参数定义 ...
}
write_file_definition = {
"name": "write_file",
"description": "Writes a file with the given contents.",
# ... 省略其余参数定义 ...
}
# --- 工具实现(Python 函数) ---
def read_file(file_path: str) -> dict:
with open(file_path, "r") as f:
return f.read()
def write_file(file_path: str, contents: str) -> bool:
"""Writes a file with the given contents."""
with open(file_path, "w") as f:
f.write(contents)
returnTrue
def list_dir(directory_path: str) -> list[str]:
"""Lists the contents of a directory."""
full_path = os.path.expanduser(directory_path)
return os.listdir(full_path)
# 将定义和实现函数组合
file_tools = {
"read_file": {"definition": read_file_definition, "function": read_file},
"write_file": {"definition": write_file_definition, "function": write_file},
"list_dir": {"definition": list_dir_definition, "function": list_dir},
}
# --- Agent 类集成工具 ---
from google import genai
from google.genai import types
class Agent:
def __init__(self, model: str, tools: dict):
self.model = model
self.client = genai.Client()
self.contents = []
self.tools = tools # 存储工具
def run(self, contents: str):
self.contents.append({"role": "user", "parts": [{"text": contents}]})
# 配置工具声明
config = types.GenerateContentConfig(
tools=[types.Tool(function_declarations=[tool["definition"] for tool in self.tools.values()])],
)
response = self.client.models.generate_content(model=self.model, contents=self.contents, config=config)
self.contents.append(response.candidates[0].content)
return response
agent = Agent(model="gemini-3-pro-preview", tools=file_tools)
response = agent.run(
contents="Can you list my files in the current directory?"
)
print(response.function_calls)
# 输出: [FunctionCall(name='list_dir', arguments={'directory_path': '.'})]
太棒了!模型成功调用了工具。现在,我们需要将工具执行逻辑添加到 Agent 类中,并让循环将结果返回给模型。
3:闭合循环(Agent 的诞生)
一个 Agent 的意义不仅在于生成一个工具调用,而在于生成一系列工具调用,将结果返回给模型,然后模型再生成下一个工具调用,如此循环,直到任务完成。
Agent 类将处理核心循环:拦截FunctionCall,在客户端执行工具,然后将 FunctionResponse发回。我们还添加了 SystemInstruction 来指导模型的行为。
注意: Gemini 3 使用思维签名(Thought signatures)来在 API 调用中维护推理上下文。你必须将这些签名原封不动地在请求中返回给模型。
# ... 此处省略了步骤 2 的工具和工具定义代码 ...
from google import genai
from google.genai import types
class Agent:
def __init__(self, model: str, tools: dict, system_instruction: str = "You are a helpful assistant."):
# ... 初始化代码与步骤 2 相同 ...
self.system_instruction = system_instruction
def run(self, contents: str | list[dict[str, str]]):
# 处理用户输入或函数响应的 parts 列表
if isinstance(contents, list):
self.contents.append({"role": "user", "parts": contents})
else:
self.contents.append({"role": "user", "parts": [{"text": contents}]})
config = types.GenerateContentConfig(
system_instruction=self.system_instruction,
tools=[types.Tool(function_declarations=[tool["definition"] for tool in self.tools.values()])],
)
response = self.client.models.generate_content(model=self.model, contents=self.contents, config=config)
self.contents.append(response.candidates[0].content)
# --- 核心循环逻辑:检查工具调用 ---
if response.function_calls:
functions_response_parts = []
for tool_call in response.function_calls:
print(f"[Function Call] {tool_call}")
# 客户端执行工具
if tool_call.name in self.tools:
result = {"result": self.tools[tool_call.name]["function"](**tool_call.args)}
else:
result = {"error": "Tool not found"}
print(f"[Function Response] {result}")
# 准备函数响应部分
functions_response_parts.append({
"functionResponse": {"name": tool_call.name, "response": result}
})
# 递归调用 run(),将结果返回给模型进行下一步推理
return self.run(functions_response_parts)
return response
agent = Agent(
model="gemini-3-pro-preview",
tools=file_tools,
system_instruction="You are a helpful Coding Assistant. Respond like you are Linus Torvalds."
)
response = agent.run(
contents="Can you list my files in the current directory?"
)
print(response.text)
# 输出: [Function Call] id=None args={'directory_path': '.'} name='list_dir'
# [Function Response] {'result': ['.venv', ... ]}
# Linus: There. Your current directory contains: `LICENSE`, ...
刚刚我们成功地构建了第一个功能完备的 Agent。
4:多轮 CLI 代理
现在,我们可以将 Agent 运行在一个简单的命令行接口(CLI)循环中。只需很少的代码,就能创建出高度智能的行为。
# ... 此处省略了 Agent、工具和工具定义代码 ...
agent = Agent(
model="gemini-3-pro-preview",
tools=file_tools,
system_instruction="You are a helpful Coding Assistant. Respond like you are Linus Torvalds."
)
print("Agent ready. Ask it to check files in this directory.")
whileTrue:
user_input = input("You: ")
if user_input.lower() in ['exit', 'quit']:
break
response = agent.run(user_input)
print(f"Linus: {response.text}\n")
Agent 工程的最佳实践
构建循环很容易;但让它可靠、透明和可控却很难。以下是源自顶尖行业实践的关键工程原则,按功能区域分组。
1. 工具定义与人体工程学
你的工具是模型的接口。不要仅仅包装你现有的内部 API。如果一个工具对人类来说很复杂,那么对模型来说也一样:
- 清晰命名: 使用一目了然的名称,例如
search_customer_database,而不是cust_db_v2_query。 - 精确描述: Gemini 会阅读函数的文档字符串(docstrings)来理解何时以及如何使用工具。花时间仔细编写这些内容,这本质上是工具的“提示工程”。
- 返回有意义的错误: 不要返回 50 行的 Java 堆栈跟踪。如果工具失败,返回一个清晰的字符串,如
错误:文件未找到。您想找 'data.csv' 吗?这样能让 Agent 进行自我修正。 - 容忍模糊输入: 如果模型经常猜错文件路径,请更新你的工具来处理相对路径或模糊输入,而不是仅仅报错。
2. 上下文工程
模型有一个有限的“注意力预算”。管理哪些信息进入上下文对于性能和成本至关重要。
- 不要“倾倒”数据: 不要让工具返回整个 10MB 的数据库表。与其创建
get_all_users(),不如创建search_users(query: str)。 - 即时加载: 不要预加载所有数据(传统的 RAG),而是使用即时策略。Agent 应该维护轻量级的标识符(文件路径、ID),并仅在需要时使用工具动态加载内容。
- 压缩: 对于运行时间很长的 Agent,可以总结历史记录,移除旧的上下文或开启新会话。
- Agent 记忆: 允许 Agent 维护一个存储在上下文窗口之外的笔记或便签本,仅在相关时才将它们拉回。
3. 不要过度工程化
构建复杂的多代理系统是很诱人的。但请不要这样做。
- 首先最大化单个 Agent 的能力: 不要立即构建复杂的系统。Gemini 具备很强的能力,可以在一个提示中处理数十个工具。
- 逃逸舱口: 确保循环可以被停止,例如设置
max_iterations中断(比如 15 轮)。 - 护栏和系统指令: 使用
system_instruction来为模型设定硬性规则(例如,“您被严格禁止提供超过 50 美元的退款”),或者使用外部分类器。 - 人机协作(Human-in-the-loop): 对于敏感操作(如
send_email或execute_code),暂停循环并要求用户确认后才能实际执行工具。 - 优先考虑透明度和调试: 记录工具调用及其参数。分析模型的推理过程有助于发现问题,并随着时间推移改进 Agent。
构建 Agent 不再是魔法,而是一个实用的工程任务。正如我们所示,你可以在不到 100 行代码内构建出一个可工作的原型。虽然理解这些基本原理很关键,但不要拘泥于一遍又一遍地重复编写相同的模式。AI 社区已经创建了优秀的开源库,可以帮助你更快地构建更复杂、更健壮的 Agent。
来源:AI AGENT领域






