第 2 章 30 分钟搭第一个 Agent
第 2 章 30 分钟搭第一个 Agent
上一章我们花了一整章讲"为什么是 ADK"。这一章不再讲道理,直接上手。我们把上一章吹过的牛——"代码优先、可调试、可测试、不绑 Google"——全部亲手验证一遍。30 分钟后,你会拥有第一个能真正干活的 Agent:一个能查订单、查退款政策的电商客服。
2.1 开工前的准备
2.1.1 你需要什么
写这本书的前提很简单,你只需要:
- Python 3.10 或更高版本。ADK 官方要求 Python 3.10+。
- 一个可用的 LLM。任何模型都行——Gemini、Claude、OpenAI、或者本地跑一个 Ollama。这一章开头先用 Gemini(免费额度足够),后面会演示怎么零成本切换到本地模型。
- 一个终端。装 Python 包、跑命令行 Agent,都在终端里进行。
不需要的东西也值得说清楚:不需要 Docker、不需要 Kubernetes、不需要任何 Google Cloud 账号。ADK 可以在你自己的机器上独立运行,这也是它"生产优先但不绑架你"的体现。
2.1.2 安装 ADK
安装就一条命令:
pip install google-adk强烈建议先建一个虚拟环境,避免污染系统 Python:
# macOS / Linux
python3 -m venv .venv
source .venv/bin/activate
# Windows
.venv\Scripts\activate.bat
# 激活后安装
pip install google-adk装完验证一下版本,确认 2.x:
python -c "import google.adk; print(google.adk.__version__)"如果输出以 2. 开头,恭喜,你已经在用最新的 ADK 2.0 了。本书所有代码都基于 2.0 编写——如果你看到 1.x,先升级。
2.1.3 配置模型:不绑 Google 的第一个证明
ADK 支持非常多的模型。配置方式有两种:
方式一:直接用模型名(适合 Gemini)
如果你有 Google AI Studio 的 API Key,在项目根目录建一个 .env 文件:
GOOGLE_API_KEY="你的_API_KEY"然后在代码里直接写模型名 'gemini-flash-latest',ADK 的注册表会自动解析到对应的后端客户端。这是"紧密集成"的路径,写起来最省事。
方式二:用模型连接器(适合非 Google 模型)
对于 Google 生态之外的模型,ADK 提供了一组模型连接器(model connector)。最通用的是 LiteLLM 连接器——它能接几百种模型。比如用本地 Ollama:
from google.adk.agents import Agent
from google.adk.models.litellm import LiteLlm
agent = Agent(
model=LiteLlm(model="ollama_chat/gemma3:latest"),
name="local_agent",
instruction="You are a helpful assistant.",
)跑之前设置环境变量指向本地 Ollama 服务:
export OLLAMA_API_BASE="http://localhost:11434"文档特别强调:连接 Ollama 要用 ollama_chat 而不是 ollama,否则可能出现无限工具调用循环——这是踩过坑的人留下的经验。
这一章先不纠结模型。为了让例子简单,后面代码默认用
'gemini-flash-latest'。到第 7 章我们会专门讲模型中立,把客服 Agent 从 Gemini 切到 Claude、切到本地 Ollama,体验一把"代码零改动换模型"。
2.2 第一个 Agent:代码优先,不是概念优先
2.2.1 最简 Agent 长什么样
在项目目录里建一个 agent.py,写下这段代码:
from google.adk.agents.llm_agent import Agent
def get_current_time(city: str) -> dict:
"""Returns the current time in a specified city."""
return {"status": "success", "city": city, "time": "10:30 AM"}
root_agent = Agent(
model='gemini-flash-latest',
name='root_agent',
description="Tells the current time in a specified city.",
instruction="You are a helpful assistant that tells the current time in cities. Use the 'get_current_time' tool for this purpose.",
tools=[get_current_time],
)注意看,这里没有一个 YAML 配置文件,没有一套需要背诵的 DSL,没有几十行初始化模板。root_agent 就是一个普通的 Python 对象。
Agent 构造函数的五个关键参数,全部一眼能懂:
| 参数 | 作用 | 类比 |
|---|---|---|
model |
用哪个模型 | 你雇的员工的大脑 |
name |
Agent 的名字 | 员工工号 |
description |
这个 Agent 是干嘛的 | 员工名片上的职位描述 |
instruction |
系统提示词,告诉它怎么干活 | 员工入职培训手册 |
tools |
它能调用哪些工具 | 员工桌上的工具包 |
这就是 ADK 的核心设计哲学——Agent 就是一个 Python 对象。你不需要学习新的"智能体语言",你只需要写 Python。
2.2.2 代码优先意味着什么
这一小节我们展开讲讲,为什么"Agent 是 Python 对象"这件事,比它看起来重要得多。
第一,可调试。
想象一个用配置文件驱动的框架:Agent 跑出问题,你打开一个 YAML,盯着几十行声明式配置,试图找出"哪一步让模型产生了幻觉"。而在 ADK 里,Agent 就是一个对象。你可以在 IDE 里给它打上断点,一步步看它怎么决定调用哪个工具、怎么组装回复。调试体验跟调试普通 Python 函数没有区别。
第二,可测试。
既然 Agent 是普通 Python 对象,你就可以像测普通函数一样测它。不需要 mock 掉整个框架,不需要启动什么运行时。后面第 10 章讲评估时你会看到,这种"可测试"的基因,让 ADK 的 Agent 质量评估天然比其他框架顺畅。
第三,可版本控制。
Agent 的逻辑就是代码,代码就进 Git。每次改动都能 diff,每个版本都能回溯。你可以 git blame 一行 prompt,看看是谁在什么时候改的。这在团队协作里是刚需——没有版本控制的 prompt,是生产环境的定时炸弹。
第四,无隐藏魔法。
配置驱动框架有个通病:框架替你做了一堆"聪明的"默认行为,但你不清楚。而代码优先的 ADK 里,你写的每一步都是显式的。Agent 就是对象,工具就是函数,编排就是代码。出问题时,你总是知道去哪找。
这一节是全书的地基。请务必理解:ADK 不把 Agent 当作一个需要特殊对待的黑盒,它把 Agent 当作一流的、普通的代码公民。这个选择贯穿了 ADK 的所有设计。
2.3 三种运行方式:同一个 Agent,三种玩法
代码写好了,怎么跑?ADK 一个 Agent 给了三种运行方式,对应三种不同场景。
2.3.1 命令行运行:adk run
在项目目录(包含 agent.py 的那个目录)运行:
adk run agentadk run 会起一个交互式命令行,你可以直接跟 Agent 对话:
You: What time is it in London?
Agent: The current time in London is 10:30 AM.
You: How about Paris?
Agent: The current time in Paris is 10:30 AM.命令行是最轻量的调试方式——不需要起服务,不需要开浏览器,适合快速验证 Agent 逻辑。
2.3.2 网页界面:adk web
adk web --port 8000然后浏览器打开 http://localhost:8000。你会看到一个聊天界面,可以跟 Agent 对话,还能看到它的思考过程、工具调用记录。这个网页界面是 ADK 的"调试工作台"——但注意,文档明确警告:ADK Web 只用于开发和调试,不用于生产部署。
2.3.3 API Server:adk api-server
当你想把 Agent 暴露成一个 HTTP 接口,给前端或者其他服务调用时:
adk api-server --port 8080这会起一个 REST API 服务,你的 Agent 变成了一个标准 HTTP 端点。这是走向生产的桥梁——到第 8 章部署时,我们会在服务器上跑它。
2.3.4 三种方式怎么选
| 方式 | 命令 | 场景 |
|---|---|---|
| 命令行 | adk run |
开发期快速对话调试 |
| 网页 | adk web |
可视化调试、看思考过程 |
| API 服务 | adk api-server |
接入其他系统、走向生产 |
同一个 agent.py,三种方式都能跑。这是"Agent 是一等公民"的另一个体现——运行方式只是外壳,Agent 本身是纯粹的。
2.4 主线项目启动:云销客服 v0.1
从这一节开始,我们启动贯穿全书的项目。这个项目会陪我们走完后面每一章,从最简单的单 Agent 一路长成生产级系统。
2.4.1 项目背景:云销
「云销」是一家做线上零售的公司,主营电子产品和家居用品。它的用户经常问客服三个问题:
- 我的订单到哪了? —— 需要查订单物流
- 这个商品能退吗? —— 需要查退款政策
- 我要退款,怎么办? —— 需要走退货流程
现在这些请求都靠人工客服处理。我们要用 ADK 做一个自动客服系统,逐步接管这些工作。
2.4.2 云销客服 v0.1:能查订单、查退款政策
v0.1 的目标很简单:一个 Agent,能处理前两类问题——查订单、查退款政策。至于退款流程,那是后面几章的事。
写 agent.py:
from google.adk.agents.llm_agent import Agent
from google.adk.tools import FunctionTool
# ---------- 工具:模拟查订单 ----------
def get_order_status(order_id: str) -> dict:
"""查询订单当前状态。
Args:
order_id: 订单号,例如 'ORD-20260901-001'
Returns:
包含订单状态和物流信息的字典。
"""
# 真实场景这里会查数据库/订单系统,这里用 mock 数据
mock_orders = {
"ORD-20260901-001": {"status": "已发货", "logistics": "顺丰 SF1234567890,预计 3 天到达"},
"ORD-20260901-002": {"status": "待发货", "logistics": "仓库备货中"},
"ORD-20260903-003": {"status": "已签收", "logistics": "已于 9 月 6 日签收"},
}
order = mock_orders.get(order_id)
if order:
return {"status": "success", "order_id": order_id, **order}
return {"status": "error", "order_id": order_id, "message": f"未找到订单 {order_id}"}
# ---------- 工具:模拟查退款政策 ----------
def get_refund_policy(product_category: str) -> dict:
"""查询某类商品的退款政策。
Args:
product_category: 商品类别,如 'electronics'(电子产品)、'home'(家居用品)
Returns:
包含退款政策的字典。
"""
policies = {
"electronics": {
"window": "7 天",
"condition": "商品未拆封、不影响二次销售",
"note": "已激活的电子产品不支持无理由退货",
},
"home": {
"window": "15 天",
"condition": "商品及包装完好",
"note": "定制类家居商品不支持无理由退货",
},
}
policy = policies.get(product_category)
if policy:
return {"status": "success", "product_category": product_category, **policy}
return {"status": "error", "message": f"未知商品类别 {product_category}"}
# ---------- 云销客服 Agent ----------
yunxiao_agent = Agent(
model='gemini-flash-latest',
name='yunxiao_cs_agent',
description="云销电商客服助手,负责回答订单状态查询和退款政策咨询。",
instruction=(
"你是云销电商的客服助手。你的职责是帮助用户查询订单状态、"
"解答退款政策相关问题。\n"
"规则:\n"
"1. 查订单时,先获取订单号,再调用 get_order_status 工具\n"
"2. 查退款政策时,先确认商品类别,再调用 get_refund_policy 工具\n"
"3. 如果工具返回 error,如实告知用户,不要编造信息\n"
"4. 语气友好、专业\n"
),
tools=[
FunctionTool(func=get_order_status),
FunctionTool(func=get_refund_policy),
],
)注意这里我们用了 FunctionTool(func=...) 把普通 Python 函数包装成工具。get_order_status 和 get_refund_policy 就是最普通的 Python 函数——带类型注解、带 docstring、返回字典。
关键点:ADK 会根据函数的类型注解和 docstring 自动生成工具的 schema(也就是告诉模型"这个工具接收什么参数、是干嘛的")。所以你写工具的规范程度,直接决定了模型调用工具的正确率。这也是为什么工具要写清晰的 docstring。
2.4.3 跑起来
在项目目录运行:
adk run agent然后试试跟它对话:
You: 帮我查一下 ORD-20260901-001 的订单
Agent: 好的,订单 ORD-20260901-001 当前状态是「已发货」,物流信息:顺丰 SF1234567890,预计 3 天到达。
You: 你们电子产品可以退货吗?
Agent: 可以的。电子产品支持 7 天无理由退货,但需要满足:商品未拆封、不影响二次销售。另外注意,已激活的电子产品不支持无理由退货哦。
You: 查一个不存在的订单
Agent: 很抱歉,未找到订单 ORD-999999-000,请确认订单号是否正确。看最后一条——当工具返回 error 时,Agent 如实告知,没有编造。这就是工具 + 指令约束带来的可靠性。
2.4.4 这个 v0.1 有什么用
你可能觉得:"这不就是个会调用函数的聊天机器人吗?" 没错,但请注意我们打下的地基:
- 一个能查数据的 Agent:它不再只会聊天,它能访问"系统"(哪怕是 mock 的)
- 结构化工具接口:订单、政策都是函数,以后换真实数据库,只改函数内部,Agent 逻辑不动
- 指令约束生效:模型没有自由发挥去编造订单信息,它学会了"查不到就直说"
这就是 Agent 和聊天机器人的分水岭:聊天机器人只会说话,Agent 会办事。后面的每一章,我们都会让"办事"的能力变强:多 Agent 分工、图工作流、人工审批、部署上云。
2.5 踩坑记录:新手最容易犯的错
这一节分享几个真实会踩的坑,帮你省时间。
2.5.1 忘记 .env 或 API Key 配置错误
如果跑 adk run 时报模型认证错误,九成是 API Key 没配好。检查:
.env文件是不是在项目根目录(跟agent.py同级)- Key 名是不是
GOOGLE_API_KEY(Gemini)或对应的其他名字 - 用
echo $GOOGLE_API_KEY确认环境变量真的加载了
2.5.2 工具 docstring 写得太随意
模型的工具调用质量,直接依赖你写的 schema。一个没有 docstring、参数含糊的函数,模型会频繁传错参数。记住:docstring 就是给模型看的说明书,值得认真写。
2.5.3 指令里没写清楚"查不到怎么办"
这是新手最容易漏的。如果你不告诉模型"查不到就直说",它很可能为了讨好用户而编造订单信息——这就是幻觉的温床。我们 v0.1 的指令里明确写了第 3 条规则"如果工具返回 error,如实告知用户",这一条规则就能挡掉大量幻觉。
2.5.4 用 adk web 当生产服务器
adk web 是调试工具,不是生产服务器。文档原话是"只用于开发和调试"。真上生产,用 adk api-server 配合部署方案(第 8 章细讲)。
「为什么 ADK 这样设计」:代码优先 vs 声明式配置
这一章的每个环节,其实都在回答同一个问题:为什么 ADK 坚持代码优先,而不是像很多框架那样用配置文件/DSL 声明 Agent?
我们对比一下两条路线:
声明式配置路线(YAML/JSON 描述 Agent):
- 优点:看起来"非程序员友好",配置和代码分离
- 致命缺点:
- 不可调试:配置不是可执行代码,没法打断点
- 不可测试:你没法对一份 YAML 写单元测试
- 不可版本控制:配置虽然能进 Git,但 diff 和 review 都很痛苦
- 抽象泄漏:配置文件早晚需要表达逻辑,然后你就会被迫在 YAML 里塞表达式、塞模板——声明式变成了"一种难写的编程语言"
代码优先路线(Agent 就是 Python 对象):
- Agent 的每个部分都是真代码,天然可调试、可测试、可版本控制
- 逻辑复杂时,直接写
if/else、写循环,不需要发明 DSL - 团队协作时,review 的是代码 diff,不是配置文件
ADK 的选择很清楚:Agent 本质上是一个软件系统,而软件系统最好的表达方式就是代码。声明式配置看似降低门槛,实际上是把复杂度藏起来了——藏起来的复杂度不会消失,它会在生产环境加倍还给你。
这不是 ADK 一家之见。回顾第 1 章的框架版图,你会发现一个规律:越是"生产优先"的框架(ADK、Claude Agent SDK),越倾向代码优先;越是"原型友好"的框架,越依赖配置。而 ADK 把代码优先贯彻到了 Agent 定义的每一个角落。
本章小结
- 安装 ADK 只需一条命令
pip install google-adk,Python 3.10+,不依赖任何云环境 - Agent 就是一个 Python 对象,五个核心参数:
model、name、description、instruction、tools,全部一眼能懂 - 代码优先是 ADK 的根基:可调试、可测试、可版本控制、无隐藏魔法——这是它和其他框架的根本区别
- 三种运行方式:
adk run(命令行)、adk web(网页调试)、adk api-server(HTTP 接口),同一个 Agent 随便切换 - 模型不绑 Google:可以用 Gemini(注册表解析),也可以用 LiteLlm 连接器接 Ollama/OpenAI/Claude 等任意模型
- 工具 = Python 函数:
FunctionTool包装普通函数,类型注解 + docstring 自动生成 schema - 主线项目启动:「云销」客服 v0.1 能查订单、查退款政策,为后续章节搭好了地基
练习
- 改造工具:给云销客服加一个
get_shipping_info工具(模拟查物流详情),并在指令里告诉 Agent 什么时候该调用它。跑通adk run验证。 - 换模型:如果你有 Ollama,尝试用
LiteLlm(model="ollama_chat/...")把客服 Agent 切到本地模型,感受"代码零改动换模型"。 - 观察工具调用:用
adk web打开网页界面,故意问一个模棱两可的问题(比如"我要退货"),观察 Agent 是怎么推理、怎么决定调不调工具的。
下一章预告:第 3 章,我们把"工具"和"上下文"讲透。你会看到 Agent 的能力来源不只是工具,还有 Session(会话)、State(状态)、事件流——而这些,正是生产级 Agent 和玩具 Agent 的分水岭。