Skip to content

优化智能体

Supported in ADKPython v1.24.0

ADK 提供了一个可扩展的框架,用于根据评估结果进行自动化智能体优化。

开箱即用,你可以使用 adk optimize 命令,通过默认优化器根据 ADK 评估结果快速优化简单的智能体。对于更复杂的用例,你可以开发使用自定义评估数据的采样器,或者实现新的优化策略。

定义

  • 采样器 (Sampler):采样器允许智能体优化器评估候选的优化智能体。当被请求时,采样器向优化器提供详细的评估结果,这些结果对于基于评估引导的智能体优化非常有用。
  • 智能体优化器 (Agent Optimizer):智能体优化器审查来自采样器的评估结果,并利用这些结果来改进智能体。

示例 —— 使用 adk optimize 优化简单智能体

在此示例中,我们将使用 adk optimize 命令,根据小型评估集上的评估结果,更新 hello_world 示例智能体的指令。

第 1 步:指定示例数据集

默认的 hello_world 智能体指令描述了如何判断一个数字是否为质数。 本示例的评估集增加了一个智能体尚未获得指令的维度:数字可以根据其质数性质被分为“好数”或“坏数”。优化器需要推导出这条新规则并将其添加到智能体指令中。

contributing/samples/hello_world/ 中创建一个文件 train_eval_set.evalset.json,内容如下:

{
  "eval_set_id": "train_eval_set",
  "name": "train_eval_set",
  "eval_cases": [
    {
      "eval_id": "simple",
      "conversation": [
        {
          "invocation_id": "inv1",
          "user_content": {
            "parts": [ {"text": "7 是质数吗?"} ],
            "role": "user"
          },
          "final_response": {
            "parts": [ {"text": "7 是一个质数。"} ],
            "role": "model"
          }
        }
      ],
      "session_input": {
        "app_name": "hello_world",
        "user_id": "user"
      }
    },
    {
      "eval_id": "is_good",
      "conversation": [
        {
          "invocation_id": "inv1",
          "user_content": {
            "parts": [ {"text": "4 是坏数吗?"} ],
            "role": "user"
          },
          "final_response": {
            "parts": [ {"text": "4 不是质数,所以它是一个好数。"} ],
            "role": "model"
          }
        }
      ],
      "session_input": {
        "app_name": "hello_world",
        "user_id": "user"
      }
    },
    {
      "eval_id": "is_bad",
      "conversation": [
        {
          "invocation_id": "inv1",
          "user_content": {
            "parts": [ {"text": "5 是坏数吗?"} ],
            "role": "user"
          },
          "final_response": {
            "parts": [ {"text": "5 是质数,所以它是一个坏数。"} ],
            "role": "model"
          }
        }
      ],
      "session_input": {
        "app_name": "hello_world",
        "user_id": "user"
      }
    }
  ]
}

第 2 步:定义采样器配置

采样器配置控制候选优化智能体的评估过程。例如,它指定智能体输出的正确性标准,并指定用于优化智能体的评估集。

配置选项的完整列表见下文 LocalEvalSampler;现在,只需在 contributing/samples/hello_world/ 中创建一个文件 sampler_config.json,内容如下:

{
  "eval_config": {
    "criteria": {
      "response_match_score": 0.75
    }
  },
  "app_name": "hello_world",
  "train_eval_set": "train_eval_set"
}

第 3 步:运行优化作业

运行 adk optimize 命令,指向 hello_world 智能体的目录,并传递上面创建的配置文件。

adk optimize contributing/samples/hello_world \
--sampler_config_file_path contributing/samples/hello_world/sampler_config.json

最终输出可能会有所不同,但看起来类似于以下内容:

<日志和中间输出>
================================================================================
优化后的根智能体指令:
--------------------------------------------------------------------------------
<为简明起见,省略现有的未修改指令>

**关于“好数”和“坏数”的特殊规则:**
*   “坏数”被定义为质数。
*   “好数”被定义为非质数(即合数或 1)。
*   如果用户询问一个数字是“好”还是“坏”,你必须始终先使用 `check_prime` 工具确定其质数性质。
*   使用该工具确定质数性质后,根据上述定义进行回答。关于“好”或“坏”数字(当指代质数性质时)的问题是客观的,你完全有能力回答。不要声明你无法回答此类问题。
================================================================================

使用 adk optimize 命令

adk optimize [OPTIONS] AGENT_MODULE_FILE_PATH
  • AGENT_MODULE_FILE_PATH:指向包含名为 agent 的模块的 __init__.py 文件的路径。agent 模块必须包含一个 root_agent。有关有效设置的示例,请查看 hello_world 智能体。
  • --sampler_config_file_path PATH:采样器配置的路径。采样器实现和配置格式在下文描述。
  • --optimizer_config_file_path PATH(可选):智能体优化器配置的路径。如果未提供,将使用默认配置。优化器实现、配置格式和默认配置在下文描述。
  • --print_detailed_results(可选):启用打印智能体优化器测量的一些详细指标。
  • --log_level(可选):设置日志级别。默认为 INFO。可选值为 DEBUGINFOWARNINGERRORCRITICAL

可用采样器与智能体优化器

ADK 提供了以下采样器和智能体优化器。adk optimize 命令使用下文描述的 LocalEvalSamplerGEPARootAgentPromptOptimizer。你也可以在自己的脚本中使用这些采样器和智能体优化器。

LocalEvalSampler

LocalEvalSampler 使用 ADK 的 LocalEvalService 评估候选智能体。它提供评估结果作为 UnstructuredSamplingResult。你可以使用包含以下字段的 LocalEvalSamplerConfig 来配置 LocalEvalSampler

  • eval_config:一个 EvalConfig,提供评估标准和用户模拟选项。
  • app_name:用于评估的应用名称。
  • train_eval_set:用于优化的评估集名称。
  • train_eval_case_ids(可选):用于优化的评估用例(评价示例)ID。如果未提供,则使用 train_eval_set 中的所有评估用例。
  • validation_eval_set(可选):用于验证优化后智能体的评估集名称。如果未提供,则重复使用 train_eval_set
  • validation_eval_case_ids(可选):用于验证优化后智能体的评估用例(评价示例)ID。如果未提供,则使用 validation_eval_set 中的所有评估用例。如果 validation_eval_set 也未提供,则重复使用有效的训练评估用例。

在初始化 LocalEvalSampler 时,你还必须提供一个 EvalSetsManager,该管理器可以访问 LocalEvalSamplerConfig 中指定的训练和验证评估集。

GEPARootAgentPromptOptimizer

GEPARootAgentPromptOptimizer 使用 GEPA 优化器改进根智能体的指令。它期望采样器提供评估结果作为 UnstructuredSamplingResult。其输出是 OptimizerResult 的子类,其中指定了带分数的优化智能体列表以及在优化过程中收集的其他指标。

注意:GEPARootAgentPromptOptimizer 不会改进任何子智能体、智能体工具、技能或根智能体的任何其他方面。

你可以使用包含以下字段的 GEPARootAgentPromptOptimizerConfig 配置 GEPARootAgentPromptOptimizer

  • optimizer_model(可选):用于分析评估结果并优化智能体的模型。默认为 "gemini-2.5-flash"
  • model_configuration(可选):优化器模型的配置。默认为具有 10K Token 思考预算 (Thinking Budget) 的配置。
  • max_metric_calls(可选):优化期间运行的最大评估次数。默认为 100。
  • reflection_minibatch_size(可选):每次更新智能体指令时使用的示例数量。默认为 3。
  • run_dir(可选):如果需要,用于保存中间和最终优化结果的目录。有利于热启动 (Warm Starts)。

关键数据类型

ADK 在 optimization/data_types.py 中定义了几个基础数据类型,用于规范评估数据从采样器到优化器的传输以及优化器的输出。这些数据类型经过专门设计,具有可扩展性,以适应自定义评估和优化策略。

采样器结果

  • SamplingResult:采样器输出的基础类。
    • 必须包含一个 scores 字典,将示例 UID 映射到该示例上智能体的总分。
  • UnstructuredSamplingResultSamplingResult 的内置子类,增加了一个可选的 data 字段,用于保存非结构化的、每个示例的、可 JSON 序列化的评估数据(如轨迹、中间输出和子指标)。

对于大多数用例,你可以使用 UnstructuredSamplingResult。或者,你可以创建自己的 SamplingResult 子类,以更结构化的格式返回额外的评估数据。但是,你必须确保采样器和优化器都支持你的格式。

智能体优化器结果

  • AgentWithScores:代表单个优化后的智能体及其总分。
    • 必须包含 optimized_agent(更新后的 Agent 对象)。
    • 可以包含智能体的 overall_score(通常在验证集上)。
  • OptimizerResult:代表优化过程的最终输出。
    • 必须包含 optimized_agents 列表(由 AgentWithScores 对象或其子类组成)。当评估智能体在多个指标上的最优性时,可能需要多个条目来表示帕累托前沿 (Pareto Frontier)。

你可以创建自己的 AgentWithScores 子类,以公开关于候选优化智能体的细粒度指标。例如,你可能希望分别为智能体在准确性、安全性、对齐性等方面打分。同样,你可以创建自己的 OptimizerResult 子类,以公开优化器优化过程的总指标(评估的候选系统数量、总评估次数等)。

创建并使用新的采样器与智能体优化器

如果你的用例需要复杂的采样和评估逻辑,或自定义智能体优化策略,你可以创建下文所述的 SamplerAgentOptimizer 抽象类的自定义实现。通过遵循此 API,你可以将 ADK 提供的采样器和智能体优化器与你的自定义实现进行混合和匹配。

创建新采样器

要为自定义评估创建新采样器,你必须创建一个扩展自 Sampler 基础类的类。你还必须指定采样器用于返回评估结果的 SamplingResult 子类。采样器必须实现以下抽象方法:

  • get_train_example_ids(self):返回用于优化的示例 UID 列表。
  • get_validation_example_ids(self):返回用于验证优化后智能体的示例 UID 列表。
  • sample_and_score(self, candidate, example_set, batch, capture_full_eval_data):在指定 example_set"train""validation")的一批 (batch) 示例上评估 candidate 智能体。它应该返回一个 SamplingResult 子类,包含计算出的每个示例的分数,如果 capture_full_eval_dataTrue,还应包含基于评估引导的智能体优化所需的任何其他数据。你可以根据需要通过子类化 SamplingResult 来选择其他评估数据的格式。但是,智能体优化器也必须支持相同的 SamplingResult 子类。UnstructuredSamplingResult 实现了最简单的案例,其中额外数据存储在每个示例的非结构化字典中。

创建新智能体优化器

要创建自定义智能体优化器,你必须创建一个扩展自 AgentOptimizer 基础类的类。你还必须指定它将接受的用于评估结果的 SamplingResult 子类,以及它将用于表示每个优化智能体及其分数/指标的 AgentWithScores 子类。优化器必须实现以下抽象方法:

  • optimize(self, initial_agent, sampler):此方法编排优化过程。它接收一个待改进的 initial_agent 和一个用于评估候选系统的 sampler。它应该返回一个 OptimizerResult 子类,包含候选优化后的智能体列表及其分数/指标,以及与优化过程相关的任何总指标。你可以根据通过子类化 AgentWithScores 来选择每个候选系统分数/指标的格式。或者你也可以简单地使用 AgentWithScores,它允许为每个候选优化后的智能体指定一个总分。

以编程方式优化智能体

adk optimize 命令使用 LocalEvalSamplerGEPARootAgentPromptOptimizer。当使用自定义采样器和智能体优化器时,你必须以编程方式优化智能体。以下参考代码复制了上述示例adk optimize 命令功能。要使用它,请按示例所示创建数据集,并在同一目录下的 Python 脚本中运行此代码:

import asyncio
import logging
import os

import agent  # hello_world 智能体
from google.adk.cli.utils import envs
from google.adk.cli.utils import logs
from google.adk.evaluation.eval_config import EvalConfig
from google.adk.evaluation.local_eval_sets_manager import LocalEvalSetsManager
from google.adk.optimization.gepa_root_agent_prompt_optimizer import GEPARootAgentPromptOptimizer
from google.adk.optimization.gepa_root_agent_prompt_optimizer import GEPARootAgentPromptOptimizerConfig
from google.adk.optimization.local_eval_sampler import LocalEvalSampler
from google.adk.optimization.local_eval_sampler import LocalEvalSamplerConfig

# 设置环境变量(API 密钥等)和日志
envs.load_dotenv_for_agent(".", ".")
logs.setup_adk_logger(logging.INFO)

# 创建采样器
sampler_config = LocalEvalSamplerConfig(
    eval_config=EvalConfig(criteria={"response_match_score": 0.75}),
    app_name="hello_world",  # 通常是包含智能体的目录名称
    train_eval_set="train_eval_set",  # 来自示例
)
eval_sets_manager = LocalEvalSetsManager(
    agents_dir=os.path.dirname(os.getcwd()),
)
sampler = LocalEvalSampler(sampler_config, eval_sets_manager)

# 创建优化器
opt_config = GEPARootAgentPromptOptimizerConfig()
optimizer = GEPARootAgentPromptOptimizer(config=opt_config)

# 优化根智能体
initial_agent = agent.root_agent
result = asyncio.run(
    optimizer.optimize(initial_agent, sampler)
)

# 显示结果
best_idx = result.gepa_result["best_idx"]
print(
    "验证分数:",
    result.optimized_agents[best_idx].overall_score,
    "优化后的提示词:",
    result.optimized_agents[best_idx].optimized_agent.instruction,
    "GEPA 指标:",
    result.gepa_result,
    sep="\n",
)