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,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()) # 执行主函数