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

View File

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