init
This commit is contained in:
BIN
ADKLearning/google-adk-tutorial/.DS_Store
vendored
Normal file
BIN
ADKLearning/google-adk-tutorial/.DS_Store
vendored
Normal file
Binary file not shown.
356
ADKLearning/google-adk-tutorial/01-introduction-and-setup.md
Normal file
356
ADKLearning/google-adk-tutorial/01-introduction-and-setup.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# 第01章:ADK 简介与环境搭建
|
||||
|
||||
## 📌 本章目标
|
||||
|
||||
- 理解 Google ADK 是什么、能做什么
|
||||
- 了解 ADK 的核心架构和设计理念
|
||||
- 完成开发环境的安装和配置
|
||||
- 验证安装是否成功
|
||||
|
||||
---
|
||||
|
||||
## 1.1 什么是 Google ADK?
|
||||
|
||||
**Agent Development Kit(ADK)** 是 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!
|
||||
388
ADKLearning/google-adk-tutorial/02-quick-start-hello-world.md
Normal file
388
ADKLearning/google-adk-tutorial/02-quick-start-hello-world.md
Normal 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 的配置和高级用法。
|
||||
434
ADKLearning/google-adk-tutorial/03-llm-agent-in-depth.md
Normal file
434
ADKLearning/google-adk-tutorial/03-llm-agent-in-depth.md
Normal 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 创建自定义工具。
|
||||
564
ADKLearning/google-adk-tutorial/04-custom-tools.md
Normal file
564
ADKLearning/google-adk-tutorial/04-custom-tools.md
Normal 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
|
||||
"""
|
||||
MCP(Model 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 协作系统。
|
||||
631
ADKLearning/google-adk-tutorial/05-multi-agent-systems.md
Normal file
631
ADKLearning/google-adk-tutorial/05-multi-agent-systems.md
Normal file
@@ -0,0 +1,631 @@
|
||||
# 第05章:多智能体系统
|
||||
|
||||
## 📌 本章目标
|
||||
|
||||
- 理解多智能体系统的设计理念
|
||||
- 掌握 Agent 层级结构的构建方法
|
||||
- 学习三种工作流 Agent:Sequential、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) → 深入了解会话和状态管理机制。
|
||||
595
ADKLearning/google-adk-tutorial/06-session-and-state.md
Normal file
595
ADKLearning/google-adk-tutorial/06-session-and-state.md
Normal 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 行为。
|
||||
565
ADKLearning/google-adk-tutorial/07-callbacks-and-events.md
Normal file
565
ADKLearning/google-adk-tutorial/07-callbacks-and-events.md
Normal 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 的性能。
|
||||
444
ADKLearning/google-adk-tutorial/08-evaluation.md
Normal file
444
ADKLearning/google-adk-tutorial/08-evaluation.md
Normal 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 部署到生产环境。
|
||||
420
ADKLearning/google-adk-tutorial/09-deployment.md
Normal file
420
ADKLearning/google-adk-tutorial/09-deployment.md
Normal 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 Manager(Google 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 协议等高级功能。
|
||||
674
ADKLearning/google-adk-tutorial/10-advanced-topics.md
Normal file
674
ADKLearning/google-adk-tutorial/10-advanced-topics.md
Normal file
@@ -0,0 +1,674 @@
|
||||
# 第10章:高级主题
|
||||
|
||||
## 📌 本章目标
|
||||
|
||||
- 掌握 ADK 的安全最佳实践
|
||||
- 了解 A2A(Agent-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 概述
|
||||
|
||||
A2A(Agent-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)
|
||||
90
ADKLearning/google-adk-tutorial/README.md
Normal file
90
ADKLearning/google-adk-tutorial/README.md
Normal 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 等)
|
||||
|
||||
---
|
||||
|
||||
> 💡 **提示**:本教程所有代码示例均提供详细中文注释,建议配合代码文件一起阅读。
|
||||
293
ADKLearning/google-adk-tutorial/code/advanced_demo.py
Normal file
293
ADKLearning/google-adk-tutorial/code/advanced_demo.py
Normal 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✅ 所有高级功能定义完成!") # 成功信息
|
||||
255
ADKLearning/google-adk-tutorial/code/callback_demo.py
Normal file
255
ADKLearning/google-adk-tutorial/code/callback_demo.py
Normal 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()) # 执行主函数
|
||||
338
ADKLearning/google-adk-tutorial/code/custom_tools_demo.py
Normal file
338
ADKLearning/google-adk-tutorial/code/custom_tools_demo.py
Normal file
@@ -0,0 +1,338 @@
|
||||
"""
|
||||
Google ADK 自定义工具开发完整示例
|
||||
展示各种工具开发技巧和最佳实践
|
||||
|
||||
对应教程:第04章 - 自定义工具开发
|
||||
"""
|
||||
|
||||
# 导入 ADK 核心模块
|
||||
from google.adk.agents import Agent # Agent 类
|
||||
from google.adk.tools import AgentTool # AgentTool(Agent 作为工具)
|
||||
|
||||
# 导入类型提示
|
||||
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()) # 执行主函数
|
||||
201
ADKLearning/google-adk-tutorial/code/deploy_demo.py
Normal file
201
ADKLearning/google-adk-tutorial/code/deploy_demo.py
Normal 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")
|
||||
272
ADKLearning/google-adk-tutorial/code/eval_demo.py
Normal file
272
ADKLearning/google-adk-tutorial/code/eval_demo.py
Normal 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()) # 执行主函数
|
||||
137
ADKLearning/google-adk-tutorial/code/hello_world.py
Normal file
137
ADKLearning/google-adk-tutorial/code/hello_world.py
Normal 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"], # 湿度
|
||||
}
|
||||
|
||||
|
||||
# ========================================
|
||||
# 定义 Agent(ADK 入口点)
|
||||
# ========================================
|
||||
|
||||
# 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, # 天气查询工具
|
||||
],
|
||||
)
|
||||
210
ADKLearning/google-adk-tutorial/code/llm_agent_demo.py
Normal file
210
ADKLearning/google-adk-tutorial/code/llm_agent_demo.py
Normal 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()) # 执行异步函数
|
||||
275
ADKLearning/google-adk-tutorial/code/multi_agent_demo.py
Normal file
275
ADKLearning/google-adk-tutorial/code/multi_agent_demo.py
Normal 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' 运行。") # 运行提示
|
||||
318
ADKLearning/google-adk-tutorial/code/session_state_demo.py
Normal file
318
ADKLearning/google-adk-tutorial/code/session_state_demo.py
Normal 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()) # 执行主函数
|
||||
69
ADKLearning/google-adk-tutorial/code/setup_demo.py
Normal file
69
ADKLearning/google-adk-tutorial/code/setup_demo.py
Normal 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() # 执行验证函数
|
||||
Reference in New Issue
Block a user