Files
Agent-Docs/ADKLearning/google-adk-tutorial/10-advanced-topics.md
Nelson 514723dbe7 init
2026-04-06 12:36:04 +08:00

675 lines
23 KiB
Markdown
Raw Blame History

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