This commit is contained in:
Nelson
2026-04-06 12:36:04 +08:00
commit 514723dbe7
23 changed files with 7529 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,356 @@
# 第01章ADK 简介与环境搭建
## 📌 本章目标
- 理解 Google ADK 是什么、能做什么
- 了解 ADK 的核心架构和设计理念
- 完成开发环境的安装和配置
- 验证安装是否成功
---
## 1.1 什么是 Google ADK
**Agent Development KitADK** 是 Google 开源的一个灵活、模块化的 AI 智能体Agent开发框架。它让开发者能够像开发软件一样来构建、评估和部署 AI 智能体。
### 核心定位
```
┌─────────────────────────────────────────────────────┐
│ Google ADK │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ 模型无关 │ │ 部署无关 │ │ 框架兼容 │ │
│ │Model │ │Deploy │ │ Framework │ │
│ │Agnostic │ │Agnostic │ │ Compatible │ │
│ └──────────┘ └──────────┘ └──────────────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ 代码优先 │ │ 模块化 │ │ 多智能体支持 │ │
│ │Code First│ │Modular │ │ Multi-Agent │ │
│ └──────────┘ └──────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────┘
```
### ADK vs 其他框架对比
| 特性 | Google ADK | LangGraph | OpenAI Agents SDK |
|------|-----------|-----------|-------------------|
| **开发语言** | Python/Go/Java | Python | Python/TypeScript |
| **模型支持** | 模型无关(优化 Gemini | 模型无关 | 优化 OpenAI |
| **多智能体** | ✅ 原生支持层级架构 | ✅ 图结构编排 | ✅ Handoff 机制 |
| **工具生态** | 丰富Google Search、MCP 等) | 丰富LangChain 生态) | 丰富OpenAI 生态) |
| **部署** | Cloud Run / Vertex AI | 多种选择 | OpenAI 平台 |
| **评估** | ✅ 内置评估系统 | ❌ 需第三方 | ❌ 需第三方 |
---
## 1.2 核心架构
### 1.2.1 Agent 类型体系
ADK 的所有智能体都继承自 `BaseAgent`,主要分为三大类:
```
BaseAgent基础智能体
┌─────────────┼─────────────┐
│ │ │
LlmAgent WorkflowAgent CustomAgent
LLM 智能体) (工作流智能体) (自定义智能体)
│ │
│ ┌──────┼──────┐
│ │ │ │
│ Sequential Parallel Loop
│ (顺序) (并行) (循环)
Agent别名
```
| 类型 | 说明 | 适用场景 |
|------|------|----------|
| **LlmAgent** | 使用 LLM 作为核心引擎 | 自然语言理解、推理、工具调用 |
| **SequentialAgent** | 顺序执行子智能体 | 数据处理流水线 |
| **ParallelAgent** | 并行执行子智能体 | 多任务同时处理 |
| **LoopAgent** | 循环执行子智能体 | 轮询、迭代优化 |
| **CustomAgent** | 继承 BaseAgent 自定义 | 特殊逻辑、非标准流程 |
### 1.2.2 运行时架构
```
用户输入
┌─────────┐ ┌──────────────┐ ┌──────────────┐
│ Runner │───▶│ Session │───▶│ Agent │
│ (运行器)│ │ Service │ │ (智能体) │
└─────────┘ │ (会话服务) │ └──────┬───────┘
└──────────────┘ │
┌──────────────┐ ▼
│ Memory │ ┌──────────────┐
│ Service │ │ Tools │
│ (记忆服务) │ │ (工具集) │
└──────────────┘ └──────────────┘
```
---
## 1.3 核心特性详解
### 1.3.1 丰富的工具生态
ADK 提供了三大类工具:
**Gemini 原生工具:**
- **Google Search** — 网络搜索
- **Code Execution** — 代码执行与调试
- **Computer Use** — 操作计算机界面
**Google Cloud 工具:**
- BigQuery、Spanner、Bigtable — 数据库
- Vertex AI RAG Engine — 私有数据检索
- Vertex AI Search — 企业搜索
- API Registry — MCP 工具集成
**第三方工具:**
- GitHub、GitLab — 代码管理
- Notion、Linear — 项目管理
- Stripe、PayPal — 支付
- ElevenLabs、Cartesia — 语音生成
- Qdrant — 向量搜索
### 1.3.2 多智能体协作模式
ADK 支持多种多智能体协作模式:
| 模式 | 说明 | 实现方式 |
|------|------|----------|
| **协调器/调度器** | 父 Agent 分配任务给子 Agent | LlmAgent + sub_agents |
| **顺序流水线** | Agent 按顺序执行 | SequentialAgent |
| **并行扇出** | Agent 同时执行 | ParallelAgent |
| **层级任务分解** | 多层 Agent 分解任务 | 嵌套 sub_agents |
| **人机协作** | 人工审批关键步骤 | Human-in-the-Loop |
| **评审/批评** | 生成器 + 评审器协作 | LoopAgent + 条件退出 |
### 1.3.3 上下文管理
ADK 在架构层面分离了三种上下文:
```
┌────────────────────────────────────────────┐
│ 上下文管理架构 │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Session会话 │ │
│ │ - 当前对话线程 │ │
│ │ - 消息历史 │ │
│ │ - 临时状态 │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ State状态 │ │
│ │ - 会话内数据 │ │
│ │ - 购物车、用户偏好等 │ │
│ │ - temp: 临时状态 │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Memory记忆 │ │
│ │ - 跨会话持久化知识 │ │
│ │ - 可搜索的知识库 │ │
│ │ - 外部数据源 │ │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────────┘
```
---
## 1.4 环境搭建
### 1.4.1 系统要求
| 要求 | 最低版本 | 推荐版本 |
|------|----------|----------|
| Python | 3.10 | 3.11+ |
| pip | 最新版 | 最新版 |
| 操作系统 | Windows/macOS/Linux | 任意 |
### 1.4.2 安装步骤
#### 步骤一:创建虚拟环境(推荐)
```bash
# 创建虚拟环境
python -m venv adk_env
# 激活虚拟环境
# macOS / Linux
source adk_env/bin/activate
# Windows CMD
adk_env\Scripts\activate.bat
# Windows PowerShell
adk_env\Scripts\Activate.ps1
```
#### 步骤二:安装 ADK
```bash
# 安装稳定版本(推荐)
pip install google-adk
# 如果需要使用非 Gemini 模型(如 DeepSeek、OpenAI 等),还需安装 litellm
pip install google-adk litellm
# 如果需要安装开发版本
pip install git+https://github.com/google/adk-python.git@main
```
#### 步骤三:验证安装
```bash
# 检查 ADK 版本
adk --help
# 应该看到以下输出:
# Usage: adk [OPTIONS] COMMAND [ARGS]...
#
# Agent Development Kit CLI tools.
#
# Options:
# --version Show the version and exit.
# --help Show this message and exit.
#
# Commands:
# api_server Starts a FastAPI server for agents.
# create Creates a new app in the current folder with prepopulated agent template.
# deploy Deploys agent to hosted environments.
# eval Evaluates an agent given the eval sets.
# run Runs an interactive CLI for a certain agent.
# web Starts a FastAPI server with Web UI for agents.
```
### 1.4.3 配置 API Key
#### 使用 Gemini 模型(默认)
```bash
# 在项目目录下创建 .env 文件
echo 'GOOGLE_API_KEY="你的_GEMINI_API_KEY"' > .env
```
> 💡 获取 Gemini API Key访问 [Google AI Studio](https://aistudio.google.com/app/apikey) 创建 API Key。
#### 使用其他模型
```bash
# 在 .env 文件中添加对应的 API Key
echo 'OPENAI_API_KEY="你的_OPENAI_API_KEY"' >> .env
echo 'DEEPSEEK_API_KEY="你的_DEEPSEEK_API_KEY"' >> .env
```
---
## 1.5 ADK CLI 工具详解
安装 ADK 后,会获得一个 `adk` 命令行工具:
| 命令 | 说明 | 示例 |
|------|------|------|
| `adk create` | 创建新的 Agent 项目 | `adk create my_agent` |
| `adk run` | 在命令行中运行 Agent | `adk run my_agent` |
| `adk web` | 启动 Web UI 界面 | `adk web --port 8000` |
| `adk api_server` | 启动 FastAPI 服务器 | `adk api_server` |
| `adk eval` | 评估 Agent 性能 | `adk eval my_agent eval_set.json` |
| `adk deploy` | 部署到托管环境 | `adk deploy my_agent` |
---
## 1.6 代码验证:环境搭建
以下代码用于验证 ADK 是否安装成功,详见 `code/setup_demo.py`
```python
"""
Google ADK 环境搭建验证脚本
验证 ADK 是否正确安装,并打印版本信息
"""
# 导入 ADK 核心模块,验证安装是否成功
from google.adk.agents import Agent # 智能体模块
from google.adk.runners import Runner # 运行器模块
from google.adk.sessions import InMemorySessionService # 内存会话服务
# 打印安装验证信息
print("=" * 50) # 打印分隔线
print("Google ADK 环境验证") # 打印标题
print("=" * 50) # 打印分隔线
print("✅ ADK 核心模块导入成功!") # 确认导入成功
# 验证 Agent 类是否可用
agent = Agent( # 创建一个简单的测试 Agent
name="test_agent", # 设置 Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction="你是一个测试助手。", # 设置系统指令
)
print(f"✅ Agent 创建成功:{agent.name}") # 打印 Agent 名称
# 验证 Runner 类是否可用
print("✅ Runner 模块可用") # 确认 Runner 可用
# 验证 SessionService 是否可用
session_service = InMemorySessionService() # 创建内存会话服务
print("✅ SessionService 可用") # 确认会话服务可用
print("=" * 50) # 打印分隔线
print("🎉 所有模块验证通过!") # 打印成功信息
print("可以开始使用 Google ADK 了!") # 提示用户
```
---
## 1.7 常见问题
### Q1: 安装失败怎么办?
```bash
# 尝试升级 pip
pip install --upgrade pip
# 使用国内镜像源
pip install google-adk -i https://pypi.tuna.tsinghua.edu.cn/simple
```
### Q2: Python 版本不满足要求?
```bash
# 检查 Python 版本
python --version
# 如果低于 3.10,需要升级 Python
# 推荐使用 pyenv 或 conda 管理 Python 版本
```
### Q3: adk 命令找不到?
```bash
# 确保虚拟环境已激活
# 确认 google-adk 已安装
pip show google-adk
# 如果安装了但找不到命令,尝试重新安装
pip install --force-reinstall google-adk
```
---
## 📌 本章小结
- ADK 是 Google 开源的模块化 AI 智能体开发框架
- 支持多种 Agent 类型LlmAgent、WorkflowAgent、CustomAgent
- 提供丰富的工具生态和完善的上下文管理
- 安装简单:`pip install google-adk`
- 提供 CLI 工具 `adk` 用于项目创建、运行和部署
**下一章**[第02章 - 快速开始Hello World](./02-quick-start-hello-world.md) → 创建并运行你的第一个 Agent

View File

@@ -0,0 +1,388 @@
# 第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 的配置和高级用法。

View File

@@ -0,0 +1,434 @@
# 第03章LLM 智能体详解
## 📌 本章目标
- 深入理解 LlmAgent 的工作原理
- 掌握 Agent 的指令设计Instruction Design
- 学习如何选择和配置不同的 LLM 模型
- 了解 output_key、before/after 回调等高级特性
- 使用 Google 内置工具(搜索、代码执行等)
---
## 3.1 LlmAgent 工作原理
### 3.1.1 执行流程
```
用户输入
┌─────────────────────────────────────────────────┐
│ LlmAgent │
│ │
│ 1. 接收用户消息 │
│ ↓ │
│ 2. before_model_callback模型调用前回调
│ ↓ │
│ 3. 构建上下文(系统指令 + 历史消息 + 工具定义) │
│ ↓ │
│ 4. 调用 LLM 获取响应 │
│ ↓ │
│ 5. after_model_callback模型调用后回调
│ ↓ │
│ 6. 判断是否需要调用工具 │
│ ├── 是 → before_tool_callback → 执行工具 │
│ │ → after_tool_callback → 回到步骤3 │
│ └── 否 → 输出最终响应 │
│ ↓ │
│ 7. 保存输出到 State如果设置了 output_key
│ ↓ │
│ 8. 返回响应给用户 │
└─────────────────────────────────────────────────┘
```
### 3.1.2 Agent 与 LlmAgent 的关系
```python
# Agent 是 LlmAgent 的别名,两者完全等价
from google.adk.agents import Agent # Agent = LlmAgent 的别名
from google.adk.agents import LlmAgent # LlmAgent 是完整类名
# 以下两种写法效果完全相同:
agent1 = Agent(name="test", model="gemini-2.0-flash")
agent2 = LlmAgent(name="test", model="gemini-2.0-flash")
```
---
## 3.2 指令设计Instruction Design
### 3.2.1 好的指令设计原则
```python
"""
Agent 指令设计示例
展示如何编写高质量的 Agent 系统指令
"""
from google.adk.agents import Agent
# ❌ 不好的指令设计:过于简单、缺乏约束
bad_agent = Agent(
name="bad_agent",
model="gemini-2.0-flash",
instruction="你是一个助手。", # 太模糊Agent 不知道该做什么
)
# ✅ 好的指令设计:清晰、具体、有约束
good_agent = Agent(
name="good_agent",
model="gemini-2.0-flash",
instruction="""你是一个专业的旅行规划助手。
## 你的职责
- 帮助用户规划旅行行程
- 提供目的地推荐和旅行建议
- 使用工具查询天气、航班等信息
## 回答规范
1. 始终使用中文回答
2. 回答要简洁明了,使用列表格式
3. 如果信息不足,主动询问用户
4. 不要编造不存在的信息
## 工具使用规则
- 查询天气时,使用 get_weather 工具
- 查询航班时,使用 search_flights 工具
- 只有在需要实时数据时才使用工具
""",
)
```
### 3.2.2 指令模板
```python
"""
常用指令模板
可以根据自己的需求修改使用
"""
# 模板一:数据分析助手
data_analyst_instruction = """你是一个数据分析专家。
## 核心能力
- 分析数据并生成洞察
- 创建数据可视化建议
- 解释统计指标的含义
## 工作流程
1. 理解用户的数据分析需求
2. 使用工具获取和处理数据
3. 生成分析报告,包含关键发现
4. 提供可操作的建议
## 输出格式
- 使用 Markdown 格式
- 包含数据表格和关键指标
- 提供结论和建议
"""
# 模板二:代码助手
code_assistant_instruction = """你是一个编程助手。
## 核心能力
- 编写和审查代码
- 调试和修复错误
- 代码重构和优化建议
## 规则
1. 代码注释使用中文
2. 遵循最佳实践和设计模式
3. 解释代码的工作原理
4. 提供测试建议
"""
# 模板三:客服助手
customer_service_instruction = """你是一个专业的客服代表。
## 核心职责
- 解答用户问题
- 处理投诉和反馈
- 引导用户完成操作
## 规则
1. 语气友好、专业
2. 先理解用户问题,再提供解决方案
3. 无法解决时,转交人工客服
4. 记录用户反馈
"""
```
---
## 3.3 模型选择与配置
### 3.3.1 Gemini 模型(默认)
```python
"""
使用 Gemini 模型配置 Agent
Gemini 是 ADK 默认优化的模型
"""
from google.adk.agents import Agent
# 使用 Gemini 2.0 Flash推荐性价比高
agent_flash = Agent(
name="flash_agent",
model="gemini-2.0-flash", # 快速、经济
instruction="你是一个高效的助手。",
)
# 使用 Gemini 2.5 Pro更强推理能力
agent_pro = Agent(
name="pro_agent",
model="gemini-2.5-pro-preview-05-06", # 更强的推理能力
instruction="你是一个需要深度推理的助手。",
)
# 使用 Gemini 3 Pro最新版本
agent_latest = Agent(
name="latest_agent",
model="gemini-3-pro-preview", # 最新版本
instruction="你是一个使用最新模型的助手。",
)
```
### 3.3.2 使用其他模型LiteLLM
```python
"""
使用 LiteLLM 适配其他模型
LiteLLM 支持 100+ 种模型
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.models.lite_llm import LiteLlm # 导入 LiteLLM 适配器
# ========================================
# 使用 DeepSeek 模型
# ========================================
deepseek_agent = Agent(
name="deepseek_agent", # Agent 名称
model=LiteLlm( # 使用 LiteLLM 适配器
model="deepseek/deepseek-chat", # 格式provider/model_name
api_key="你的_DEEPSEEK_API_KEY", # DeepSeek API Key
# api_base="https://api.deepseek.com", # 可选:自定义 API 地址
),
instruction="你是一个使用 DeepSeek 模型的助手。",
)
# ========================================
# 使用 OpenAI 模型
# ========================================
openai_agent = Agent(
name="openai_agent", # Agent 名称
model=LiteLlm( # 使用 LiteLLM 适配器
model="openai/gpt-4o", # OpenAI GPT-4o 模型
api_key="你的_OPENAI_API_KEY", # OpenAI API Key
),
instruction="你是一个使用 GPT-4o 模型的助手。",
)
# ========================================
# 使用 Claude 模型
# ========================================
claude_agent = Agent(
name="claude_agent", # Agent 名称
model=LiteLlm( # 使用 LiteLLM 适配器
model="anthropic/claude-3-5-sonnet-20241022", # Claude 模型
api_key="你的_ANTHROPIC_API_KEY", # Anthropic API Key
),
instruction="你是一个使用 Claude 模型的助手。",
)
```
### 3.3.3 模型选择指南
| 场景 | 推荐模型 | 原因 |
|------|----------|------|
| 简单对话 | gemini-2.0-flash | 速度快、成本低 |
| 复杂推理 | gemini-2.5-pro | 推理能力强 |
| 代码生成 | gemini-2.5-pro / gpt-4o | 代码质量高 |
| 中文场景 | deepseek-chat | 中文能力强 |
| 长文本处理 | gemini-2.5-pro | 支持长上下文 |
---
## 3.4 使用 Google 内置工具
### 3.4.1 Google Search网络搜索
```python
"""
使用 Google Search 工具
让 Agent 能够搜索互联网获取最新信息
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.tools import google_search # 导入 Google 搜索工具
# 创建带有搜索能力的 Agent
search_agent = Agent(
name="search_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction=( # 系统指令
"你是一个信息查询助手。\n"
"当用户提问时,使用 Google Search 工具搜索最新信息。\n"
"根据搜索结果提供准确、全面的回答。\n"
"如果搜索结果不够充分,可以多次搜索不同关键词。"
),
tools=[google_search], # 注册 Google 搜索工具
)
```
### 3.4.2 Code Execution代码执行
```python
"""
使用 Code Execution 工具
让 Agent 能够执行 Python 代码进行计算和分析
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.tools import code_execution # 导入代码执行工具
# 创建带有代码执行能力的 Agent
code_agent = Agent(
name="code_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction=( # 系统指令
"你是一个数据分析助手。\n"
"当需要进行数学计算、数据分析或生成图表时,\n"
"使用 Code Execution 工具执行 Python 代码。\n"
"确保代码正确且高效。"
),
tools=[code_execution], # 注册代码执行工具
)
```
### 3.4.3 组合多个内置工具
```python
"""
组合使用多个 Google 内置工具
创建功能更强大的 Agent
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.tools import google_search # 导入 Google 搜索工具
from google.adk.tools import code_execution # 导入代码执行工具
# 创建多功能 Agent
super_agent = Agent(
name="super_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction=( # 系统指令
"你是一个全能助手。\n"
"你可以搜索互联网获取最新信息。\n"
"你也可以执行代码来进行计算和分析。\n"
"根据用户的需求选择合适的工具。"
),
tools=[ # 注册多个工具
google_search, # 网络搜索
code_execution, # 代码执行
],
)
```
---
## 3.5 output_key — 输出到 State
```python
"""
output_key 参数详解
将 Agent 的输出保存到会话状态State
用于在多 Agent 系统中传递数据
"""
from google.adk.agents import Agent # 导入 Agent 类
# 创建一个将输出保存到 State 的 Agent
capital_agent = Agent(
name="capital_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction="回答用户关于国家首都的问题。只回答首都名称,不要多余内容。", # 指令
output_key="capital_city", # 🔑 关键参数:将输出保存到 state['capital_city']
)
# 当这个 Agent 运行后,它的最终响应文本会自动保存到 session.state['capital_city']
# 其他 Agent 可以通过 {capital_city} 在指令中引用这个值
```
---
## 3.6 Agent Transfer — 动态任务委派
```python
"""
Agent Transfer 机制
LLM 可以根据用户需求动态地将任务委派给其他 Agent
"""
from google.adk.agents import LlmAgent # 导入 LlmAgent 类
# 定义专门的子 Agent
booking_agent = LlmAgent(
name="BookingAgent", # 预订 Agent
model="gemini-2.0-flash", # 使用 Gemini 模型
description="负责处理航班和酒店的预订请求。", # 描述:用于 LLM 路由决策
instruction="你是一个预订助手,帮助用户预订航班和酒店。", # 系统指令
)
info_agent = LlmAgent(
name="InfoAgent", # 信息查询 Agent
model="gemini-2.0-flash", # 使用 Gemini 模型
description="负责回答一般性问题和提供信息。", # 描述:用于 LLM 路由决策
instruction="你是一个信息助手,回答用户的各种问题。", # 系统指令
)
# 定义协调器 Agent父 Agent
coordinator = LlmAgent(
name="Coordinator", # 协调器 Agent
model="gemini-2.0-flash", # 使用 Gemini 模型
description="主协调器,负责将用户请求分配给合适的子 Agent。", # 描述
instruction=( # 系统指令
"你是一个协调器助手。\n"
"当用户需要预订航班或酒店时,将任务委派给 BookingAgent。\n"
"当用户询问一般性问题时,将任务委派给 InfoAgent。\n"
"使用 transfer_to_agent 工具来委派任务。"
),
sub_agents=[ # 🔑 注册子 Agent
booking_agent, # 预订 Agent
info_agent, # 信息 Agent
],
)
# 工作原理:
# 1. 用户发送消息给 Coordinator
# 2. Coordinator 的 LLM 分析消息,决定委派给哪个子 Agent
# 3. LLM 生成 transfer_to_agent(agent_name='BookingAgent') 调用
# 4. ADK 框架自动将执行切换到目标 Agent
# 5. 目标 Agent 处理请求并返回结果
```
---
## 3.7 完整代码示例
详见 `code/llm_agent_demo.py` — 包含完整的 LlmAgent 配置和运行示例。
---
## 📌 本章小结
- LlmAgent 是 ADK 最常用的智能体类型,以 LLM 为核心引擎
- 好的指令设计是 Agent 效果的关键
- ADK 默认优化 Gemini 模型,通过 LiteLLM 支持其他模型
- Google 内置工具(搜索、代码执行)可直接使用
- `output_key` 用于在多 Agent 系统中传递数据
- Agent Transfer 实现动态任务委派
**下一章**[第04章 - 自定义工具开发](./04-custom-tools.md) → 学习如何为 Agent 创建自定义工具。

View File

@@ -0,0 +1,564 @@
# 第04章自定义工具开发
## 📌 本章目标
- 理解 ADK 工具系统的工作原理
- 掌握函数工具Function Tool的开发方法
- 学习 MCP 工具的集成方式
- 了解 OpenAPI 工具的生成方法
- 掌握工具间数据传递的技巧
---
## 4.1 工具系统概述
### 4.1.1 工具是什么?
在 ADK 中,**工具Tool** 是 Agent 与外部世界交互的桥梁。Agent 通过调用工具来执行特定任务,如查询数据库、调用 API、执行计算等。
```
┌─────────────────────────────────────────────┐
│ Agent │
│ │
│ 用户: "帮我查一下北京的天气" │
│ ↓ │
│ LLM 分析: 需要调用天气查询工具 │
│ ↓ │
│ 调用工具: get_weather(city="北京") │
│ ↓ │
│ 工具返回: {"status": "success", │
│ "report": "晴天25°C"} │
│ ↓ │
│ LLM 生成回复: "北京今天天气晴朗气温25°C" │
└─────────────────────────────────────────────┘
```
### 4.1.2 工具类型
| 类型 | 说明 | 适用场景 |
|------|------|----------|
| **Function Tool** | Python 函数自动转换 | 自定义逻辑、API 调用 |
| **MCP Tool** | 连接 MCP 服务器 | 标准化工具集成 |
| **OpenAPI Tool** | 从 OpenAPI 规范生成 | REST API 集成 |
| **Agent Tool** | 将 Agent 作为工具使用 | Agent 复用 |
| **内置工具** | Google 提供的预置工具 | 搜索、代码执行等 |
---
## 4.2 函数工具Function Tool
### 4.2.1 基本原理
ADK 会自动检查 Python 函数的以下信息来生成工具 Schema
- **函数名**:工具的名称
- **docstring**:工具的描述(发送给 LLM
- **参数类型**:参数的类型提示
- **默认值**:区分必选和可选参数
- **返回值**:工具的输出
### 4.2.2 基础示例
```python
"""
函数工具基础示例
演示如何创建简单的自定义工具
"""
from google.adk.agents import Agent # 导入 Agent 类
# ========================================
# 示例一:简单的天气查询工具
# ========================================
def get_weather(city: str) -> dict:
"""
获取指定城市的天气信息
Args:
city (str): 城市名称,例如"北京""上海"
Returns:
dict: 包含天气状态的字典
"""
# 模拟天气数据(实际应用中应调用真实天气 API
weather_data = { # 模拟天气数据库
"北京": {"temp": "25°C", "condition": "晴天"}, # 北京天气
"上海": {"temp": "28°C", "condition": "多云"}, # 上海天气
"广州": {"temp": "32°C", "condition": "雷阵雨"}, # 广州天气
}
# 查找城市天气数据
data = weather_data.get(city) # 从字典中获取天气数据
# 如果找不到该城市的天气
if not data: # 检查数据是否存在
return { # 返回错误信息
"status": "error", # 状态:错误
"error_message": f"未找到'{city}'的天气信息" # 错误描述
}
# 返回成功结果
return { # 返回天气信息
"status": "success", # 状态:成功
"city": city, # 城市名称
"temperature": data["temp"], # 温度
"condition": data["condition"], # 天气状况
}
# ========================================
# 示例二:带可选参数的工具
# ========================================
def search_restaurant(
city: str, # 必选参数:城市
cuisine: str = "中餐", # 可选参数:菜系,默认中餐
price_range: str = "中等", # 可选参数:价格范围,默认中等
) -> dict:
"""
搜索指定城市的餐厅
Args:
city (str): 城市名称
cuisine (str, optional): 菜系类型,默认为"中餐"
price_range (str, optional): 价格范围,默认为"中等"
Returns:
dict: 包含搜索结果的字典
"""
# 模拟餐厅搜索结果
return { # 返回搜索结果
"status": "success", # 状态:成功
"city": city, # 城市
"cuisine": cuisine, # 菜系
"price_range": price_range, # 价格范围
"results": [ # 餐厅列表
{"name": "美味餐厅", "rating": 4.5}, # 餐厅1
{"name": "佳肴轩", "rating": 4.2}, # 餐厅2
],
}
# ========================================
# 创建使用工具的 Agent
# ========================================
agent = Agent(
name="tool_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction=( # 系统指令
"你是一个生活助手。\n"
"你可以查询天气和搜索餐厅。\n"
"根据用户需求使用合适的工具。"
),
tools=[ # 注册工具列表
get_weather, # 天气查询工具
search_restaurant, # 餐厅搜索工具
],
)
```
### 4.2.3 参数类型详解
```python
"""
工具参数类型详解
展示不同类型的参数定义方式
"""
from typing import Optional, List # 导入类型提示
from google.adk.agents import Agent # 导入 Agent 类
# ========================================
# 必选参数 vs 可选参数
# ========================================
def calculate_loan(
principal: float, # 必选:贷款本金
annual_rate: float, # 必选:年利率
years: int, # 必选:贷款年限
extra_payment: float = 0.0, # 可选额外月供默认0
) -> dict:
"""
计算贷款月供和总利息
Args:
principal (float): 贷款本金(元)
annual_rate (float): 年利率(百分比,如 4.5 表示 4.5%
years (int): 贷款年限
extra_payment (float, optional): 每月额外还款金额,默认为 0
Returns:
dict: 包含月供、总利息等信息的字典
"""
monthly_rate = annual_rate / 100 / 12 # 计算月利率
total_months = years * 12 # 计算总月数
# 计算等额本息月供
monthly_payment = principal * monthly_rate * (1 + monthly_rate) ** total_months / ((1 + monthly_rate) ** total_months - 1)
total_amount = monthly_payment * total_months + extra_payment * total_months # 总还款额
total_interest = total_amount - principal # 总利息
return { # 返回计算结果
"status": "success", # 状态
"monthly_payment": round(monthly_payment, 2), # 月供
"total_interest": round(total_interest, 2), # 总利息
"total_amount": round(total_amount, 2), # 总还款额
}
# ========================================
# 使用 Optional 标记可选参数
# ========================================
def create_user_profile(
username: str, # 必选:用户名
bio: Optional[str] = None, # 可选:个人简介
avatar_url: Optional[str] = None, # 可选:头像 URL
) -> dict:
"""
创建用户档案
Args:
username (str): 用户名
bio (str, optional): 个人简介
avatar_url (str, optional): 头像图片 URL
Returns:
dict: 创建结果
"""
profile = { # 创建用户档案字典
"username": username, # 用户名
"bio": bio or "这个人很懒,什么都没写。", # 简介(默认值)
"avatar_url": avatar_url or "", # 头像 URL
}
return { # 返回结果
"status": "success", # 状态
"profile": profile, # 用户档案
}
# ========================================
# 使用 List 类型参数
# ========================================
def compare_cities(cities: List[str]) -> dict:
"""
比较多个城市的信息
Args:
cities (List[str]): 城市名称列表
Returns:
dict: 城市比较结果
"""
city_info = { # 城市信息数据库
"北京": {"population": "2189万", "area": "16410km²"}, # 北京
"上海": {"population": "2487万", "area": "6341km²"}, # 上海
"广州": {"population": "1881万", "area": "7434km²"}, # 广州
}
results = [] # 初始化结果列表
for city in cities: # 遍历城市列表
info = city_info.get(city) # 获取城市信息
if info: # 如果信息存在
results.append({ # 添加到结果列表
"city": city, # 城市名
**info, # 展开城市信息
})
return { # 返回比较结果
"status": "success" if results else "error", # 状态
"results": results, # 比较结果
}
# 创建 Agent
agent = Agent(
name="param_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个多功能助手。", # 指令
tools=[ # 工具列表
calculate_loan, # 贷款计算器
create_user_profile, # 用户档案创建
compare_cities, # 城市比较
],
)
```
### 4.2.4 工具返回值最佳实践
```python
"""
工具返回值最佳实践
返回值会被 LLM 读取,因此需要清晰、结构化
"""
# ❌ 不好的返回值:信息不清晰
def bad_tool(query: str) -> str:
"""不好的返回值示例"""
return "0" # LLM 不知道 0 代表什么
# ✅ 好的返回值:结构化、有状态码
def good_tool(query: str) -> dict:
"""
好的返回值示例
包含状态码和详细描述
"""
return {
"status": "success", # 状态码success / error / pending
"query": query, # 原始查询
"result": "查询结果详情", # 具体结果
"metadata": { # 可选的元数据
"source": "database", # 数据来源
"timestamp": "2025-04-05T10:00:00Z", # 时间戳
},
}
# ✅ 错误处理:返回有意义的错误信息
def robust_tool(query: str) -> dict:
"""
健壮的工具:包含完善的错误处理
"""
if not query or not query.strip(): # 检查空输入
return { # 返回参数错误
"status": "error", # 状态:错误
"error_code": "INVALID_INPUT", # 错误码
"error_message": "查询内容不能为空,请提供有效的查询参数。" # 人类可读的错误信息
}
try: # 尝试执行操作
result = do_something(query) # 执行实际操作
return { # 返回成功结果
"status": "success", # 状态:成功
"result": result, # 操作结果
}
except Exception as e: # 捕获异常
return { # 返回异常信息
"status": "error", # 状态:错误
"error_code": "INTERNAL_ERROR", # 错误码
"error_message": f"处理请求时发生错误:{str(e)}" # 错误描述
}
def do_something(query: str) -> str:
"""模拟操作函数"""
return f"处理结果: {query}" # 返回处理结果
```
---
## 4.3 工具间数据传递
```python
"""
工具间数据传递
使用 session.state 的 temp: 前缀在工具间传递数据
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.tools import FunctionTool # 导入 FunctionTool
from google.adk.tools import ToolContext # 导入工具上下文
# ========================================
# 使用 ToolContext 访问会话状态
# ========================================
def step1_fetch_data(
query: str, # 搜索关键词
ctx: ToolContext, # 工具上下文(自动注入)
) -> dict:
"""
第一步:获取原始数据
Args:
query (str): 搜索关键词
ctx (ToolContext): 工具上下文,可用于访问会话状态
Returns:
dict: 原始数据
"""
# 模拟获取数据
raw_data = f"关于'{query}'的原始数据..." # 模拟数据
# 将数据保存到临时状态temp: 前缀)
# temp: 状态只在当前调用中有效,调用结束后自动清除
ctx.state["temp:raw_data"] = raw_data # 保存到临时状态
return { # 返回结果
"status": "success", # 状态
"message": f"已获取关于'{query}'的数据", # 消息
"data_length": len(raw_data), # 数据长度
}
def step2_analyze_data(ctx: ToolContext) -> dict:
"""
第二步:分析第一步获取的数据
Args:
ctx (ToolContext): 工具上下文
Returns:
dict: 分析结果
"""
# 从临时状态中读取第一步保存的数据
raw_data = ctx.state.get("temp:raw_data") # 读取临时状态
if not raw_data: # 如果没有数据
return { # 返回错误
"status": "error",
"error_message": "没有找到原始数据,请先执行数据获取步骤。"
}
# 模拟数据分析
analysis = f"对数据进行了深度分析:{raw_data[:50]}..." # 分析结果
return { # 返回分析结果
"status": "success", # 状态
"analysis": analysis, # 分析结果
}
# 创建 Agent
agent = Agent(
name="pipeline_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个数据分析助手。\n"
"当用户需要分析数据时,先使用 step1_fetch_data 获取数据,\n"
"然后使用 step2_analyze_data 进行分析。"
),
tools=[ # 工具列表
step1_fetch_data, # 数据获取工具
step2_analyze_data, # 数据分析工具
],
)
```
---
## 4.4 Agent-as-a-Tool智能体作为工具
```python
"""
Agent-as-a-Tool
将一个 Agent 包装为工具,供其他 Agent 调用
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.tools import AgentTool # 导入 AgentTool 类
# ========================================
# 定义一个专门的翻译 Agent
# ========================================
translator_agent = Agent(
name="translator", # 翻译 Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction="你是一个专业翻译,将用户输入的文本翻译成目标语言。只输出翻译结果。", # 指令
)
# ========================================
# 将翻译 Agent 包装为工具
# ========================================
translation_tool = AgentTool( # 创建 AgentTool
agent=translator_agent, # 传入要包装的 Agent
)
# ========================================
# 在主 Agent 中使用翻译工具
# ========================================
main_agent = Agent(
name="main_agent", # 主 Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction=( # 系统指令
"你是一个多语言助手。\n"
"当用户需要翻译时,使用 translation_tool 工具。\n"
"其他情况直接回答用户问题。"
),
tools=[translation_tool], # 注册翻译工具
)
# AgentTool 与 sub_agents 的区别:
# - sub_agents通过 LLM Transfer 机制委派任务Agent 切换执行
# - AgentTool作为工具被调用在当前 Agent 上下文中执行
```
---
## 4.5 MCP 工具集成
```python
"""
MCPModel Context Protocol工具集成
连接外部 MCP 服务器作为 Agent 的工具
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset # 导入 MCP 工具集
# ========================================
# 连接本地 MCP 服务器
# ========================================
async def create_mcp_agent():
"""创建使用 MCP 工具的 Agent"""
# 创建 MCP 工具集
# 需要先启动 MCP 服务器(如 filesystem、database 等)
mcp_tools = McpToolset(
connection_params={ # MCP 服务器连接参数
"url": "http://localhost:3000", # MCP 服务器地址
},
tool_names=[ # 要导入的工具名称列表
"read_file", # 读取文件工具
"write_file", # 写入文件工具
"list_directory", # 列出目录工具
],
)
# 获取 MCP 工具列表
tools = await mcp_tools.get_tools() # 异步获取工具
# 创建使用 MCP 工具的 Agent
agent = Agent(
name="mcp_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction="你是一个文件管理助手,可以读写文件和浏览目录。", # 指令
tools=tools, # 注册 MCP 工具
)
return agent # 返回 Agent
```
---
## 4.6 完整代码示例
详见 `code/custom_tools_demo.py` — 包含完整的自定义工具开发示例。
---
## 📌 本章小结
- ADK 自动将 Python 函数转换为工具,通过函数签名生成 Schema
- 好的 docstring 是 LLM 理解工具的关键
- 返回值推荐使用字典,包含 `status` 字段
- `ToolContext` 允许工具访问会话状态,实现工具间数据传递
- `AgentTool` 可以将 Agent 包装为工具供其他 Agent 使用
- MCP 工具集支持连接外部标准化工具服务器
**下一章**[第05章 - 多智能体系统](./05-multi-agent-systems.md) → 学习如何构建多 Agent 协作系统。

View File

@@ -0,0 +1,631 @@
# 第05章多智能体系统
## 📌 本章目标
- 理解多智能体系统的设计理念
- 掌握 Agent 层级结构的构建方法
- 学习三种工作流 AgentSequential、Parallel、Loop
- 掌握常见多 Agent 协作模式
- 了解 Agent 间的通信机制
---
## 5.1 多智能体系统概述
### 5.1.1 为什么需要多智能体?
当应用变得复杂时,使用单个"巨型"Agent 会面临以下问题:
| 问题 | 说明 |
|------|------|
| **指令膨胀** | 系统指令过长LLM 难以同时遵循所有规则 |
| **工具过多** | 工具列表过长LLM 选择工具的准确率下降 |
| **难以维护** | 修改一个功能可能影响其他功能 |
| **难以调试** | 出错时难以定位问题来源 |
**多智能体系统**通过将复杂任务分解为多个专门的 Agent 来解决这些问题。
### 5.1.2 ADK 多智能体架构
```
root_agent根智能体
┌─────────────┼─────────────┐
│ │ │
LlmAgent SequentialAgent ParallelAgent
(协调器) (顺序流水线) (并行执行器)
│ │ │
┌────┼────┐ ┌────┼────┐ ┌───┴───┐
│ │ │ │ │ │ │ │
Agent Agent Agent Agent Agent Agent Agent
(子1) (子2) (子3) (步骤1)(步骤2)(任务A) (任务B)
```
---
## 5.2 Agent 层级结构
### 5.2.1 父子关系
```python
"""
Agent 层级结构基础
通过 sub_agents 参数建立父子关系
"""
from google.adk.agents import LlmAgent # 导入 LlmAgent 类
# ========================================
# 第一步:定义叶子节点 Agent最底层的 Agent
# ========================================
# 定义一个专门处理问候的 Agent
greeter_agent = LlmAgent(
name="Greeter", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
description="负责向用户打招呼和寒暄。", # 描述:用于路由决策
instruction=( # 系统指令
"你是一个友好的问候助手。\n"
"热情地向用户打招呼,并询问有什么可以帮助的。"
),
)
# 定义一个专门处理任务执行的 Agent
task_agent = LlmAgent(
name="TaskExecutor", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
description="负责执行用户的具体任务。", # 描述:用于路由决策
instruction=( # 系统指令
"你是一个任务执行助手。\n"
"认真完成用户交给你的任务。"
),
)
# ========================================
# 第二步:定义父 Agent通过 sub_agents 建立层级
# ========================================
coordinator = LlmAgent(
name="Coordinator", # 父 Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
description="主协调器,负责分配任务。", # 描述
instruction=( # 系统指令
"你是一个协调器。\n"
"根据用户的请求,将任务分配给合适的子 Agent。\n"
"问候和寒暄类请求交给 Greeter。\n"
"具体任务交给 TaskExecutor。\n"
"使用 transfer_to_agent 工具来委派任务。"
),
sub_agents=[ # 🔑 注册子 Agent 列表
greeter_agent, # 问候 Agent
task_agent, # 任务执行 Agent
],
)
# ========================================
# 层级关系说明
# ========================================
# coordinator
# ├── greeter_agent
# └── task_agent
#
# ADK 会自动设置:
# greeter_agent.parent_agent == coordinator
# task_agent.parent_agent == coordinator
#
# 注意:一个 Agent 只能有一个父 Agent
# 尝试将同一个 Agent 添加到多个父 Agent 会抛出 ValueError
```
### 5.2.2 多层级嵌套
```python
"""
多层级嵌套的 Agent 结构
构建更复杂的多 Agent 系统
"""
from google.adk.agents import LlmAgent, SequentialAgent # 导入 Agent 类型
# ========================================
# 第三层:具体执行 Agent
# ========================================
code_reviewer = LlmAgent(
name="CodeReviewer", # 代码审查 Agent
model="gemini-2.0-flash", # 模型
description="审查代码质量,提出改进建议。", # 描述
instruction="你是代码审查专家,审查代码并给出改进建议。", # 指令
)
test_writer = LlmAgent(
name="TestWriter", # 测试编写 Agent
model="gemini-2.0-flash", # 模型
description="为代码编写单元测试。", # 描述
instruction="你是测试工程师,为给定的代码编写单元测试。", # 指令
)
# ========================================
# 第二层:开发流水线(顺序执行)
# ========================================
dev_pipeline = SequentialAgent(
name="DevPipeline", # 流水线名称
sub_agents=[ # 按顺序执行子 Agent
code_reviewer, # 先审查代码
test_writer, # 再编写测试
],
)
# ========================================
# 第二层:其他专门 Agent
# ========================================
doc_writer = LlmAgent(
name="DocWriter", # 文档编写 Agent
model="gemini-2.0-flash", # 模型
description="编写技术文档。", # 描述
instruction="你是技术文档工程师,编写清晰的技术文档。", # 指令
)
# ========================================
# 第一层:根 Agent协调器
# ========================================
root_agent = LlmAgent(
name="RootCoordinator", # 根协调器
model="gemini-2.0-flash", # 模型
description="开发团队协调器。", # 描述
instruction=( # 指令
"你是一个开发团队协调器。\n"
"代码审查和测试任务交给 DevPipeline。\n"
"文档编写任务交给 DocWriter。"
),
sub_agents=[ # 子 Agent 列表
dev_pipeline, # 开发流水线
doc_writer, # 文档编写
],
)
# 完整层级结构:
# root_agent根协调器
# ├── dev_pipeline开发流水线 - SequentialAgent
# │ ├── code_reviewer代码审查
# │ └── test_writer测试编写
# └── doc_writer文档编写
```
---
## 5.3 工作流 Agent
### 5.3.1 SequentialAgent顺序执行
```python
"""
SequentialAgent — 顺序执行工作流
子 Agent 按照列表顺序依次执行
适用于数据处理流水线等场景
"""
from google.adk.agents import LlmAgent, SequentialAgent # 导入 Agent 类型
# ========================================
# 定义流水线的各个步骤
# ========================================
# 步骤一:信息提取 Agent
extractor = LlmAgent(
name="Extractor", # 信息提取 Agent
model="gemini-2.0-flash", # 模型
instruction="从用户输入中提取关键信息。只输出提取的关键信息。", # 指令
output_key="extracted_info", # 🔑 将输出保存到 state['extracted_info']
)
# 步骤二:信息分析 Agent
analyzer = LlmAgent(
name="Analyzer", # 信息分析 Agent
model="gemini-2.0-flash", # 模型
instruction=( # 指令:引用上一步的输出
"分析以下提取的信息,给出深度分析报告。\n"
"提取的信息:{extracted_info}" # 通过 {key} 引用 state 中的值
),
output_key="analysis_result", # 将分析结果保存到 state
)
# 步骤三:报告生成 Agent
reporter = LlmAgent(
name="Reporter", # 报告生成 Agent
model="gemini-2.0-flash", # 模型
instruction=( # 指令:引用前两步的输出
"根据分析结果生成一份简洁的报告。\n"
"原始信息:{extracted_info}\n" # 引用提取的信息
"分析结果:{analysis_result}" # 引用分析结果
),
)
# ========================================
# 创建顺序流水线
# ========================================
pipeline = SequentialAgent(
name="InfoPipeline", # 流水线名称
sub_agents=[ # 🔑 按顺序执行
extractor, # 第一步:提取信息
analyzer, # 第二步:分析信息
reporter, # 第三步:生成报告
],
)
# 执行流程:
# 1. Extractor 运行 → 输出保存到 state['extracted_info']
# 2. Analyzer 运行 → 读取 state['extracted_info'] → 输出保存到 state['analysis_result']
# 3. Reporter 运行 → 读取两个 state 值 → 生成最终报告
```
### 5.3.2 ParallelAgent并行执行
```python
"""
ParallelAgent — 并行执行工作流
子 Agent 同时执行,互不阻塞
适用于多任务同时处理的场景
"""
from google.adk.agents import LlmAgent, ParallelAgent # 导入 Agent 类型
# ========================================
# 定义并行执行的任务
# ========================================
# 任务一:天气查询 Agent
weather_agent = LlmAgent(
name="WeatherFetcher", # 天气查询 Agent
model="gemini-2.0-flash", # 模型
instruction="查询并返回用户指定城市的天气信息。只返回天气数据。", # 指令
output_key="weather_data", # 输出保存到 state['weather_data']
)
# 任务二:新闻查询 Agent
news_agent = LlmAgent(
name="NewsFetcher", # 新闻查询 Agent
model="gemini-2.0-flash", # 模型
instruction="查询并返回用户指定城市的最新新闻。只返回新闻摘要。", # 指令
output_key="news_data", # 输出保存到 state['news_data']
)
# 任务三:交通查询 Agent
traffic_agent = LlmAgent(
name="TrafficFetcher", # 交通查询 Agent
model="gemini-2.0-flash", # 模型
instruction="查询并返回用户指定城市的交通状况。只返回交通信息。", # 指令
output_key="traffic_data", # 输出保存到 state['traffic_data']
)
# ========================================
# 创建并行执行器
# ========================================
gatherer = ParallelAgent(
name="InfoGatherer", # 并行执行器名称
sub_agents=[ # 🔑 这些 Agent 将同时执行
weather_agent, # 天气查询(并行)
news_agent, # 新闻查询(并行)
traffic_agent, # 交通查询(并行)
],
)
# 执行流程:
# 1. WeatherFetcher、NewsFetcher、TrafficFetcher 同时开始执行
# 2. 各自独立运行,互不影响
# 3. 所有 Agent 完成后,后续 Agent 可以读取三个 state 值
#
# 注意事项:
# - 并行 Agent 共享同一个 session.state
# - 使用不同的 output_key 避免数据覆盖
# - 事件Event可能交错输出
```
### 5.3.3 LoopAgent循环执行
```python
"""
LoopAgent — 循环执行工作流
子 Agent 按顺序循环执行,直到满足退出条件
适用于轮询、迭代优化等场景
"""
from google.adk.agents import ( # 导入所需类型
LlmAgent, # LLM 智能体
LoopAgent, # 循环工作流
BaseAgent, # 基础智能体(用于自定义)
)
from google.adk.events import Event, EventActions # 导入事件和事件动作
from google.adk.agents.invocation_context import InvocationContext # 导入调用上下文
from typing import AsyncGenerator # 导入异步生成器类型
# ========================================
# 自定义条件检查 Agent
# ========================================
class QualityChecker(BaseAgent):
"""
质量检查 Agent
检查内容质量是否达标,决定是否继续循环
"""
async def _run_async_impl(
self,
ctx: InvocationContext, # 调用上下文
) -> AsyncGenerator[Event, None]: # 返回异步事件生成器
"""异步执行方法"""
# 从会话状态中获取当前质量分数
quality_score = ctx.session.state.get( # 读取状态
"quality_score", # 状态键名
0, # 默认值
)
# 判断质量是否达标(分数 >= 80 则通过)
is_passed = quality_score >= 80 # 质量阈值
# 生成事件,设置 escalate 标志
# escalate=True 表示退出循环
# escalate=False 表示继续循环
yield Event(
author=self.name, # 事件作者
actions=EventActions(
escalate=is_passed, # 🔑 达标则退出循环
),
content=f"质量分数: {quality_score}, {'通过' if is_passed else '未通过'}",
)
# ========================================
# 内容优化 Agent
# ========================================
content_improver = LlmAgent(
name="ContentImprover", # 内容优化 Agent
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个内容优化专家。\n"
"根据当前内容的质量分数进行优化。\n"
"优化后评估质量分数0-100并更新到 state['quality_score']。"
),
)
# ========================================
# 创建循环执行器
# ========================================
optimizer_loop = LoopAgent(
name="ContentOptimizer", # 循环执行器名称
max_iterations=5, # 🔑 最大循环次数(防止无限循环)
sub_agents=[ # 每次循环依次执行
content_improver, # 第一步:优化内容
QualityChecker(name="Checker"), # 第二步:检查质量
],
)
# 执行流程:
# 迭代1: ContentImprover → Checker(分数<80, 继续)
# 迭代2: ContentImprover → Checker(分数<80, 继续)
# 迭代3: ContentImprover → Checker(分数>=80, 退出)
#
# 退出条件(满足任一即退出):
# 1. QualityChecker 设置 escalate=True
# 2. 达到 max_iterations 上限
```
---
## 5.4 常见多 Agent 协作模式
### 5.4.1 协调器/调度器模式
```python
"""
协调器/调度器模式Coordinator/Dispatcher Pattern
最常用的多 Agent 模式
父 Agent 根据用户请求动态分配任务给子 Agent
"""
from google.adk.agents import LlmAgent # 导入 LlmAgent
# 定义专门的子 Agent
math_agent = LlmAgent(
name="MathExpert", # 数学专家
model="gemini-2.0-flash", # 模型
description="擅长数学计算和统计分析。", # 描述
instruction="你是数学专家,解决数学问题。", # 指令
)
writing_agent = LlmAgent(
name="WritingExpert", # 写作专家
model="gemini-2.0-flash", # 模型
description="擅长文案撰写和内容创作。", # 描述
instruction="你是写作专家,帮助用户撰写内容。", # 指令
)
coding_agent = LlmAgent(
name="CodingExpert", # 编程专家
model="gemini-2.0-flash", # 模型
description="擅长编程和代码调试。", # 描述
instruction="你是编程专家,帮助用户解决编程问题。", # 指令
)
# 定义协调器
coordinator = LlmAgent(
name="Coordinator", # 协调器
model="gemini-2.0-flash", # 模型
description="根据问题类型分配给合适的专家。", # 描述
instruction=( # 指令
"你是一个任务协调器。\n"
"根据用户的问题类型,将任务分配给合适的专家:\n"
"- 数学相关 → MathExpert\n"
"- 写作相关 → WritingExpert\n"
"- 编程相关 → CodingExpert\n"
"使用 transfer_to_agent 工具进行任务分配。"
),
sub_agents=[ # 注册所有专家 Agent
math_agent, # 数学专家
writing_agent, # 写作专家
coding_agent, # 编程专家
],
)
```
### 5.4.2 评审/批评模式Generator-Critic
```python
"""
评审/批评模式Generator-Critic Pattern
生成器创建内容,评审器评估并给出改进建议
通过 LoopAgent 实现迭代优化
"""
from google.adk.agents import LlmAgent, LoopAgent, BaseAgent # 导入类型
from google.adk.events import Event, EventActions # 导入事件类型
from google.adk.agents.invocation_context import InvocationContext # 导入上下文
from typing import AsyncGenerator # 导入异步生成器
# 生成器 Agent创建初稿
generator = LlmAgent(
name="Generator", # 生成器
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个创意写手。\n"
"根据用户需求创作内容。\n"
"将创作的内容保存到 state['draft']。"
),
output_key="draft", # 输出保存到 state['draft']
)
# 评审器 Agent评估内容质量
critic = LlmAgent(
name="Critic", # 评审器
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个严格的内容评审。\n"
"评审以下内容给出评分1-10和改进建议。\n"
"当前草稿:{draft}\n"
"如果评分 >= 8将 state['approved'] 设为 True。\n"
"否则,将改进建议保存到 state['feedback']。"
),
)
# 条件检查 Agent决定是否继续循环
class ApprovalChecker(BaseAgent):
"""检查内容是否通过审批"""
async def _run_async_impl(
self,
ctx: InvocationContext, # 调用上下文
) -> AsyncGenerator[Event, None]: # 返回事件生成器
"""执行检查"""
approved = ctx.session.state.get("approved", False) # 获取审批状态
yield Event( # 生成事件
author=self.name, # 事件作者
actions=EventActions(
escalate=approved, # 通过则退出循环
),
)
# 创建迭代优化循环
review_loop = LoopAgent(
name="ReviewLoop", # 循环名称
max_iterations=3, # 最多迭代3次
sub_agents=[ # 循环体
critic, # 第一步:评审
ApprovalChecker(name="Checker"), # 第二步:检查是否通过
generator, # 第三步:如果未通过,重新生成
],
)
# 执行流程:
# 1. Generator 创建初稿 → state['draft']
# 2. Critic 评审 → 评分 < 8 → state['feedback']
# 3. Checker → approved=False → 继续循环
# 4. Generator 根据反馈修改 → state['draft']
# 5. Critic 再次评审 → 评分 >= 8 → state['approved']=True
# 6. Checker → approved=True → 退出循环
```
---
## 5.5 Agent 间通信机制
### 5.5.1 共享状态Shared State
```python
"""
Agent 间通过共享状态通信
最基础、最常用的通信方式
"""
from google.adk.agents import LlmAgent, SequentialAgent # 导入类型
# Agent A收集信息
collector = LlmAgent(
name="Collector", # 信息收集 Agent
model="gemini-2.0-flash", # 模型
instruction="收集用户需求信息。", # 指令
output_key="user_requirements", # 输出保存到 state
)
# Agent B基于 Agent A 的输出进行设计
designer = LlmAgent(
name="Designer", # 设计 Agent
model="gemini-2.0-flash", # 模型
instruction=( # 指令:引用 Agent A 的输出
"根据用户需求进行系统设计。\n"
"用户需求:{user_requirements}" # 通过 {key} 引用
),
output_key="design_doc", # 输出保存到 state
)
# Agent C基于 Agent B 的输出进行评审
reviewer = LlmAgent(
name="Reviewer", # 评审 Agent
model="gemini-2.0-flash", # 模型
instruction=( # 指令:引用 Agent A 和 B 的输出
"评审系统设计文档。\n"
"用户需求:{user_requirements}\n" # 引用需求
"设计文档:{design_doc}" # 引用设计
),
)
# 创建流水线
pipeline = SequentialAgent(
name="DesignPipeline", # 流水线名称
sub_agents=[collector, designer, reviewer], # 顺序执行
)
```
### 5.5.2 通信机制对比
| 机制 | 说明 | 适用场景 |
|------|------|----------|
| **共享 State** | 通过 `session.state` 读写数据 | 顺序流水线、数据传递 |
| **output_key** | 自动将输出保存到 State | 简单的数据传递 |
| **LLM Transfer** | LLM 动态决定委派给哪个 Agent | 灵活的任务路由 |
| **AgentTool** | 将 Agent 作为工具调用 | Agent 复用 |
| **temp: State** | 临时状态,当前调用有效 | 工具间数据传递 |
---
## 📌 本章小结
- 多智能体系统通过 `sub_agents` 参数建立层级关系
- **SequentialAgent**:子 Agent 按顺序执行,适合流水线
- **ParallelAgent**:子 Agent 并行执行,适合多任务同时处理
- **LoopAgent**:子 Agent 循环执行,适合迭代优化
- Agent 间通信主要通过共享 State 和 LLM Transfer
- `output_key` 是最简单的数据传递方式
**下一章**[第06章 - 会话与状态管理](./06-session-and-state.md) → 深入了解会话和状态管理机制。

View File

@@ -0,0 +1,595 @@
# 第06章会话与状态管理
## 📌 本章目标
- 理解 Session、State、Memory 三层上下文架构
- 掌握 SessionService 的使用方法
- 学习 State 的读写和管理
- 了解 MemoryService 和跨会话记忆
- 掌握 Runner 的使用方法
---
## 6.1 上下文管理架构
### 6.1.1 三层上下文模型
```
┌──────────────────────────────────────────────────────┐
│ 上下文管理架构 │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ Memory记忆层 │ │
│ │ - 跨会话持久化知识 │ │
│ │ - 可搜索的知识库 │ │
│ │ - 管理者MemoryService │ │
│ │ - 实现InMemory / Vector DB / Cloud │ │
│ └────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ Session会话层 │ │
│ │ - 单次对话线程 │ │
│ │ - 消息历史Events │ │
│ │ - 管理者SessionService │ │
│ │ - 实现InMemory / Firestore / DB │ │
│ └────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ State状态层 │ │
│ │ - 会话内的临时数据 │ │
│ │ - temp: 临时状态(当前调用有效) │ │
│ │ - 持久状态(会话期间有效) │ │
│ └────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
```
### 6.1.2 各层对比
| 层级 | 生命周期 | 用途 | 管理服务 |
|------|----------|------|----------|
| **Memory** | 跨会话持久化 | 长期知识、用户偏好 | MemoryService |
| **Session** | 单次对话 | 消息历史、对话上下文 | SessionService |
| **State** | 会话期间 | 临时数据、中间结果 | session.state |
| **temp: State** | 单次调用 | 工具间数据传递 | session.state |
---
## 6.2 Session会话
### 6.2.1 Session 是什么?
Session 代表用户与 Agent 系统之间的一次对话交互。它包含:
- **消息历史**:按时间顺序记录的所有消息和事件
- **状态数据**:对话过程中产生的临时数据
- **元信息**:应用名称、用户 ID、会话 ID 等
### 6.2.2 SessionService会话服务
```python
"""
SessionService — 会话管理服务
负责创建、读取、更新和删除会话
"""
from google.adk.sessions import InMemorySessionService # 导入内存会话服务
import asyncio # 导入异步库
async def session_demo():
"""演示 SessionService 的使用"""
# ========================================
# 创建会话服务
# ========================================
# InMemorySessionService将数据存储在内存中
# 适用于开发和测试,数据在程序重启后丢失
session_service = InMemorySessionService()
# ========================================
# 创建会话
# ========================================
session = await session_service.create_session(
app_name="my_app", # 应用名称(用于标识应用)
user_id="user_001", # 用户唯一标识
session_id="session_001", # 会话唯一标识
# state={"key": "value"}, # 可选:初始化状态
)
print(f"会话创建成功: {session.id}") # 打印会话 ID
print(f"应用名称: {session.app_name}") # 打印应用名称
print(f"用户 ID: {session.user_id}") # 打印用户 ID
# ========================================
# 获取会话
# ========================================
existing_session = await session_service.get_session(
app_name="my_app", # 应用名称
user_id="user_001", # 用户 ID
session_id="session_001", # 会话 ID
)
print(f"获取会话: {existing_session.id}") # 打印会话信息
# ========================================
# 删除会话
# ========================================
await session_service.delete_session(
app_name="my_app", # 应用名称
user_id="user_001", # 用户 ID
session_id="session_001", # 会话 ID
)
print("会话已删除") # 确认删除
# 运行演示
asyncio.run(session_demo()) # 执行异步函数
```
### 6.2.3 SessionService 实现对比
| 实现 | 存储方式 | 持久化 | 适用场景 |
|------|----------|--------|----------|
| **InMemorySessionService** | 内存 | ❌ | 开发、测试 |
| **FirestoreSessionService** | Google Firestore | ✅ | 生产环境 |
| **SqliteSessionService** | SQLite 文件 | ✅ | 本地生产 |
| **自定义** | 自定义后端 | ✅ | 特殊需求 |
---
## 6.3 State状态管理
### 6.3.1 State 基础操作
```python
"""
State 状态管理
在会话中读写和管理数据
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.runners import Runner # 导入运行器
from google.adk.sessions import InMemorySessionService # 导入会话服务
from google.genai import types # 导入类型定义
import asyncio # 导入异步库
# ========================================
# 定义使用 State 的工具
# ========================================
def add_to_cart(item: str, price: float, ctx) -> dict:
"""
将商品添加到购物车
Args:
item (str): 商品名称
price (float): 商品价格
ctx: 工具上下文(自动注入),用于访问会话状态
Returns:
dict: 操作结果
"""
# 获取当前购物车(如果不存在则初始化为空列表)
cart = ctx.state.get("cart", []) # 从 state 中读取购物车
# 添加新商品到购物车
cart.append({ # 追加商品信息
"item": item, # 商品名称
"price": price, # 商品价格
})
# 更新 state 中的购物车
ctx.state["cart"] = cart # 写回 state
# 计算购物车总价
total = sum(item["price"] for item in cart) # 求和
return { # 返回结果
"status": "success", # 状态
"message": f"已将 {item} 添加到购物车", # 消息
"cart_items": len(cart), # 购物车商品数量
"total": total, # 总价
}
def get_cart(ctx) -> dict:
"""
获取购物车内容
Args:
ctx: 工具上下文
Returns:
dict: 购物车内容
"""
cart = ctx.state.get("cart", []) # 读取购物车
if not cart: # 如果购物车为空
return { # 返回空购物车信息
"status": "success",
"message": "购物车是空的",
"items": [],
"total": 0,
}
total = sum(item["price"] for item in cart) # 计算总价
return { # 返回购物车内容
"status": "success",
"items": cart, # 商品列表
"total": total, # 总价
}
def clear_cart(ctx) -> dict:
"""
清空购物车
Args:
ctx: 工具上下文
Returns:
dict: 操作结果
"""
ctx.state["cart"] = [] # 清空购物车
return { # 返回结果
"status": "success",
"message": "购物车已清空",
}
# ========================================
# 创建 Agent
# ========================================
shopping_agent = Agent(
name="shopping_assistant", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个购物助手。\n"
"帮助用户管理购物车:添加商品、查看购物车、清空购物车。"
),
tools=[ # 注册工具
add_to_cart, # 添加商品
get_cart, # 查看购物车
clear_cart, # 清空购物车
],
)
```
### 6.3.2 temp: 临时状态
```python
"""
temp: 临时状态
只在当前调用invocation中有效调用结束后自动清除
适用于工具间传递中间数据
"""
def step1_process(query: str, ctx) -> dict:
"""
处理步骤一:预处理数据
Args:
query (str): 用户查询
ctx: 工具上下文
Returns:
dict: 预处理结果
"""
# 对查询进行预处理
processed = query.strip().lower() # 去除空格并转小写
# 保存到临时状态temp: 前缀)
# 这个数据只在当前调用中有效
ctx.state["temp:processed_query"] = processed # 临时存储
return { # 返回结果
"status": "success",
"processed_query": processed,
}
def step2_enhance(ctx) -> dict:
"""
处理步骤二:增强数据
Args:
ctx: 工具上下文
Returns:
dict: 增强结果
"""
# 从临时状态读取步骤一的数据
processed = ctx.state.get("temp:processed_query") # 读取临时状态
if not processed: # 如果没有数据
return { # 返回错误
"status": "error",
"error_message": "请先执行预处理步骤。"
}
# 增强处理
enhanced = f"[增强] {processed}" # 模拟增强处理
return { # 返回结果
"status": "success",
"enhanced_query": enhanced,
}
# temp: 状态 vs 普通状态:
# - temp:xxx当前调用结束后自动清除
# - xxx会话期间持久保存
```
---
## 6.4 Runner运行器
### 6.4.1 Runner 基础用法
```python
"""
Runner — ADK 的核心执行引擎
负责协调 Agent、会话服务和工具调用
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.runners import Runner # 导入运行器
from google.adk.sessions import InMemorySessionService # 导入会话服务
from google.genai import types # 导入类型定义
import asyncio # 导入异步库
# ========================================
# 定义一个简单的 Agent
# ========================================
def get_greeting(name: str) -> dict:
"""生成问候语"""
return { # 返回问候语
"status": "success",
"greeting": f"你好,{name}!欢迎来到 ADK 教程。",
}
agent = Agent(
name="greeting_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个问候助手。", # 指令
tools=[get_greeting], # 注册工具
)
# ========================================
# 使用 Runner 运行 Agent
# ========================================
APP_NAME = "demo_app" # 应用名称
USER_ID = "user_001" # 用户 ID
SESSION_ID = "session_001" # 会话 ID
async def run_agent():
"""运行 Agent 的完整流程"""
# 第一步:创建会话服务
session_service = InMemorySessionService() # 内存会话服务
# 第二步:创建会话
session = await session_service.create_session(
app_name=APP_NAME, # 应用名称
user_id=USER_ID, # 用户 ID
session_id=SESSION_ID, # 会话 ID
)
# 第三步:创建 Runner
runner = Runner(
agent=agent, # 要运行的 Agent
app_name=APP_NAME, # 应用名称
session_service=session_service, # 会话服务
# max_turns=10, # 可选:最大对话轮数
)
# 第四步:构造用户消息
user_message = types.Content(
role='user', # 角色:用户
parts=[types.Part(text='你好,请向我打招呼')], # 消息内容
)
# 第五步:运行 Agent 并处理事件流
events = runner.run_async(
user_id=USER_ID, # 用户 ID
session_id=SESSION_ID, # 会话 ID
new_message=user_message, # 用户消息
)
# 第六步:遍历事件流,提取响应
async for event in events: # 遍历所有事件
print(f"事件类型: {type(event).__name__}") # 打印事件类型
# 检查是否为最终响应
if event.is_final_response(): # 如果是最终响应
response_text = event.content.parts[0].text # 提取文本
print(f"Agent 回复: {response_text}") # 打印回复
# 检查是否为工具调用事件
if hasattr(event, 'function_call'): # 如果有函数调用
print(f"工具调用: {event.function_call}") # 打印调用信息
# 运行
asyncio.run(run_agent()) # 执行异步函数
```
### 6.4.2 多轮对话
```python
"""
多轮对话示例
在同一个会话中进行多轮交互
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.runners import Runner # 导入运行器
from google.adk.sessions import InMemorySessionService # 导入会话服务
from google.genai import types # 导入类型定义
import asyncio # 导入异步库
agent = Agent(
name="chat_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个友好的聊天助手,记住用户告诉你的信息。", # 指令
)
async def multi_turn_chat():
"""多轮对话演示"""
# 初始化服务
session_service = InMemorySessionService() # 会话服务
await session_service.create_session( # 创建会话
app_name="chat_app", # 应用名称
user_id="user_001", # 用户 ID
session_id="chat_001", # 会话 ID
)
# 创建 Runner
runner = Runner(
agent=agent, # Agent
app_name="chat_app", # 应用名称
session_service=session_service, # 会话服务
)
# 模拟多轮对话
questions = [ # 对话列表
"我叫小明今年25岁。", # 第一轮:自我介绍
"我叫什么名字?", # 第二轮:测试记忆
"我今年多大?", # 第三轮:测试记忆
]
for question in questions: # 遍历每轮对话
print(f"\n[用户]: {question}") # 打印用户消息
# 构造消息
content = types.Content(
role='user', # 角色
parts=[types.Part(text=question)], # 内容
)
# 运行 Agent
events = runner.run_async(
user_id="user_001", # 用户 ID
session_id="chat_001", # 会话 ID同一个会话
new_message=content, # 新消息
)
# 获取响应
async for event in events: # 遍历事件
if event.is_final_response(): # 最终响应
print(f"[Agent]: {event.content.parts[0].text}") # 打印回复
asyncio.run(multi_turn_chat()) # 执行多轮对话
```
---
## 6.5 Memory记忆服务
### 6.5.1 Memory 概述
Memory 是跨会话的持久化知识存储Agent 可以搜索和检索历史信息。
```python
"""
MemoryService — 记忆管理服务
管理跨会话的持久化知识
"""
from google.adk.memory import InMemoryMemoryService # 导入内存记忆服务
async def memory_demo():
"""演示 MemoryService 的使用"""
# ========================================
# 创建记忆服务
# ========================================
# InMemoryMemoryService将记忆存储在内存中
# 适用于开发测试,数据在重启后丢失
memory_service = InMemoryMemoryService()
# ========================================
# 添加记忆
# ========================================
await memory_service.add_session_events_to_memory(
app_name="my_app", # 应用名称
user_id="user_001", # 用户 ID
session_id="session_001", # 会话 ID
)
# ========================================
# 搜索记忆
# ========================================
results = await memory_service.search(
query="用户偏好", # 搜索查询
app_name="my_app", # 应用名称
user_id="user_001", # 用户 ID
)
# 处理搜索结果
for result in results: # 遍历结果
print(f"记忆内容: {result}") # 打印记忆
# 运行演示
import asyncio
asyncio.run(memory_demo())
```
### 6.5.2 Memory 与 Session 的区别
```
┌────────────────────────────────────────────────────┐
│ Session会话
│ ┌──────────────────────────────────────────────┐ │
│ │ 消息1 → 消息2 → 消息3 → ... → 消息N │ │
│ │ state: {cart: [...], preferences: {...}} │ │
│ └──────────────────────────────────────────────┘ │
│ 生命周期:会话开始 → 会话结束 │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ Memory记忆
│ ┌──────────────────────────────────────────────┐ │
│ │ 会话1摘要 → 会话2摘要 → 会话3摘要 → ... │ │
│ │ 用户偏好 → 历史交互 → 长期知识 │ │
│ └──────────────────────────────────────────────┘ │
│ 生命周期:跨会话持久化 │
└────────────────────────────────────────────────────┘
```
---
## 📌 本章小结
- ADK 采用三层上下文架构Memory > Session > State
- **Session**:单次对话线程,由 SessionService 管理
- **State**:会话内数据,通过 `ctx.state` 读写
- **temp: State**:临时状态,当前调用有效
- **Memory**:跨会话持久化知识,由 MemoryService 管理
- **Runner** 是核心执行引擎,协调 Agent 和会话
**下一章**[第07章 - 回调机制与事件系统](./07-callbacks-and-events.md) → 学习如何使用回调函数控制 Agent 行为。

View File

@@ -0,0 +1,565 @@
# 第07章回调机制与事件系统
## 📌 本章目标
- 理解 ADK 的事件系统和工作原理
- 掌握四种回调函数的使用方法
- 学习如何通过回调实现自定义逻辑
- 了解 Human-in-the-Loop人机协作模式
- 掌握事件流的处理和过滤
---
## 7.1 事件系统概述
### 7.1.1 什么是事件Event
在 ADK 中Agent 的每一次操作都会产生**事件Event**。事件是 Agent 执行过程中的基本单元,记录了从接收用户输入到返回最终响应的完整过程。
```
用户输入 → [事件1] → [事件2] → ... → [事件N] → 最终响应
│ │ │
│ │ └── 最终响应事件
│ └── 工具调用事件
└── 模型调用事件
```
### 7.1.2 事件类型
| 事件类型 | 说明 | 触发时机 |
|----------|------|----------|
| **Model Event** | LLM 模型调用事件 | Agent 调用 LLM 时 |
| **Tool Event** | 工具调用事件 | Agent 调用工具时 |
| **Transfer Event** | Agent 转移事件 | LLM 决定委派给其他 Agent 时 |
| **Final Response** | 最终响应事件 | Agent 生成最终回复时 |
| **Error Event** | 错误事件 | 执行过程中发生错误时 |
---
## 7.2 回调函数详解
ADK 提供了四种回调函数,允许你在 Agent 执行的关键节点插入自定义逻辑:
```
用户输入
┌─────────────────────────────────────┐
│ before_model_callback │ ← 模型调用前
│ (可以修改输入、记录日志等) │
├─────────────────────────────────────┤
│ LLM 模型调用 │
├─────────────────────────────────────┤
│ after_model_callback │ ← 模型调用后
│ (可以修改输出、记录响应等) │
├─────────────────────────────────────┤
│ before_tool_callback │ ← 工具调用前
│ (可以验证参数、阻止调用等) │
├─────────────────────────────────────┤
│ 工具执行 │
├─────────────────────────────────────┤
│ after_tool_callback │ ← 工具调用后
│ (可以处理结果、记录日志等) │
└─────────────────────────────────────┘
```
### 7.2.1 before_model_callback模型调用前
```python
"""
before_model_callback 示例
在 LLM 模型调用之前执行
可以修改输入、记录日志、添加上下文等
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.agents.invocation_context import InvocationContext # 导入调用上下文
# ========================================
# 定义 before_model_callback
# ========================================
async def my_before_model_callback(
callback_context, # 回调上下文
invocation_context: InvocationContext, # 调用上下文
):
"""
模型调用前的回调函数
Args:
callback_context: 回调上下文,包含工具调用信息
invocation_context: 调用上下文,包含会话和状态信息
"""
# 记录日志:模型即将被调用
print(f"[日志] Agent '{invocation_context.agent.name}' 即将调用模型") # 打印日志
# 访问会话状态
call_count = invocation_context.session.state.get( # 读取调用计数
"model_call_count", # 状态键名
0, # 默认值
)
# 更新调用计数
invocation_context.session.state["model_call_count"] = call_count + 1 # 递增计数
print(f"[日志] 这是第 {call_count + 1} 次模型调用") # 打印调用次数
# 可以在这里添加额外的上下文信息
# 例如:将用户偏好注入到状态中
invocation_context.session.state["temp:call_timestamp"] = "2025-04-05T10:00:00Z" # 记录时间戳
# ========================================
# 创建使用回调的 Agent
# ========================================
agent = Agent(
name="callback_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个助手。", # 指令
before_model_callback=my_before_model_callback, # 🔑 注册模型前回调
)
```
### 7.2.2 after_model_callback模型调用后
```python
"""
after_model_callback 示例
在 LLM 模型调用之后执行
可以修改输出、记录响应、触发后续操作等
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.agents.invocation_context import InvocationContext # 导入调用上下文
async def my_after_model_callback(
callback_context, # 回调上下文
invocation_context: InvocationContext, # 调用上下文
):
"""
模型调用后的回调函数
Args:
callback_context: 回调上下文,包含模型响应信息
invocation_context: 调用上下文
"""
# 获取模型响应
response = callback_context.response # 模型响应对象
if response: # 如果有响应
# 记录响应信息
print(f"[日志] 模型返回了响应") # 打印日志
# 检查响应中是否有工具调用
if response.function_calls: # 如果有函数调用
for fc in response.function_calls: # 遍历函数调用
print(f"[日志] 模型决定调用工具: {fc.name}") # 打印工具名
else: # 如果没有函数调用
print(f"[日志] 模型直接返回了文本响应") # 打印日志
# 可以在这里进行响应后处理
# 例如:记录 token 使用量、响应延迟等
invocation_context.session.state["temp:last_model_call"] = "completed" # 标记完成
agent = Agent(
name="callback_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个助手。", # 指令
after_model_callback=my_after_model_callback, # 🔑 注册模型后回调
)
```
### 7.2.3 before_tool_callback工具调用前
```python
"""
before_tool_callback 示例
在工具调用之前执行
可以验证参数、阻止调用、修改参数等
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.agents.invocation_context import InvocationContext # 导入调用上下文
async def my_before_tool_callback(
callback_context, # 回调上下文
invocation_context: InvocationContext, # 调用上下文
):
"""
工具调用前的回调函数
Args:
callback_context: 回调上下文,包含工具调用信息
invocation_context: 调用上下文
"""
# 获取工具调用信息
tool_call = callback_context.function_call # 函数调用对象
tool_name = tool_call.name # 工具名称
tool_args = tool_call.args # 工具参数
print(f"[日志] 即将调用工具: {tool_name}") # 打印工具名
print(f"[日志] 工具参数: {tool_args}") # 打印参数
# ========================================
# 示例敏感操作确认Human-in-the-Loop
# ========================================
sensitive_tools = ["delete_data", "send_email", "make_payment"] # 敏感操作列表
if tool_name in sensitive_tools: # 如果是敏感操作
print(f"[警告] 敏感操作: {tool_name},需要人工确认!") # 打印警告
# 在实际应用中,这里可以:
# 1. 发送确认请求给用户
# 2. 阻止工具执行
# 3. 记录审计日志
# ========================================
# 示例:参数验证
# ========================================
if tool_name == "search": # 如果是搜索工具
query = tool_args.get("query", "") # 获取查询参数
if len(query) < 2: # 如果查询太短
print("[警告] 搜索查询过短") # 打印警告
agent = Agent(
name="callback_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个助手。", # 指令
before_tool_callback=my_before_tool_callback, # 🔑 注册工具前回调
)
```
### 7.2.4 after_tool_callback工具调用后
```python
"""
after_tool_callback 示例
在工具调用之后执行
可以处理结果、记录日志、触发后续操作等
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.agents.invocation_context import InvocationContext # 导入调用上下文
async def my_after_tool_callback(
callback_context, # 回调上下文
invocation_context: InvocationContext, # 调用上下文
):
"""
工具调用后的回调函数
Args:
callback_context: 回调上下文,包含工具调用结果
invocation_context: 调用上下文
"""
# 获取工具调用信息
tool_call = callback_context.function_call # 函数调用对象
tool_result = callback_context.tool_result # 工具执行结果
print(f"[日志] 工具 {tool_call.name} 执行完成") # 打印完成日志
print(f"[日志] 执行结果: {tool_result}") # 打印结果
# ========================================
# 示例:结果后处理
# ========================================
if tool_result: # 如果有结果
# 将工具结果保存到状态中
invocation_context.session.state[ # 保存到状态
f"temp:last_{tool_call.name}_result" # 动态键名
] = str(tool_result) # 保存结果字符串
# ========================================
# 示例:错误处理
# ========================================
if tool_result and tool_result.get("status") == "error": # 如果工具返回错误
print(f"[错误] 工具执行失败: {tool_result.get('error_message')}") # 打印错误
# 可以在这里进行错误恢复或通知
agent = Agent(
name="callback_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个助手。", # 指令
after_tool_callback=my_after_tool_callback, # 🔑 注册工具后回调
)
```
### 7.2.5 组合使用所有回调
```python
"""
组合使用所有回调函数
创建一个完整的带日志和监控的 Agent
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.agents.invocation_context import InvocationContext # 导入调用上下文
import time # 导入时间模块
# ========================================
# 日志回调:记录所有操作
# ========================================
async def log_before_model(cb_ctx, inv_ctx):
"""模型调用前:记录开始时间"""
inv_ctx.session.state["temp:model_start_time"] = time.time() # 记录开始时间
print(f"🔍 [模型] 开始调用 LLM...") # 打印日志
async def log_after_model(cb_ctx, inv_ctx):
"""模型调用后:计算耗时"""
start = inv_ctx.session.state.get("temp:model_start_time", time.time()) # 获取开始时间
elapsed = time.time() - start # 计算耗时
print(f"✅ [模型] LLM 调用完成,耗时: {elapsed:.2f}s") # 打印耗时
async def log_before_tool(cb_ctx, inv_ctx):
"""工具调用前:记录工具信息"""
fc = cb_ctx.function_call # 获取函数调用
print(f"🔧 [工具] 调用工具: {fc.name}({fc.args})") # 打印工具信息
async def log_after_tool(cb_ctx, inv_ctx):
"""工具调用后:记录结果"""
fc = cb_ctx.function_call # 获取函数调用
result = cb_ctx.tool_result # 获取工具结果
status = "成功" if result and result.get("status") == "success" else "失败" # 判断状态
print(f"📊 [工具] {fc.name} 执行{status}") # 打印结果
# ========================================
# 创建带完整回调的 Agent
# ========================================
def get_weather(city: str) -> dict:
"""获取天气"""
return {"status": "success", "weather": "晴天"} # 模拟天气数据
monitored_agent = Agent(
name="monitored_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个天气助手。", # 指令
tools=[get_weather], # 工具
before_model_callback=log_before_model, # 模型前回调
after_model_callback=log_after_model, # 模型后回调
before_tool_callback=log_before_tool, # 工具前回调
after_tool_callback=log_after_tool, # 工具后回调
)
```
---
## 7.3 Human-in-the-Loop人机协作
### 7.3.1 实现人工确认
```python
"""
Human-in-the-Loop 模式
在关键操作前暂停,等待人工确认
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.agents.invocation_context import InvocationContext # 导入调用上下文
# ========================================
# 定义需要人工确认的工具
# ========================================
def delete_file(filename: str) -> dict:
"""
删除文件(危险操作)
Args:
filename (str): 要删除的文件名
Returns:
dict: 操作结果
"""
# 在实际应用中,这里会执行真正的删除操作
print(f"🗑️ 已删除文件: {filename}") # 打印删除信息
return { # 返回结果
"status": "success", # 状态
"message": f"文件 '{filename}' 已删除", # 消息
}
def send_email(to: str, subject: str, body: str) -> dict:
"""
发送邮件(敏感操作)
Args:
to (str): 收件人邮箱
subject (str): 邮件主题
body (str): 邮件正文
Returns:
dict: 操作结果
"""
print(f"📧 已发送邮件到: {to}") # 打印发送信息
return { # 返回结果
"status": "success",
"message": f"邮件已发送到 {to}",
}
# ========================================
# 工具调用前回调:人工确认
# ========================================
async def human_confirmation_callback(
callback_context, # 回调上下文
invocation_context: InvocationContext, # 调用上下文
):
"""
人工确认回调
在执行敏感操作前暂停,等待人工确认
"""
tool_name = callback_context.function_call.name # 获取工具名
tool_args = callback_context.function_call.args # 获取工具参数
# 定义需要人工确认的操作
dangerous_operations = { # 危险操作映射
"delete_file": "删除文件", # 删除文件
"send_email": "发送邮件", # 发送邮件
}
if tool_name in dangerous_operations: # 如果是危险操作
operation = dangerous_operations[tool_name] # 获取操作描述
print(f"\n⚠️ 需要人工确认!") # 打印警告
print(f"操作: {operation}") # 打印操作
print(f"参数: {tool_args}") # 打印参数
print(f"请确认是否继续...") # 提示确认
# 在实际应用中,这里应该:
# 1. 通过 API 发送确认请求给前端
# 2. 等待用户确认
# 3. 根据确认结果决定是否继续
# 模拟人工确认(实际应用中应替换为真正的确认机制)
# 如果用户拒绝,可以抛出异常来阻止工具执行
# raise ValueError("用户拒绝了此操作")
# ========================================
# 创建带人工确认的 Agent
# ========================================
safe_agent = Agent(
name="safe_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个文件管理助手。\n"
"可以查看和删除文件,也可以发送邮件。\n"
"执行敏感操作前需要人工确认。"
),
tools=[ # 工具列表
delete_file, # 删除文件
send_email, # 发送邮件
],
before_tool_callback=human_confirmation_callback, # 🔑 人工确认回调
)
```
---
## 7.4 事件流处理
### 7.4.1 处理不同类型的事件
```python
"""
事件流处理
从 Runner 的事件流中提取和处理不同类型的事件
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.runners import Runner # 导入运行器
from google.adk.sessions import InMemorySessionService # 导入会话服务
from google.genai import types # 导入类型定义
import asyncio # 导入异步库
agent = Agent(
name="event_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个助手。", # 指令
)
async def process_events():
"""处理事件流的完整示例"""
# 初始化
session_service = InMemorySessionService() # 会话服务
await session_service.create_session( # 创建会话
app_name="event_app", # 应用名称
user_id="user_001", # 用户 ID
session_id="event_001", # 会话 ID
)
runner = Runner(
agent=agent, # Agent
app_name="event_app", # 应用名称
session_service=session_service, # 会话服务
)
# 构造消息
content = types.Content(
role='user', # 角色
parts=[types.Part(text='你好')], # 内容
)
# 运行并处理事件
events = runner.run_async(
user_id="user_001", # 用户 ID
session_id="event_001", # 会话 ID
new_message=content, # 消息
)
async for event in events: # 遍历事件流
# 检查事件类型并分别处理
event_type = type(event).__name__ # 获取事件类型名
print(f"[事件] 类型: {event_type}") # 打印事件类型
# 处理最终响应
if event.is_final_response(): # 如果是最终响应
text = event.content.parts[0].text # 提取文本
print(f"[最终响应] {text}") # 打印响应
# 处理工具调用
if hasattr(event, 'function_call') and event.function_call: # 如果有工具调用
fc = event.function_call # 获取函数调用
print(f"[工具调用] {fc.name}({fc.args})") # 打印调用信息
# 处理工具响应
if hasattr(event, 'function_response') and event.function_response: # 如果有工具响应
fr = event.function_response # 获取函数响应
print(f"[工具响应] {fr.name}: {fr.response}") # 打印响应
asyncio.run(process_events()) # 执行事件处理
```
---
## 📌 本章小结
- ADK 通过事件系统记录 Agent 的完整执行过程
- 四种回调函数before_model、after_model、before_tool、after_tool
- 回调函数可用于日志记录、参数验证、人工确认等
- Human-in-the-Loop 模式通过 before_tool_callback 实现
- Runner 返回的事件流可以逐个处理不同类型的事件
**下一章**[第08章 - 智能体评估](./08-evaluation.md) → 学习如何系统地评估 Agent 的性能。

View File

@@ -0,0 +1,444 @@
# 第08章智能体评估
## 📌 本章目标
- 理解 Agent 评估的重要性和方法
- 掌握评估集Eval Set的创建方法
- 学习使用 `adk eval` 命令进行自动化评估
- 了解评估指标和结果分析
- 掌握持续评估的最佳实践
---
## 8.1 评估概述
### 8.1.1 为什么需要评估?
Agent 的行为是非确定性的(由 LLM 驱动),同样的输入可能产生不同的输出。因此,需要系统化的评估来确保:
| 目标 | 说明 |
|------|------|
| **正确性** | Agent 是否给出了正确的回答 |
| **工具使用** | Agent 是否正确地使用了工具 |
| **指令遵循** | Agent 是否遵循了系统指令 |
| **安全性** | Agent 是否避免了不当输出 |
| **一致性** | Agent 在不同场景下表现是否一致 |
### 8.1.2 ADK 评估方法
ADK 提供了两种评估维度:
```
┌────────────────────────────────────────────┐
│ ADK 评估体系 │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ 最终响应评估Response Evaluation │ │
│ │ - 评估 Agent 的最终输出质量 │ │
│ │ - 准确性、完整性、格式等 │ │
│ └──────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ 执行轨迹评估Trajectory Evaluation│ │
│ │ - 评估 Agent 的执行过程 │ │
│ │ - 工具调用顺序、决策逻辑等 │ │
│ └──────────────────────────────────────┘ │
└────────────────────────────────────────────┘
```
---
## 8.2 创建评估集
### 8.2.1 评估集格式
评估集是一个 JSON 文件,包含多个测试用例:
```json
{
"eval_cases": [
{
"case_id": "test_001",
"description": "测试天气查询功能",
"user_input": "北京今天天气怎么样?",
"expected_function_calls": [
{
"name": "get_weather",
"args": {
"city": "北京"
}
}
],
"expected_keywords": ["北京", "天气"]
},
{
"case_id": "test_002",
"description": "测试时间查询功能",
"user_input": "现在几点了?",
"expected_function_calls": [
{
"name": "get_current_time",
"args": {
"city": "北京"
}
}
]
}
]
}
```
### 8.2.2 创建评估集文件
```python
"""
创建评估集文件
使用 Python 代码生成 .evalset.json 文件
"""
import json # 导入 JSON 模块
def create_eval_set():
"""创建评估集"""
# 定义评估用例列表
eval_cases = [ # 评估用例列表
{
# 用例标识
"case_id": "weather_beijing", # 用例唯一 ID
# 用例描述
"description": "测试北京天气查询", # 用例说明
# 用户输入
"user_input": "北京今天天气怎么样?", # 模拟用户提问
# 期望的工具调用(可选)
"expected_function_calls": [ # 期望 Agent 调用的工具
{
"name": "get_weather", # 工具名称
"args": { # 期望的参数
"city": "北京",
},
},
],
# 期望的关键词(可选)
"expected_keywords": ["北京", "天气"], # 回复中应包含的关键词
},
{
"case_id": "weather_shanghai", # 用例 ID
"description": "测试上海天气查询", # 描述
"user_input": "帮我查一下上海的天气", # 用户输入
"expected_function_calls": [ # 期望工具调用
{
"name": "get_weather",
"args": {"city": "上海"},
},
],
},
{
"case_id": "greeting", # 用例 ID
"description": "测试问候功能", # 描述
"user_input": "你好!", # 用户输入
"expected_keywords": ["你好"], # 期望关键词
"not_expected_keywords": ["工具", "错误"], # 不应出现的关键词
},
{
"case_id": "unknown_city", # 用例 ID
"description": "测试未知城市处理", # 描述
"user_input": "查询一下月球基地的天气", # 用户输入
"expected_keywords": ["找不到", "不支持", "无法"], # 应包含的错误提示
},
]
# 构建评估集对象
eval_set = { # 评估集
"eval_cases": eval_cases, # 用例列表
}
# 写入 JSON 文件
with open("my_agent_eval_set.evalset.json", "w", encoding="utf-8") as f: # 打开文件
json.dump( # 写入 JSON
eval_set, # 评估集数据
f, # 文件对象
ensure_ascii=False, # 允许中文
indent=2, # 缩进格式化
)
print("评估集已创建: my_agent_eval_set.evalset.json") # 打印成功信息
# 执行创建
create_eval_set() # 调用函数
```
---
## 8.3 运行评估
### 8.3.1 使用 CLI 运行评估
```bash
# 基本评估命令
adk eval my_agent my_agent_eval_set.evalset.json
# 指定输出格式
adk eval my_agent my_agent_eval_set.evalset.json --output-format json
# 指定输出文件
adk eval my_agent my_agent_eval_set.evalset.json --output-file results.json
```
### 8.3.2 评估输出示例
```json
{
"summary": {
"total_cases": 4,
"passed": 3,
"failed": 1,
"pass_rate": 0.75
},
"results": [
{
"case_id": "weather_beijing",
"status": "passed",
"response": "北京今天天气晴朗气温25°C。",
"function_calls_matched": true
},
{
"case_id": "weather_shanghai",
"status": "passed",
"response": "上海今天多云气温28°C。",
"function_calls_matched": true
},
{
"case_id": "greeting",
"status": "passed",
"response": "你好!有什么可以帮助你的吗?"
},
{
"case_id": "unknown_city",
"status": "failed",
"response": "月球基地的天气信息暂时无法获取。",
"reason": "Missing expected keywords: ['找不到', '不支持', '无法']"
}
]
}
```
---
## 8.4 代码方式评估
```python
"""
通过代码方式运行评估
适用于集成到 CI/CD 流水线
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.runners import Runner # 导入运行器
from google.adk.sessions import InMemorySessionService # 导入会话服务
from google.genai import types # 导入类型定义
import asyncio # 导入异步库
import json # 导入 JSON 模块
# ========================================
# 定义 Agent 和工具
# ========================================
def get_weather(city: str) -> dict:
"""获取天气信息"""
weather_data = { # 天气数据
"北京": {"temp": "25°C", "condition": "晴天"},
"上海": {"temp": "28°C", "condition": "多云"},
}
data = weather_data.get(city) # 查找数据
if not data: # 如果找不到
return {"status": "error", "error_message": f"未找到'{city}'的天气信息"}
return {"status": "success", "city": city, **data} # 返回天气
agent = Agent(
name="weather_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是天气助手,使用 get_weather 工具查询天气。", # 指令
tools=[get_weather], # 工具
)
# ========================================
# 定义评估逻辑
# ========================================
async def evaluate_agent(test_cases: list) -> dict:
"""
评估 Agent
Args:
test_cases: 测试用例列表
Returns:
dict: 评估结果
"""
session_service = InMemorySessionService() # 会话服务
runner = Runner( # 运行器
agent=agent, # Agent
app_name="eval_app", # 应用名称
session_service=session_service, # 会话服务
)
results = [] # 结果列表
passed = 0 # 通过计数
for i, case in enumerate(test_cases): # 遍历测试用例
# 创建独立会话
session_id = f"eval_session_{i}" # 会话 ID
await session_service.create_session( # 创建会话
app_name="eval_app", # 应用名称
user_id="eval_user", # 用户 ID
session_id=session_id, # 会话 ID
)
# 构造用户消息
content = types.Content(
role='user', # 角色
parts=[types.Part(text=case["user_input"])], # 内容
)
# 运行 Agent
events = runner.run_async(
user_id="eval_user", # 用户 ID
session_id=session_id, # 会话 ID
new_message=content, # 消息
)
# 收集响应
response_text = "" # 初始化响应文本
async for event in events: # 遍历事件
if event.is_final_response(): # 最终响应
response_text = event.content.parts[0].text # 提取文本
# 检查是否通过
case_passed = True # 默认通过
failure_reason = "" # 失败原因
# 检查期望关键词
if "expected_keywords" in case: # 如果有关键词要求
for keyword in case["expected_keywords"]: # 遍历关键词
if keyword not in response_text: # 如果关键词不存在
case_passed = False # 标记为失败
failure_reason = f"缺少关键词: '{keyword}'" # 记录原因
break # 跳出循环
if case_passed: # 如果通过
passed += 1 # 递增通过计数
# 记录结果
results.append({ # 添加结果
"case_id": case.get("case_id", f"case_{i}"), # 用例 ID
"status": "passed" if case_passed else "failed", # 状态
"response": response_text[:200], # 响应(截断)
"reason": failure_reason, # 失败原因
})
# 返回汇总结果
return { # 返回评估结果
"total": len(test_cases), # 总用例数
"passed": passed, # 通过数
"failed": len(test_cases) - passed, # 失败数
"pass_rate": passed / len(test_cases), # 通过率
"results": results, # 详细结果
}
# ========================================
# 运行评估
# ========================================
async def main():
"""主函数"""
# 定义测试用例
test_cases = [ # 测试用例列表
{
"case_id": "test_001", # 用例 ID
"user_input": "北京天气怎么样?", # 用户输入
"expected_keywords": ["北京", "天气"], # 期望关键词
},
{
"case_id": "test_002",
"user_input": "上海天气如何?",
"expected_keywords": ["上海"],
},
{
"case_id": "test_003",
"user_input": "你好",
"expected_keywords": ["你好"],
},
]
# 执行评估
result = await evaluate_agent(test_cases) # 运行评估
# 打印结果
print("=" * 50) # 分隔线
print("评估结果") # 标题
print("=" * 50) # 分隔线
print(f"总用例: {result['total']}") # 总数
print(f"通过: {result['passed']}") # 通过数
print(f"失败: {result['failed']}") # 失败数
print(f"通过率: {result['pass_rate']:.1%}") # 通过率
print("-" * 50) # 分隔线
for r in result["results"]: # 遍历详细结果
status_icon = "" if r["status"] == "passed" else "" # 状态图标
print(f"{status_icon} {r['case_id']}: {r['status']}") # 打印结果
if r["reason"]: # 如果有失败原因
print(f" 原因: {r['reason']}") # 打印原因
asyncio.run(main()) # 执行主函数
```
---
## 8.5 评估最佳实践
### 8.5.1 评估用例设计原则
| 原则 | 说明 | 示例 |
|------|------|------|
| **覆盖核心功能** | 每个工具至少一个正向用例 | 天气查询 → 查询有效城市 |
| **边界情况** | 测试异常输入的处理 | 查询不存在的城市 |
| **多轮对话** | 测试上下文记忆能力 | 先说名字,再问名字 |
| **安全测试** | 测试不当请求的处理 | 注入攻击、敏感信息 |
| **回归测试** | 修复 bug 后添加用例 | 之前出错的场景 |
### 8.5.2 持续评估流程
```
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 开发 Agent │───▶│ 编写评估集 │───▶│ 运行评估 │───▶│ 分析结果 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
▲ │
│ │
└───────────────────────────────────────────────┘
根据结果优化 Agent
```
---
## 📌 本章小结
- Agent 评估是确保质量的关键环节
- 评估集是 JSON 格式的测试用例集合
- 使用 `adk eval` 命令或代码方式运行评估
- 评估维度包括最终响应和执行轨迹
- 好的评估集应覆盖核心功能、边界情况和安全场景
**下一章**[第09章 - 部署指南](./09-deployment.md) → 学习如何将 Agent 部署到生产环境。

View File

@@ -0,0 +1,420 @@
# 第09章部署指南
## 📌 本章目标
- 了解 ADK Agent 的多种部署方式
- 掌握使用 Docker 容器化部署
- 学习部署到 Google Cloud Run
- 了解部署到 Vertex AI Agent Engine
- 掌握 API Server 的使用方法
---
## 9.1 部署方式概览
```
┌──────────────────────────────────────────────────────┐
│ ADK 部署选项 │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ 本地运行 │ │ Cloud Run │ │ Vertex AI │ │
│ │ (开发测试) │ │ (容器部署) │ │ Agent Eng. │ │
│ └──────────────┘ └──────────────┘ └────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ API Server │ │ 自定义部署 │ │
│ │ (FastAPI) │ │ (Docker) │ │
│ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────┘
```
| 方式 | 适用场景 | 复杂度 | 成本 |
|------|----------|--------|------|
| **本地运行** | 开发、测试 | 低 | 免费 |
| **API Server** | 集成到现有应用 | 低 | 取决于服务器 |
| **Cloud Run** | 生产环境、自动扩缩 | 中 | 按使用付费 |
| **Vertex AI** | 企业级、大规模 | 高 | 按使用付费 |
| **Docker** | 自托管、私有化 | 中 | 取决于服务器 |
---
## 9.2 API Server 部署
### 9.2.1 启动 API Server
```bash
# 启动 FastAPI 服务器
adk api_server --port 8080
# 指定 Agent 目录
adk api_server --agent-dir ./my_agent --port 8080
# 启动后API Server 提供以下端点:
# POST /apps/{app_name}/users/{user_id}/sessions/{session_id}:run
# GET /apps/{app_name}/users/{user_id}/sessions
# POST /apps/{app_name}/users/{user_id}/sessions
```
### 9.2.2 调用 API Server
```python
"""
调用 ADK API Server
通过 HTTP 请求与 Agent 交互
"""
import requests # 导入 HTTP 请求库
import json # 导入 JSON 模块
# ========================================
# 配置 API Server 地址
# ========================================
API_BASE_URL = "http://localhost:8080" # API Server 地址
APP_NAME = "my_agent" # 应用名称
USER_ID = "user_001" # 用户 ID
SESSION_ID = "session_001" # 会话 ID
# ========================================
# 创建会话
# ========================================
def create_session():
"""创建新的会话"""
url = f"{API_BASE_URL}/apps/{APP_NAME}/users/{USER_ID}/sessions" # API URL
response = requests.post( # 发送 POST 请求
url, # 请求 URL
json={ # 请求体
"session_id": SESSION_ID, # 会话 ID
},
)
session = response.json() # 解析 JSON 响应
print(f"会话创建成功: {session}") # 打印结果
return session # 返回会话信息
# ========================================
# 发送消息给 Agent
# ========================================
def send_message(message: str):
"""发送消息并获取 Agent 响应"""
url = ( # 构建 URL
f"{API_BASE_URL}/apps/{APP_NAME}/"
f"users/{USER_ID}/sessions/{SESSION_ID}:run"
)
payload = { # 请求体
"user_id": USER_ID, # 用户 ID
"session_id": SESSION_ID, # 会话 ID
"new_message": { # 新消息
"role": "user", # 角色
"parts": [{"text": message}], # 消息内容
},
}
response = requests.post( # 发送 POST 请求
url, # URL
json=payload, # 请求体
stream=True, # 流式响应
)
# 处理流式响应
for line in response.iter_lines(): # 逐行读取
if line: # 如果有内容
data = json.loads(line) # 解析 JSON
print(f"事件: {data}") # 打印事件
# ========================================
# 使用示例
# ========================================
if __name__ == "__main__":
create_session() # 创建会话
send_message("你好!") # 发送消息
```
---
## 9.3 Docker 容器化部署
### 9.3.1 创建 Dockerfile
```dockerfile
# ========================================
# ADK Agent Docker 部署文件
# ========================================
# 使用 Python 3.11 作为基础镜像
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 设置环境变量
# PYTHONUNBUFFERED: 确保 Python 输出直接显示到控制台
ENV PYTHONUNBUFFERED=1
# 复制依赖文件
COPY requirements.txt .
# 安装 Python 依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制 Agent 代码
COPY my_agent/ ./my_agent/
# 复制启动脚本
COPY start.sh .
# 赋予启动脚本执行权限
RUN chmod +x start.sh
# 暴露端口
EXPOSE 8080
# 启动 API Server
CMD ["./start.sh"]
```
### 9.3.2 创建 requirements.txt
```text
# ADK Agent 依赖文件
google-adk>=1.0.0
# 如果需要使用其他模型
litellm>=1.0.0
```
### 9.3.3 创建启动脚本
```bash
#!/bin/bash
# ADK Agent 启动脚本
# 设置 API Key从环境变量读取
export GOOGLE_API_KEY="${GOOGLE_API_KEY}"
# 启动 API Server
adk api_server \
--agent-dir ./my_agent \
--port 8080 \
--host 0.0.0.0
```
### 9.3.4 构建和运行 Docker 镜像
```bash
# 构建 Docker 镜像
docker build -t my-adk-agent .
# 运行 Docker 容器
docker run -d \
--name my-agent \
-p 8080:8080 \
-e GOOGLE_API_KEY="your_api_key" \
my-adk-agent
# 查看日志
docker logs -f my-agent
# 测试 API
curl -X POST http://localhost:8080/apps/my_agent/users/user_001/sessions \
-H "Content-Type: application/json" \
-d '{"session_id": "test_session"}'
```
---
## 9.4 部署到 Google Cloud Run
### 9.4.1 使用 ADK CLI 部署
```bash
# 使用 adk deploy 命令部署到 Cloud Run
adk deploy my_agent --platform cloud-run
# 部署过程:
# 1. 自动构建 Docker 镜像
# 2. 推送到 Google Container Registry
# 3. 部署到 Cloud Run
# 4. 配置环境变量和密钥
```
### 9.4.2 手动部署到 Cloud Run
```bash
# 第一步:配置 gcloud CLI
gcloud auth login # 登录 Google Cloud
gcloud config set project YOUR_PROJECT_ID # 设置项目 ID
# 第二步:构建并推送镜像
gcloud builds submit --tag gcr.io/YOUR_PROJECT_ID/my-agent
# 第三步:部署到 Cloud Run
gcloud run deploy my-agent \
--image gcr.io/YOUR_PROJECT_ID/my-agent \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars "GOOGLE_API_KEY=your_api_key"
# 第四步:获取服务 URL
gcloud run services describe my-agent \
--region us-central1 \
--format 'value(status.url)'
```
---
## 9.5 部署到 Vertex AI Agent Engine
### 9.5.1 使用 ADK CLI 部署
```bash
# 部署到 Vertex AI Agent Engine
adk deploy my_agent --platform vertex-ai
# Vertex AI Agent Engine 提供:
# - 自动扩缩容
# - 内置监控和日志
# - 企业级安全
# - 与 Google Cloud 生态集成
```
### 9.5.2 部署配置
```python
"""
Vertex AI Agent Engine 部署配置
"""
# 在 agent.py 中添加部署元数据
from google.adk.agents import Agent # 导入 Agent 类
root_agent = Agent(
name="my_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个助手。", # 指令
# Vertex AI 部署相关配置
# output_key="response", # 输出键
# before_model_callback=..., # 回调函数
)
```
---
## 9.6 生产环境最佳实践
### 9.6.1 安全配置
```python
"""
生产环境安全配置
"""
# ========================================
# 1. API Key 管理
# ========================================
# ❌ 不要硬编码 API Key
# BAD: api_key = "AIzaSy..."
# ✅ 使用环境变量
import os # 导入 os 模块
api_key = os.environ.get("GOOGLE_API_KEY") # 从环境变量读取
# ✅ 使用 Secret ManagerGoogle Cloud
# from google.cloud import secretmanager
# client = secretmanager.SecretManagerServiceClient()
# secret = client.access_secret_version(name="projects/.../secrets/api-key/versions/latest")
# ========================================
# 2. 输入验证
# ========================================
async def safe_before_model_callback(cb_ctx, inv_ctx):
"""安全回调:验证输入"""
# 获取用户输入
user_input = inv_ctx.session.state.get("temp:user_input", "") # 读取输入
# 检查输入长度
if len(user_input) > 10000: # 如果输入过长
raise ValueError("输入过长,请缩短您的消息。") # 抛出异常
# 检查敏感内容(示例)
sensitive_patterns = [ # 敏感内容模式
"密码", "credit card", "ssn", # 敏感词列表
]
for pattern in sensitive_patterns: # 遍历模式
if pattern.lower() in user_input.lower(): # 如果匹配
print(f"[安全] 检测到敏感内容: {pattern}") # 记录日志
# ========================================
# 3. 速率限制
# ========================================
# 在 API Server 前面配置速率限制
# 可以使用 Nginx、Cloud Armor 等工具
```
### 9.6.2 监控和日志
```python
"""
生产环境监控配置
"""
import logging # 导入日志模块
import time # 导入时间模块
# 配置日志
logging.basicConfig( # 配置日志格式
level=logging.INFO, # 日志级别
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # 格式
)
logger = logging.getLogger("adk_agent") # 创建日志记录器
async def monitoring_callback(cb_ctx, inv_ctx):
"""监控回调:记录性能指标"""
start_time = time.time() # 记录开始时间
# 记录请求信息
logger.info( # 记录信息日志
f"Agent: {inv_ctx.agent.name}, " # Agent 名称
f"User: {inv_ctx.session.user_id}" # 用户 ID
)
# 在 after 回调中计算耗时
elapsed = time.time() - start_time # 计算耗时
logger.info(f"处理耗时: {elapsed:.3f}s") # 记录耗时
# 如果耗时过长,记录警告
if elapsed > 5.0: # 如果超过5秒
logger.warning(f"请求处理耗时过长: {elapsed:.3f}s") # 记录警告
```
---
## 📌 本章小结
- ADK 支持多种部署方式本地、API Server、Cloud Run、Vertex AI
- API Server 基于 FastAPI提供 REST API 接口
- Docker 容器化是推荐的生产部署方式
- Cloud Run 提供自动扩缩容和按使用付费
- Vertex AI Agent Engine 提供企业级功能
- 生产环境需要注意安全、监控和日志
**下一章**[第10章 - 高级主题](./10-advanced-topics.md) → 学习安全最佳实践、A2A 协议等高级功能。

View File

@@ -0,0 +1,674 @@
# 第10章高级主题
## 📌 本章目标
- 掌握 ADK 的安全最佳实践
- 了解 A2AAgent-to-Agent协议
- 学习多模型混合使用策略
- 掌握自定义 Agent 的开发方法
- 了解性能优化技巧
---
## 10.1 安全最佳实践
### 10.1.1 输入安全
```python
"""
输入安全:防止注入攻击和不当输入
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.agents.invocation_context import InvocationContext # 导入上下文
import re # 导入正则表达式模块
# ========================================
# 输入过滤器
# ========================================
async def input_filter_callback(
callback_context, # 回调上下文
invocation_context: InvocationContext, # 调用上下文
):
"""
输入安全过滤回调
在模型调用前检查用户输入的安全性
"""
# 获取用户消息
# 从调用上下文中获取最近的用户消息
events = invocation_context.session.events # 获取会话事件
user_messages = [ # 筛选用户消息
e for e in events
if hasattr(e, 'content') and e.content.role == 'user'
]
if not user_messages: # 如果没有用户消息
return # 直接返回
last_message = user_messages[-1] # 获取最后一条用户消息
user_text = last_message.content.parts[0].text # 提取文本
# ========================================
# 检查1提示注入检测
# ========================================
injection_patterns = [ # 注入攻击模式
r"忽略.*指令", # "忽略之前的指令"
r"ignore.*instruction", # 英文注入
r"你现在是", # 角色切换
r"pretend.*you are", # 英文角色切换
r"system\s*:", # 系统提示伪造
]
for pattern in injection_patterns: # 遍历模式
if re.search(pattern, user_text, re.IGNORECASE): # 如果匹配
print(f"[安全] 检测到可能的注入攻击: {pattern}") # 记录警告
# 在实际应用中,可以拒绝处理或返回安全提示
# ========================================
# 检查2输入长度限制
# ========================================
MAX_INPUT_LENGTH = 5000 # 最大输入长度
if len(user_text) > MAX_INPUT_LENGTH: # 如果超过限制
print(f"[安全] 输入过长: {len(user_text)} > {MAX_INPUT_LENGTH}") # 记录警告
# ========================================
# 检查3敏感信息检测
# ========================================
sensitive_patterns = [ # 敏感信息模式
r"\b\d{3}-\d{2}-\d{4}\b", # SSN 格式
r"\b\d{16}\b", # 信用卡号格式
r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", # 邮箱
]
for pattern in sensitive_patterns: # 遍历模式
if re.search(pattern, user_text): # 如果匹配
print(f"[安全] 检测到可能的敏感信息") # 记录警告
# 不要在日志中记录实际的敏感信息
# ========================================
# 创建安全 Agent
# ========================================
safe_agent = Agent(
name="safe_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个安全的助手。遵循安全准则。", # 指令
before_model_callback=input_filter_callback, # 🔑 安全过滤回调
)
```
### 10.1.2 输出安全
```python
"""
输出安全:过滤不当输出
"""
async def output_filter_callback(
callback_context, # 回调上下文
invocation_context: InvocationContext, # 调用上下文
):
"""
输出安全过滤回调
在模型返回响应后检查输出内容
"""
response = callback_context.response # 获取模型响应
if not response: # 如果没有响应
return # 直接返回
# 检查响应中是否有文本内容
if response.candidates: # 如果有候选响应
for candidate in response.candidates: # 遍历候选
if candidate.content: # 如果有内容
text = candidate.content.parts[0].text # 提取文本
# 检查不当内容
forbidden_words = [ # 禁止词列表
"暴力", "违法", # 中文禁止词
]
for word in forbidden_words: # 遍历禁止词
if word in text: # 如果包含禁止词
print(f"[安全] 输出包含不当内容: {word}") # 记录警告
agent = Agent(
name="output_safe_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是一个安全的助手。", # 指令
after_model_callback=output_filter_callback, # 🔑 输出过滤回调
)
```
---
## 10.2 A2A 协议Agent-to-Agent
### 10.2.1 A2A 概述
A2AAgent-to-Agent协议是 Google 提出的开放标准,用于实现不同 Agent 系统之间的远程通信。
```
┌──────────────┐ ┌──────────────┐
│ Agent A │ │ Agent B │
│ (ADK) │◄────────►│ (其他框架) │
│ │ A2A 协议 │ │
│ - 发送任务 │ │ - 接收任务 │
│ - 接收结果 │ │ - 返回结果 │
└──────────────┘ └──────────────┘
```
### 10.2.2 A2A 集成示例
```python
"""
A2A 协议集成示例
让 ADK Agent 与远程 Agent 通信
"""
from google.adk.agents import Agent # 导入 Agent 类
from google.adk.tools import AgentTool # 导入 AgentTool
# ========================================
# 创建远程 Agent 客户端
# ========================================
# 假设有一个远程 Agent 服务运行在 http://remote-agent:3000
# 可以通过 A2A 协议与之通信
# 在实际使用中,需要安装 A2A 客户端库
# pip install a2a-sdk
# 创建远程 Agent 的代理
# remote_agent_proxy = create_remote_agent_proxy(
# url="http://remote-agent:3000", # 远程 Agent 地址
# agent_name="RemoteExpert", # 远程 Agent 名称
# )
# 将远程 Agent 包装为工具
# remote_tool = AgentTool(agent=remote_agent_proxy)
# ========================================
# 在本地 Agent 中使用远程 Agent
# ========================================
local_agent = Agent(
name="local_agent", # 本地 Agent 名称
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个协调助手。\n"
"对于本地可以处理的问题,直接回答。\n"
"对于需要专业知识的问题,使用远程 Agent 工具。"
),
# tools=[remote_tool], # 注册远程 Agent 工具
)
```
---
## 10.3 自定义 Agent 开发
### 10.3.1 继承 BaseAgent
```python
"""
自定义 Agent 开发
通过继承 BaseAgent 实现独特的 Agent 逻辑
"""
from google.adk.agents import BaseAgent # 导入基础 Agent 类
from google.adk.agents.invocation_context import InvocationContext # 导入调用上下文
from google.adk.events import Event, EventActions # 导入事件类型
from typing import AsyncGenerator # 导入异步生成器
import json # 导入 JSON 模块
import random # 导入随机数模块
# ========================================
# 示例一:随机决策 Agent
# ========================================
class RandomDecisionAgent(BaseAgent):
"""
随机决策 Agent
随机选择一个子 Agent 来处理请求
"""
async def _run_async_impl(
self,
ctx: InvocationContext, # 调用上下文
) -> AsyncGenerator[Event, None]: # 返回事件生成器
"""异步执行方法"""
# 获取子 Agent 列表
sub_agents = self.sub_agents # 获取子 Agent
if not sub_agents: # 如果没有子 Agent
yield Event( # 生成错误事件
author=self.name, # 作者
content="没有可用的子 Agent。", # 错误消息
)
return # 退出
# 随机选择一个子 Agent
chosen = random.choice(sub_agents) # 随机选择
print(f"[随机] 选择了 {chosen.name}") # 打印选择结果
# 将选择结果保存到状态
ctx.session.state["temp:chosen_agent"] = chosen.name # 保存选择
# 生成事件,指示委派给选中的 Agent
yield Event( # 生成委派事件
author=self.name, # 作者
actions=EventActions( # 事件动作
transfer_to_agent=chosen.name, # 委派给选中的 Agent
),
)
# ========================================
# 示例二:规则引擎 Agent
# ========================================
class RuleEngineAgent(BaseAgent):
"""
规则引擎 Agent
基于预定义规则处理请求,不使用 LLM
"""
def __init__(self, rules: dict, **kwargs):
"""
初始化规则引擎
Args:
rules: 规则字典key 为匹配模式value 为响应
"""
super().__init__(**kwargs) # 调用父类初始化
self.rules = rules # 保存规则
async def _run_async_impl(
self,
ctx: InvocationContext, # 调用上下文
) -> AsyncGenerator[Event, None]: # 返回事件生成器
"""异步执行方法"""
# 获取用户消息
events = ctx.session.events # 获取会话事件
user_events = [ # 筛选用户消息
e for e in events
if hasattr(e, 'content') and e.content.role == 'user'
]
if not user_events: # 如果没有用户消息
yield Event( # 生成提示事件
author=self.name,
content="请输入您的问题。",
)
return # 退出
user_text = user_events[-1].content.parts[0].text # 提取文本
# 匹配规则
matched_response = None # 匹配的响应
for pattern, response in self.rules.items(): # 遍历规则
if pattern.lower() in user_text.lower(): # 如果匹配
matched_response = response # 保存响应
break # 跳出循环
if matched_response: # 如果匹配到规则
yield Event( # 生成响应事件
author=self.name, # 作者
content=matched_response, # 响应内容
)
else: # 如果没有匹配
yield Event( # 生成默认响应
author=self.name,
content="抱歉,我无法处理您的请求。请尝试其他问题。",
)
# ========================================
# 使用自定义 Agent
# ========================================
# 创建规则引擎 Agent
faq_agent = RuleEngineAgent(
name="FAQBot", # Agent 名称
rules={ # 规则字典
"价格": "我们的产品价格请参考官网定价页面。", # 价格相关
"地址": "我们的地址是北京市海淀区xxx路xxx号。", # 地址相关
"电话": "客服电话400-xxx-xxxx。", # 电话相关
"营业时间": "营业时间:周一至周五 9:00-18:00。", # 营业时间
},
)
# 创建随机决策 Agent
random_agent = RandomDecisionAgent(
name="RandomRouter", # Agent 名称
sub_agents=[ # 子 Agent 列表
faq_agent, # FAQ Agent
],
)
```
---
## 10.4 多模型混合策略
```python
"""
多模型混合使用
根据任务类型选择不同的模型
"""
from google.adk.agents import LlmAgent # 导入 LlmAgent
from google.adk.models.lite_llm import LiteLlm # 导入 LiteLLM
# ========================================
# 策略一:不同 Agent 使用不同模型
# ========================================
# 简单任务使用快速模型
simple_agent = LlmAgent(
name="SimpleAgent", # 简单任务 Agent
model="gemini-2.0-flash", # 快速模型
description="处理简单的问答和翻译任务。", # 描述
instruction="快速准确地回答简单问题。", # 指令
)
# 复杂推理使用强力模型
reasoning_agent = LlmAgent(
name="ReasoningAgent", # 推理 Agent
model="gemini-2.5-pro-preview-05-06", # 强力模型
description="处理需要深度推理的复杂任务。", # 描述
instruction="仔细分析问题,给出深入的推理过程。", # 指令
)
# 代码生成使用专用模型
code_agent = LlmAgent(
name="CodeAgent", # 代码 Agent
model=LiteLlm( # 使用 LiteLLM
model="deepseek/deepseek-coder", # DeepSeek Coder
api_key="your_api_key", # API Key
),
description="处理代码生成和调试任务。", # 描述
instruction="编写高质量的代码。", # 指令
)
# 协调器使用快速模型
coordinator = LlmAgent(
name="Coordinator", # 协调器
model="gemini-2.0-flash", # 快速模型
instruction=( # 指令
"根据任务复杂度分配给合适的 Agent\n"
"- 简单问答 → SimpleAgent\n"
"- 复杂推理 → ReasoningAgent\n"
"- 代码相关 → CodeAgent"
),
sub_agents=[simple_agent, reasoning_agent, code_agent], # 子 Agent
)
```
---
## 10.5 性能优化技巧
### 10.5.1 指令优化
```python
"""
指令优化技巧
减少 token 使用,提高响应速度
"""
# ❌ 冗长的指令(浪费 token
verbose_instruction = """
你是一个非常专业的、经验丰富的、知识渊博的助手。
你擅长回答各种各样的问题,包括但不限于技术问题、
生活问题、工作问题、学习问题等等。
当用户问你问题时,你应该:
1. 首先理解用户的问题
2. 然后分析问题
3. 最后给出答案
请始终使用中文回答。
"""
# ✅ 精简的指令(节省 token
concise_instruction = """
你是专业助手。用中文简洁回答。
"""
# ✅ 使用结构化指令
structured_instruction = """
## 角色
技术助手
## 规则
1. 使用中文
2. 简洁回答(<200字
3. 不确定时说"不确定"
"""
```
### 10.5.2 工具优化
```python
"""
工具优化技巧
减少不必要的工具调用
"""
# ❌ 工具描述过于详细(浪费 token
def bad_tool(query: str) -> dict:
"""
这是一个非常详细的工具描述,包含了大量的信息,
但是大部分信息对 LLM 来说是不必要的。
LLM 需要读取所有这些文本来理解工具的功能,
这会消耗大量的 token 并降低响应速度。
"""
return {"result": "done"}
# ✅ 精简的工具描述
def good_tool(query: str) -> dict:
"""搜索知识库并返回相关文档片段。"""
return {"result": "done"}
# ✅ 合并相似工具
# 如果有多个功能相似的工具,考虑合并
def unified_search(
query: str, # 搜索关键词
search_type: str = "all", # 搜索类型all/doc/faq
) -> dict:
"""
统一搜索工具
Args:
query: 搜索关键词
search_type: 搜索类型,可选 "all"(全部)、"doc"(文档)、"faq"(FAQ)
"""
return {"status": "success", "results": []}
```
---
## 10.6 完整实战项目:智能客服系统
```python
"""
完整实战项目:多智能体客服系统
整合本教程所有知识点
"""
from google.adk.agents import ( # 导入所有 Agent 类型
Agent, # LLM Agent
LlmAgent, # LLM Agent完整名
SequentialAgent, # 顺序工作流
ParallelAgent, # 并行工作流
LoopAgent, # 循环工作流
BaseAgent, # 基础 Agent
)
from google.adk.events import Event, EventActions # 导入事件类型
from google.adk.agents.invocation_context import InvocationContext # 导入上下文
from google.adk.tools import google_search # 导入 Google 搜索
from typing import AsyncGenerator # 导入异步生成器
import asyncio # 导入异步库
# ========================================
# 第一步:定义工具
# ========================================
def search_faq(query: str) -> dict:
"""搜索常见问题解答"""
faq_db = { # FAQ 数据库
"退款": "退款将在3-5个工作日内处理完成。", # 退款
"配送": "标准配送3-5天加急配送1-2天。", # 配送
"退换货": "7天无理由退换货请保持商品完好。", # 退换货
}
for key, value in faq_db.items(): # 遍历 FAQ
if key in query: # 如果匹配
return {"status": "success", "answer": value} # 返回答案
return {"status": "not_found", "answer": "未找到相关FAQ。"} # 未找到
def create_ticket(
category: str, # 工单类别
description: str, # 问题描述
priority: str = "normal", # 优先级
) -> dict:
"""创建客户工单"""
ticket_id = f"TK{len(description)}" # 生成工单号
return { # 返回工单信息
"status": "success",
"ticket_id": ticket_id,
"message": f"工单 {ticket_id} 已创建,我们会尽快处理。",
}
# ========================================
# 第二步:定义子 Agent
# ========================================
# FAQ Agent处理常见问题
faq_agent = LlmAgent(
name="FAQBot", # FAQ 机器人
model="gemini-2.0-flash", # 快速模型
description="处理常见问题,如退款、配送、退换货等。", # 描述
instruction="你是FAQ助手使用 search_faq 工具查找答案。", # 指令
tools=[search_faq], # FAQ 搜索工具
)
# 搜索 Agent处理需要联网查询的问题
search_agent = LlmAgent(
name="SearchBot", # 搜索机器人
model="gemini-2.0-flash", # 快速模型
description="处理需要搜索互联网的问题。", # 描述
instruction="使用 Google Search 搜索最新信息。", # 指令
tools=[google_search], # Google 搜索工具
)
# 工单 Agent处理需要人工介入的问题
ticket_agent = LlmAgent(
name="TicketBot", # 工单机器人
model="gemini-2.0-flash", # 快速模型
description="创建客户工单,转交人工处理。", # 描述
instruction="收集用户问题描述,使用 create_ticket 创建工单。", # 指令
tools=[create_ticket], # 创建工单工具
)
# ========================================
# 第三步:定义协调器
# ========================================
coordinator = LlmAgent(
name="CustomerServiceCoordinator", # 客服协调器
model="gemini-2.0-flash", # 快速模型
description="智能客服主协调器,负责路由用户请求。", # 描述
instruction=( # 系统指令
"你是一个智能客服协调器。\n"
"根据用户问题类型,分配给合适的子 Agent\n"
"- 退款、配送、退换货等常见问题 → FAQBot\n"
"- 需要最新信息的问题 → SearchBot\n"
"- 无法自动解决的问题 → TicketBot\n"
"使用 transfer_to_agent 进行任务分配。"
),
sub_agents=[ # 注册子 Agent
faq_agent, # FAQ 机器人
search_agent, # 搜索机器人
ticket_agent, # 工单机器人
],
)
# ========================================
# 第四步:添加监控回调
# ========================================
async def monitoring_callback(cb_ctx, inv_ctx):
"""监控回调:记录所有操作"""
agent_name = inv_ctx.agent.name # 获取 Agent 名称
print(f"[监控] Agent '{agent_name}' 正在处理请求") # 打印日志
coordinator.before_model_callback = monitoring_callback # 添加监控
# ========================================
# 第五步:设置 root_agent
# ========================================
root_agent = coordinator # 设置根 Agent
print("✅ 智能客服系统构建完成!") # 打印成功信息
print(f"根 Agent: {root_agent.name}") # 打印根 Agent 名称
print(f"子 Agent 数量: {len(root_agent.sub_agents)}") # 打印子 Agent 数量
```
---
## 📌 本章小结
- 输入安全:检测注入攻击、限制输入长度、过滤敏感信息
- 输出安全:过滤不当内容、记录审计日志
- A2A 协议:实现不同 Agent 系统间的远程通信
- 自定义 Agent继承 BaseAgent 实现独特逻辑
- 多模型策略:根据任务类型选择合适的模型
- 性能优化:精简指令、合并工具、合理选择模型
---
## 🎉 教程完结
恭喜你完成了 Google ADK 完整教程的学习!
### 学习路径回顾
```
第01章 环境搭建 → 第02章 Hello World
第03章 LLM Agent → 第04章 自定义工具
第05章 多智能体 → 第06章 会话状态
第07章 回调机制 → 第08章 评估
第09章 部署 → 第10章 高级主题
```
### 后续学习建议
1. **实践项目**:基于教程知识,构建自己的 Agent 应用
2. **阅读源码**:深入研究 [adk-python](https://github.com/google/adk-python) 源码
3. **社区参与**:关注 ADK 的更新和社区讨论
4. **探索生态**:了解 ADK 的 Java、Go 版本和 Web 版本
### 官方资源
- 📖 [官方文档](https://google.github.io/adk-docs/)
- 💻 [GitHub 仓库](https://github.com/google/adk-python)
- 📦 [示例代码](https://github.com/google/adk-samples)

View File

@@ -0,0 +1,90 @@
# Google ADK 完整使用教程
> **Agent Development Kit (ADK)** — Google 开源的 AI 智能体开发框架
>
> 版本v1.8.0+ | 语言Python | 许可证Apache 2.0
---
## 📖 教程目录
### 第一部分:入门基础
| 章节 | 标题 | 说明 |
|------|------|------|
| [第01章](./01-introduction-and-setup.md) | ADK 简介与环境搭建 | 框架概述、核心特性、安装配置 |
| [第02章](./02-quick-start-hello-world.md) | 快速开始Hello World | 创建第一个 Agent、运行与测试 |
### 第二部分:核心开发
| 章节 | 标题 | 说明 |
|------|------|------|
| [第03章](./03-llm-agent-in-depth.md) | LLM 智能体详解 | Agent 配置、指令设计、模型选择 |
| [第04章](./04-custom-tools.md) | 自定义工具开发 | 函数工具、MCP 工具、OpenAPI 集成 |
| [第05章](./05-multi-agent-systems.md) | 多智能体系统 | 协调器模式、工作流编排、Agent 间通信 |
| [第06章](./06-session-and-state.md) | 会话与状态管理 | Session、State、Memory 机制详解 |
### 第三部分:进阶与部署
| 章节 | 标题 | 说明 |
|------|------|------|
| [第07章](./07-callbacks-and-events.md) | 回调机制与事件系统 | Before/After 回调、事件处理、自定义逻辑 |
| [第08章](./08-evaluation.md) | 智能体评估 | 评估集设计、自动化测试、性能分析 |
| [第09章](./09-deployment.md) | 部署指南 | Docker 容器化、Cloud Run、Vertex AI Agent Engine |
| [第10章](./10-advanced-topics.md) | 高级主题 | 安全最佳实践、A2A 协议、多模型适配 |
---
## 📂 代码文件说明
| 文件 | 对应章节 | 说明 |
|------|----------|------|
| `code/setup_demo.py` | 第01章 | 环境搭建与安装验证 |
| `code/hello_world.py` | 第02章 | Hello World 示例 |
| `code/llm_agent_demo.py` | 第03章 | LLM 智能体完整示例 |
| `code/custom_tools_demo.py` | 第04章 | 自定义工具开发示例 |
| `code/multi_agent_demo.py` | 第05章 | 多智能体系统示例 |
| `code/session_state_demo.py` | 第06章 | 会话与状态管理示例 |
| `code/callback_demo.py` | 第07章 | 回调机制示例 |
| `code/eval_demo.py` | 第08章 | 评估示例 |
| `code/deploy_demo.py` | 第09章 | 部署配置示例 |
| `code/advanced_demo.py` | 第10章 | 高级功能示例 |
---
## 🚀 快速导航
### 我是新手,从哪里开始?
👉 从 [第01章](./01-introduction-and-setup.md) 开始,按顺序学习。
### 我只想快速体验?
👉 直接跳到 [第02章](./02-quick-start-hello-world.md) 的 Hello World。
### 我想构建多智能体系统?
👉 重点学习 [第05章](./05-multi-agent-systems.md)。
### 我想部署到生产环境?
👉 查看 [第09章](./09-deployment.md)。
---
## 🔗 官方资源
- **官方文档**https://google.github.io/adk-docs/
- **GitHub 仓库**https://github.com/google/adk-python
- **示例代码**https://github.com/google/adk-samples
- **Java 版本**https://github.com/google/adk-java
- **Web 版本**https://github.com/google/adk-web
---
## 📋 前置要求
- **Python**3.10 或更高版本v1.19.0+ 要求)
- **pip**Python 包管理器
- **Google AI Studio API Key**:用于 Gemini 模型调用
- **(可选)** 其他 LLM 的 API Key如 OpenAI、DeepSeek 等)
---
> 💡 **提示**:本教程所有代码示例均提供详细中文注释,建议配合代码文件一起阅读。

View File

@@ -0,0 +1,293 @@
"""
Google ADK 高级主题完整示例
展示安全、自定义 Agent、多模型等高级功能
对应教程第10章 - 高级主题
"""
# 导入 ADK 核心模块
from google.adk.agents import ( # 导入所有 Agent 类型
Agent, # LLM Agent
LlmAgent, # LLM Agent完整名
BaseAgent, # 基础 Agent
)
from google.adk.events import Event, EventActions # 导入事件类型
from google.adk.agents.invocation_context import InvocationContext # 导入上下文
from google.adk.models.lite_llm import LiteLlm # LiteLLM 适配器
# 导入辅助模块
from typing import AsyncGenerator # 异步生成器
import re # 正则表达式
import random # 随机数
import asyncio # 异步编程
# ========================================
# 示例一:输入安全过滤
# ========================================
async def input_security_filter(cb_ctx, inv_ctx):
"""
输入安全过滤回调
检测注入攻击和敏感信息
Args:
cb_ctx: 回调上下文
inv_ctx: 调用上下文
"""
# 获取用户消息
events = inv_ctx.session.events # 获取会话事件
user_msgs = [ # 筛选用户消息
e for e in events
if hasattr(e, 'content') and e.content.role == 'user'
]
if not user_msgs: # 如果没有用户消息
return # 直接返回
text = user_msgs[-1].content.parts[0].text # 提取文本
# 检测注入攻击
injection_patterns = [ # 注入模式
r"忽略.*指令", # 中文注入
r"ignore.*instruction", # 英文注入
r"你现在是", # 角色切换
r"system\s*:", # 系统提示伪造
]
for pattern in injection_patterns: # 遍历模式
if re.search(pattern, text, re.IGNORECASE): # 如果匹配
print(f"[安全] ⚠️ 检测到可能的注入攻击") # 记录警告
break # 跳出循环
# 检测敏感信息
sensitive_patterns = [ # 敏感信息模式
r"\b\d{3}-\d{2}-\d{4}\b", # SSN
r"\b\d{16}\b", # 信用卡号
]
for pattern in sensitive_patterns: # 遍历模式
if re.search(pattern, text): # 如果匹配
print(f"[安全] ⚠️ 检测到可能的敏感信息") # 记录警告
break # 跳出循环
# 检查输入长度
if len(text) > 5000: # 如果过长
print(f"[安全] ⚠️ 输入过长: {len(text)} 字符") # 记录警告
# ========================================
# 示例二:自定义 Agent — 规则引擎
# ========================================
class RuleEngineAgent(BaseAgent):
"""
规则引擎 Agent
基于预定义规则处理请求,不使用 LLM
"""
def __init__(self, rules: dict, **kwargs):
"""
初始化规则引擎
Args:
rules: 规则字典 {匹配模式: 响应}
"""
super().__init__(**kwargs) # 调用父类初始化
self.rules = rules # 保存规则
async def _run_async_impl(
self,
ctx: InvocationContext, # 调用上下文
) -> AsyncGenerator[Event, None]: # 返回事件生成器
"""执行规则匹配"""
# 获取用户消息
events = ctx.session.events # 获取事件
user_events = [ # 筛选用户消息
e for e in events
if hasattr(e, 'content') and e.content.role == 'user'
]
if not user_events: # 如果没有消息
yield Event( # 生成提示
author=self.name,
content="请输入您的问题。",
)
return # 退出
text = user_events[-1].content.parts[0].text # 提取文本
# 匹配规则
for pattern, response in self.rules.items(): # 遍历规则
if pattern.lower() in text.lower(): # 如果匹配
yield Event( # 生成响应
author=self.name,
content=response,
)
return # 退出
# 没有匹配的规则
yield Event( # 生成默认响应
author=self.name,
content="抱歉,我无法处理您的请求。请尝试其他问题。",
)
# ========================================
# 示例三:自定义 Agent — 随机路由
# ========================================
class RandomRouter(BaseAgent):
"""
随机路由 Agent
随机选择一个子 Agent 处理请求
"""
async def _run_async_impl(
self,
ctx: InvocationContext, # 调用上下文
) -> AsyncGenerator[Event, None]: # 返回事件生成器
"""随机选择子 Agent"""
sub_agents = self.sub_agents # 获取子 Agent
if not sub_agents: # 如果没有子 Agent
yield Event( # 生成错误
author=self.name,
content="没有可用的子 Agent。",
)
return # 退出
chosen = random.choice(sub_agents) # 随机选择
print(f"[随机路由] 选择了 {chosen.name}") # 打印选择
yield Event( # 生成委派事件
author=self.name,
actions=EventActions(
transfer_to_agent=chosen.name, # 委派
),
)
# ========================================
# 示例四:多模型混合策略
# ========================================
# 简单任务:使用快速模型
simple_agent = LlmAgent(
name="SimpleAgent", # 简单任务 Agent
model="gemini-2.0-flash", # 快速模型
description="处理简单的问答和翻译。", # 描述
instruction="快速准确地回答简单问题。", # 指令
)
# 复杂推理:使用强力模型
reasoning_agent = LlmAgent(
name="ReasoningAgent", # 推理 Agent
model="gemini-2.5-pro-preview-05-06", # 强力模型
description="处理需要深度推理的复杂任务。", # 描述
instruction="仔细分析,给出深入的推理过程。", # 指令
)
# 代码生成:使用 DeepSeek
code_agent = LlmAgent(
name="CodeAgent", # 代码 Agent
model=LiteLlm( # 使用 LiteLLM
model="deepseek/deepseek-coder", # DeepSeek Coder
api_key="your_api_key", # API Key
),
description="处理代码生成和调试。", # 描述
instruction="编写高质量的代码。", # 指令
)
# 协调器:使用快速模型
multi_model_coordinator = LlmAgent(
name="MultiModelCoordinator", # 多模型协调器
model="gemini-2.0-flash", # 快速模型
instruction=( # 指令
"根据任务类型分配给合适的 Agent\n"
"- 简单问答 → SimpleAgent\n"
"- 复杂推理 → ReasoningAgent\n"
"- 代码相关 → CodeAgent"
),
sub_agents=[simple_agent, reasoning_agent, code_agent], # 子 Agent
)
# ========================================
# 示例五:完整实战 — 智能客服系统
# ========================================
def search_faq(query: str) -> dict:
"""搜索 FAQ"""
faq_db = { # FAQ 数据库
"退款": "退款将在3-5个工作日内处理完成。",
"配送": "标准配送3-5天加急配送1-2天。",
"退换货": "7天无理由退换货请保持商品完好。",
}
for key, value in faq_db.items(): # 遍历 FAQ
if key in query: # 如果匹配
return {"status": "success", "answer": value}
return {"status": "not_found"}
def create_ticket(category: str, description: str) -> dict:
"""创建工单"""
ticket_id = f"TK{hash(description) % 10000:04d}" # 生成工单号
return { # 返回结果
"status": "success",
"ticket_id": ticket_id,
"message": f"工单 {ticket_id} 已创建。",
}
# FAQ Agent
faq_agent = LlmAgent(
name="FAQBot", # FAQ 机器人
model="gemini-2.0-flash", # 模型
description="处理常见问题。", # 描述
instruction="使用 search_faq 工具查找答案。", # 指令
tools=[search_faq], # FAQ 工具
)
# 工单 Agent
ticket_agent = LlmAgent(
name="TicketBot", # 工单机器人
model="gemini-2.0-flash", # 模型
description="创建客户工单。", # 描述
instruction="使用 create_ticket 创建工单。", # 指令
tools=[create_ticket], # 工单工具
)
# 客服协调器
customer_service = LlmAgent(
name="CustomerService", # 客服系统
model="gemini-2.0-flash", # 模型
description="智能客服主协调器。", # 描述
instruction=( # 指令
"你是智能客服协调器。\n"
"常见问题 → FAQBot\n"
"无法解决 → TicketBot\n"
"使用 transfer_to_agent 委派任务。"
),
sub_agents=[faq_agent, ticket_agent], # 子 Agent
before_model_callback=input_security_filter, # 安全过滤
)
# ========================================
# 打印信息
# ========================================
if __name__ == "__main__":
print("=" * 60) # 分隔线
print("Google ADK 高级主题示例") # 标题
print("=" * 60) # 分隔线
print("\n🔒 安全过滤回调: input_security_filter") # 安全
print("🤖 自定义 Agent: RuleEngineAgent, RandomRouter") # 自定义
print("🔀 多模型策略: SimpleAgent + ReasoningAgent + CodeAgent") # 多模型
print("🏢 实战项目: 智能客服系统 (CustomerService)") # 实战
print("\n✅ 所有高级功能定义完成!") # 成功信息

View File

@@ -0,0 +1,255 @@
"""
Google ADK 回调机制完整示例
展示四种回调函数的使用方法
对应教程第07章 - 回调机制与事件系统
"""
# 导入 ADK 核心模块
from google.adk.agents import Agent # Agent 类
from google.adk.agents.invocation_context import InvocationContext # 调用上下文
from google.adk.runners import Runner # 运行器
from google.adk.sessions import InMemorySessionService # 会话服务
from google.genai import types # 类型定义
# 导入异步和时间模块
import asyncio # 异步编程库
import time # 时间模块
# ========================================
# 示例一:日志回调(记录所有操作)
# ========================================
async def log_before_model(cb_ctx, inv_ctx):
"""
模型调用前回调
Args:
cb_ctx: 回调上下文
inv_ctx: 调用上下文
"""
# 记录开始时间
inv_ctx.session.state["temp:model_start_time"] = time.time() # 保存时间戳
# 获取调用计数
count = inv_ctx.session.state.get("model_call_count", 0) # 读取计数
inv_ctx.session.state["model_call_count"] = count + 1 # 递增
print(f"🔍 [模型前] Agent '{inv_ctx.agent.name}' 即将调用 LLM (第{count+1}次)") # 打印日志
async def log_after_model(cb_ctx, inv_ctx):
"""
模型调用后回调
Args:
cb_ctx: 回调上下文
inv_ctx: 调用上下文
"""
# 计算耗时
start = inv_ctx.session.state.get("temp:model_start_time", time.time()) # 开始时间
elapsed = time.time() - start # 计算耗时
response = cb_ctx.response # 获取模型响应
if response and response.function_calls: # 如果有工具调用
tools = [fc.name for fc in response.function_calls] # 工具名列表
print(f"✅ [模型后] LLM 调用完成 ({elapsed:.2f}s),决定调用工具: {tools}") # 打印
else: # 如果直接响应
print(f"✅ [模型后] LLM 调用完成 ({elapsed:.2f}s),直接响应") # 打印
async def log_before_tool(cb_ctx, inv_ctx):
"""
工具调用前回调
Args:
cb_ctx: 回调上下文
inv_ctx: 调用上下文
"""
fc = cb_ctx.function_call # 获取函数调用
print(f"🔧 [工具前] 调用工具: {fc.name}({fc.args})") # 打印工具信息
async def log_after_tool(cb_ctx, inv_ctx):
"""
工具调用后回调
Args:
cb_ctx: 回调上下文
inv_ctx: 调用上下文
"""
fc = cb_ctx.function_call # 获取函数调用
result = cb_ctx.tool_result # 获取工具结果
status = "成功" # 默认成功
if result and result.get("status") == "error": # 如果错误
status = "失败" # 标记失败
print(f"📊 [工具后] {fc.name} 执行{status}") # 打印结果
# ========================================
# 示例二:人工确认回调
# ========================================
async def human_confirmation(cb_ctx, inv_ctx):
"""
人工确认回调
在敏感操作前暂停
Args:
cb_ctx: 回调上下文
inv_ctx: 调用上下文
"""
tool_name = cb_ctx.function_call.name # 获取工具名
tool_args = cb_ctx.function_call.args # 获取工具参数
# 定义敏感操作
sensitive_ops = { # 敏感操作映射
"delete_file": "删除文件", # 删除文件
"send_email": "发送邮件", # 发送邮件
"make_payment": "发起支付", # 发起支付
}
if tool_name in sensitive_ops: # 如果是敏感操作
op = sensitive_ops[tool_name] # 获取操作描述
print(f"\n⚠️ [人工确认] 需要人工确认!") # 打印警告
print(f" 操作: {op}") # 打印操作
print(f" 参数: {tool_args}") # 打印参数
print(f" 状态: 已记录(模拟自动通过)") # 模拟通过
# ========================================
# 示例三:参数验证回调
# ========================================
async def validate_params(cb_ctx, inv_ctx):
"""
参数验证回调
在工具调用前验证参数
Args:
cb_ctx: 回调上下文
inv_ctx: 调用上下文
"""
tool_name = cb_ctx.function_call.name # 工具名
tool_args = cb_ctx.function_call.args # 工具参数
# 验证搜索查询长度
if tool_name == "search": # 如果是搜索工具
query = tool_args.get("query", "") # 获取查询
if len(query) < 2: # 如果太短
print(f"⚠️ [验证] 搜索查询过短: '{query}'") # 打印警告
# 验证数值范围
if tool_name == "calculate": # 如果是计算工具
value = tool_args.get("value", 0) # 获取数值
if value < 0: # 如果为负数
print(f"⚠️ [验证] 数值不能为负: {value}") # 打印警告
# ========================================
# 定义工具函数
# ========================================
def get_weather(city: str) -> dict:
"""获取天气信息"""
weather_data = { # 天气数据
"北京": {"temp": "25°C", "condition": "晴天"},
"上海": {"temp": "28°C", "condition": "多云"},
}
data = weather_data.get(city) # 查找数据
if not data: # 如果找不到
return {"status": "error", "error_message": f"未找到'{city}'的天气信息"}
return {"status": "success", "city": city, **data} # 返回天气
def delete_file(filename: str) -> dict:
"""删除文件(模拟)"""
print(f"🗑️ 执行删除: {filename}") # 模拟删除
return {"status": "success", "message": f"文件 '{filename}' 已删除"}
# ========================================
# 创建带回调的 Agent
# ========================================
monitored_agent = Agent(
name="monitored_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction=( # 系统指令
"你是一个天气助手。\n"
"使用 get_weather 工具查询天气。\n"
"使用 delete_file 工具删除文件(需要确认)。"
),
tools=[get_weather, delete_file], # 注册工具
before_model_callback=log_before_model, # 模型前回调
after_model_callback=log_after_model, # 模型后回调
before_tool_callback=log_before_tool, # 工具前回调
after_tool_callback=log_after_tool, # 工具后回调
)
# ========================================
# 运行演示
# ========================================
APP_NAME = "callback_demo" # 应用名称
USER_ID = "user_001" # 用户 ID
SESSION_ID = "session_001" # 会话 ID
async def main():
"""主函数"""
print("=" * 60) # 分隔线
print("Google ADK 回调机制演示") # 标题
print("=" * 60) # 分隔线
# 初始化
session_service = InMemorySessionService() # 会话服务
await session_service.create_session( # 创建会话
app_name=APP_NAME, # 应用名称
user_id=USER_ID, # 用户 ID
session_id=SESSION_ID, # 会话 ID
)
# 创建 Runner
runner = Runner(
agent=monitored_agent, # Agent
app_name=APP_NAME, # 应用名称
session_service=session_service, # 会话服务
)
# 测试问题
queries = [ # 测试列表
"北京天气怎么样?", # 天气查询
]
for query in queries: # 遍历测试
print(f"\n{'='*60}") # 分隔线
print(f"[用户]: {query}") # 打印用户输入
# 构造消息
content = types.Content(
role='user', # 角色
parts=[types.Part(text=query)], # 内容
)
# 运行 Agent
events = runner.run_async(
user_id=USER_ID, # 用户 ID
session_id=SESSION_ID, # 会话 ID
new_message=content, # 消息
)
# 处理事件
async for event in events: # 遍历事件
if event.is_final_response(): # 最终响应
print(f"[Agent]: {event.content.parts[0].text}") # 打印回复
if __name__ == "__main__": # 直接运行
asyncio.run(main()) # 执行主函数

View File

@@ -0,0 +1,338 @@
"""
Google ADK 自定义工具开发完整示例
展示各种工具开发技巧和最佳实践
对应教程第04章 - 自定义工具开发
"""
# 导入 ADK 核心模块
from google.adk.agents import Agent # Agent 类
from google.adk.tools import AgentTool # AgentToolAgent 作为工具)
# 导入类型提示
from typing import Optional, List # 可选类型和列表类型
# 导入运行相关模块
from google.adk.runners import Runner # 运行器
from google.adk.sessions import InMemorySessionService # 会话服务
from google.genai import types # 类型定义
# 导入异步库
import asyncio # 异步编程库
# ========================================
# 示例一:基础函数工具
# ========================================
def get_weather(city: str) -> dict:
"""
获取指定城市的天气信息
Args:
city (str): 城市名称,例如"北京""上海"
Returns:
dict: 包含天气状态的字典
- status: "success""error"
- city: 城市名称
- temperature: 温度
- condition: 天气状况
"""
# 模拟天气数据
weather_data = { # 天气数据库
"北京": {"temp": "25°C", "condition": "晴天"}, # 北京
"上海": {"temp": "28°C", "condition": "多云"}, # 上海
"广州": {"temp": "32°C", "condition": "雷阵雨"}, # 广州
}
# 查找城市天气
data = weather_data.get(city) # 获取天气数据
if not data: # 如果找不到
return { # 返回错误
"status": "error",
"error_message": f"未找到'{city}'的天气信息"
}
return { # 返回成功结果
"status": "success",
"city": city,
"temperature": data["temp"],
"condition": data["condition"],
}
# ========================================
# 示例二:带可选参数的工具
# ========================================
def search_restaurant(
city: str, # 必选参数
cuisine: str = "中餐", # 可选参数,默认中餐
price_range: str = "中等", # 可选参数,默认中等
) -> dict:
"""
搜索指定城市的餐厅
Args:
city (str): 城市名称
cuisine (str, optional): 菜系类型,默认"中餐"
price_range (str, optional): 价格范围,默认"中等"
Returns:
dict: 搜索结果
"""
return { # 返回搜索结果
"status": "success",
"city": city,
"cuisine": cuisine,
"price_range": price_range,
"results": [ # 餐厅列表
{"name": "美味餐厅", "rating": 4.5, "price": "人均100元"},
{"name": "佳肴轩", "rating": 4.2, "price": "人均80元"},
],
}
# ========================================
# 示例三:使用 Optional 类型
# ========================================
def create_user_profile(
username: str, # 必选参数
bio: Optional[str] = None, # 可选参数
avatar_url: Optional[str] = None, # 可选参数
) -> dict:
"""
创建用户档案
Args:
username (str): 用户名
bio (str, optional): 个人简介
avatar_url (str, optional): 头像 URL
Returns:
dict: 创建结果
"""
profile = { # 创建档案
"username": username, # 用户名
"bio": bio or "这个人很懒,什么都没写。", # 简介
"avatar_url": avatar_url or "", # 头像
}
return { # 返回结果
"status": "success",
"profile": profile,
}
# ========================================
# 示例四List 类型参数
# ========================================
def compare_cities(cities: List[str]) -> dict:
"""
比较多个城市的信息
Args:
cities (List[str]): 城市名称列表
Returns:
dict: 比较结果
"""
city_info = { # 城市信息
"北京": {"population": "2189万", "area": "16410km²"},
"上海": {"population": "2487万", "area": "6341km²"},
"广州": {"population": "1881万", "area": "7434km²"},
}
results = [] # 结果列表
for city in cities: # 遍历城市
info = city_info.get(city) # 获取信息
if info: # 如果存在
results.append({"city": city, **info}) # 添加到结果
return { # 返回结果
"status": "success" if results else "error",
"results": results,
}
# ========================================
# 示例五:健壮的工具(完善的错误处理)
# ========================================
def calculate_loan(
principal: float, # 贷款本金
annual_rate: float, # 年利率
years: int, # 贷款年限
) -> dict:
"""
计算贷款月供和总利息
Args:
principal (float): 贷款本金(元)
annual_rate (float): 年利率(百分比,如 4.5
years (int): 贷款年限
Returns:
dict: 计算结果
"""
# 参数验证
if principal <= 0: # 本金必须大于0
return { # 返回错误
"status": "error",
"error_code": "INVALID_PRINCIPAL",
"error_message": "贷款本金必须大于0。"
}
if annual_rate <= 0 or annual_rate > 30: # 利率范围检查
return { # 返回错误
"status": "error",
"error_code": "INVALID_RATE",
"error_message": "年利率必须在 0-30% 之间。"
}
if years <= 0 or years > 30: # 年限范围检查
return { # 返回错误
"status": "error",
"error_code": "INVALID_YEARS",
"error_message": "贷款年限必须在 1-30 年之间。"
}
try: # 尝试计算
monthly_rate = annual_rate / 100 / 12 # 月利率
total_months = years * 12 # 总月数
# 等额本息月供公式
if monthly_rate == 0: # 如果利率为0
monthly_payment = principal / total_months # 直接除
else: # 正常计算
monthly_payment = ( # 月供计算
principal * monthly_rate * (1 + monthly_rate) ** total_months
/ ((1 + monthly_rate) ** total_months - 1)
)
total_amount = monthly_payment * total_months # 总还款额
total_interest = total_amount - principal # 总利息
return { # 返回计算结果
"status": "success",
"monthly_payment": round(monthly_payment, 2), # 月供
"total_interest": round(total_interest, 2), # 总利息
"total_amount": round(total_amount, 2), # 总还款额
}
except Exception as e: # 捕获计算异常
return { # 返回错误
"status": "error",
"error_code": "CALCULATION_ERROR",
"error_message": f"计算出错:{str(e)}"
}
# ========================================
# 示例六Agent-as-a-Tool
# ========================================
# 定义一个翻译 Agent
translator_agent = Agent(
name="translator", # 翻译 Agent
model="gemini-2.0-flash", # 模型
instruction="你是翻译专家,将文本翻译成目标语言。只输出翻译结果。", # 指令
)
# 将翻译 Agent 包装为工具
translation_tool = AgentTool(
agent=translator_agent, # 传入翻译 Agent
)
# ========================================
# 创建综合 Agent
# ========================================
comprehensive_agent = Agent(
name="comprehensive_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction=( # 系统指令
"你是一个多功能助手。\n"
"你可以:\n"
"1. 查询天气get_weather\n"
"2. 搜索餐厅search_restaurant\n"
"3. 创建用户档案create_user_profile\n"
"4. 比较城市compare_cities\n"
"5. 计算贷款calculate_loan\n"
"6. 翻译文本translation_tool\n"
"根据用户需求选择合适的工具。"
),
tools=[ # 注册所有工具
get_weather, # 天气查询
search_restaurant, # 餐厅搜索
create_user_profile, # 用户档案
compare_cities, # 城市比较
calculate_loan, # 贷款计算
translation_tool, # 翻译工具
],
)
# ========================================
# 运行示例
# ========================================
APP_NAME = "tools_demo" # 应用名称
USER_ID = "user_001" # 用户 ID
SESSION_ID = "session_001" # 会话 ID
async def main():
"""运行工具演示"""
# 创建会话服务
session_service = InMemorySessionService() # 会话服务
await session_service.create_session( # 创建会话
app_name=APP_NAME, # 应用名称
user_id=USER_ID, # 用户 ID
session_id=SESSION_ID, # 会话 ID
)
# 创建运行器
runner = Runner(
agent=comprehensive_agent, # Agent
app_name=APP_NAME, # 应用名称
session_service=session_service, # 会话服务
)
# 测试查询
queries = [ # 测试问题列表
"北京天气怎么样?", # 天气查询
"帮我搜索上海的意大利餐厅", # 餐厅搜索
"贷款100万利率4.5%30年月供多少", # 贷款计算
]
for query in queries: # 遍历测试问题
print(f"\n[用户]: {query}") # 打印用户输入
# 构造消息
content = types.Content(
role='user', # 角色
parts=[types.Part(text=query)], # 内容
)
# 运行 Agent
events = runner.run_async(
user_id=USER_ID, # 用户 ID
session_id=SESSION_ID, # 会话 ID
new_message=content, # 消息
)
# 获取响应
async for event in events: # 遍历事件
if event.is_final_response(): # 最终响应
print(f"[Agent]: {event.content.parts[0].text}") # 打印回复
if __name__ == "__main__": # 直接运行
asyncio.run(main()) # 执行主函数

View File

@@ -0,0 +1,201 @@
"""
Google ADK 部署配置完整示例
展示各种部署方式的配置
对应教程第09章 - 部署指南
"""
# 导入 ADK 核心模块
from google.adk.agents import Agent # Agent 类
from google.adk.tools import google_search # Google 搜索工具
# ========================================
# 定义 Agent部署入口
# ========================================
def get_weather(city: str) -> dict:
"""
获取天气信息
Args:
city (str): 城市名称
Returns:
dict: 天气信息
"""
weather_data = { # 天气数据
"北京": {"temp": "25°C", "condition": "晴天"},
"上海": {"temp": "28°C", "condition": "多云"},
}
data = weather_data.get(city) # 查找数据
if not data: # 如果找不到
return {"status": "error", "error_message": f"未找到'{city}'的天气信息"}
return {"status": "success", "city": city, **data} # 返回天气
# root_agent 是 ADK 的入口点
# 部署时 ADK 会自动查找这个变量
root_agent = Agent(
name="deploy_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
description="部署演示 Agent。", # 描述
instruction=( # 系统指令
"你是一个多功能助手。\n"
"可以查询天气和搜索信息。\n"
"使用中文回答。"
),
tools=[get_weather, google_search], # 注册工具
)
# ========================================
# 以下为部署配置参考
# ========================================
# ----------------------------------------
# Dockerfile 内容(保存为 Dockerfile
# ----------------------------------------
"""
# 使用 Python 3.11 作为基础镜像
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 设置环境变量
ENV PYTHONUNBUFFERED=1
# 复制依赖文件
COPY requirements.txt .
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制 Agent 代码
COPY deploy_demo.py .
# 暴露端口
EXPOSE 8080
# 启动 API Server
CMD ["adk", "api_server", "--port", "8080", "--host", "0.0.0.0"]
"""
# ----------------------------------------
# requirements.txt 内容
# ----------------------------------------
"""
google-adk>=1.0.0
"""
# ----------------------------------------
# start.sh 启动脚本
# ----------------------------------------
"""
#!/bin/bash
# ADK Agent 启动脚本
# 从环境变量读取 API Key
export GOOGLE_API_KEY="${GOOGLE_API_KEY}"
# 启动 API Server
adk api_server \\
--port 8080 \\
--host 0.0.0.0
"""
# ----------------------------------------
# docker-compose.yml 内容
# ----------------------------------------
"""
version: '3.8'
services:
adk-agent:
build: .
ports:
- "8080:8080"
environment:
- GOOGLE_API_KEY=${GOOGLE_API_KEY}
restart: unless-stopped
"""
# ----------------------------------------
# 部署命令参考
# ----------------------------------------
"""
# 构建镜像
docker build -t my-adk-agent .
# 运行容器
docker run -d \\
--name my-agent \\
-p 8080:8080 \\
-e GOOGLE_API_KEY="your_api_key" \\
my-adk-agent
# 使用 adk deploy 部署到 Cloud Run
adk deploy . --platform cloud-run
# 使用 adk deploy 部署到 Vertex AI
adk deploy . --platform vertex-ai
"""
# ========================================
# API 调用示例
# ========================================
def api_call_example():
"""
调用 ADK API Server 的示例代码
需要先启动 API Server: adk api_server --port 8080
"""
import requests # HTTP 请求库
import json # JSON 处理
API_BASE = "http://localhost:8080" # API 地址
APP_NAME = "deploy_agent" # 应用名称
USER_ID = "user_001" # 用户 ID
SESSION_ID = "session_001" # 会话 ID
# 创建会话
session_url = f"{API_BASE}/apps/{APP_NAME}/users/{USER_ID}/sessions"
response = requests.post( # 发送请求
session_url, # URL
json={"session_id": SESSION_ID}, # 请求体
)
print(f"会话创建: {response.json()}") # 打印结果
# 发送消息
run_url = f"{session_url}/{SESSION_ID}:run"
payload = { # 请求体
"user_id": USER_ID, # 用户 ID
"session_id": SESSION_ID, # 会话 ID
"new_message": { # 新消息
"role": "user", # 角色
"parts": [{"text": "北京天气怎么样?"}], # 内容
},
}
response = requests.post( # 发送请求
run_url, # URL
json=payload, # 请求体
stream=True, # 流式响应
)
for line in response.iter_lines(): # 逐行读取
if line: # 如果有内容
data = json.loads(line) # 解析 JSON
print(f"事件: {data}") # 打印事件
if __name__ == "__main__":
print("✅ 部署配置示例") # 打印信息
print("请参考文件中的注释配置部署。") # 提示
print("\n快速启动命令:") # 打印命令
print(" adk api_server --port 8080 # 启动 API Server")
print(" adk web --port 8000 # 启动 Web UI")
print(" adk deploy . --platform cloud-run # 部署到 Cloud Run")

View File

@@ -0,0 +1,272 @@
"""
Google ADK 智能体评估完整示例
展示评估集创建和评估运行方法
对应教程第08章 - 智能体评估
"""
# 导入 ADK 核心模块
from google.adk.agents import Agent # Agent 类
from google.adk.runners import Runner # 运行器
from google.adk.sessions import InMemorySessionService # 会话服务
from google.genai import types # 类型定义
# 导入辅助模块
import asyncio # 异步编程库
import json # JSON 处理
# ========================================
# 定义被评估的 Agent
# ========================================
def get_weather(city: str) -> dict:
"""
获取天气信息
Args:
city (str): 城市名称
Returns:
dict: 天气信息
"""
weather_data = { # 天气数据
"北京": {"temp": "25°C", "condition": "晴天"},
"上海": {"temp": "28°C", "condition": "多云"},
"广州": {"temp": "32°C", "condition": "雷阵雨"},
}
data = weather_data.get(city) # 查找数据
if not data: # 如果找不到
return {"status": "error", "error_message": f"未找到'{city}'的天气信息"}
return {"status": "success", "city": city, **data} # 返回天气
agent = Agent(
name="weather_agent", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction="你是天气助手,使用 get_weather 工具查询天气。用中文回答。", # 指令
tools=[get_weather], # 工具
)
# ========================================
# 示例一:创建评估集
# ========================================
def create_eval_set():
"""
创建评估集文件
生成 .evalset.json 文件供 adk eval 使用
"""
# 定义评估用例
eval_cases = [ # 用例列表
{
"case_id": "weather_beijing", # 用例 ID
"description": "测试北京天气查询", # 描述
"user_input": "北京今天天气怎么样?", # 用户输入
"expected_keywords": ["北京", "天气"], # 期望关键词
},
{
"case_id": "weather_shanghai", # 用例 ID
"description": "测试上海天气查询", # 描述
"user_input": "帮我查一下上海的天气", # 用户输入
"expected_keywords": ["上海"], # 期望关键词
},
{
"case_id": "greeting", # 用例 ID
"description": "测试问候功能", # 描述
"user_input": "你好!", # 用户输入
"expected_keywords": ["你好"], # 期望关键词
},
{
"case_id": "unknown_city", # 用例 ID
"description": "测试未知城市处理", # 描述
"user_input": "查询月球基地的天气", # 用户输入
"expected_keywords": ["找不到", "无法", "不支持"], # 期望关键词
},
{
"case_id": "multiple_cities", # 用例 ID
"description": "测试多城市查询", # 描述
"user_input": "北京和上海的天气对比", # 用户输入
"expected_keywords": ["北京", "上海"], # 期望关键词
},
]
# 构建评估集
eval_set = { # 评估集对象
"eval_cases": eval_cases, # 用例列表
}
# 写入文件
filepath = "weather_agent_eval_set.evalset.json" # 文件路径
with open(filepath, "w", encoding="utf-8") as f: # 打开文件
json.dump( # 写入 JSON
eval_set, # 数据
f, # 文件对象
ensure_ascii=False, # 允许中文
indent=2, # 格式化
)
print(f"✅ 评估集已创建: {filepath}") # 打印成功信息
print(f" 用例数量: {len(eval_cases)}") # 打印用例数
return filepath # 返回文件路径
# ========================================
# 示例二:代码方式运行评估
# ========================================
async def evaluate_agent(test_cases: list) -> dict:
"""
评估 Agent
Args:
test_cases: 测试用例列表
Returns:
dict: 评估结果汇总
"""
# 初始化服务
session_service = InMemorySessionService() # 会话服务
runner = Runner( # 运行器
agent=agent, # Agent
app_name="eval_app", # 应用名称
session_service=session_service, # 会话服务
)
results = [] # 结果列表
passed = 0 # 通过计数
for i, case in enumerate(test_cases): # 遍历用例
case_id = case.get("case_id", f"case_{i}") # 用例 ID
print(f"\n📋 测试用例: {case_id}") # 打印用例 ID
print(f" 输入: {case['user_input']}") # 打印输入
# 创建独立会话
session_id = f"eval_session_{i}" # 会话 ID
await session_service.create_session( # 创建会话
app_name="eval_app", # 应用名称
user_id="eval_user", # 用户 ID
session_id=session_id, # 会话 ID
)
# 构造消息
content = types.Content(
role='user', # 角色
parts=[types.Part(text=case["user_input"])], # 内容
)
# 运行 Agent
events = runner.run_async(
user_id="eval_user", # 用户 ID
session_id=session_id, # 会话 ID
new_message=content, # 消息
)
# 收集响应
response_text = "" # 初始化响应
async for event in events: # 遍历事件
if event.is_final_response(): # 最终响应
response_text = event.content.parts[0].text # 提取文本
# 检查期望关键词
case_passed = True # 默认通过
failure_reason = "" # 失败原因
if "expected_keywords" in case: # 如果有关键词要求
missing = [] # 缺失的关键词
for kw in case["expected_keywords"]: # 遍历关键词
if kw not in response_text: # 如果缺失
missing.append(kw) # 记录缺失
if missing: # 如果有缺失
case_passed = False # 标记失败
failure_reason = f"缺少关键词: {missing}" # 失败原因
# 检查不应出现的关键词
if "not_expected_keywords" in case: # 如果有排除关键词
for kw in case["not_expected_keywords"]: # 遍历
if kw in response_text: # 如果出现
case_passed = False # 标记失败
failure_reason = f"不应包含关键词: '{kw}'" # 失败原因
if case_passed: # 如果通过
passed += 1 # 递增通过数
print(f" ✅ 通过") # 打印通过
else: # 如果失败
print(f" ❌ 失败: {failure_reason}") # 打印失败原因
# 记录结果
results.append({ # 添加结果
"case_id": case_id, # 用例 ID
"status": "passed" if case_passed else "failed", # 状态
"response": response_text[:200], # 响应(截断)
"reason": failure_reason, # 失败原因
})
# 返回汇总
return { # 返回结果
"total": len(test_cases), # 总数
"passed": passed, # 通过数
"failed": len(test_cases) - passed, # 失败数
"pass_rate": passed / len(test_cases) if test_cases else 0, # 通过率
"results": results, # 详细结果
}
# ========================================
# 示例三:打印评估报告
# ========================================
def print_report(result: dict):
"""
打印评估报告
Args:
result: 评估结果
"""
print("\n" + "=" * 60) # 分隔线
print("📊 评估报告") # 标题
print("=" * 60) # 分隔线
print(f"总用例数: {result['total']}") # 总数
print(f"通过: {result['passed']}") # 通过数
print(f"失败: {result['failed']}") # 失败数
print(f"通过率: {result['pass_rate']:.1%}") # 通过率
print("-" * 60) # 分隔线
for r in result["results"]: # 遍历详细结果
icon = "" if r["status"] == "passed" else "" # 状态图标
print(f" {icon} {r['case_id']}: {r['status']}") # 打印结果
if r["reason"]: # 如果有失败原因
print(f" 原因: {r['reason']}") # 打印原因
if r["status"] == "passed": # 如果通过
print(f" 响应: {r['response'][:100]}...") # 打印响应片段
print("=" * 60) # 分隔线
# ========================================
# 主函数
# ========================================
async def main():
"""主函数"""
# 第一步:创建评估集
filepath = create_eval_set() # 创建评估集文件
# 第二步:加载评估用例
with open(filepath, "r", encoding="utf-8") as f: # 读取文件
eval_set = json.load(f) # 解析 JSON
test_cases = eval_set["eval_cases"] # 获取用例列表
# 第三步:运行评估
result = await evaluate_agent(test_cases) # 执行评估
# 第四步:打印报告
print_report(result) # 打印评估报告
if __name__ == "__main__": # 直接运行
asyncio.run(main()) # 执行主函数

View File

@@ -0,0 +1,137 @@
"""
Google ADK Hello World 示例
一个带有自定义工具的完整 Agent 示例
对应教程第02章 - 快速开始Hello World
运行方式adk run hello_world将此文件放在 hello_world/agent.py 中)
"""
# 导入 ADK 的 Agent 类
from google.adk.agents import Agent # Agent 是 LlmAgent 的别名
# 导入标准库:日期时间处理
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/Shanghai", # 广州使用上海时区
"深圳": "Asia/Shanghai", # 深圳使用上海时区
"东京": "Asia/Tokyo", # 东京时区
"首尔": "Asia/Seoul", # 首尔时区
"纽约": "America/New_York", # 纽约时区
"伦敦": "Europe/London", # 伦敦时区
"巴黎": "Europe/Paris", # 巴黎时区
"洛杉矶": "America/Los_Angeles", # 洛杉矶时区
}
# 查找城市对应的时区标识符
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 # 格式化后的时间字符串
}
def get_weather(city: str) -> dict:
"""
获取指定城市的天气信息(模拟数据)
Args:
city (str): 城市名称
Returns:
dict: 包含天气信息的字典
"""
# 模拟天气数据(实际应用中应调用真实天气 API
weather_data = { # 模拟天气数据库
"北京": {"temp": "25°C", "condition": "晴天", "humidity": "45%"}, # 北京
"上海": {"temp": "28°C", "condition": "多云", "humidity": "65%"}, # 上海
"广州": {"temp": "32°C", "condition": "雷阵雨", "humidity": "80%"}, # 广州
}
# 查找城市天气数据
data = weather_data.get(city) # 从字典中获取天气数据
# 如果找不到该城市的天气
if not data: # 检查数据是否存在
return { # 返回错误信息
"status": "error", # 状态:错误
"error_message": f"未找到'{city}'的天气信息" # 错误描述
}
# 返回成功结果
return { # 返回天气信息
"status": "success", # 状态:成功
"city": city, # 城市名称
"temperature": data["temp"], # 温度
"condition": data["condition"], # 天气状况
"humidity": data["humidity"], # 湿度
}
# ========================================
# 定义 AgentADK 入口点)
# ========================================
# root_agent 是 ADK 自动识别的入口变量
# 变量名必须是 root_agent不能修改
root_agent = Agent(
name="hello_agent", # Agent 的唯一标识名称
model="gemini-2.0-flash", # 使用 Gemini 2.0 Flash 模型
description="一个能查询城市时间和天气的友好助手。", # Agent 的描述
instruction=( # 系统指令(多行字符串)
"你是一个友好的助手,可以帮助用户查询世界各城市的当前时间和天气。\n"
"当用户询问某个城市的时间时,使用 get_current_time 工具来获取。\n"
"当用户询问某个城市的天气时,使用 get_weather 工具来获取。\n"
"如果工具返回错误,请友好地告知用户并建议尝试其他城市。\n"
"回复时请使用中文,语气亲切友好。"
),
tools=[ # 将自定义函数注册为 Agent 的工具
get_current_time, # 时间查询工具
get_weather, # 天气查询工具
],
)

View File

@@ -0,0 +1,210 @@
"""
Google ADK LLM 智能体完整示例
展示 LlmAgent 的各种配置和用法
对应教程第03章 - LLM 智能体详解
"""
# 导入 ADK 核心模块
from google.adk.agents import Agent, LlmAgent # Agent 和 LlmAgent
from google.adk.agents import SequentialAgent # 顺序工作流
from google.adk.models.lite_llm import LiteLlm # LiteLLM 适配器
from google.adk.tools import google_search # Google 搜索工具
from google.adk.tools import code_execution # 代码执行工具
# 导入运行相关模块
from google.adk.runners import Runner # 运行器
from google.adk.sessions import InMemorySessionService # 内存会话服务
from google.genai import types # Google GenAI 类型定义
# 导入异步库
import asyncio # 异步编程库
# ========================================
# 示例一:基础 LlmAgent 配置
# ========================================
# 使用 Gemini 模型(默认)
gemini_agent = Agent(
name="gemini_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 2.0 Flash
instruction="你是一个专业的编程助手。", # 系统指令
description="擅长编程和代码审查。", # 描述(用于多 Agent 路由)
)
# 使用 LiteLLM 适配 DeepSeek 模型
deepseek_agent = Agent(
name="deepseek_agent", # Agent 名称
model=LiteLlm( # 使用 LiteLLM 适配器
model="deepseek/deepseek-chat", # 格式provider/model_name
api_key="你的_DEEPSEEK_API_KEY", # API Key
),
instruction="你是一个中文写作助手。", # 系统指令
)
# 使用 LiteLLM 适配 OpenAI 模型
openai_agent = Agent(
name="openai_agent", # Agent 名称
model=LiteLlm( # 使用 LiteLLM 适配器
model="openai/gpt-4o", # OpenAI GPT-4o
api_key="你的_OPENAI_API_KEY", # API Key
),
instruction="你是一个数据分析助手。", # 系统指令
)
# ========================================
# 示例二:使用 Google 内置工具
# ========================================
# 带搜索能力的 Agent
search_agent = Agent(
name="search_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction=( # 系统指令
"你是一个信息查询助手。\n"
"使用 Google Search 工具搜索最新信息。\n"
"根据搜索结果提供准确、全面的回答。"
),
tools=[google_search], # 注册 Google 搜索工具
)
# 带代码执行能力的 Agent
code_agent = Agent(
name="code_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction=( # 系统指令
"你是一个数据分析助手。\n"
"使用 Code Execution 工具执行 Python 代码。\n"
"确保代码正确且高效。"
),
tools=[code_execution], # 注册代码执行工具
)
# 多功能 Agent搜索 + 代码执行)
super_agent = Agent(
name="super_agent", # Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction="你是一个全能助手,可以搜索信息和执行代码。", # 指令
tools=[google_search, code_execution], # 注册多个工具
)
# ========================================
# 示例三output_key 用法
# ========================================
# 第一步:提取信息的 Agent
extractor = Agent(
name="Extractor", # 信息提取 Agent
model="gemini-2.0-flash", # 模型
instruction="从用户输入中提取关键信息。只输出提取的信息。", # 指令
output_key="extracted_info", # 将输出保存到 state['extracted_info']
)
# 第二步:分析信息的 Agent引用第一步的输出
analyzer = Agent(
name="Analyzer", # 信息分析 Agent
model="gemini-2.0-flash", # 模型
instruction=( # 指令:通过 {key} 引用 state 中的值
"分析以下提取的信息,给出深度分析。\n"
"提取的信息:{extracted_info}"
),
output_key="analysis_result", # 将分析结果保存到 state
)
# 创建顺序流水线
pipeline = SequentialAgent(
name="InfoPipeline", # 流水线名称
sub_agents=[extractor, analyzer], # 按顺序执行
)
# ========================================
# 示例四Agent Transfer动态委派
# ========================================
# 定义专门的子 Agent
booking_agent = LlmAgent(
name="BookingAgent", # 预订 Agent
model="gemini-2.0-flash", # 模型
description="负责处理航班和酒店的预订请求。", # 描述
instruction="你是一个预订助手,帮助用户预订航班和酒店。", # 指令
)
info_agent = LlmAgent(
name="InfoAgent", # 信息查询 Agent
model="gemini-2.0-flash", # 模型
description="负责回答一般性问题和提供信息。", # 描述
instruction="你是一个信息助手,回答用户的各种问题。", # 指令
)
# 定义协调器 Agent
coordinator = LlmAgent(
name="Coordinator", # 协调器
model="gemini-2.0-flash", # 模型
description="主协调器,负责分配任务。", # 描述
instruction=( # 指令
"你是一个协调器。\n"
"预订任务交给 BookingAgent。\n"
"信息查询交给 InfoAgent。\n"
"使用 transfer_to_agent 工具委派任务。"
),
sub_agents=[booking_agent, info_agent], # 注册子 Agent
)
# ========================================
# 示例五:通过代码运行 Agent
# ========================================
# 定义应用信息
APP_NAME = "llm_demo_app" # 应用名称
USER_ID = "user_001" # 用户 ID
SESSION_ID = "session_001" # 会话 ID
async def run_demo():
"""运行 LlmAgent 演示"""
# 创建会话服务
session_service = InMemorySessionService() # 内存会话服务
# 创建会话
session = await session_service.create_session(
app_name=APP_NAME, # 应用名称
user_id=USER_ID, # 用户 ID
session_id=SESSION_ID, # 会话 ID
)
# 创建运行器
runner = Runner(
agent=gemini_agent, # 使用 Gemini Agent
app_name=APP_NAME, # 应用名称
session_service=session_service, # 会话服务
)
# 构造用户消息
content = types.Content(
role='user', # 角色:用户
parts=[types.Part(text='用 Python 写一个冒泡排序算法')], # 消息
)
# 运行 Agent
events = runner.run_async(
user_id=USER_ID, # 用户 ID
session_id=SESSION_ID, # 会话 ID
new_message=content, # 用户消息
)
# 处理事件流
async for event in events: # 遍历所有事件
if event.is_final_response(): # 如果是最终响应
response = event.content.parts[0].text # 提取文本
print(f"Agent 回复:\n{response}") # 打印回复
# 运行演示
if __name__ == "__main__": # 当脚本被直接运行时
asyncio.run(run_demo()) # 执行异步函数

View File

@@ -0,0 +1,275 @@
"""
Google ADK 多智能体系统完整示例
展示各种多 Agent 协作模式
对应教程第05章 - 多智能体系统
"""
# 导入 ADK 核心模块
from google.adk.agents import ( # 导入所有 Agent 类型
Agent, # LLM Agent别名
LlmAgent, # LLM Agent完整名
SequentialAgent, # 顺序工作流
ParallelAgent, # 并行工作流
LoopAgent, # 循环工作流
BaseAgent, # 基础 Agent自定义用
)
from google.adk.events import Event, EventActions # 导入事件类型
from google.adk.agents.invocation_context import InvocationContext # 导入上下文
from typing import AsyncGenerator # 导入异步生成器
# ========================================
# 模式一:协调器/调度器模式
# ========================================
# 定义专门的子 Agent
math_agent = LlmAgent(
name="MathExpert", # 数学专家
model="gemini-2.0-flash", # 模型
description="擅长数学计算和统计分析。", # 描述
instruction="你是数学专家,解决数学问题。给出详细的计算过程。", # 指令
)
writing_agent = LlmAgent(
name="WritingExpert", # 写作专家
model="gemini-2.0-flash", # 模型
description="擅长文案撰写和内容创作。", # 描述
instruction="你是写作专家,帮助用户撰写高质量的内容。", # 指令
)
coding_agent = LlmAgent(
name="CodingExpert", # 编程专家
model="gemini-2.0-flash", # 模型
description="擅长编程和代码调试。", # 描述
instruction="你是编程专家,帮助用户解决编程问题。提供带注释的代码。", # 指令
)
# 定义协调器
coordinator = LlmAgent(
name="Coordinator", # 协调器
model="gemini-2.0-flash", # 模型
description="根据问题类型分配给合适的专家。", # 描述
instruction=( # 指令
"你是一个任务协调器。\n"
"根据用户的问题类型,将任务分配给合适的专家:\n"
"- 数学相关 → MathExpert\n"
"- 写作相关 → WritingExpert\n"
"- 编程相关 → CodingExpert\n"
"使用 transfer_to_agent 工具进行任务分配。"
),
sub_agents=[math_agent, writing_agent, coding_agent], # 注册子 Agent
)
# ========================================
# 模式二:顺序流水线模式
# ========================================
# 步骤一:信息提取
extractor = LlmAgent(
name="Extractor", # 信息提取 Agent
model="gemini-2.0-flash", # 模型
instruction="从用户输入中提取关键信息。只输出提取的关键信息。", # 指令
output_key="extracted_info", # 输出保存到 state
)
# 步骤二:信息分析
analyzer = LlmAgent(
name="Analyzer", # 信息分析 Agent
model="gemini-2.0-flash", # 模型
instruction=( # 指令:引用上一步输出
"分析以下提取的信息,给出深度分析报告。\n"
"提取的信息:{extracted_info}"
),
output_key="analysis_result", # 输出保存到 state
)
# 步骤三:报告生成
reporter = LlmAgent(
name="Reporter", # 报告生成 Agent
model="gemini-2.0-flash", # 模型
instruction=( # 指令:引用前两步输出
"根据分析结果生成一份简洁的报告。\n"
"原始信息:{extracted_info}\n"
"分析结果:{analysis_result}"
),
)
# 创建顺序流水线
pipeline = SequentialAgent(
name="InfoPipeline", # 流水线名称
sub_agents=[extractor, analyzer, reporter], # 按顺序执行
)
# ========================================
# 模式三:并行扇出模式
# ========================================
# 并行任务一:天气查询
weather_agent = LlmAgent(
name="WeatherFetcher", # 天气查询
model="gemini-2.0-flash", # 模型
instruction="查询并返回天气信息。只返回天气数据。", # 指令
output_key="weather_data", # 输出保存到 state
)
# 并行任务二:新闻查询
news_agent = LlmAgent(
name="NewsFetcher", # 新闻查询
model="gemini-2.0-flash", # 模型
instruction="查询并返回最新新闻。只返回新闻摘要。", # 指令
output_key="news_data", # 输出保存到 state
)
# 并行任务三:交通查询
traffic_agent = LlmAgent(
name="TrafficFetcher", # 交通查询
model="gemini-2.0-flash", # 模型
instruction="查询并返回交通状况。只返回交通信息。", # 指令
output_key="traffic_data", # 输出保存到 state
)
# 创建并行执行器
gatherer = ParallelAgent(
name="InfoGatherer", # 并行执行器名称
sub_agents=[weather_agent, news_agent, traffic_agent], # 并行执行
)
# ========================================
# 模式四循环优化模式Generator-Critic
# ========================================
# 生成器 Agent
generator = LlmAgent(
name="Generator", # 生成器
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个创意写手。\n"
"根据用户需求创作内容。\n"
"将创作的内容保存到 state['draft']。"
),
output_key="draft", # 输出保存到 state
)
# 评审器 Agent
critic = LlmAgent(
name="Critic", # 评审器
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个严格的内容评审。\n"
"评审以下内容给出评分1-10和改进建议。\n"
"当前草稿:{draft}\n"
"如果评分 >= 8将 state['approved'] 设为 True。\n"
"否则,将改进建议保存到 state['feedback']。"
),
)
# 条件检查 Agent
class ApprovalChecker(BaseAgent):
"""检查内容是否通过审批"""
async def _run_async_impl(
self,
ctx: InvocationContext, # 调用上下文
) -> AsyncGenerator[Event, None]: # 返回事件生成器
"""执行检查"""
approved = ctx.session.state.get("approved", False) # 获取审批状态
yield Event( # 生成事件
author=self.name, # 事件作者
actions=EventActions(
escalate=approved, # 通过则退出循环
),
)
# 创建迭代优化循环
review_loop = LoopAgent(
name="ReviewLoop", # 循环名称
max_iterations=3, # 最多迭代3次
sub_agents=[critic, ApprovalChecker(name="Checker"), generator], # 循环体
)
# ========================================
# 模式五:多层级嵌套
# ========================================
# 第三层:具体执行 Agent
code_reviewer = LlmAgent(
name="CodeReviewer", # 代码审查
model="gemini-2.0-flash", # 模型
description="审查代码质量。", # 描述
instruction="审查代码并给出改进建议。", # 指令
)
test_writer = LlmAgent(
name="TestWriter", # 测试编写
model="gemini-2.0-flash", # 模型
description="编写单元测试。", # 描述
instruction="为代码编写单元测试。", # 指令
)
# 第二层:开发流水线
dev_pipeline = SequentialAgent(
name="DevPipeline", # 流水线
sub_agents=[code_reviewer, test_writer], # 顺序执行
)
# 第二层:其他 Agent
doc_writer = LlmAgent(
name="DocWriter", # 文档编写
model="gemini-2.0-flash", # 模型
description="编写技术文档。", # 描述
instruction="编写清晰的技术文档。", # 指令
)
# 第一层:根协调器
root_agent = LlmAgent(
name="RootCoordinator", # 根协调器
model="gemini-2.0-flash", # 模型
description="开发团队协调器。", # 描述
instruction=( # 指令
"你是一个开发团队协调器。\n"
"代码审查和测试交给 DevPipeline。\n"
"文档编写交给 DocWriter。"
),
sub_agents=[dev_pipeline, doc_writer], # 子 Agent
)
# ========================================
# 打印结构信息
# ========================================
if __name__ == "__main__":
print("=" * 60) # 分隔线
print("Google ADK 多智能体系统示例") # 标题
print("=" * 60) # 分隔线
print("\n📦 模式一:协调器/调度器") # 模式一
print(f" 根 Agent: {coordinator.name}") # 根 Agent
print(f" 子 Agent: {[a.name for a in coordinator.sub_agents]}") # 子 Agent
print("\n📦 模式二:顺序流水线") # 模式二
print(f" 流水线: {pipeline.name}") # 流水线
print(f" 步骤: {[a.name for a in pipeline.sub_agents]}") # 步骤
print("\n📦 模式三:并行扇出") # 模式三
print(f" 并行器: {gatherer.name}") # 并行器
print(f" 任务: {[a.name for a in gatherer.sub_agents]}") # 任务
print("\n📦 模式四:循环优化") # 模式四
print(f" 循环: {review_loop.name}") # 循环
print(f" 最大迭代: {review_loop.max_iterations}") # 最大迭代
print("\n📦 模式五:多层级嵌套") # 模式五
print(f" 根: {root_agent.name}") # 根
print(f" 第二层: {[a.name for a in root_agent.sub_agents]}") # 第二层
print(f" 第三层: {[a.name for a in dev_pipeline.sub_agents]}") # 第三层
print("\n✅ 所有模式定义完成!") # 成功信息
print("使用 'adk web''adk run' 运行。") # 运行提示

View File

@@ -0,0 +1,318 @@
"""
Google ADK 会话与状态管理完整示例
展示 Session、State、Memory 的使用方法
对应教程第06章 - 会话与状态管理
"""
# 导入 ADK 核心模块
from google.adk.agents import Agent # Agent 类
from google.adk.runners import Runner # 运行器
from google.adk.sessions import InMemorySessionService # 内存会话服务
from google.adk.memory import InMemoryMemoryService # 内存记忆服务
from google.genai import types # 类型定义
# 导入异步库
import asyncio # 异步编程库
# ========================================
# 示例一State 管理(购物车)
# ========================================
def add_to_cart(item: str, price: float, ctx) -> dict:
"""
将商品添加到购物车
Args:
item (str): 商品名称
price (float): 商品价格
ctx: 工具上下文(自动注入)
Returns:
dict: 操作结果
"""
# 获取当前购物车
cart = ctx.state.get("cart", []) # 从 state 读取购物车
# 添加新商品
cart.append({ # 追加商品
"item": item, # 商品名
"price": price, # 价格
})
# 更新 state
ctx.state["cart"] = cart # 写回 state
# 计算总价
total = sum(item["price"] for item in cart) # 求和
return { # 返回结果
"status": "success",
"message": f"已将 {item} 添加到购物车",
"cart_items": len(cart),
"total": total,
}
def get_cart(ctx) -> dict:
"""
获取购物车内容
Args:
ctx: 工具上下文
Returns:
dict: 购物车内容
"""
cart = ctx.state.get("cart", []) # 读取购物车
if not cart: # 如果为空
return { # 返回空信息
"status": "success",
"message": "购物车是空的",
"items": [],
"total": 0,
}
total = sum(item["price"] for item in cart) # 计算总价
return { # 返回购物车
"status": "success",
"items": cart,
"total": total,
}
def clear_cart(ctx) -> dict:
"""
清空购物车
Args:
ctx: 工具上下文
Returns:
dict: 操作结果
"""
ctx.state["cart"] = [] # 清空购物车
return { # 返回结果
"status": "success",
"message": "购物车已清空",
}
# ========================================
# 示例二temp: 临时状态
# ========================================
def step1_process(query: str, ctx) -> dict:
"""
处理步骤一:预处理数据
Args:
query (str): 用户查询
ctx: 工具上下文
Returns:
dict: 预处理结果
"""
processed = query.strip().lower() # 预处理
# 保存到临时状态(当前调用结束后自动清除)
ctx.state["temp:processed_query"] = processed # 临时存储
return { # 返回结果
"status": "success",
"processed_query": processed,
}
def step2_enhance(ctx) -> dict:
"""
处理步骤二:增强数据
Args:
ctx: 工具上下文
Returns:
dict: 增强结果
"""
# 从临时状态读取
processed = ctx.state.get("temp:processed_query") # 读取临时状态
if not processed: # 如果没有数据
return { # 返回错误
"status": "error",
"error_message": "请先执行预处理步骤。"
}
enhanced = f"[增强] {processed}" # 增强处理
return { # 返回结果
"status": "success",
"enhanced_query": enhanced,
}
# ========================================
# 创建 Agent
# ========================================
shopping_agent = Agent(
name="shopping_assistant", # Agent 名称
model="gemini-2.0-flash", # 模型
instruction=( # 指令
"你是一个购物助手。\n"
"帮助用户管理购物车:\n"
"- 添加商品:使用 add_to_cart\n"
"- 查看购物车:使用 get_cart\n"
"- 清空购物车:使用 clear_cart"
),
tools=[add_to_cart, get_cart, clear_cart], # 注册工具
)
# ========================================
# 示例三Session 管理
# ========================================
async def session_management_demo():
"""演示 Session 管理功能"""
print("=" * 50) # 分隔线
print("Session 管理演示") # 标题
print("=" * 50) # 分隔线
# 创建会话服务
session_service = InMemorySessionService() # 内存会话服务
# 创建会话
session = await session_service.create_session(
app_name="shopping_app", # 应用名称
user_id="user_001", # 用户 ID
session_id="session_001", # 会话 ID
state={"cart": []}, # 初始化状态
)
print(f"✅ 会话创建成功: {session.id}") # 打印会话 ID
# 获取会话
existing = await session_service.get_session(
app_name="shopping_app",
user_id="user_001",
session_id="session_001",
)
print(f"✅ 获取会话: {existing.id}") # 打印会话信息
# 删除会话
await session_service.delete_session(
app_name="shopping_app",
user_id="user_001",
session_id="session_001",
)
print("✅ 会话已删除") # 确认删除
# ========================================
# 示例四:多轮对话
# ========================================
async def multi_turn_demo():
"""演示多轮对话"""
print("\n" + "=" * 50) # 分隔线
print("多轮对话演示") # 标题
print("=" * 50) # 分隔线
# 初始化
session_service = InMemorySessionService() # 会话服务
await session_service.create_session( # 创建会话
app_name="chat_app", # 应用名称
user_id="user_001", # 用户 ID
session_id="chat_001", # 会话 ID
)
# 创建 Runner
runner = Runner(
agent=shopping_agent, # Agent
app_name="chat_app", # 应用名称
session_service=session_service, # 会话服务
)
# 模拟多轮对话
conversations = [ # 对话列表
"帮我添加一个苹果价格5元", # 第一轮
"再添加一个香蕉价格3元", # 第二轮
"查看我的购物车", # 第三轮
"清空购物车", # 第四轮
]
for msg in conversations: # 遍历对话
print(f"\n[用户]: {msg}") # 打印用户消息
# 构造消息
content = types.Content(
role='user', # 角色
parts=[types.Part(text=msg)], # 内容
)
# 运行 Agent
events = runner.run_async(
user_id="user_001", # 用户 ID
session_id="chat_001", # 同一会话
new_message=content, # 消息
)
# 获取响应
async for event in events: # 遍历事件
if event.is_final_response(): # 最终响应
print(f"[Agent]: {event.content.parts[0].text}") # 打印回复
# ========================================
# 示例五Memory 管理
# ========================================
async def memory_demo():
"""演示 Memory 管理功能"""
print("\n" + "=" * 50) # 分隔线
print("Memory 管理演示") # 标题
print("=" * 50) # 分隔线
# 创建记忆服务
memory_service = InMemoryMemoryService() # 内存记忆服务
# 添加记忆(从会话事件中提取)
await memory_service.add_session_events_to_memory(
app_name="my_app", # 应用名称
user_id="user_001", # 用户 ID
session_id="session_001", # 会话 ID
)
print("✅ 记忆已添加") # 确认添加
# 搜索记忆
results = await memory_service.search(
query="用户偏好", # 搜索查询
app_name="my_app", # 应用名称
user_id="user_001", # 用户 ID
)
print(f"✅ 搜索完成,结果数: {len(results)}") # 打印结果数
# ========================================
# 主函数
# ========================================
async def main():
"""主函数"""
# 运行所有演示
await session_management_demo() # Session 管理
await multi_turn_demo() # 多轮对话
await memory_demo() # Memory 管理
if __name__ == "__main__": # 直接运行
asyncio.run(main()) # 执行主函数

View File

@@ -0,0 +1,69 @@
"""
Google ADK 环境搭建验证脚本
验证 ADK 是否正确安装,并打印版本信息
对应教程第01章 - ADK 简介与环境搭建
"""
# 导入 ADK 核心模块,验证安装是否成功
from google.adk.agents import Agent # 智能体模块
from google.adk.runners import Runner # 运行器模块
from google.adk.sessions import InMemorySessionService # 内存会话服务
def verify_installation():
"""验证 ADK 安装是否成功"""
# 打印分隔线
print("=" * 50) # 打印分隔线
print("Google ADK 环境验证") # 打印标题
print("=" * 50) # 打印分隔线
# 验证 Agent 类是否可用
try: # 尝试导入
agent = Agent( # 创建一个简单的测试 Agent
name="test_agent", # 设置 Agent 名称
model="gemini-2.0-flash", # 使用 Gemini 模型
instruction="你是一个测试助手。", # 设置系统指令
)
print(f"✅ Agent 模块可用") # 确认 Agent 可用
print(f" Agent 名称: {agent.name}") # 打印 Agent 名称
except ImportError as e: # 如果导入失败
print(f"❌ Agent 模块导入失败: {e}") # 打印错误信息
return False # 返回失败
# 验证 Runner 类是否可用
try: # 尝试导入
print(f"✅ Runner 模块可用") # 确认 Runner 可用
except ImportError as e: # 如果导入失败
print(f"❌ Runner 模块导入失败: {e}") # 打印错误信息
return False # 返回失败
# 验证 SessionService 是否可用
try: # 尝试创建
session_service = InMemorySessionService() # 创建内存会话服务
print(f"✅ SessionService 可用") # 确认会话服务可用
except Exception as e: # 如果创建失败
print(f"❌ SessionService 创建失败: {e}") # 打印错误信息
return False # 返回失败
# 验证 ADK 版本
try: # 尝试获取版本
import importlib.metadata # 导入元数据模块
version = importlib.metadata.version("google-adk") # 获取版本号
print(f"✅ ADK 版本: {version}") # 打印版本号
except Exception: # 如果获取失败
print("⚠️ 无法获取 ADK 版本") # 打印警告
# 打印成功信息
print("=" * 50) # 打印分隔线
print("🎉 所有模块验证通过!") # 打印成功信息
print("可以开始使用 Google ADK 了!") # 提示用户
print("=" * 50) # 打印分隔线
return True # 返回成功
# 当脚本被直接运行时执行验证
if __name__ == "__main__": # 判断是否直接运行
verify_installation() # 执行验证函数