Files
Agent-Docs/ADKLearning/google-adk-tutorial/07-callbacks-and-events.md
Nelson 514723dbe7 init
2026-04-06 12:36:04 +08:00

566 lines
20 KiB
Markdown
Raw Permalink 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.
# 第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 的性能。