Skip to content

回调的设计模式和最佳实践

回调提供了强大的智能体生命周期钩子。以下是常见的设计模式,说明如何在 ADK 中有效地利用回调,随后是实施的最佳实践。

设计模式

这些模式展示了使用回调增强或控制智能体行为的典型方式:

1. 护栏和政策执行

模式概述: 在请求到达 LLM 或工具之前拦截它们以执行规则。

实施:

  • 使用 before_model_callback 检查 LlmRequest 提示
  • 使用 before_tool_callback 检查工具参数
  • 如果检测到政策违规(例如,禁止的主题、亵渎):
  • 返回预定义的响应(LlmResponsedict/Map)以阻止操作
  • 可选地更新 context.state 以记录违规

示例用例: before_model_callback 检查 llm_request.contents 中的敏感关键词,如果找到则返回标准的"Cannot process this request" LlmResponse,阻止 LLM 调用。

2. 动态状态管理

模式概述: 在回调中读取和写入会话状态,使智能体行为具有上下文感知能力,并在步骤之间传递数据。

实施:

  • 访问 callback_context.statetool_context.state
  • 修改(state['key'] = value)会自动在后续的 Event.actions.state_delta 中跟踪
  • 更改由 SessionService 持久化

示例用例: after_tool_callback 将工具结果中的 transaction_id 保存到 tool_context.state['last_transaction_id']。稍后的 before_agent_callback 可能会读取 state['user_tier'] 来自定义智能体的问候。

3. 日志记录和监控

模式概述: 在特定的生命周期点添加详细的日志记录,以便进行可观察性和调试。

实施:

  • 实现回调(例如,before_agent_callbackafter_tool_callbackafter_model_callback
  • 打印或发送包含以下内容的结构化日志:
  • 智能体名称
  • 工具名称
  • 调用 ID
  • 来自上下文或参数的相关数据

示例用例: 记录消息如 INFO: [Invocation: e-123] Before Tool: search_api - Args: {'query': 'ADK'}

4. 缓存

模式概述: 通过缓存结果避免冗余的 LLM 调用或工具执行。

实施步骤:

  1. 操作前:before_model_callbackbefore_tool_callback 中:
  2. 基于请求/参数生成缓存键
  3. context.state(或外部缓存)中检查此键
  4. 如果找到,直接返回缓存的 LlmResponse 或结果

  5. 操作后: 如果发生缓存未命中:

  6. 使用相应的 after_ 回调将新结果存储在使用该键的缓存中

示例用例: before_tool_callback 用于 get_stock_price(symbol) 检查 state[f"cache:stock:{symbol}"]。如果存在,返回缓存的价格;否则,允许 API 调用,after_tool_callback 将结果保存到状态键。

5. 请求/响应修改

模式概述: 在数据发送到 LLM/工具之前或接收之后立即更改数据。

实施选项:

  • before_model_callback 修改 llm_request(例如,基于 state 添加系统指令)
  • after_model_callback 修改返回的 LlmResponse(例如,格式化文本、过滤内容)
  • before_tool_callback 修改工具 args 字典(或 Java 中的 Map)
  • after_tool_callback 修改 tool_response 字典(或 Java 中的 Map)

示例用例: 如果 context.state['lang'] == 'es'before_model_callback 将"User language preference: Spanish"附加到 llm_request.config.system_instruction

6. 条件跳过步骤

模式概述: 基于某些条件阻止标准操作(智能体运行、LLM 调用、工具执行)。

实施:

  • before_ 回调返回值以跳过正常执行:
  • 来自 before_agent_callbackContent
  • 来自 before_model_callbackLlmResponse
  • 来自 before_tool_callbackdict
  • 框架将此返回值解释为该步骤的结果

示例用例: before_tool_callback 检查 tool_context.state['api_quota_exceeded']。如果为 True,它返回 {'error': 'API quota exceeded'},阻止实际工具函数运行。

7. 工具特定操作(认证和摘要控制)

模式概述: 处理特定于工具生命周期的操作,主要是认证和控制 LLM 对工具结果的摘要。

实施: 在工具回调(before_tool_callbackafter_tool_callback)中使用 ToolContext

  • 认证: 如果需要凭证但未找到(例如,通过 tool_context.get_auth_response 或状态检查),在 before_tool_callback 中调用 tool_context.request_credential(auth_config)。这会启动认证流程。
  • 摘要: 如果工具的原始字典输出应该传递回 LLM 或可能直接显示,绕过默认的 LLM 摘要步骤,则设置 tool_context.actions.skip_summarization = True

示例用例: 安全 API 的 before_tool_callback 检查状态中的认证令牌;如果缺失,它调用 request_credential。返回结构化 JSON 的工具的 after_tool_callback 可能会设置 skip_summarization = True

8. 制品处理

模式概述: 在智能体生命周期期间保存或加载与会话相关的文件或大型数据块。

实施:

  • 保存: 使用 callback_context.save_artifact / await tool_context.save_artifact 存储数据:
  • 生成的报告
  • 日志
  • 中间数据
  • 加载: 使用 load_artifact 检索先前存储的制品
  • 跟踪: 通过 Event.actions.artifact_delta 跟踪更改

示例用例: "generate_report"工具的 after_tool_callback 使用 await tool_context.save_artifact("report.pdf", report_part) 保存输出文件。before_agent_callback 可能使用 callback_context.load_artifact("agent_config.json") 加载配置制品。

回调的最佳实践

设计原则

保持专注: 为每个回调设计单一、明确定义的目的(例如,仅日志记录、仅验证)。避免整体式回调。

注意性能: 回调在智能体的处理循环中同步执行。避免长时间运行或阻塞操作(网络调用、重计算)。如有必要,卸载,但要注意这会增加复杂性。

错误处理

优雅地处理错误:

  • 在回调函数中使用 try...except/catch
  • 适当地记录错误
  • 决定智能体调用是否应该停止或尝试恢复
  • 不要让回调错误崩溃整个进程

状态管理

谨慎管理状态:

  • 对从 context.state 读取和写入要有目的性
  • 更改在当前调用中立即可见,并在事件处理结束时持久化
  • 使用特定的状态键而不是修改广泛的结构,以避免意外的副作用
  • 考虑使用状态前缀(State.APP_PREFIXState.USER_PREFIXState.TEMP_PREFIX)以提高清晰度,特别是对于持久的 SessionService 实现

可靠性

考虑幂等性: 如果回调执行具有外部副作用的操作(例如,递增外部计数器),如果可能,将其设计为幂等(使用相同输入多次运行是安全的),以处理框架或应用程序中的潜在重试。

测试和文档

彻底测试:

  • 使用模拟上下文对象对回调函数进行单元测试
  • 执行集成测试以确保回调在完整智能体流程中正确运行

确保清晰:

  • 为回调函数使用描述性名称
  • 添加清晰的文档字符串,解释其目的、何时运行以及任何副作用(特别是状态修改)

使用正确的上下文类型: 始终使用提供的特定上下文类型(智能体/模型的 CallbackContext,工具的 ToolContext)以确保访问适当的方法和属性。

通过应用这些模式和最佳实践,你可以有效地使用回调在 ADK 中创建更健壮、可观察和自定义的智能体行为。