CodexQA

行业与实践案例研究

外环先看洞察:用 AQuA 诊断生产里的旅行智能体

CodexQA 团队阅读约 16 分钟

Google 用 AQuA 扫 travel-concierge 的 32 个生产会话:42 条发现收成 6 个已核验问题。两处提示词修改后,绕过选座从 15 个会话降到 2 个,整段通过从 5/32 升到 13/32。

本文目录

外环先看洞察:一个随时在岗的质量智能体,诊断你的生产智能体

Dima Melnyk,产品经理,Cloud AI。Elia Secchi,解决方案专家。2026 年 10 月 8 日。

你的智能体返回 HTTP 200,停在延迟预算之内,没有一次工具调用报错。它仍然会把座位 3A 记成已确认,却从未问过选座子智能体 3A 是否空着;或者向资料里写明吃纯素的旅客推荐佛罗伦萨牛排馆。

前 80% 相对容易

他们接触的、已经把智能体跑在生产环境里的每个团队,都在回答同样三个问题:我的智能体现在怎么样?它的损失模式是什么,也就是那些反复出现的失败方式?我如何往上走:改一点东西,知道它帮上了忙,并且不让它滑回去?

把智能体做到前 80%,也就是处理你事先预料到的测试用例,今天可以相对直接、相对快。一份评测数据集、一个编程智能体,再加上一条紧的内环(跑、打分、修改、比较),通常几天就能在已知场景上达到任务成功。然后你上线,质量曲线变平或者变差。

图 1 的说明:智能体质量在上线前爬得很快,到了生产环境里就起伏。

剩下的 20% 更难,是因为上线之后地面在动。用户发现智能体能实际做什么之后,用法会变,线上流量不再像你最初的评测集。与此同时,底下的系统也在变:你升级模型、更新支架(harness,包在模型外面、负责路由和状态的程序),或改一个工具或技能时,一切仍在跑,健康检查仍是绿的(图 2),但对话质量和任务成功可能以标准发布流水线从不警告你的方式偏移。

图 2 的说明:每一项基础设施健康检查都是绿的。失败是静默的,发生在对话里面。

一次会话真的出错时,要弄清原因,就得把从外面看起来几乎一样的几层分开。例如:

  • 模型可能编造了一个参数,或丢掉了三轮之前的一条约束。
  • 编排支架可能路由到了错误的子智能体,或在轮次之间丢掉了状态。
  • 工具契约可能因为合法取值从未写进数据模式而拒绝了输入。
  • 指令或技能可能只是缺了一条谁都没想到要写下来的规则。

这些失败方式可以有不同的负责人和不同的修法。原始轨迹记下的是“接下来发生了什么挨着什么”,不是“什么造成了什么”。要把这些层分开,需要把失败轨迹和产生它们的那一版代码放在一起读。线上评测仪表盘告诉你分数动了;一旦你知道往哪看,编程智能体可以检查一条轨迹。但在生产体量下,没有人能把每段对话都用手读完。

图 3 的说明:AQuA 做生产环境的外环,并把找到的东西送回你的内环。

在他们 6 月的文章里,他们用 travel-concierge 把上线前的内环从头走到尾。这一篇讲外环。它以可组合的构件开源,你可以在自己的项目里跑、改到自己的技术栈上,并在他们探索团队如何在生产里做持续的智能体质量时帮他们定形。仓库:https://github.com/google/adk-recipes/tree/main/core/python/ambient-quality-agent

AQuA 是什么:一个 7×24 的质量智能体,在你合上笔记本时观看、聚类并诊断生产失败

AQuA(Ambient Quality Agent,环境质量智能体,放在旁边持续看生产轨迹的质检智能体)在你的 Google Cloud 项目里、在你的智能体旁边无人值守运行。它按时间表、在每次部署之后,或在你合上笔记本时按需,从 Cloud Trace、Cloud Logging 或 BigQuery 扫生产轨迹。原始会话记录、源码快照和 BigQuery 表留在你的项目边界内。AQuA 从不坐在请求路径上,也不写回你的智能体。

图 4 的说明:AQuA 在你的 Google Cloud 项目里跑在智能体旁边,在请求路径之外读取轨迹和部署时的源码快照。

每一次运行读一份近期会话的样本,并把它送进五段流水线。

图 5 的说明:五段扫描流水线把原始生产会话变成经过核验的洞察;根因诊断按需对着已部署的源码快照跑。

  1. 抽样。从遥测(Cloud Trace、Cloud Logging 或 BigQuery)里随机抽取最多 1,000 个近期会话(设上限是为了让运行费用可预期)。
  2. 复查。对照一份九点清单给每个会话打分。清单覆盖常见故障:流程、工具选择和参数、接地(回答是否扣住给定材料)、任务完成,以及其他项。复查时看得到智能体的指令、工具,以及可选的 goal.md。会话失败时,它写一条结构化的“实际 / 预期”发现。
  3. 聚类。把共享同一失败机制的会话发现分成候选问题簇。
  4. 核验。另一个模型对照最多三段完整记录检查每个候选簇,丢掉证据不支持的簇。
  5. 跟踪。把活下来的簇与 BigQuery 里未关闭的洞察(跨运行跟踪的、已核验的问题)匹配,标成 NEW、RECURRING,或在 14 天没再见到之后自动标成 RESOLVED。

可以把它看成初级质量工程师做第一遍:读对话、滤掉噪声,并为值班的人准备带证据会话的案卷。

要把第 2 段拨向你的领域,你写一份普通英语的开发者目标(goal.md),它会附加到每一次复查提示词上;确定的 Python 自定义指标(eval_config.yaml)和裁判一起跑,用来看通过率的趋势。默认情况下,AQuA 用单遍的 session_review 裁判:每个会话一次模型调用,对照那份清单评对话(让按计划的扫描保持省钱,并产出驱动聚类的“实际 / 预期”差异)。你也可以选用 Gemini 平台托管的轨迹自动评分器(task_success、tool_use_quality、trajectory_quality)。它们按指标单独跑评分器,例如你想用标准化的现成评分细则给会话打分,或让指标和离线评测对齐。

一条洞察值得查时,你从仪表盘的 Chat 或 agents-cli aqua run 触发根因分析。AQuA 把失败轨迹和部署时抓住的、不可变的源码快照放在一起读:缺陷在你的仓库里时,它引用 <路径>:<起始>-<结束>,并提议一个锚在快照行上的修改;故障在你的代码之外(上游依赖、交接,或取回的载荷)时,它把失败归到轨迹里的那一步,不提议代码差异。它从不自己应用修改,也不自己开拉取请求。

它坐在你已经有的环之间:离线评测对照已知测试用例给候选构建打分,线上评测看生产里的通过率趋势,编程智能体或专门的优化器改提示词和代码。AQuA 把原始生产流量变成经过诊断、锚在代码上的洞察,以及可以给内环当种子的、归档的失败记录。

在 travel-concierge 上走一遍多智能体扫描

下面走一遍 travel-concierge 上的运行(仓库 https://github.com/google/adk-recipes/tree/0521e23/python/agents/travel-concierge)。它把旅客分到子智能体: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 的运行器、BigQuery 数据集和 Cloud Run 仪表盘(放在身份感知代理后面)之外,agents-cli deploy 把 travel-concierge 的源码树写成一份不可变快照,存进 Cloud Storage,键是部署修订(Revision 1)。

在仪表盘的 Configuration 页,他们保存一份开发者目标(goal.md),把复查拨向高层的产品不变量,并压住文风噪声;同时仍要求每条发现引用一个具体轮次,在那一轮某个智能体或子智能体违反了自己的指令或工具状态。页面上的 goal.md 正文在文本抓取里是空的,这里不补写页面没有留下的文字。

如果还想要 eval_config.yaml 里的确定 Python 评分细则来看通过率趋势,可以用 agents-cli aqua metrics publish 把它和扫描一起发布。

2. 扫描并核验簇

一次 32 个会话的多智能体扫描(四段写好脚本的旅客行程,各重放,共 32 次;在 travel_concierge 及其子智能体上产生 1,583 个 OpenTelemetry 跨度)里,5 个会话干净通过,27 个会话产生 42 条结构化发现。每条发现按跨度限定到那个子智能体自己的指令和工具声明。下面是 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 智能体的提示词包含旅客资料(food_preference: vegan),但 place_agent 被包成一个隔离工具,它的提示词收不到这份资料。inspiration_agent 向 place_agent 要餐饮行程点子时没有在请求里传入 “vegan”,place_agent 就推荐了卡波纳拉和佛罗伦萨牛排(Bistecca alla Fiorentina)。
  • poi_agent 里提示词和工具集矛盾(5 个会话,poi_agent)。兴趣点子智能体的提示词要求来自 Google Maps Grounding Lite 的、经过核验的图像、地图和地点 ID 字段,但这个智能体被声明成空工具列表(tools=[]),于是它编造占位的 https://example.com/... 网址。

图 6 的说明:travel-concierge 扫描得到的已核验洞察,按受影响会话数排序。

3. 对照已部署的代码做诊断

在仪表盘里打开最上面的洞察,会看到这个簇的完整分解:匹配会话数(15 条轨迹)、核验摘要,以及 15 条链上的会话轨迹和触发它的那些发现。

图 7 的说明:仪表盘里的洞察详情,显示失败摘要和 15 条链上的会话轨迹。

从洞察卡片点 Investigate,会启动根因诊断智能体(Gemini 3.8 Flash),对着 Revision 1 的 33 个文件的源码快照。它把 15 条失败轨迹和仓库树对照之后,把缺陷追到 travel_concierge/sub_agents/planning/prompt.py 第 93 行:航班搜索指令只告诉 planning_agent 在向用户展示座位图、让用户挑选时才调用 flight_seat_selection_agent,漏掉了用户直接说出座位号的情况。它在对话里提议一个锚在行上的修改;同样把纯素资料交接的缺陷追到 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 技能)可以无界面地触发诊断,在分支上应用修改,把失败会话的用户输入抽成本地重放文件,并在开拉取请求之前核验修复。页面此处有一块命令文本,抓取结果是空的,这里不补写没有留下的命令。

应用两处各一行的提示词修改(planning/prompt.py:93 和 inspiration/prompt.py:23)并部署 Revision 2 之后,在同一份开发者目标下重放同样的 32 个会话,结果是:

  • 绕过选座检查下降 87%,从 15 个会话降到 2 个边界会话。
  • 纯素资料遗漏从 7 个会话降到 0。
  • 整段会话通过数增加一倍以上,从 5/32 到 13/32。
  • 没有动过的、5 个会话的 poi_agent 缺陷(tools=[])仍留在队列里跟踪。

你敢拿去行动的发现

一个诊断你的智能体的智能体,有时会错。用模型当裁判,这是内在的。他们设计 AQuA 时,核心工程要求是:未核验的假设看起来不能和已核验的发现一样。

  • 簇在对照完整记录核验之前只是主张。每个簇抽查最多三段完整记录,用来确认失败模式是真的,然后才进队列(在一个 87 条轨迹的内部基准上,核验器拒绝了 24 个候选簇里的 4 个)。簇的轨迹数给你一个粗的优先级,而不是核验每一个成员会话。
  • 一条洞察就是它的引用,不是自报的置信分。模式里没有 confidence 字段:每一次出现都链到 Cloud Trace 里的会话 ID,每一个根因都引用 <路径>:<起始>-<结束>,服务器对照那一修订的快照校验这些范围。指向不存在的文件或行的引用会被拒绝。
  • 跳过或失败的工作出现在运行记录上。被拒绝的簇、超过每轮 50 个簇核验上限的簇、评分细则错误,以及空的或没抓到的轨迹窗口,都明确记在这次运行上,而不算成干净会话。
  • 按设计如此的行为可以永久驳回。你在一条发现上点 Dismiss 之后,以后的扫描不会把它重新开成 NEW。

智能体质量工程里的难题,以及这会走向哪里

这个参考实现里的若干边界,是他们在生产里看到的实际问题上故意做的取舍:

  1. 大规模时选哪些会话来读。深评一条多轮轨迹,比查一个 HTTP 状态码贵得多,所以这个最小可用版本每次运行随机抽最多 1,000 个会话(ORDER BY RAND())。下一步直接的做法是加上便宜的结构预过滤(重试、延迟尖峰、轮次很多、用户点踩)。只按异常过滤,往往会捞来一百份同样的超时。更难的问题是抓住一次静默回归:它弄坏关键工作流的 1%,每个结构信号看起来都正常,而又不必对全部 50,000 个每日会话都跑深裁判。
  2. 通用清单,对照领域目标和领域专家校准。goal.md 和自定义指标把复查拨向领域规则,但让模型裁判和领域专家一致,仍是真正的工程工作。
  3. 有界核验和可预期的运行费用。每轮最多核验 50 个簇、每个簇最多 3 段记录,使轨迹变深时费用仍可预期。在一次 96 个会话的单智能体扫描上(大约每个会话 5 到 6 个跨度),复查、聚类和核验合计 0.70 美元(约每个会话 0.007 美元;Gemini 3.1 Pro 和 Gemini 3.7 Flash 合计输入 220,841、输出 48,736 个 token)。在上面这次 32 个会话的 travel-concierge 扫描上(travel_concierge 及其子智能体共 1,583 个跨度,大约每个会话 50 个跨度),复查和聚类(Gemini 3.1 Pro,输入 1,635,197、输出 128,740)加上 9 次簇核验(Gemini 3.7 Flash,输入 1,131,937、输出 36,976)合计 3.76 美元(约每个会话 0.12 美元),按 Gemini 平台标准价格。根因诊断(Gemini 3.8 Flash)只按需跑,每条被调查的洞察 0.33 到 2.47 美元,取决于它拉取多少完整轨迹。
  4. 长程轨迹和上下文压缩。整段记录对十轮对话够用,但在跑几百轮、工具输出达到兆字节的编码或研究智能体上会失效。那些需要语义上的轨迹压缩,把 500 轮的轨迹折成子任务里程碑,并隔离它在哪里脱轨。
  5. 从归档记录到可复现的测试用例。adk run --replay 对着本地代码重发录下的用户轮次。工具幂等或被伪造时这可行,但静态重放不重建外部环境状态(数据库行变了、接口超时,或第 3 轮依赖智能体在第 2 轮说了什么,重发静态轮次就会分叉)。

在平台层真正能泛化的是什么。在 Google 自己的一方智能体之间,低层原语(轨迹和反馈接入、核心评分器、数据集管理)可以干净地共用;外环工作流则常常按一个产品的工具、领域不变量、数据流水线和编排支架单独做。随着基础模型和智能体更会读轨迹、更会在代码里走动,单条打分和根因推理会自动变强。所以他们把 AQuA 的提示词看成模块化的配方,把洞察看成原始轨迹和源码快照上的索引,这样更强的模型或编程智能体始终可以把完整的示例记录直接拉进上下文。更强的模型和智能体单独给不了你的,是周围的平台。他们接着在想的方向包括:

  • 多信号接入和静默失败的差异。把轨迹扫描与线上评测分数、延迟或费用尖峰、领域专家校准评分,以及通过 Feedback 服务来的最终用户反应合在一起,就可以把用户明确抱怨的会话和一般流量比较,然后在用户从未点踩、只是安静放弃工作流的会话里找到同样的缺陷。
  • 从诊断过的洞察到沙箱里的反事实测试用例。相邻的一个领域里,CodeMender 扫描代码里的漏洞,并提出它已经用静态与动态分析、差异测试、模糊测试和 SMT 求解器验证过的补丁。它“先扫描再验证”的形状,直接对应 AQuA 的“先扫描再核验”。补救则不同:外部状态已经往前走之后,一段真实对话没有确定的裁判(没有会崩溃的输入,也没有失败的测试),修复可能在工具契约、编排交接或上游依赖里,而不在一条可以单独爬山的提示词里。把一个已核验的失败簇变成密封的、沙箱化的测试用例,带上伪造的工具状态和模拟用户,让编程智能体或优化器可以同时在代码、工具和提示词上爬,是记录重放之后自然的下一步。
  • 对一队智能体做零支架接入。今天的参考实现是 1 对 1,脚手架放在单个 ADK 智能体的仓库旁边。对在生产里跑几十个智能体的团队,他们在探索如何直接从已有的项目遥测,给一队智能体(1 对多)接上环境质量扫描,而不必每个智能体都部署边车。
  • 声明式和托管智能体。当被观察的智能体本身是声明式或托管的,而不是任意应用代码时,“源代码”收成系统指令、工具模式和技能。这把根因搜索空间收成一组有界的结构化产物,让平台自动接上轨迹捕获和修订快照,并把已核验的通过与失败轨迹变成有依据的学习信号——线上生产流量没有标准答案标签——供优化器或智能体自己的记忆与自学习环使用。

试一试,并帮他们定这会走向哪里

要在本地看仪表盘,用合成的一个月运行和洞察,不需要云项目、不需要凭据、不需要模型调用。页面此处有一块 shell 命令,抓取结果是空的,这里不补写。

要把 AQuA 接到你自己在 Google Cloud 上的智能体,用现成的 ADK 脚手架(全部遥测、源码快照和 BigQuery 表留在你的项目里,在你的服务账号下,放在 IAP 后面)。页面此处也有一块 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 公开,是为了和正在生产里跑智能体的团队一起做,并一起定它走向哪里。你在自己的技术栈上试的时候,他们想听:

  1. 一个并排的质量智能体适合你的架构吗?这条工作流里,哪些部分你想留在自己的仓库里可定制,哪些最终希望由平台替你跑(例如接到一队智能体上)?
  2. 一个环境质量智能体会怎样进你的工程工作流?团队里谁先分拣一条洞察,在什么界面(命令行或编程智能体、IDE,或 Cloud Console),接下来发生什么?
  3. 智能体质量工程里的哪些难题应该先解:从 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 平台团队做成。

出处:Dima Melnyk,Elia Secchi,The Outer Loop, Insights First: An Ambient Quality Agent That Diagnoses Your Production Agent,2026-10-08,https://developers.googleblog.com/the-outer-loop-insights-first-an-ambient-quality-agent-that-diagnoses-your-production-agent/,CC BY 4.0。

觉得有用,转给同事

微信扫码

用微信扫一扫,在手机上打开后即可转发。

用 RSS 订阅

提交勘误