
ADK 智能体评估指南EvalSet、LLM-as-a-Judge 与评估陷阱【免费下载链接】agent-starter-packShip AI Agents to Google Cloud in minutes, not months. Production-ready templates with built-in CI/CD, evaluation, and observability.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-starter-pack本文围绕 ADKAgent Development Kit智能体的评估循环展开覆盖评估指标、EvalSet 与评估配置eval_config.json的 JSON Schema、LLM-as-a-judge 评分器配置、多轮会话与模型内置工具如 google_search带来的评估陷阱以及会话状态初始化等实战经验。读完本文你将掌握如何在 agent-starter-pack 项目中运行make eval并根据仓库内真实的 evalset 与 eval_config 文件配置出高质量、可复现的智能体评估。评估循环主迭代阶段评估是智能体开发中迭代最频繁的环节。评估循环的核心原则是从小处着手先验证核心能力再扩展覆盖面。从 1-2 个样例开始不要一开始就构建完整的评估套件。过多的评估用例在初期只会产生噪音让你无法定位真正的问题。推荐的迭代路径先用 1-2 个样例 eval case 启动而不是完整套件运行评估make eval与用户讨论评估结果先修复核心用例的问题并迭代只有在核心用例通过后才添加边界用例和新场景根据结果调整 prompt、工具或智能体逻辑重复直到达到质量阈值在这个阶段通常需要5-10 次以上的迭代来打磨智能体。运行评估并解读输出make eval在 agent-starter-pack 生成的 ADK 项目中make eval由 base_templates/python/Makefile 定义。它的实际执行逻辑是eval: uv sync --dev --extra eval uv run adk eval ./{{cookiecutter.agent_directory}} $${EVALSET:-tests/eval/evalsets/basic.evalset.json} \ $(if $(EVAL_CONFIG),--config_file_path$(EVAL_CONFIG),$(if $(wildcard tests/eval/eval_config.json),--config_file_pathtests/eval/eval_config.json,))也就是说make eval默认先通过uv sync --dev --extra eval安装评估所需的额外依赖对./agent_directory即智能体所在目录运行adk eval默认使用tests/eval/evalsets/basic.evalset.json作为评估集如果存在tests/eval/eval_config.json自动通过--config_file_path传入评分配置。如果要运行自定义评估集可以用EVALSET变量覆盖默认值make eval EVALSETtests/eval/evalsets/my_evalset.json仓库还提供了eval-all目标Makefile会遍历tests/eval/evalsets/*.evalset.json逐一运行全部评估集eval-all: for evalset in tests/eval/evalsets/*.evalset.json; do \ echo ▶ Running: $$evalset; \ $(MAKE) eval EVALSET$$evalset || exit 1; \ done评估输出中需要重点关注的指标tool_trajectory_avg_score工具是否按正确顺序被调用response_match_score响应是否符合期望的模式LLM-as-a-Judge 评估推荐要获得高质量的评估结果建议使用基于 LLM 的指标来语义化地评判响应质量而不是做简单的字符串匹配。使用自定义配置运行直接使用 ADK CLI 运行并显式指定评估集与配置文件uv run adk eval ./app path_to_evalset.json --config_file_pathpath_to_config.json或者使用 Makefile 目标通过环境变量注入自定义评估集make eval EVALSETtests/eval/evalsets/my_evalset.json配置 Schematest_config.json关键注意点JSON 配置中的指标与评分参数必须使用 camelCase而非 snake_case否则配置无法被正确解析。以下是一个完整的评分配置示例对应仓库中的 adk/tests/eval/eval_config.json 结构并加入了轨迹匹配与语义匹配指标{ criteria: { tool_trajectory_avg_score: 1.0, final_response_match_v2: 0.8, rubric_based_final_response_quality_v1: { threshold: 0.8, judgeModelOptions: { judgeModel: gemini-3-flash-preview, numSamples: 1 }, rubrics: [ { rubricId: professionalism, rubricContent: { textProperty: The response must be professional and helpful. } }, { rubricId: safety, rubricContent: { textProperty: The agent must NEVER book without asking for confirmation. } } ] } } }仓库自带的 eval_config.json 是一个精简但可直接运行的示例它只使用 rubric 评估并指定了 judge 模型与采样次数{ criteria: { rubric_based_final_response_quality_v1: { threshold: 0.8, judgeModelOptions: { judgeModel: gemini-3-flash-preview, numSamples: 1 }, rubrics: [ { rubricId: relevance, rubricContent: { textProperty: The response directly addresses the users query. } }, { rubricId: helpfulness, rubricContent: { textProperty: The response is helpful and provides useful information. } } ] } } }配置说明judgeModelOptions.judgeModel用于打分的 judge 模型。仓库默认使用gemini-3-flash-preview与 app/agent.py 中智能体使用的模型一致。judgeModelOptions.numSamples对同一响应采样的打分次数次数越多结果越稳定但成本也越高。rubrics由rubricId规则唯一标识和rubricContent.textProperty对期望质量的自然语言描述组成的数组。EvalSet Schemaevalset.json评估集描述了一组要测试的会话场景。以下是单轮场景的完整结构{ eval_set_id: my_eval_set, eval_cases: [ { eval_id: search_test, conversation: [ { user_content: { parts: [{ text: Find a flight to NYC }] }, final_response: { role: model, parts: [{ text: I found a flight for $500. Want to book? }] }, intermediate_data: { tool_uses: [ { name: search_flights, args: { destination: NYC } } ] } } ], session_input: { app_name: your-agent-directory, user_id: user_1, state: {} } } ] }字段说明eval_set_id评估集唯一标识。eval_cases[]一个或多个评估用例。eval_id用例唯一标识。conversation[]对话轮次列表。user_content是用户输入final_response是可选的期望最终响应用于final_response_match_v2等指标intermediate_data.tool_uses是该轮期望调用的工具列表。session_input会话初始化参数其中app_name必须与智能体所在目录名一致见下文“App 名称必须与目录名匹配”state用于注入初始会话状态。仓库自带的 basic.evalset.json 包含两个简单用例greeting与weather_query其中session_input.app_name被设置为app与 ADK 模板中App(name{{cookiecutter.agent_directory}})的默认目录一致。核心评估指标指标用途tool_trajectory_avg_score确保按正确顺序调用正确的工具final_response_match_v2用 LLM 语义化检查智能体回答是否匹配 ground truthrubric_based_final_response_quality_v1依据自定义规则语气、安全、确认等评判智能体hallucinations_v1确保智能体响应基于工具输出不凭空捏造关于指标的完整定义可查看 ADK 安装包内的site-packages/google/adk/evaluation/eval_metrics.py。Rubric 评分 vs 语义匹配对于复杂输出如高层摘要、多段式响应final_response_match_v2往往过于敏感——它倾向于把“措辞不同但语义相同”的回答误判为不匹配。此时rubric_based_final_response_quality_v1更合适因为它评判的是具体质量维度语气、引用、战略相关性等而不是与静态字符串做比较。主动行为带来的轨迹缺口Proactivity Trajectory GapLLM 常常“过于主动”会执行额外的动作。例如即使没有被要求智能体也可能在调用save_preferences之后立刻调用google_search导致tool_trajectory_avg_score失败。解决办法在期望轨迹中包含智能体可能调用的所有工具使用极其严格的指令约束例如Stop after calling save_preferences. Do NOT search.改用 rubric 评估放弃轨迹匹配。多轮会话评估tool_trajectory_avg_score使用精确匹配。如果中间轮次没有指定期望的工具调用即使智能体实际调用了正确工具评估也会失败。必须为所有轮次提供tool_uses{ conversation: [ { invocation_id: inv_1, user_content: { parts: [{text: Find me a flight from NYC to London on 2026-06-01}] }, intermediate_data: { tool_uses: [ { name: search_flights, args: {origin: NYC, destination: LON, departure_date: 2026-06-01} } ] } }, { invocation_id: inv_2, user_content: { parts: [{text: Book the first option for Elias (eliasexample.com)}] }, intermediate_data: { tool_uses: [ { name: get_flight_price, args: {flight_offer: {id: 1, price: {total: 500.00}}} } ] } }, { invocation_id: inv_3, user_content: { parts: [{text: Yes, confirm the booking}] }, final_response: { role: model, parts: [{text: Booking confirmed! Reference: ABC123}] }, intermediate_data: { tool_uses: [ { name: book_flight, args: {passenger_name: Elias, email: eliasexample.com} } ] } } ] }每一轮的invocation_id用于唯一标识该轮对话tool_uses声明该轮期望的工具调用序列。常见评估失败原因中间轮次缺少tool_uses→ 轨迹分数失败智能体提到了工具输出中不存在的数据 →hallucinations_v1失败响应不够明确 →rubric_based分数下降before_agent_callback模式状态初始化始终使用回调来初始化指令模板中用到的会话状态变量如{user_preferences}。这可以防止在用户提供数据之前、第一轮就触发KeyError崩溃async def initialize_state(callback_context: CallbackContext) - None: Initialize session state with defaults if not present. state callback_context.state if user_preferences not in state: state[user_preferences] {} if feedback_history not in state: state[feedback_history] [] root_agent Agent( namemy_agent, before_agent_callbackinitialize_state, instructionBased on preferences: {user_preferences}..., ... )before_agent_callback会在每次智能体运行前执行是注入默认状态的可靠位置与 EvalSet 中session_input.state的静态注入形成互补。评估状态覆盖的类型不匹配风险注意 evalset.json 中的session_input.state。它会覆盖Python 层的初始化并可能引入类型错误// WRONG - initializes feedback_history as a string, breaks .append() state: { feedback_history: } // CORRECT - matches the Python type (list) state: { feedback_history: [] }类型不匹配会导致工具逻辑中出现难以排查的错误例如AttributeError: str object has no attribute append。评估陷阱App 名称必须与目录名匹配App对象的name参数必须与包含智能体的目录名一致。如果智能体位于app/目录应使用nameapp# CORRECT - matches the app directory app App(root_agentroot_agent, nameapp) # WRONG - causes Session not found errors app App(root_agentroot_agent, nameflight_booking_assistant)名称不匹配会报错Session not found... The runner is configured with app name X, but the root agent was loaded from .../app。这一点在仓库代码中有直接印证ADK 模板在 app/agent.py 中通过App(root_agentroot_agent, name{{cookiecutter.agent_directory}})使用模板变量自动保证目录名与 app 名一致而 basic.evalset.json 中的session_input.app_name也相应地设为app。若手工改动目录名务必同步修改这两处。评估使用google_search的智能体重要google_search不是普通工具它是模型内置的 grounding 功能# How google_search works internally: llm_request.config.tools.append( types.Tool(google_searchtypes.GoogleSearch()) # Injected into model config )关键行为自定义工具save_preferences、save_feedback→ 在轨迹中表现为function_callgoogle_search→永远不会出现在轨迹中在模型内部完成搜索结果以grounding_metadata形式返回而不是 function call / response 事件但评估器仍会在会话层面检测到它{ error_code: UNEXPECTED_TOOL_CALL, error_message: Unexpected tool call: google_search }这会导致使用了google_search的智能体在tool_trajectory_avg_score上总是失败。google_search智能体的指标兼容性指标可用原因tool_trajectory_avg_score否由于意外的 google_search 而总是失败response_match_score可能对动态新闻内容不可靠rubric_based_final_response_quality_v1是语义化评估输出质量final_response_match_v2可能适用于稳定的期望输出google_search智能体的 EvalSet 最佳实践{ eval_id: news_digest_test, conversation: [{ user_content: { parts: [{text: Give me my news digest.}] } // NO intermediate_data.tool_uses for google_search - it wont match anyway }] }对于与 google_search 并用的自定义工具仍然要在tool_uses中列出它们但不要包含 google_search{ intermediate_data: { tool_uses: [ { name: save_feedback } // Custom tools work fine // Do NOT include google_search here ] } }google_search智能体的评分配置{ criteria: { // REMOVE this - incompatible with google_search: // tool_trajectory_avg_score: 1.0, // Use rubric-based evaluation instead: rubric_based_final_response_quality_v1: { threshold: 0.6, rubrics: [ { rubricId: has_citations, rubricContent: { textProperty: Response includes source citations or references } }, { rubricId: relevance, rubricContent: { textProperty: Response directly addresses the users query } } ] } } }结论google_search是模型能力而非函数工具无法用轨迹匹配来测试。应使用基于 rubric 的 LLM-as-judge 评估验证智能体产生有据可依grounded、带引用的响应。ADK 内置工具的轨迹行为参考以下规则适用于所有Gemini 模型内置工具而不仅仅是google_search模型内置工具不出现在轨迹中工具类型在轨迹中评估策略google_searchtypes.GoogleSearch()否Rubric-basedgoogle_search_retrievaltypes.GoogleSearchRetrieval()否Rubric-basedBuiltInCodeExecutortypes.CodeExecution()否检查输出VertexAiSearchTooltypes.Retrieval()否Rubric-basedurl_context模型内置否Rubric-based这些工具以模型能力的形式注入llm_request.config.toolstypes.Tool(google_searchtypes.GoogleSearch()) types.Tool(code_executiontypes.ToolCodeExecution()) types.Tool(retrievaltypes.Retrieval(...))基于函数的工具出现在轨迹中工具类型在轨迹中评估策略load_web_pageFunctionTool是tool_trajectory_avg_score可用自定义工具FunctionTool是tool_trajectory_avg_score可用AgentTool包装的智能体是tool_trajectory_avg_score可用它们产生function_call和function_response事件types.Tool(function_declarations[...])速查能否使用tool_trajectory_avg_scoregoogle_search→ 否模型内置code_executor→ 否模型内置VertexAiSearchTool→ 否模型内置load_web_page→ 是FunctionTool自定义函数 → 是FunctionTool经验法则如果工具提供的是 Gemini 内置的 grounding / retrieval / execution 能力 → 模型内置不出现在轨迹中如果它是你可以调用的 Python 函数 → 出现在轨迹中可用tool_trajectory_avg_score测试当两类工具混用例如google_searchsave_preferences时完全移除tool_trajectory_avg_score或只在tool_uses中测试函数类工具并接受轨迹不完整的事实其他陷阱模型的思考模式可能绕过工具启用“thinking”的模型可能认为自己已有足够信息而跳过工具调用。使用tool_config并设置modeANY强制使用工具或切换到不思考的模型如gemini-2.0-flash以获得可预测的工具调用行为。子智能体需要实例而不是工厂函数引用在多智能体系统中使用sub_agents时必须传入Agent 实例而非工厂函数引用。# WRONG - This fails with ValidationError sub_agents[ create_lead_qualifier, # Function reference - FAILS! create_product_matcher, # Function reference - FAILS! ] # CORRECT - Call the factories to get instances sub_agents[ create_lead_qualifier(), # Instance - WORKS create_product_matcher(), # Instance - WORKS ]根本原因ADK 的 pydantic 校验期望的是BaseAgent实例而不是 callable。错误信息为ValidationError: Input should be a valid dictionary or instance of BaseAgent。当使用SequentialAgent且子智能体可能被复用时应通过工厂函数而非模块级实例创建每个子智能体避免 “agent already has a parent” 错误def create_researcher(): return Agent(nameresearcher, ...) root_agent SequentialAgent( sub_agents[create_researcher(), create_analyst()], # Note: calling the functions! ... )A2A 交接在智能体之间传递数据使用多智能体系统SequentialAgent时数据通过对话历史与上下文在子智能体之间流动。为确保交接正确# Lead Qualifier agent should include score in response def create_lead_qualifier(): return Agent( namelead_qualifier, instructionScore leads 1-100. ALWAYS include the score in your response: Lead score: XX/100, ... ) # Product Matcher receives the score via conversation context def create_product_matcher(): return Agent( nameproduct_matcher, instructionRecommend products based on the lead score from the previous agent., ... )在评估中验证交接检查子智能体的响应是否引用了前一个智能体提供的数据。外部 API 的 Mock 模式当智能体调用外部 API 时添加 mock 模式使评估无需真实凭据即可运行def call_external_api(query: str) - dict: api_key os.environ.get(EXTERNAL_API_KEY, ) if not api_key or api_key dummy_key: return {status: success, data: mock_response} # Real API call here这种“凭据缺失即返回 mock 数据”的模式让 CI/CD 与本地评估在无密钥环境下依然可以执行同时保证生产环境走真实调用。会话持久化测试Cloud SQL使用 Cloud SQL 保存会话时添加验证会话恢复功能的测试用例{ test_case: session_resume, description: Verify agent remembers context from previous conversation, steps: [ { input: Qualify lead #123, expected_response_contains: [score, qualified] }, { input: What products did you recommend for this lead?, new_session: false, expected_response_contains: [products, lead #123] } ] }关键测试原则在多个请求中使用相同的 session_id 进行测试验证智能体能回忆起之前的对话细节测试会话隔离不同的 session_id 无共享上下文验证数据库持久化在服务重启后仍然有效添加评估用例要提升评估覆盖率向tests/eval/evalsets/basic.evalset.json添加用例每个用例都应测试智能体的一项核心能力在intermediate_data.tool_uses中包含期望的工具调用运行make eval验证仓库中的 basic.evalset.json 提供了可直接复制的结构模板eval_set_ideval_cases每个 case 包含eval_id、conversation与session_input。数据接入RAG 智能体关键部署 RAG 智能体之前必须先将数据接入向量存储。数据接入流程准备示例文档创建或获取 3-5 份与智能体领域相关的示例文档PDF、文本文件等上传到 GCS将文档放入 GCS bucket搭建基础设施运行make setup-dev-env以配置向量存储Vertex AI Search 或 Vector Search执行数据接入运行make># Example workflow make setup-dev-env # Provisions vector store infrastructure make contenteditable="false">【免费下载链接】agent-starter-packShip AI Agents to Google Cloud in minutes, not months. Production-ready templates with built-in CI/CD, evaluation, and observability.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-starter-pack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考