389 lines
13 KiB
Markdown
389 lines
13 KiB
Markdown
# 第02章:快速开始 — Hello World
|
||
|
||
## 📌 本章目标
|
||
|
||
- 使用 `adk create` 创建第一个 Agent 项目
|
||
- 理解 Agent 项目结构
|
||
- 编写一个带有自定义工具的 Agent
|
||
- 通过 CLI 和 Web UI 两种方式运行 Agent
|
||
|
||
---
|
||
|
||
## 2.1 创建 Agent 项目
|
||
|
||
### 2.1.1 使用 CLI 创建项目
|
||
|
||
```bash
|
||
# 在工作目录下创建一个新的 Agent 项目
|
||
adk create hello_world
|
||
|
||
# 创建完成后,会生成以下目录结构:
|
||
# hello_world/
|
||
# ├── agent.py # Agent 主代码文件(核心)
|
||
# ├── .env # 环境变量配置文件(API Key 等)
|
||
# └── __init__.py # Python 包初始化文件
|
||
```
|
||
|
||
### 2.1.2 项目结构说明
|
||
|
||
```
|
||
hello_world/
|
||
│
|
||
├── agent.py # 🔑 核心文件:定义 Agent 的行为和工具
|
||
│ # - root_agent 变量是 ADK 的入口点
|
||
│ # - 可以定义自定义工具函数
|
||
│
|
||
├── .env # 🔑 配置文件:存储 API Key 等敏感信息
|
||
│ # - GOOGLE_API_KEY="your_key_here"
|
||
│ # - 不会被提交到版本控制
|
||
│
|
||
└── __init__.py # Python 包标识文件
|
||
# - 使目录成为 Python 包
|
||
# - 通常为空文件
|
||
```
|
||
|
||
> ⚠️ **重要**:`agent.py` 中的 `root_agent` 是 ADK 识别 Agent 的入口点,这个变量名**不能修改**。
|
||
|
||
---
|
||
|
||
## 2.2 编写第一个 Agent
|
||
|
||
### 2.2.1 最简单的 Agent
|
||
|
||
打开 `agent.py`,编写最基础的 Agent:
|
||
|
||
```python
|
||
"""
|
||
第一个 Google ADK Agent 示例
|
||
这是一个最简单的 Agent,不包含任何自定义工具
|
||
"""
|
||
|
||
# 从 ADK 的 agents 模块导入 Agent 类
|
||
# Agent 是 LlmAgent 的别名,是最常用的智能体类型
|
||
from google.adk.agents import Agent
|
||
|
||
# 定义 root_agent —— 这是 ADK 的入口点
|
||
# ADK 会自动查找名为 root_agent 的变量
|
||
root_agent = Agent(
|
||
name="hello_agent", # Agent 的名称,用于标识和日志
|
||
model="gemini-2.0-flash", # 使用的 LLM 模型
|
||
instruction="你是一个友好的助手。", # 系统指令,定义 Agent 的角色和行为
|
||
)
|
||
```
|
||
|
||
### 2.2.2 带工具的 Agent
|
||
|
||
让我们给 Agent 添加一个获取当前时间的工具:
|
||
|
||
```python
|
||
"""
|
||
带自定义工具的 Hello World Agent
|
||
演示如何为 Agent 添加自定义函数工具
|
||
"""
|
||
|
||
# 导入 ADK 的 Agent 类
|
||
from google.adk.agents import Agent
|
||
|
||
# 导入标准库:日期时间处理
|
||
import datetime # 用于获取当前时间
|
||
from zoneinfo import ZoneInfo # 用于处理时区信息
|
||
|
||
|
||
# ========================================
|
||
# 定义自定义工具函数
|
||
# ========================================
|
||
|
||
def get_current_time(city: str) -> dict:
|
||
"""
|
||
获取指定城市的当前时间
|
||
|
||
这个函数会被 ADK 自动转换为工具(FunctionTool),
|
||
Agent 可以在对话中调用这个工具来获取时间信息。
|
||
|
||
Args:
|
||
city (str): 需要查询时间的城市名称,例如"北京"、"东京"、"纽约"
|
||
|
||
Returns:
|
||
dict: 包含状态和结果的字典
|
||
- status: "success" 或 "error"
|
||
- report: 时间信息或错误消息
|
||
"""
|
||
# 定义城市到时区的映射字典
|
||
city_timezone_map = { # 常见城市对应的时区
|
||
"北京": "Asia/Shanghai", # 北京使用上海时区
|
||
"上海": "Asia/Shanghai", # 上海使用上海时区
|
||
"东京": "Asia/Tokyo", # 东京时区
|
||
"纽约": "America/New_York", # 纽约时区
|
||
"伦敦": "Europe/London", # 伦敦时区
|
||
"巴黎": "Europe/Paris", # 巴黎时区
|
||
}
|
||
|
||
# 查找城市对应的时区标识符
|
||
tz_identifier = city_timezone_map.get(city) # 从字典中获取时区
|
||
|
||
# 如果找不到该城市的时区信息
|
||
if not tz_identifier:
|
||
return { # 返回错误信息
|
||
"status": "error", # 状态标记为错误
|
||
"error_message": f"未找到'{city}'的时区信息,请尝试其他城市。" # 错误描述
|
||
}
|
||
|
||
# 根据时区标识符创建时区对象
|
||
tz = ZoneInfo(tz_identifier) # 创建 ZoneInfo 时区对象
|
||
|
||
# 获取该时区的当前时间
|
||
now = datetime.datetime.now(tz) # 获取指定时区的当前时间
|
||
|
||
# 格式化时间字符串
|
||
time_str = now.strftime( # 将时间格式化为字符串
|
||
"%Y-%m-%d %H:%M:%S %Z%z" # 格式:年-月-日 时:分:秒 时区
|
||
)
|
||
|
||
# 返回成功结果
|
||
return { # 返回包含时间信息的字典
|
||
"status": "success", # 状态标记为成功
|
||
"city": city, # 城市名称
|
||
"time": time_str # 格式化后的时间字符串
|
||
}
|
||
|
||
|
||
# ========================================
|
||
# 定义 Agent
|
||
# ========================================
|
||
|
||
root_agent = Agent(
|
||
name="hello_agent", # Agent 的唯一标识名称
|
||
model="gemini-2.0-flash", # 使用 Gemini 2.0 Flash 模型
|
||
description="一个能查询城市时间的友好助手。", # Agent 的描述,用于多 Agent 场景
|
||
instruction=( # 系统指令(多行字符串)
|
||
"你是一个友好的助手,可以帮助用户查询世界各城市的当前时间。\n"
|
||
"当用户询问某个城市的时间时,使用 get_current_time 工具来获取。\n"
|
||
"如果工具返回错误,请友好地告知用户并建议尝试其他城市。\n"
|
||
"回复时请使用中文。"
|
||
),
|
||
tools=[get_current_time], # 将自定义函数注册为 Agent 的工具
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 2.3 配置 API Key
|
||
|
||
在运行 Agent 之前,需要配置 API Key:
|
||
|
||
```bash
|
||
# 方法一:在 .env 文件中配置(推荐)
|
||
echo 'GOOGLE_API_KEY="你的_GEMINI_API_KEY"' > hello_world/.env
|
||
|
||
# 方法二:通过环境变量设置
|
||
export GOOGLE_API_KEY="你的_GEMINI_API_KEY"
|
||
|
||
# 方法三:直接在代码中设置(不推荐,有安全风险)
|
||
```
|
||
|
||
> 💡 **获取 API Key**:访问 [Google AI Studio](https://aistudio.google.com/app/apikey),点击 "Create API Key" 即可免费获取。
|
||
|
||
---
|
||
|
||
## 2.4 运行 Agent
|
||
|
||
### 2.4.1 命令行交互模式(CLI)
|
||
|
||
```bash
|
||
# 在项目父目录下运行(不是在 hello_world 目录内)
|
||
adk run hello_world
|
||
|
||
# 运行后进入交互式对话界面:
|
||
# Running agent hello_agent, type exit to exit.
|
||
# [user]: 北京现在几点?
|
||
# [hello_agent]: 北京当前时间是 2025-04-05 14:30:00 CST+0800。
|
||
# [user]: exit
|
||
```
|
||
|
||
### 2.4.2 Web UI 模式
|
||
|
||
```bash
|
||
# 启动 Web 服务器(默认端口 8000)
|
||
adk web --port 8000
|
||
|
||
# 浏览器打开 http://localhost:8000
|
||
# 在左上角选择 Agent,然后输入问题进行对话
|
||
```
|
||
|
||
Web UI 提供以下功能:
|
||
- 💬 **对话界面**:与 Agent 进行交互式对话
|
||
- 📊 **Event 面板**:查看工具调用和事件流
|
||
- 📋 **Request/Response**:查看原始请求和响应数据
|
||
- 🔍 **调试信息**:帮助排查问题
|
||
|
||
### 2.4.3 代码方式运行
|
||
|
||
除了 CLI 和 Web UI,还可以通过 Python 代码直接运行 Agent:
|
||
|
||
```python
|
||
"""
|
||
通过 Python 代码直接运行 Agent
|
||
适用于集成到自己的应用中
|
||
"""
|
||
|
||
# 导入必要的模块
|
||
from google.adk.agents import Agent # Agent 类
|
||
from google.adk.runners import Runner # 运行器,负责执行 Agent
|
||
from google.adk.sessions import InMemorySessionService # 内存会话服务
|
||
from google.genai import types # Google GenAI 类型定义
|
||
|
||
# 导入异步运行支持
|
||
import asyncio # 异步编程库
|
||
|
||
|
||
# ========================================
|
||
# 定义工具函数
|
||
# ========================================
|
||
|
||
def get_current_time(city: str) -> dict:
|
||
"""获取指定城市的当前时间"""
|
||
import datetime # 导入日期时间模块
|
||
from zoneinfo import ZoneInfo # 导入时区模块
|
||
|
||
city_timezone_map = { # 城市时区映射
|
||
"北京": "Asia/Shanghai",
|
||
"上海": "Asia/Shanghai",
|
||
"东京": "Asia/Tokyo",
|
||
"纽约": "America/New_York",
|
||
"伦敦": "Europe/London",
|
||
}
|
||
|
||
tz_identifier = city_timezone_map.get(city) # 查找时区
|
||
if not tz_identifier: # 如果找不到时区
|
||
return { # 返回错误
|
||
"status": "error",
|
||
"error_message": f"未找到'{city}'的时区信息"
|
||
}
|
||
|
||
tz = ZoneInfo(tz_identifier) # 创建时区对象
|
||
now = datetime.datetime.now(tz) # 获取当前时间
|
||
time_str = now.strftime("%Y-%m-%d %H:%M:%S %Z%z") # 格式化时间
|
||
|
||
return { # 返回结果
|
||
"status": "success",
|
||
"city": city,
|
||
"time": time_str
|
||
}
|
||
|
||
|
||
# ========================================
|
||
# 定义 Agent
|
||
# ========================================
|
||
|
||
root_agent = Agent(
|
||
name="hello_agent", # Agent 名称
|
||
model="gemini-2.0-flash", # 使用 Gemini 模型
|
||
instruction="你是一个友好的时间查询助手。使用工具获取城市时间。", # 系统指令
|
||
tools=[get_current_time], # 注册工具
|
||
)
|
||
|
||
|
||
# ========================================
|
||
# 定义运行逻辑
|
||
# ========================================
|
||
|
||
# 定义应用名称、用户ID和会话ID
|
||
APP_NAME = "hello_world_app" # 应用名称,用于标识
|
||
USER_ID = "user_001" # 用户唯一标识
|
||
SESSION_ID = "session_001" # 会话唯一标识
|
||
|
||
|
||
async def main():
|
||
"""主函数:异步运行 Agent"""
|
||
|
||
# 第一步:创建会话服务
|
||
# InMemorySessionService 将会话数据存储在内存中
|
||
# 适用于开发和测试,数据在程序重启后会丢失
|
||
session_service = InMemorySessionService()
|
||
|
||
# 第二步:创建会话
|
||
# 会话(Session)代表一次对话交互
|
||
# 包含消息历史、状态数据等
|
||
session = await session_service.create_session(
|
||
app_name=APP_NAME, # 关联的应用名称
|
||
user_id=USER_ID, # 用户标识
|
||
session_id=SESSION_ID, # 会话标识
|
||
)
|
||
|
||
# 第三步:创建运行器
|
||
# Runner 是 ADK 的核心执行引擎
|
||
# 负责协调 Agent、会话服务和工具调用
|
||
runner = Runner(
|
||
agent=root_agent, # 要运行的 Agent
|
||
app_name=APP_NAME, # 应用名称
|
||
session_service=session_service, # 会话服务
|
||
)
|
||
|
||
# 第四步:构造用户消息
|
||
# Content 对象表示一条消息
|
||
# role='user' 表示这是用户发送的消息
|
||
content = types.Content(
|
||
role='user', # 消息角色:用户
|
||
parts=[types.Part(text='北京现在几点?')] # 消息内容
|
||
)
|
||
|
||
# 第五步:运行 Agent 并获取响应
|
||
# run_async 返回一个异步事件流
|
||
# 我们需要遍历所有事件来获取最终响应
|
||
events = runner.run_async(
|
||
user_id=USER_ID, # 用户标识
|
||
session_id=SESSION_ID, # 会话标识
|
||
new_message=content, # 用户消息
|
||
)
|
||
|
||
# 第六步:处理事件流
|
||
async for event in events: # 遍历所有事件
|
||
if event.is_final_response(): # 如果是最终响应事件
|
||
# 从事件中提取文本内容
|
||
final_response = event.content.parts[0].text
|
||
print(f"Agent 回复: {final_response}") # 打印 Agent 的回复
|
||
|
||
|
||
# 运行主函数
|
||
if __name__ == "__main__": # 当脚本被直接运行时
|
||
asyncio.run(main()) # 使用 asyncio 运行异步主函数
|
||
```
|
||
|
||
---
|
||
|
||
## 2.5 理解 Agent 的核心参数
|
||
|
||
`Agent` 构造函数接受以下关键参数:
|
||
|
||
| 参数 | 类型 | 必需 | 说明 |
|
||
|------|------|------|------|
|
||
| `name` | str | ✅ | Agent 的唯一标识名称 |
|
||
| `model` | str | ✅ | 使用的 LLM 模型名称 |
|
||
| `instruction` | str | ❌ | 系统指令,定义 Agent 的角色和行为 |
|
||
| `description` | str | ❌ | Agent 描述,用于多 Agent 场景中的路由 |
|
||
| `tools` | list | ❌ | Agent 可用的工具列表 |
|
||
| `sub_agents` | list | ❌ | 子 Agent 列表,用于多 Agent 系统 |
|
||
| `output_key` | str | ❌ | 输出保存到 State 的键名 |
|
||
| `before_model_callback` | func | ❌ | 模型调用前的回调函数 |
|
||
| `after_model_callback` | func | ❌ | 模型调用后的回调函数 |
|
||
| `before_tool_callback` | func | ❌ | 工具调用前的回调函数 |
|
||
| `after_tool_callback` | func | ❌ | 工具调用后的回调函数 |
|
||
|
||
---
|
||
|
||
## 2.6 完整代码示例
|
||
|
||
详见 `code/hello_world.py` — 包含完整的带工具的 Hello World Agent 示例。
|
||
|
||
---
|
||
|
||
## 📌 本章小结
|
||
|
||
- 使用 `adk create` 快速创建 Agent 项目
|
||
- `agent.py` 中的 `root_agent` 是 ADK 的入口点
|
||
- 自定义工具就是普通的 Python 函数,ADK 会自动转换为 FunctionTool
|
||
- 三种运行方式:CLI(`adk run`)、Web UI(`adk web`)、代码调用
|
||
- Runner 是 ADK 的核心执行引擎,负责协调 Agent 和会话
|
||
|
||
**下一章**:[第03章 - LLM 智能体详解](./03-llm-agent-in-depth.md) → 深入了解 LlmAgent 的配置和高级用法。
|