Google 开源 AQuA:诊断生产环境智能体的常驻质量智能体
The Outer Loop, Insights First: An Ambient Quality Agent That Diagnoses Your Production Agent
Google 在 adk-recipes 仓库开源 AQuA(Ambient Quality Agent),一个在 Google Cloud 项目内常驻运行、不进入请求路径的质量智能体,按计划或部署后扫描 Cloud Trace、Cloud Logging 或 BigQuery 中的生产轨迹,经采样、评审、聚类、验证、跟踪五阶段流水线输出可验证的问题洞察。
Google 开源了生产环境智能体质量诊断工具,给出五阶段流水线与真实成本数据,可据此评估自建方案。
2026年10月8日
你的 agent 返回 HTTP 200,保持在延迟预算之内,也没有任何工具调用报错。然而它仍然在从未询问选座子 agent 3A 是否空闲的情况下,就把 3A 座标记为已确认;或者向一位资料显示为纯素食者的旅客推荐佛罗伦萨牛排馆。
前 80% 是相对容易的部分
我们合作过的每一个在生产环境中运行 agent 的团队,都在试图回答同样的三个问题:我的 agent 表现如何?它的损失模式是什么,也就是那些反复出现的失败方式?以及我该如何爬升:做出改变,知道它确实有帮助,并防止它回退?
让 agent 达到前 80%——即处理你预期到的测试用例——在今天可以相对直接且快速。一个评估数据集、一个编码 agent,以及一个运行、评分、修复、比较的紧密内循环,通常能在几天内让你在已知场景上实现任务成功。然后你上线了,质量曲线就趋于平缓或下降。

图 1. Agent 质量在上线前快速攀升,随后在生产环境中波动。
让剩下 20% 更难的原因在于,上线后地基会移动。随着用户发现 agent 实际能做什么,使用方式会发生变化,线上流量也不再像你最初的评估集。与此同时,底层系统也在变化:当你升级模型、更新 harness,或更改某个工具或技能时,一切仍然能运行,健康检查也保持绿色(图 2),但对话质量和任务成功可能会以标准部署流水线永远不会警告你的方式发生变化。

图 2. 每一项基础设施健康检查都保持绿色。失败是静默的,发生在对话内部。
而当一次会话真的出错时,弄清为什么意味着要区分几个从外部看几乎一模一样的层面。例如:
- 模型可能幻觉出了一个参数,或丢掉了三轮之前的一个约束。
- 编排 harness可能路由到了错误的子 agent,或在轮次之间丢失了状态。
- 工具契约可能拒绝了某个输入,因为其有效值从未在 schema 中记录。
- 指令或技能可能只是缺少了一条没人想到要写下来的规则。
这些失败模式可能有不同的负责人和不同的修复方式。原始轨迹记录的是什么接在什么之后,而不是什么导致了什么。要区分这些层面,需要把失败的轨迹与产生它们的精确代码版本放在一起阅读。在线评估仪表盘告诉你分数发生了变化,而编码 agent 可以在你知道该看哪里之后检查一条 trace,但在生产规模下,没有人能手工阅读每一段对话。

图 3. AQuA 负责生产环境的外循环,并将发现反馈到你的内循环中。
在我们六月的文章中,我们端到端地走过了 travel-concierge 上线前的内循环。本文涵盖外循环,以开源形式发布为可组合的构建块,你可以在自己的项目中运行、适配到你的技术栈,并在我们探索团队如何在生产环境中持续保障 agent 质量的过程中帮助我们塑造它。
AQuA 是什么:一个 24/7 质量 agent,在你的笔记本电脑合上时仍持续观察、聚类并诊断生产故障
AQuA(Ambient Quality Agent)在你的 Google Cloud 项目中与你的 agent 并排运行,无需人工值守,按计划、在每次部署后或按需(即使你的笔记本电脑已关闭)从 Cloud Trace、Cloud Logging 或 BigQuery 中扫描生产轨迹。原始会话记录、源代码快照和 BigQuery 表都保留在你的项目边界内,AQuA 从不位于请求路径中,也不会写回你的 agent。

图 4. AQuA 在你的 Google Cloud 项目中与你的 agent 并排运行,在请求路径之外读取轨迹和部署时的源代码快照。
每次运行都会读取近期会话的样本,并将其送入一个五阶段流水线:

图 5. 五阶段扫描流水线将原始生产会话转化为经过验证的洞察,根因诊断则按需针对已部署的源代码快照运行。
- 采样(Sample)。 从你的遥测数据(Cloud Trace / Cloud Logging 或 BigQuery)中随机抽取最多 1,000 个近期会话(设有上限以保持运行成本可预测)。
- 审查(Review)。 在可见 agent 的指令、工具以及可选的
goal.md的情况下,依据一份包含九项常见故障(流程、工具选择与参数、依据性、任务完成度等)的检查清单对每个会话进行评分。当某个会话失败时,它会写入一条结构化的actual / expected发现。 - 聚类(Cluster)。 将具有相同故障机制的会话发现归入候选问题聚类。
- 验证(Verify)。 由另一个模型依据最多三份完整会话记录检查每个候选聚类,并丢弃证据不支持的聚类。
- 跟踪(Track)。 将存活的聚类与 BigQuery 中的未解决洞察(跨运行跟踪的已验证问题)进行匹配,标记为
NEW、RECURRING,或在 14 天未见后自动标记为RESOLVED。
可以把它看作一位做第一轮筛查的初级质量工程师:阅读对话、过滤噪声,并为轮值人员准备好带有证据会话的案卷。
为了让第 2 阶段贴合你的领域,你编写一段通俗英语的开发者目标(goal.md),它会附加到每个审查提示词中;确定性的 Python 自定义指标(eval_config.yaml)与评判器并行运行,以跟踪通过率趋势。默认情况下,AQuA 使用单次通过的 session_review 评判器,它在每个会话一次模型调用中依据该检查清单评估对话(使计划扫描保持经济,并生成驱动聚类的 actual / expected 差异)。你也可以选择启用 Gemini 平台托管的轨迹 AutoRaters(task_success、tool_use_quality、trajectory_quality),它们运行专门的按指标评估器(例如,如果你想依据标准化的开箱即用评分标准对会话评分,或使指标与离线评估保持一致)。
当某个洞察值得调查时,你可以从仪表板的 Chat 或 agents-cli aqua run 触发根因分析。AQuA 会读取失败的轨迹以及部署时捕获的不可变源代码快照:当缺陷位于你的代码库中时,它会引用 <path>:<start>-<end> 并提出锚定到快照行的编辑建议;当故障位于你的代码之外(上游依赖、交接或检索到的负载)时,它会将失败归因于轨迹中的该步骤,而不提出代码差异。它从不自行应用编辑或发起拉取请求。
它位于你已有的两个循环之间:离线评估根据已知测试用例对候选构建进行评分,在线评估监控生产环境中的通过率趋势,而编码智能体或专用优化器则负责编辑提示词和代码。AQuA 将原始生产流量转化为经过诊断、锚定代码的洞察,以及归档的失败记录,为你的内层循环提供素材。
在 travel-concierge 上运行多智能体扫描
让我们在 travel-concierge(google/adk-recipes)上走一遍运行流程,它会把旅行者路由到各个子智能体(inspiration_agent、place_agent、poi_agent, planning_agent、flight_search_agent、flight_seat_selection_agent、booking_agent 和 pre_trip / in_trip / post_trip),并通过 memorize(key, value)(travel_concierge/tools/memory.py)将进行中的行程存储在会话状态中。
1. 部署、捕获源快照并设置开发者目标
将 AQuA 接入 travel-concierge 项目需要三条 agents-cli 命令:
agents-cli extension add "${AQUA_CHECKOUT}"
agents-cli infra single-project --project="${GOOGLE_CLOUD_PROJECT}" --apply
agents-cli deploy --project="${GOOGLE_CLOUD_PROJECT}" --region us-east1
除了 AQuA 的 runner、BigQuery 数据集和 Cloud Run 仪表盘(位于 Identity-Aware Proxy 之后),agents-cli deploy 还会将 travel-concierge 源代码树的不可变快照写入 Cloud Storage,并以部署修订版本(Revision 1)作为键。
在仪表盘的 Configuration 页面上,我们保存一个开发者目标(goal.md),以引导审查聚焦于高层产品不变量,并抑制风格上的噪声(同时仍要求每条发现都引用一个具体轮次,即某个智能体或子智能体违反了其指令或工具状态):
Goal: Help travelers move from trip inspiration to a concrete itinerary and confirmed bookings across our sub-agents, with every confirmed flight, hotel, seat, and recommendation grounded in tool results and the traveler's profile.
Failure modes to make sure we cover:
- Mid-conversation changes (a weak spot in pre-launch testing): if a user updates destination, dates, or flight/hotel choices after an initial plan, make sure subsequent sub-agent tool calls and state updates reflect the change.
- Dropped context when handing off or delegating across inspiration_agent, planning_agent, and booking_agent (such as traveler profile preferences or prior selections).
Ignore: tone, greetings, small talk, or sessions where the user browses options and leaves without booking.纯文本
已复制
如果你还希望在 eval_config.yaml 中使用确定性的 Python 评分标准来跟踪通过率随时间的变化,可以将其与扫描一起通过 agents-cli aqua metrics publish 发布。
2. 扫描并验证聚类
在一次 32 会话的多智能体扫描中(对四条脚本化旅行者旅程进行 32 次重放,在 travel_concierge 及其子智能体上产生 1,583 个 OpenTelemetry span),5 个会话顺利通过,27 个会话产生 42 条结构化发现,每条发现都按 span 限定到特定子智能体的隔离指令和工具声明。以下是 planning_agent 上的一条发现:
实际:当用户在同一轮中选择去程航班 UA204 并请求座位 3A 和 3B 时,planning_agent 直接将这两个座位号保存到会话状态,而没有调用 flight_seat_selection_agent 来检查 3A 和 3B 是否可用。
预期:planning_agent 应先调用 flight_seat_selection_agent 验证座位可用性和价格,然后再将去程或返程座位号保存到会话状态。
聚类将这 42 条发现归为 9 个候选聚类。验证器(Gemini 3.7 Flash)根据最多三份完整记录以及每个子智能体的定义检查每个聚类,并拒绝了 3 个误报聚类:其中两个中,旅行者已明确要求乘坐第一班返程航班或直接跳到预订;第三个中,聚类将来自两个不同子智能体(flight_search_agent 和 inspiration_agent)的不相关提示词-工具不匹配合并到了一起。这样就剩下 6 个已验证问题,其中最突出的是:
- 当用户主动指定座位时绕过座位可用性检查(15 个会话,planning_agent):当旅行者在选择航班的同时(或在行程中途切换目的地之后立即)指定座位时,planning_agent 会直接将座位写入会话状态(返回 HTTP 200 且零工具错误),而不调用 flight_seat_selection_agent 来检查该座位是否存在或是否开放。
- 跨子代理边界丢失饮食约束(7 个会话,inspiration_agent → place_agent): inspiration agent 的提示词中包含旅行者的个人资料(food_preference: vegan),但 place_agent 被包装为一个隔离的工具,其提示词从不接收该资料。当 inspiration_agent 在未于请求中传入“vegan”的情况下向 place_agent 询问美食旅行建议时,place_agent 推荐了 Carbonara 和 Bistecca alla Fiorentina(牛排)。
- poi_agent 中的提示词与工具集矛盾(5 个会话,poi_agent):兴趣点子代理的提示词要求从 Google Maps Grounding Lite 获取经过验证的图片、地图和地点 ID 字段,但该代理被声明为空工具列表(
tools=[]),因此它伪造了占位符https://example.com/...URL。

图 6. 来自旅行礼宾扫描的已验证洞察,按受影响的会话数排序。
3. 对照已部署的代码进行诊断
在仪表板中打开排名第一的洞察,会显示该集群的完整细分:匹配的会话数(15 条追踪)、验证摘要,以及 15 条关联的会话追踪及其触发该洞察的确切发现:

图 7. 仪表板中的洞察详情视图,显示失败摘要和 15 条关联的会话追踪。
从洞察卡片点击 Investigate,会针对 Revision 1 的 33 个文件源代码快照启动根因诊断代理(Gemini 3.8 Flash)。将该 15 条失败追踪与仓库树进行交叉比对后,该代理将缺陷追溯到 travel_concierge/sub_agents/planning/prompt.py 的第 93 行:航班搜索指令只告诉 planning_agent 在向用户展示座位图供其选择时调用 flight_seat_selection_agent,却遗漏了用户直接主动提供座位号的情况。它在聊天中提出了一个锚定到具体行的修复方案(并同样将 vegan 资料传递缺陷追溯到 travel_concierge/sub_agents/inspiration/prompt.py 的第 23 行):

图 8. 仪表板聊天中的根因诊断,将建议的指令修复锚定到已部署的源代码快照。
4. 从编码代理闭环并在 Revision 2 上验证
同样的洞察负载,包括其锚定的 edits[] 和 occurrences[].rubrics[].trace,可通过 agents-cli aqua get-insight 获取。编码代理(使用 agents-cli-aqua 和 google-agents-cli-eval 技能)可以无头触发诊断、在分支上应用编辑、将失败会话的用户输入提取到本地重放文件,并在打开拉取请求之前验证修复:
# 1. Pull new insights affecting >= 10 sessions without a root cause
agents-cli aqua list-insights --status NEW --root-cause false \
| jq -r '.insights | sort_by(-.trace_count) | .[] | select(.trace_count >= 10) | "\(.insight_id) \(.label)"'
# 2. Trigger root-cause analysis headlessly and fetch the anchored edit + evidence traces
agents-cli aqua run 'Diagnose insight 220d9209e27d4e16a73b4ad4741caa81. What is the root cause, and how would you fix it?'
agents-cli aqua get-insight 220d9209e27d4e16a73b4ad4741caa81 > insight.json
# 3. Extract the user turns from the attached trace in insight.json for local replay (or add to your eval set)
jq '{state: {}, queries: [.occurrences[0].rubrics[0].trace[] | select(.role == "user") | .content]}' \
insight.json > ./b4b38471-inputs.json
adk run --replay ./b4b38471-inputs.json travel_concierge纯文本
已复制
在应用两处单行提示词修复(planning/prompt.py:93 和 inspiration/prompt.py:23)并部署 Revision 2 后,在相同的开发者目标下对其重放同样的 32 个会话,结果显示:
- 座位选择绕过下降 87%,从 15 个会话降至 2 个边缘案例会话。
- Vegan 资料遗漏从 7 个会话降至 0。
- 全会话通过数增加一倍以上,从 5/32 提升至 13/32。
- 未触及的 5 个会话 poi_agent 缺陷(
tools=[])仍保留在队列中跟踪。
足以让你放心采取行动的发现
诊断你的代理的代理有时也会出错。这是将模型用作评判者的固有特性。在设计 AQuA 时,核心工程要求是确保未经验证的假设永远不会看起来与已验证的发现相同:
- 在对照完整转录文本验证之前,聚类只是主张。每个聚类抽查最多三份完整转录文本,可在其进入你的队列之前验证失败模式确实存在(在 87 条轨迹的内部基准测试中,验证器拒绝了 24 个候选聚类中的 4 个),而聚类的轨迹数量为你提供粗略的优先级排序,而非验证每一个成员会话。
- 洞察力在于其引用,而非自报的置信度分数。该 schema 没有
confidence字段:每次出现都链接到其在 Cloud Trace 中的会话 ID,每个根因都引用<path>:<start>-<end>范围,服务器会对照该修订版本的快照进行验证。任何对不存在文件或行的引用都会被拒绝。 - 跳过或失败的工作会显示在运行记录上。被拒绝的聚类、超过 50 个聚类验证上限的聚类、评分标准错误,以及空或未捕获的轨迹窗口,都会明确记录在运行中,而不会被计为干净的会话。
- 设计使然的行为可以被永久忽略。一旦你在某个发现上点击 Dismiss,未来的扫描不会将其作为 NEW 重新打开。
智能体质量工程中的难题,以及这项工作的走向
此参考实现中的若干边界是针对我们在生产环境中看到的实际问题所做的有意权衡:
- 大规模选择要读取哪些会话。深度评估多轮轨迹的成本远高于检查 HTTP 状态码,因此此 MVP 每次运行随机抽样最多 1,000 个会话(
ORDER BY RAND())。添加廉价的结构性预过滤器(重试、延迟尖峰、高轮次、用户点踩)是直接的下一步,但仅基于异常进行过滤往往会拉出上百个相同的超时。更难的问题是在每个结构信号看起来都正常的情况下,捕捉破坏关键工作流 1% 的静默回归,而又不对全部 50,000 个每日会话运行深度评判器。 - 通用检查清单与领域目标和 SME 校准。
goal.md和自定义指标将审查导向领域规则,但让模型评判者与领域专家达成一致仍是实实在在的工程工作。 - 有界验证与可预测的运行成本。每次运行最多验证 50 个聚类、每个聚类最多 3 份转录文本,使运行成本随轨迹深度扩展时保持可预测。在一次 96 会话的单智能体扫描中(每会话约 5 到 6 个 span),审查、聚类和验证总成本为 $0.70(约 $0.007/会话;跨 Gemini 3.1 Pro 和 Gemini 3.7 Flash 共 220,841 输入 / 48,736 输出 token)。在上述 32 会话的旅行礼宾扫描中(travel_concierge 及其子智能体共 1,583 个 span,每会话约 50 个 span),审查和聚类(Gemini 3.1 Pro,1,635,197 输入 / 128,740 输出 token)加上 9 次聚类验证(Gemini 3.7 Flash,1,131,937 输入 / 36,976 输出 token)按 标准 Gemini 平台定价 总成本为 $3.76(约 $0.12/会话)。根因诊断(Gemini 3.8 Flash)仅按需运行,每个被调查的洞察成本为 $0.33 到 $2.47,取决于它拉取多少完整轨迹。
- 长时程轨迹与上下文压缩。传递完整转录文本适用于十轮对话,但在运行数百轮、产生数兆字节工具输出的编码或研究智能体上就会失效。这些需要语义轨迹压缩,将 500 轮的轨迹折叠为子任务里程碑,并隔离出它在何处脱轨。
- 从归档记录到可复现的测试用例。
adk run --replay会将记录下来的用户轮次重新发送到本地代码上执行,这在工具是幂等或已被 mock 的情况下可行,但静态回放无法重建外部环境状态(如果某个数据库行发生了变化、某个 API 超时了,或者第 3 轮依赖于智能体在第 2 轮所说的话,那么重新发送静态轮次就会产生偏差)。
在平台层面真正具有通用性的东西。在 Google 自家的一手智能体中,底层原语(trace 与反馈摄取、核心评估器、数据集管理)可以干净地共享,而外环工作流往往因产品的工具、领域不变量、数据管线和编排框架而各不相同。随着基础模型和智能体在阅读轨迹和浏览代码方面不断进步,单个评分和根因推理会自动变得更强,这就是为什么我们把 AQuA 的提示词视为模块化的配方,把它的洞见视为对原始轨迹和源码快照的索引,这样更强的模型或编码智能体总能将完整的示例记录直接拉入上下文。更强的模型和智能体本身无法给你的,是周边平台。以下是我们接下来正在思考的一些方向:
- 多信号摄取与静默失败差异。将 trace 扫描与在线评估分数、延迟/成本尖峰、SME 校准评分,以及通过 Feedback 服务获得的终端用户反应结合起来,就可以把用户明确抱怨过的会话与一般流量进行对比,然后在用户从未点过踩、却悄悄放弃工作流的会话中发现同样的缺陷。
- 从诊断出的洞见到沙箱化的反事实测试用例。在另一个领域,CodeMender 会扫描代码中的漏洞,并提出它已经通过静态和动态分析、差分测试、模糊测试和 SMT 求解器验证过的补丁。它这种先扫描后验证的形态,直接对应 AQuA 的先扫描后验证。但修复环节则不同:一旦外部状态已经发生变化,实时对话就没有确定性的判定基准(没有崩溃输入或失败测试),而且修复可能位于工具契约、编排交接或上游依赖中,而不是一个你可以孤立地爬坡优化的提示词。将一个已验证的失败簇转化为一个封闭、沙箱化的测试用例,并带有 mock 的工具状态和模拟用户,使编码智能体或优化器能够跨代码、工具和提示词进行爬坡优化,这是超越记录回放的自然的下一步。
- 跨智能体集群的零脚手架接入。今天的参考实现是在单个 ADK 智能体旁边以 1:1 的方式在其仓库中搭建脚手架的;对于在生产环境中运行数十个智能体的团队,我们正在探索如何直接从现有项目遥测中,在智能体集群(1:N)上接入环境质量扫描,而无需为每个智能体部署 sidecar。
- 声明式与托管式智能体。当被观察的智能体本身是声明式或托管式的,而不是任意应用代码时,“源代码”就坍缩为系统指令、工具 schema 和技能。这会将根因搜索空间缩小到一组有界的结构化产物,让平台能够自动接入 trace 捕获和修订快照,并将已验证的通过和失败轨迹转化为有依据的学习信号——而实时生产流量没有真值标签——供优化器或智能体自身的内存与自学习循环使用。
试用它,帮助塑造它的发展方向
要在本地使用合成的一个月运行数据和洞察来探索该仪表板,且无需云项目、无需凭据、无需模型调用:
git clone https://github.com/google/adk-recipes.git
cd adk-recipes/core/python/ambient-quality-agent
make demo # serves the dashboard locally on synthetic dataShell
已复制
要将 AQuA 通过开箱即用的 ADK 脚手架接入你在 Google Cloud 中的自有代理(所有遥测数据、源快照和 BigQuery 表都保留在你项目内的服务账号下,位于 IAP 之后):
agents-cli extension add "${AQUA_CHECKOUT}"
agents-cli infra single-project --project="${GOOGLE_CLOUD_PROJECT}" --apply
agents-cli deploy --project="${GOOGLE_CLOUD_PROJECT}"Shell
已复制
要从你的编码代理(Antigravity、Gemini CLI、Claude Code 或 Cursor)驱动这两个循环,请安装 agents-cli-aqua 技能(skills/agents-cli-aqua/SKILL.md)以及内循环评估技能(npx skills add https://github.com/google/agents-cli --skill google-agents-cli-eval)。
我们公开分享 AQuA,以与在生产环境中运行代理的团队协作,共同塑造它的发展方向。当你在自己的技术栈上试用它时,我们很想听听你的想法:
- 并行的质量代理是否契合你的架构?这个工作流中哪些部分你希望在自己的仓库中保持可定制,哪些部分最终希望由平台为你运行(例如跨一组代理进行接入)?
- 环境质量代理将如何融入你的工程工作流?你团队中谁首先对洞察进行分诊,在哪个界面(CLI/编码代理、IDE 还是 Cloud Console),接下来会发生什么?
- 在代理质量工程中的这些难题中,我们应该先解决哪一个:从 50,000 个会话中选出值得阅读的 40 个、将静默失败与用户反馈进行差异对比、将失败聚类转化为可复现的沙箱测试用例、压缩长时程轨迹,还是其他什么?
致谢(按字母顺序): AQuA 由 Ákos Frohner、 Aleksandra Grzegorczyk、 Alessandro Grassi、 Andrzej Kiewicz、 Angelica Bilanenko、 Dima Melnyk、 Elia Secchi、 Iwo Naglik、 Lucas Matuszkowiak、 Ludwik Trammer、 Maciej Pawłowski、 Max Gasztych、 Pavel Sirotkin、 Saksham Singhal、 Xi Liu、 Yaroslav Polyakov 以及更广泛的 Gemini 平台团队构建。
了解更多: 环境质量代理仓库 · 从你的编码代理驱动代理质量飞轮 · 代理评估文档
上一页
下一页
来源:Google Developers Blog · developers.googleblog.com